From 02610fe820927b0f68a55a2e2544bd2c362e2bf5 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Sun, 25 Jan 2015 13:06:59 +1900 Subject: [PATCH] Re: [PATCH 08/11] emacs: Support caching in notmuch-get-bodypart-{binary, text} --- 2b/da2f1280a92343a255a5b4a0e213ea116d984d | 212 ++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 2b/da2f1280a92343a255a5b4a0e213ea116d984d diff --git a/2b/da2f1280a92343a255a5b4a0e213ea116d984d b/2b/da2f1280a92343a255a5b4a0e213ea116d984d new file mode 100644 index 000000000..7bdb4a8ec --- /dev/null +++ b/2b/da2f1280a92343a255a5b4a0e213ea116d984d @@ -0,0 +1,212 @@ +Return-Path: +X-Original-To: notmuch@notmuchmail.org +Delivered-To: notmuch@notmuchmail.org +Received: from localhost (localhost [127.0.0.1]) + by olra.theworths.org (Postfix) with ESMTP id 9BB9C431FCB + for ; Sat, 24 Jan 2015 10:07:03 -0800 (PST) +X-Virus-Scanned: Debian amavisd-new at olra.theworths.org +X-Spam-Flag: NO +X-Spam-Score: 0.138 +X-Spam-Level: +X-Spam-Status: No, score=0.138 tagged_above=-999 required=5 + tests=[DNS_FROM_AHBL_RHSBL=2.438, RCVD_IN_DNSWL_MED=-2.3] + autolearn=disabled +Received: from olra.theworths.org ([127.0.0.1]) + by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) + with ESMTP id cPyXdJMYb6El for ; + Sat, 24 Jan 2015 10:07:00 -0800 (PST) +Received: from outgoing.csail.mit.edu (outgoing.csail.mit.edu [128.30.2.149]) + by olra.theworths.org (Postfix) with ESMTP id 568A2431FAE + for ; Sat, 24 Jan 2015 10:07:00 -0800 (PST) +Received: from [104.131.20.129] (helo=awakeningjr) + by outgoing.csail.mit.edu with esmtpsa (TLS1.0:RSA_AES_128_CBC_SHA1:16) + (Exim 4.72) (envelope-from ) + id 1YF56p-0006Kr-OO; Sat, 24 Jan 2015 13:06:59 -0500 +Received: from amthrax by awakeningjr with local (Exim 4.84) + (envelope-from ) + id 1YF56p-0003m9-3E; Sat, 24 Jan 2015 13:06:59 -0500 +From: Austin Clements +To: Mark Walters +Subject: Re: [PATCH 08/11] emacs: Support caching in + notmuch-get-bodypart-{binary, text} +In-Reply-To: <87d2g5uczr.fsf@qmul.ac.uk> +References: <1398105468-14317-1-git-send-email-amdragon@mit.edu> + <1398105468-14317-9-git-send-email-amdragon@mit.edu> + <87sip3t37f.fsf@qmul.ac.uk> <20140424181216.GO25817@mit.edu> + <87d2g5uczr.fsf@qmul.ac.uk> +User-Agent: Notmuch/0.18.1+86~gef5e66a (http://notmuchmail.org) Emacs/24.4.1 + (x86_64-pc-linux-gnu) +Date: Sat, 24 Jan 2015 13:06:59 -0500 +Message-ID: <87vbjwt5mk.fsf@csail.mit.edu> +MIME-Version: 1.0 +Content-Type: text/plain +Cc: notmuch@notmuchmail.org +X-BeenThere: notmuch@notmuchmail.org +X-Mailman-Version: 2.1.13 +Precedence: list +List-Id: "Use and development of the notmuch mail system." + +List-Unsubscribe: , + +List-Archive: +List-Post: +List-Help: +List-Subscribe: , + +X-List-Received-Date: Sat, 24 Jan 2015 18:07:03 -0000 + +On Fri, 25 Apr 2014, Mark Walters wrote: +> On Thu, 24 Apr 2014, Austin Clements wrote: +>> Quoth Mark Walters on Apr 24 at 11:46 am: +>>> +>>> On Mon, 21 Apr 2014, Austin Clements wrote: +>>> > (The actual code change here is small, but requires re-indenting +>>> > existing code.) +>>> > --- +>>> > emacs/notmuch-lib.el | 52 ++++++++++++++++++++++++++++++---------------------- +>>> > 1 file changed, 30 insertions(+), 22 deletions(-) +>>> > +>>> > diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el +>>> > index fc67b14..fee8512 100644 +>>> > --- a/emacs/notmuch-lib.el +>>> > +++ b/emacs/notmuch-lib.el +>>> > @@ -503,33 +503,39 @@ (defun notmuch-parts-filter-by-type (parts type) +>>> > (lambda (part) (notmuch-match-content-type (plist-get part :content-type) type)) +>>> > parts)) +>>> > +>>> > -(defun notmuch-get-bodypart-binary (msg part process-crypto) +>>> > +(defun notmuch-get-bodypart-binary (msg part process-crypto &optional cache) +>>> > "Return the unprocessed content of PART in MSG as a unibyte string. +>>> > +>>> > This returns the \"raw\" content of the given part after content +>>> > transfer decoding, but with no further processing (see the +>>> > discussion of --format=raw in man notmuch-show). In particular, +>>> > this does no charset conversion." +>>> > - (let ((args `("show" "--format=raw" +>>> > - ,(format "--part=%d" (plist-get part :id)) +>>> > - ,@(when process-crypto '("--decrypt")) +>>> > - ,(notmuch-id-to-query (plist-get msg :id))))) +>>> > - (with-temp-buffer +>>> > - ;; Emacs internally uses a UTF-8-like multibyte string +>>> > - ;; representation by default (regardless of the coding system, +>>> > - ;; which only affects how it goes from outside data to this +>>> > - ;; internal representation). This *almost* never matters. +>>> > - ;; Annoyingly, it does matter if we use this data in an image +>>> > - ;; descriptor, since Emacs will use its internal data buffer +>>> > - ;; directly and this multibyte representation corrupts binary +>>> > - ;; image formats. Since the caller is asking for binary data, a +>>> > - ;; unibyte string is a more appropriate representation anyway. +>>> > - (set-buffer-multibyte nil) +>>> > - (let ((coding-system-for-read 'no-conversion)) +>>> > - (apply #'call-process notmuch-command nil '(t nil) nil args) +>>> > - (buffer-string))))) +>>> > - +>>> > -(defun notmuch-get-bodypart-text (msg part process-crypto) +>>> > + (let ((data (plist-get part :binary-content))) +>>> > + (when (not data) +>>> > + (let ((args `("show" "--format=raw" +>>> > + ,(format "--part=%d" (plist-get part :id)) +>>> > + ,@(when process-crypto '("--decrypt")) +>>> > + ,(notmuch-id-to-query (plist-get msg :id))))) +>>> > + (with-temp-buffer +>>> > + ;; Emacs internally uses a UTF-8-like multibyte string +>>> > + ;; representation by default (regardless of the coding +>>> > + ;; system, which only affects how it goes from outside data +>>> > + ;; to this internal representation). This *almost* never +>>> > + ;; matters. Annoyingly, it does matter if we use this data +>>> > + ;; in an image descriptor, since Emacs will use its internal +>>> > + ;; data buffer directly and this multibyte representation +>>> > + ;; corrupts binary image formats. Since the caller is +>>> > + ;; asking for binary data, a unibyte string is a more +>>> > + ;; appropriate representation anyway. +>>> > + (set-buffer-multibyte nil) +>>> > + (let ((coding-system-for-read 'no-conversion)) +>>> > + (apply #'call-process notmuch-command nil '(t nil) nil args) +>>> > + (setq data (buffer-string))))) +>>> > + (when cache +>>> > + (plist-put part :binary-content data))) +>>> > + data)) +>>> +>>> I am a little puzzled by this but that could be lack of familiarity with +>>> elisp. As far as I can see plist-put will sometimes modify the original +>>> plist and sometimes return a new plist. If the latter happens then I +>>> think it works out as if we hadn't cached anything as the part passed to +>>> the function is unmodified. That might not matter in this case (though I +>>> find the lack of determinism disturbing). +>>> +>>> Or is something else going on? +>> +>> No, your familiarity with Elisp serves you well. I'm completely +>> cheating here. According to the specification of plist-put, it's +>> allowed to return a new list but in reality this only happens when the +>> original plist is nil. We lean on this already all over the +>> notmuch-emacs code, but maybe that doesn't excuse me adding one more +>> cheat. +>> +>> I could add a comment here explaining what's going on, I could +>> manually do the list insertion in a way that's guaranteed to mutate it +>> in place, or I could add a nil :binary-content property when parts are +>> created (since plist-put is guaranteed to mutate existing keys in +>> place). +> +> I think a comment is fine. + +Done. + +> (Incidentally what is the best way of telling if emacs has changed an +> object or returned a new one for other commands? Something like (setq +> oldobject object) (setq object (operation-on object)) (if (eq object +> oldobject) ... )) + +If `eq' returns t, it definitely returned the original object, though it +may or may not have modified it. If `eq' returns nil, there's no +guarantee that it *didn't* modify the object you passed in (maybe it +modified it and tacked a new cons on the beginning). In short, there's +really no way of knowing. + +> Also, I think the function should have a comment about the lifetime of +> the caching. I think in some cases the addition of :binary-content could +> occur on load and thus the plist with binary content added would get +> saved in the buffer when the msg plist was saved as a +> text-property. However, maybe in other cases this gets called after the +> initial insertion and thus the cached value is just used during this +> operation on msg? Sorry that is a little incoherent as I haven't checked +> all callers. + +I believe the caching will always last as long as the buffer. We only +have one instance of each message plist. This plist goes in to the text +properties and caching works by mutating this same plist. + +However, I updated the docstring to clarify that the cache is stored in +MSG. These functions don't ultimately control the lifetime of MSG, but +this gives the caller enough information to know the lifetime of the +cache if it knows the lifetime of MSG. + +> Best wishes +> +> Mark +> +> +> +>>> Best wishes +>>> +>>> Mark +>>> +>>> +>>> +>>> > + +>>> > +(defun notmuch-get-bodypart-text (msg part process-crypto &optional cache) +>>> > "Return the text content of PART in MSG. +>>> > +>>> > This returns the content of the given part as a multibyte Lisp +>>> > @@ -546,7 +552,9 @@ (defun notmuch-get-bodypart-text (msg part process-crypto) +>>> > (npart (apply #'notmuch-call-notmuch-sexp args))) +>>> > (setq content (plist-get npart :content)) +>>> > (when (not content) +>>> > - (error "Internal error: No :content from %S" args)))) +>>> > + (error "Internal error: No :content from %S" args))) +>>> > + (when cache +>>> > + (plist-put part :content content))) +>>> > content)) +>>> > +>>> > ;; Workaround: The call to `mm-display-part' below triggers a bug in -- 2.26.2