The INSERTIONSORT crash, all three rulings (FIX.org 2026-09-20): - (bytes s) allocates a writable copy through the allocator surface — context or (bytes s a), StorageExhausted with retry, a registry note in dev builds (flan_bytes_dup, lowered like vec-new). (bytes-view s) is the old zero-cost reinterpret, renamed, read-only by convention; every in-repo reader swept over to it. (string b) unchanged. - String constants were already read-only on both backends at -O0; now pinned — bytes-copy.flan rows on LLVM/-O0/--x86, and dies_segv rows asserting the write-through-view trap on both backends. - A dev build installs a SIGSEGV/SIGBUS handler by the same dev-only constructor slot that arms the registry: one line naming the address and the innermost frame, then the trap-hook park — stopped, not dead, the daemon serving. No agent: message and re-raise. Release builds untouched. Pinned by trap_park over dev-segv.flan.
64 lines
2.7 KiB
Plaintext
64 lines
2.7 KiB
Plaintext
;;;; (string b) — a [u8] seen as a string.
|
|
;;;;
|
|
;;;; The conversion emits no instructions: String and Slice _ are both %slice,
|
|
;;;; 16 bytes at align 8. So what is worth testing is not arithmetic, it is the
|
|
;;;; four places a zero-instruction reinterpretation could still be wrong about
|
|
;;;; the *length* or about who owns the bytes.
|
|
;;;;
|
|
;;;; Run at -O2 and at -O0. The pair matters here for the same reason it
|
|
;;;; matters for the literal-write sharp edge: a conversion that accidentally
|
|
;;;; produced undefined behaviour would be a SIGSEGV at one level and a silent
|
|
;;;; deletion at the other, and agreeing at one level alone proves nothing.
|
|
|
|
;; puts and not a Flan printer: the point of this declaration is the *shim*,
|
|
;; which takes ptr+len and NUL-terminates a copy. A shim that instead trusted
|
|
;; the bytes to already be terminated would print the rest of the buffer for
|
|
;; every sub-view below, and nothing inside Flan would notice.
|
|
;;
|
|
;; Its return is C's "some nonnegative value", not a number worth printing, so
|
|
;; it is only shown as a sign — and it is printed through Flan's own writer,
|
|
;; which shares stdout's buffer with puts, so the interleaving is stable.
|
|
(declare-c c-puts [s string] i32 "puts")
|
|
|
|
(defn shows [s string] ()
|
|
(print "[")
|
|
(print s)
|
|
(print "] ")
|
|
(print (len (bytes-view s)))
|
|
(println ""))
|
|
|
|
(defn main [] i32
|
|
;; A number. The gap this closes: i64->bytes answers a [u8], every text
|
|
;; parameter wants a string, and until now nothing joined them.
|
|
(shows (string (i64->bytes 42)))
|
|
(shows (string (i64->bytes -7)))
|
|
(shows (string (i64->bytes 0)))
|
|
|
|
;; An empty slice. Length 0, and no read of the pointer.
|
|
(shows (string (slice (bytes-view "abc") 1 1)))
|
|
|
|
;; A sub-view, whose length is not the underlying storage's. The bytes after
|
|
;; index 5 are still there and must not appear.
|
|
(let [s (bytes-view "hello world")]
|
|
(shows (string (slice s 0 5)))
|
|
(shows (string (slice s 6 11)))
|
|
(shows (string (slice s 11 11))))
|
|
|
|
;; Round trip: (bytes-view (string b)) is b, and both directions are the identity.
|
|
(let [b (i64->bytes 1234567)]
|
|
(print (len (bytes-view (string b))))
|
|
(println ""))
|
|
|
|
;; Across the declare-c boundary. The first is a sub-view — five bytes out of
|
|
;; eleven, the sixth of which is a space and not a NUL — so a shim that did
|
|
;; not copy would print "hello world" here.
|
|
(let [s (bytes-view "hello world")]
|
|
(print (if (>= (c-puts (string (slice s 0 5))) 0) "ok" "no"))
|
|
(println ""))
|
|
(print (if (>= (c-puts (string (i64->bytes 12345))) 0) "ok" "no"))
|
|
(println "")
|
|
;; And an empty one: the shim's copy of a zero-length slice is "".
|
|
(print (if (>= (c-puts (string (slice (bytes-view "abc") 1 1))) 0) "ok" "no"))
|
|
(println "")
|
|
0)
|