From c5e1e0544a32916cf5bb54a00c58b4027b992299 Mon Sep 17 00:00:00 2001 From: Yogthos Date: Mon, 22 Jun 2026 13:11:08 -0400 Subject: [PATCH] jolt.ffi: read-array/write-array for binary-faithful buffer I/O MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit read-bytes/write-bytes go through UTF-8 (with a latin1 fallback), which mangles arbitrary binary — gzip payloads, TLS records, any non-text body. An HTTP client moving bytes between jolt byte-arrays and foreign socket/zlib/OpenSSL buffers needs byte-exact transfer. read-array returns a fresh byte-array of n bytes from foreign memory; write-array copies a byte-array's bytes into a pointer. Test covers a round-trip preserving high bytes (200, 255). --- host/chez/ffi.ss | 17 +++++++++++++++++ test/chez/ffi-binding-test.ss | 13 +++++++++++++ 2 files changed, 30 insertions(+) diff --git a/host/chez/ffi.ss b/host/chez/ffi.ss index c3c7ceb..2c1f98c 100644 --- a/host/chez/ffi.ss +++ b/host/chez/ffi.ss @@ -80,6 +80,23 @@ (def-var! "jolt.ffi" "read-bytes" ffi-read-bytes) (def-var! "jolt.ffi" "write-bytes" ffi-write-bytes) +;; --- byte-array buffer I/O (binary-faithful) -------------------------------- +;; Move raw bytes between a jolt byte-array (jolt-array kind 'byte) and foreign +;; memory, byte-exact (no UTF-8 / latin1 decode) — for socket recv/send and the +;; zlib / OpenSSL buffers an HTTP client passes through. read-array returns a +;; fresh byte-array of n bytes; write-array copies a byte-array's bytes into ptr +;; and returns the count. +(define (ffi-read-array ptr n) + (let* ((n (jnum->exact n)) (p (jnum->exact ptr)) (v (make-vector n 0))) + (do ((i 0 (+ i 1))) ((= i n)) (vector-set! v i (foreign-ref 'unsigned-8 p i))) + (make-jolt-array v 'byte))) +(define (ffi-write-array ptr arr) + (let* ((v (jolt-array-vec arr)) (n (vector-length v)) (p (jnum->exact ptr))) + (do ((i 0 (+ i 1))) ((= i n)) (foreign-set! 'unsigned-8 p i (bitwise-and (exact (vector-ref v i)) #xff))) + n)) +(def-var! "jolt.ffi" "read-array" ffi-read-array) +(def-var! "jolt.ffi" "write-array" ffi-write-array) + ;; --- string / bytevector marshaling ------------------------------------------ ;; A C string result already comes back as a jolt string (the `string` foreign ;; type). For a `void*` that points at a NUL-terminated C string, read it here. diff --git a/test/chez/ffi-binding-test.ss b/test/chez/ffi-binding-test.ss index ec4cb9d..db8f290 100644 --- a/test/chez/ffi-binding-test.ss +++ b/test/chez/ffi-binding-test.ss @@ -39,6 +39,19 @@ (let [v (jolt.ffi/read p :int)] (jolt.ffi/free p) v))")))) (ok "sizeof :pointer is a word" (let ((n (jnum->exact (ev "(jolt.ffi/sizeof :pointer)")))) (or (= n 8) (= n 4)))) +;; byte-array buffer I/O: write a byte-array into foreign memory and read it back +;; byte-exact (high bytes preserved, no UTF-8 mangling). +(ok "byte-array roundtrip (binary-faithful)" + (jolt-truthy? + (ev "(let [src (byte-array [0 65 200 255 10]) + p (jolt.ffi/alloc 5)] + (jolt.ffi/write-array p src) + (let [back (jolt.ffi/read-array p 5)] + (jolt.ffi/free p) + (and (= 5 (alength back)) + (= 0 (aget back 0)) (= 65 (aget back 1)) + (= 200 (aget back 2)) (= 255 (aget back 3)) (= 10 (aget back 4)))))"))) + ;; a :blocking foreign call is collect-safe: a thread parked in it must not pin ;; the stop-the-world collector. (collect) here would throw "cannot collect when ;; multiple threads are active" if usleep weren't emitted __collect_safe.