fuse: encode readdir dirents directly into one sized buffer (P2)
ober
ed956e655fde8e5133019b7324a0dafffac7c1ce
--- a/lib/jerboa-fuse/codec.sls +++ b/lib/jerboa-fuse/codec.sls @@ -285,24 +285,38 @@ (bytevector-copy! name-bv 0 bv 24 namelen) bv)) (def (encode-dirents unique dirents max-size) - (let loop ([ds dirents] [chunks '()] [total-payload 0]) + (let measure ([ds dirents] [items '()] [total-payload 0]) (if (null? ds) - (finalize-dirents unique (reverse chunks) total-payload) - (let* ([encoded (encode-dirent (car ds))] - [elen (bytevector-length encoded)] - [new-total (+ total-payload elen)]) + (build-dirents unique (reverse items) total-payload) + (let* ([d (car ds)] + [name-bv (string->utf8 (fuse-dirent-name d))] + [reclen (fuse-rec-align + (+ FUSE-DIRENT-HEADER-SIZE + (bytevector-length name-bv)))] + [new-total (+ total-payload reclen)]) (if (> new-total max-size) - (finalize-dirents unique (reverse chunks) total-payload) - (loop (cdr ds) (cons encoded chunks) new-total)))))) - (def (finalize-dirents unique chunks total-payload) + (build-dirents unique (reverse items) total-payload) + (measure + (cdr ds) + (cons (vector d name-bv reclen) items) + new-total)))))) + (def (build-dirents unique items total-payload) (let* ([total (+ FUSE-OUT-HEADER-SIZE total-payload)] [bv (make-bytevector total 0)]) (u32-set! bv 0 total) (s32-set! bv 4 0) (u64-set! bv 8 unique) - (let loop ([cs chunks] [pos FUSE-OUT-HEADER-SIZE]) - (unless (null? cs) - (let ([chunk (car cs)]) - (bytevector-copy! chunk 0 bv pos (bytevector-length chunk)) - (loop (cdr cs) (+ pos (bytevector-length chunk)))))) + (let loop ([is items] [pos FUSE-OUT-HEADER-SIZE]) + (unless (null? is) + (let* ([item (car is)] + [d (vector-ref item 0)] + [name-bv (vector-ref item 1)] + [reclen (vector-ref item 2)] + [namelen (bytevector-length name-bv)]) + (u64-set! bv pos (fuse-dirent-ino d)) + (u64-set! bv (+ pos 8) (fuse-dirent-off d)) + (u32-set! bv (+ pos 16) namelen) + (u32-set! bv (+ pos 20) (fuse-dirent-type d)) + (bytevector-copy! name-bv 0 bv (+ pos 24) namelen) + (loop (cdr is) (+ pos reclen))))) bv))) --- a/src/jerboa-fuse/codec.ss +++ b/src/jerboa-fuse/codec.ss @@ -444,29 +444,41 @@ ;; Pack a list of fuse-dirent records into a single response bytevector ;; that fits within max-size bytes. Returns complete response (header + dirents). + ;; Measures the dirents that fit, then encodes them directly into one sized + ;; output buffer (no per-dirent temporary bytevector, no final copy). (def (encode-dirents unique dirents max-size) - (let loop ([ds dirents] [chunks '()] [total-payload 0]) + (let measure ([ds dirents] [items '()] [total-payload 0]) (if (null? ds) - (finalize-dirents unique (reverse chunks) total-payload) - (let* ([encoded (encode-dirent (car ds))] - [elen (bytevector-length encoded)] - [new-total (+ total-payload elen)]) + (build-dirents unique (reverse items) total-payload) + (let* ([d (car ds)] + [name-bv (string->utf8 (fuse-dirent-name d))] + [reclen (fuse-rec-align (+ FUSE-DIRENT-HEADER-SIZE (bytevector-length name-bv)))] + [new-total (+ total-payload reclen)]) (if (> new-total max-size) - (finalize-dirents unique (reverse chunks) total-payload) - (loop (cdr ds) (cons encoded chunks) new-total)))))) + (build-dirents unique (reverse items) total-payload) + (measure (cdr ds) (cons (vector d name-bv reclen) items) new-total)))))) - ;; Assemble header + dirent chunks into a single bytevector. - (def (finalize-dirents unique chunks total-payload) + ;; Encode the measured dirents straight into one zeroed response buffer, + ;; matching encode-dirent's wire layout (ino@0 off@8 namelen@16 type@24… name). + (def (build-dirents unique items total-payload) (let* ([total (+ FUSE-OUT-HEADER-SIZE total-payload)] [bv (make-bytevector total 0)]) (u32-set! bv 0 total) (s32-set! bv 4 0) (u64-set! bv 8 unique) - (let loop ([cs chunks] [pos FUSE-OUT-HEADER-SIZE]) - (unless (null? cs) - (let ([chunk (car cs)]) - (bytevector-copy! chunk 0 bv pos (bytevector-length chunk)) - (loop (cdr cs) (+ pos (bytevector-length chunk)))))) + (let loop ([is items] [pos FUSE-OUT-HEADER-SIZE]) + (unless (null? is) + (let* ([item (car is)] + [d (vector-ref item 0)] + [name-bv (vector-ref item 1)] + [reclen (vector-ref item 2)] + [namelen (bytevector-length name-bv)]) + (u64-set! bv pos (fuse-dirent-ino d)) + (u64-set! bv (+ pos 8) (fuse-dirent-off d)) + (u32-set! bv (+ pos 16) namelen) + (u32-set! bv (+ pos 20) (fuse-dirent-type d)) + (bytevector-copy! name-bv 0 bv (+ pos 24) namelen) + (loop (cdr is) (+ pos reclen))))) bv))