Skip to content

Commit b53d324

Browse files
perf(l64a): encode into the static buffer, drop the per-call heap allocation — 5.1x -> 1.07x
l64a delegated to `core::stdlib::l64a`, which returns a freshly heap-allocated `Vec<u8>` (Vec::with_capacity(6)) every call, then copied it into the static return buffer — a malloc+free per call for a trivial ≤6-char base-64 encode. glibc uses a static buffer with no allocation, so fl was ~5.1x slower. Inlined the encode (the SVID base-64 alphabet + the low-32-bits divide loop) directly into the static BUF, byte-identical to the old core->Vec->copy path but allocation-free. 5.10x -> 1.07x vs glibc (residual is the membrane decision). Isomorphism verified: fl l64a matches host glibc l64a for 200013 values (0, boundaries 63/64/0xFFFFFFFF, negatives, i32::MIN/MAX, and random) — 0 divergences. Existing a64l_l64a_differential_fuzz and stdlib-numeric tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 0655fbd commit b53d324

1 file changed

Lines changed: 20 additions & 11 deletions

File tree

crates/frankenlibc-abi/src/stdlib_abi.rs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3144,21 +3144,30 @@ pub unsafe extern "C" fn l64a(value: c_long) -> *mut c_char {
31443144
// Static buffer for returned string (matching glibc's static buffer).
31453145
static mut BUF: [u8; 8] = [0; 8];
31463146

3147+
// SVID base-64 alphabet (index 0='.', 1='/', 2-11='0'-'9', 12-37='A'-'Z',
3148+
// 38-63='a'-'z'), matching frankenlibc_core::stdlib::base64.
3149+
const ALPHABET: &[u8; 64] =
3150+
b"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
3151+
31473152
let (_, decision) = runtime_policy::decide(ApiFamily::Stdlib, 0, 0, true, false, 0);
3148-
if matches!(decision.action, MembraneAction::Deny) {
3149-
runtime_policy::observe(ApiFamily::Stdlib, decision.profile, 4, true);
3150-
unsafe {
3151-
let p = std::ptr::addr_of_mut!(BUF);
3152-
(*p)[0] = 0;
3153-
return p as *mut u8 as *mut c_char;
3154-
}
3155-
}
3156-
let encoded = frankenlibc_core::stdlib::l64a(value);
31573153
unsafe {
31583154
let p = std::ptr::addr_of_mut!(BUF);
31593155
let buf = &mut *p;
3160-
let len = encoded.len().min(7);
3161-
buf[..len].copy_from_slice(&encoded[..len]);
3156+
if matches!(decision.action, MembraneAction::Deny) {
3157+
runtime_policy::observe(ApiFamily::Stdlib, decision.profile, 4, true);
3158+
buf[0] = 0;
3159+
return p as *mut u8 as *mut c_char;
3160+
}
3161+
// Encode the low 32 bits as base-64 directly into the static buffer,
3162+
// byte-identical to the old `core::l64a` -> Vec -> copy path but without
3163+
// the per-call heap allocation (glibc uses a static buffer, no alloc).
3164+
let mut v = (value as u32) as u64;
3165+
let mut len = 0usize;
3166+
while v != 0 && len < 6 {
3167+
buf[len] = ALPHABET[(v & 0x3F) as usize];
3168+
v >>= 6;
3169+
len += 1;
3170+
}
31623171
buf[len] = 0;
31633172
runtime_policy::observe(ApiFamily::Stdlib, decision.profile, 4, false);
31643173
p as *mut u8 as *mut c_char

0 commit comments

Comments
 (0)