From 24e25f966383052b272a8c84c7706a5c2266a2a1 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Thu, 21 Aug 2025 16:44:52 +0300 Subject: [PATCH] scm: mfind: use cons instead of append, eval lazy For the list building, using cons is much faster. So traverse the results _backwards_, and then cons gives us the right order. Don't eval when creating the list, but do so lazily (when requested in the message, in mu-scm.scm) --- scm/mu-scm-message.cc | 6 +++--- scm/mu-scm-store.cc | 20 ++++++++---------- scm/mu-scm.scm | 48 +++++++++++++++++++------------------------ 3 files changed, 32 insertions(+), 42 deletions(-) diff --git a/scm/mu-scm-message.cc b/scm/mu-scm-message.cc index 47725a3e..7c54bed3 100644 --- a/scm/mu-scm-message.cc +++ b/scm/mu-scm-message.cc @@ -42,7 +42,6 @@ using MessageMap = std::unordered_map; static MessageMap message_map; } - static const Message& to_message(SCM scm, const char *func, int pos) { @@ -138,8 +137,9 @@ subr_cc_message_plist(SCM message_scm) try { constexpr auto func{"cc-message-plist"}; const auto& message{to_message(message_scm, func, 1)}; - const auto plist{"'" + message.sexp().to_string()}; - return scm_c_eval_string(plist.c_str()); + // return the serialized (mu4e) message + const auto plist{message.sexp().to_string()}; + return to_scm(plist); } catch (const ScmError& err) { err.throw_scm(); diff --git a/scm/mu-scm-store.cc b/scm/mu-scm-store.cc index 4bf22e27..9564dce2 100644 --- a/scm/mu-scm-store.cc +++ b/scm/mu-scm-store.cc @@ -157,23 +157,19 @@ subr_cc_store_mfind(SCM store_scm, SCM query_scm, SCM related_scm, SCM skip_dups (related ? QueryFlags::IncludeRelated: QueryFlags::None ) | (reverse ? QueryFlags::Descending : QueryFlags::None); - SCM msgs{SCM_EOL}; std::lock_guard lock{store.lock()}; - const auto qres = store.run_query(query, sort_field_id, qflags, maxnum); - + const auto qres{store.run_query(query, sort_field_id, qflags, maxnum)}; if (!qres) throw ScmError{ScmError::Id::WrongArg, func, 2, query_scm, ""}; - for (const auto& mi: *qres) { - if (auto plist{mi.document()->get_data()}; plist.empty()) - continue; - else { - SCM scm_plist{scm_c_eval_string(("'" + plist).c_str())}; - msgs = scm_append_x(scm_list_2( msgs, scm_list_1(scm_plist))); - } - } - + SCM msgs{SCM_EOL}; + // iterate in reverse order, so the message get consed + // into the list in the right order. + for (auto it{qres->end()}; it-- != qres->begin();) + if (auto plist{it.document()->get_data()}; !plist.empty()) + msgs = scm_cons(to_scm(plist), msgs); return msgs; + } catch (const ScmError& err) { err.throw_scm(); } diff --git a/scm/mu-scm.scm b/scm/mu-scm.scm index b7b53a6f..679162a4 100644 --- a/scm/mu-scm.scm +++ b/scm/mu-scm.scm @@ -239,8 +239,8 @@ Otherwise, trying to overwrite an existing file raises an error." ;; Message (define-class () (cc-message #:init-value #f #:init-keyword #:cc-message) + (serialized #:init-value #f #:init-keyword #:serialized) (parts #:init-value #f #:init-keyword #:parts) - (plist #:init-value #f #:init-keyword #:plist) (alist #:init-value #f)) (set-documentation! @@ -253,33 +253,33 @@ these are the slots: - cc-message: this is a foreign-object representing the mu message object, and needs to be passed to some 'cc-' methods. - parts: a list of objects -- plist: this is an Emacs-style property list which is cached - for each message in the store; this was originally added - for use in mu4e, but we re-use it here. -- alist: an association list; this is just more 'scheme-like' - version of the plist, it is created on-demand (message->alist). +- data: this is a string containing an Emacs-style property list + which is cached for each message in the store; this was + originally added for use in mu4e, but we re-use it here. +- alist: an association list; an alist with properties, as + created from the data (converted from the plist) A message that came from a search such as 'mfind' initially only -has the plist, but when a message is loaded from file, either +has the data, but when a message is loaded from file, either through make-message or by calling a function that needs a -full message, such as header or body, the cc-message is initialized. - -Only having a plist is cheaper.") +full message, such as header or body, the cc-message is initialized.") (define (make-message path) "Create a from file at PATH." (make #:cc-message (cc-message-make path))) -(define-method (plist (message )) - "Get the PLIST for this MESSAGE." - (when (not (slot-ref message 'plist)) - (slot-set! message 'plist (cc-message-plist (cc-message message)))) - (slot-ref message 'plist)) - (define-method (message->alist (message )) "Get an association-list (alist) representation for MESSAGE." (when (not (slot-ref message 'alist)) - (slot-set! message 'alist (plist->alist (plist message)))) + (let* ((serialized + (or (slot-ref message 'serialized) + (cc-message-plist (slot-ref message 'cc-message)))) + ;; parse the serialized message (the mu4e plist) + ;; and convert into alist. We need to _quote_ the + ;; the serialized string before we can parse it. + (alist (plist->alist (eval-string (string-append "'" serialized))))) + (slot-set! message 'alist alist) + (slot-set! message 'serialized #f))) ;; no longer needed (slot-ref message 'alist)) (define-method (cc-message (message )) @@ -290,13 +290,6 @@ path of the message." (slot-set! message 'cc-message (cc-message-make (path message)))) (slot-ref message 'cc-message)) -(define-method (sexp (message )) - "Get the s-expression (plist) for this MESSAGE. - -This is an internal data-structure, originally for use with mu4e, but useful -here as well. However, the precise details are not part of mu-scm API." - (plist message)) - ;; Accessors for the fields (define-method (subject (message )) @@ -540,10 +533,11 @@ The query is mandatory, the other (keyword) arguments are optional. #:sort-field? field to sort by, a symbol. Default: date #:reverse? sort in descending order (z-a) #:max-results max. number of matches. Default: false (unlimited))." - (map (lambda (plist) - (make #:plist plist)) + (map (lambda (data) + (make #:serialized data)) (cc-store-mfind (cc-store store) query - related? skip-dups? sort-field reverse? max-results))) + related? skip-dups? sort-field + reverse? max-results))) (define* (mcount #:key