std/ffi/libc/cstr
libc <string.h> functions taking an Axle string (marshalled as a
NUL-terminated i8*) rather than a raw ptr. Declared once here and
shared across the stdlib by use std::ffi::libc::cstr::…, so the@link(symbol = …) bindings live in a single place — the same patternfs.axle / mem.axle already follow.
These sit beside mem's ptr-typed libc_strlen / libc_memmove:
both forms bind the same libc symbol, which the compiler declares once
however many bindings a program imports. A module picks the form matching
its operands — the string-typed bindings here for pure-Axle text code,
the ptr-typed ones in mem for raw-buffer code.
Reference: https://en.cppreference.com/w/c/string/byte
Free functions
| Type | Method and description |
|---|---|
| strlen(s : string) : i64 Byte length of a string — an Axle string carries no interior NUL,so this is its exact byte count. This is the libc strlen (an O(N) NULscan); prefer strByteLen below, which reads the length header in O(1). |
| strByteLen(s : string) : i64 Byte length of a string in O(1) — reads the i64 length headerevery Axle string carries, not a NUL scan. Binds to the runtime ABI symbol axle_string_byte_len (the runtime owns the string layout, likeaxle_string_free); declared here in a low module so every text modulereaches it without a dependency cycle. Prefer this over strlen.Null-safe (returns 0 for a null pointer) — the property that costs an opaque call LLVM cannot see through the ABI boundary of. TheHashMap<string, V> probe's compare side (__stringEquals /__stringEqualsIgnoreCase / __stringCompare / __stringHashCode instd/text/text) instead reads the same header through the__string_byte_len compiler builtin — no FFI call, and textuallyidentical to the read hashStringSeeded (std/text/hashing) alreadyemits for the same key, so LLVM's CSE collapses the two per probed slot. That builtin skips the null guard, which is sound only where the operand is proven non-nullable string; everywhere else (an operandthat might be a raw/foreign pointer) stays on strByteLen. |
| strByteAt(s : string, i : i64) : i32 Bounds-checked byte read of s at byte-index i — -1 when i isnegative or at/beyond s's byte length. Binds to the runtime ABIsymbol axle_string_byte_at (the runtime owns the string layout, likeaxle_string_byte_len); backs String.byteAt, the primitive everyhand-rolled parser (CSV / JSON / base64) scans through. |
| strCharCountPrefix(s : string, byteEnd : i64) : i32 UTF-8 character count of the byte prefix [0, byteEnd) of s — everybyte that is not a continuation byte ( 0b10xxxxxx) starts a newcharacter; byteEnd is clamped to s's byte length. Binds to theruntime ABI symbol axle_string_char_count_prefix (the runtime ownsthe string layout, like axle_string_byte_len); backs String.length/ indexOf / substring's character-offset maths. |
| copyFromString(dst : ptr, src : string, n : i64) : ptr Bulk-copy n bytes of a string's payload to dst, which may notoverlap the source. The read side of materialising a string into ascratch buffer. The non-overlap is a property of the string representation, not a promise asked of the caller: a string's payload is either a staticliteral global or its own heap allocation. stringFromBytes — the onebridge from raw bytes to a string — performs one allocation and onecopy, so a string is never a view into a scratch buffer, and nodst a caller can name is reachable from src. Fabricating aborrowing string would take an unsafe pointer cast the surface doesnot offer. That is what earns memcpy over memmove: the overlap test is deadwork, and glibc's memcpy reaches its ERMS paths where memmove doesnot. |
Method detail
#strlen
Byte length of a string — an Axle string carries no interior NUL,
so this is its exact byte count. This is the libc strlen (an O(N) NUL
scan); prefer strByteLen below, which reads the length header in O(1).
#strByteLen
Byte length of a string in O(1) — reads the i64 length header
every Axle string carries, not a NUL scan. Binds to the runtime ABI
symbol axle_string_byte_len (the runtime owns the string layout, likeaxle_string_free); declared here in a low module so every text module
reaches it without a dependency cycle. Prefer this over strlen.
Null-safe (returns 0 for a null pointer) — the property that costs an
opaque call LLVM cannot see through the ABI boundary of. TheHashMap<string, V> probe's compare side (__stringEquals /__stringEqualsIgnoreCase / __stringCompare / __stringHashCode instd/text/text) instead reads the same header through the__string_byte_len compiler builtin — no FFI call, and textually
identical to the read hashStringSeeded (std/text/hashing) already
emits for the same key, so LLVM's CSE collapses the two per probed
slot. That builtin skips the null guard, which is sound only where the
operand is proven non-nullable string; everywhere else (an operand
that might be a raw/foreign pointer) stays on strByteLen.
s string whose byte length is read#strByteAt
Bounds-checked byte read of s at byte-index i — -1 when i is
negative or at/beyond s's byte length. Binds to the runtime ABI
symbol axle_string_byte_at (the runtime owns the string layout, likeaxle_string_byte_len); backs String.byteAt, the primitive every
hand-rolled parser (CSV / JSON / base64) scans through.
s string to readi byte offset into s, 0-based#strCharCountPrefix
UTF-8 character count of the byte prefix [0, byteEnd) of s — every
byte that is not a continuation byte (0b10xxxxxx) starts a new
character; byteEnd is clamped to s's byte length. Binds to the
runtime ABI symbol axle_string_char_count_prefix (the runtime owns
the string layout, like axle_string_byte_len); backs String.length
/ indexOf / substring's character-offset maths.
s source stringbyteEnd exclusive end byte offset of the prefix to count#copyFromString
Bulk-copy n bytes of a string's payload to dst, which may not
overlap the source. The read side of materialising a string into a
scratch buffer.
The non-overlap is a property of the string representation, not a
promise asked of the caller: a string's payload is either a static
literal global or its own heap allocation. stringFromBytes — the one
bridge from raw bytes to a string — performs one allocation and one
copy, so a string is never a view into a scratch buffer, and nodst a caller can name is reachable from src. Fabricating a
borrowing string would take an unsafe pointer cast the surface does
not offer.
That is what earns memcpy over memmove: the overlap test is dead
work, and glibc's memcpy reaches its ERMS paths where memmove does
not.