Skip to main content

seq_runtime/
bytes_ops.rs

1//! Byte construction primitives.
2//!
3//! Conversions from numeric values to fixed-width big-endian byte
4//! strings, for binary protocol encoders written in Seq itself
5//! (OSC, DNS, NTP, MessagePack, Protobuf, etc).
6//!
7//! Output Strings are byte-clean — `SeqString` carries arbitrary
8//! bytes since the byte-cleanliness landing, so a Seq program can
9//! pack and `concat` these into a complete wire payload and hand it
10//! straight to `udp.send-to` / `tcp.write` / `file.spit`.
11//!
12//! These functions are exported with C ABI for LLVM codegen.
13
14use crate::seqstring::global_bytes;
15use crate::stack::{Stack, pop, push};
16use crate::value::Value;
17
18/// Convert Int to 4-byte big-endian i32 String: ( Int -- String )
19///
20/// The value is narrowed to `i32` via `as i32`, which preserves the
21/// low 32 bits. Out-of-range Ints wrap rather than fault — the same
22/// semantics as Rust's `as` cast. OSC encoders pass values they
23/// already know fit in i32; callers needing range checks should
24/// validate before calling.
25///
26/// # Safety
27/// Stack must have an Int value on top.
28#[unsafe(no_mangle)]
29pub unsafe extern "C" fn patch_seq_int_to_bytes_i32_be(stack: Stack) -> Stack {
30    assert!(!stack.is_null(), "int.to-bytes-i32-be: stack is empty");
31    let (stack, val) = unsafe { pop(stack) };
32
33    match val {
34        Value::Int(i) => {
35            let bytes = (i as i32).to_be_bytes().to_vec();
36            unsafe { push(stack, Value::String(global_bytes(bytes))) }
37        }
38        _ => panic!("int.to-bytes-i32-be: expected Int on stack, got {:?}", val),
39    }
40}
41
42/// Convert Float to 4-byte big-endian f32 String: ( Float -- String )
43///
44/// Precision-converts the f64 to f32 via `as f32`, then emits the
45/// IEEE-754 binary32 big-endian bytes. NaN/Infinity round-trip
46/// through the standard IEEE-754 encoding.
47///
48/// # Safety
49/// Stack must have a Float value on top.
50#[unsafe(no_mangle)]
51pub unsafe extern "C" fn patch_seq_float_to_bytes_f32_be(stack: Stack) -> Stack {
52    assert!(!stack.is_null(), "float.to-bytes-f32-be: stack is empty");
53    let (stack, val) = unsafe { pop(stack) };
54
55    match val {
56        Value::Float(f) => {
57            let bytes = (f as f32).to_be_bytes().to_vec();
58            unsafe { push(stack, Value::String(global_bytes(bytes))) }
59        }
60        _ => panic!(
61            "float.to-bytes-f32-be: expected Float on stack, got {:?}",
62            val
63        ),
64    }
65}
66
67pub use patch_seq_float_to_bytes_f32_be as float_to_bytes_f32_be;
68pub use patch_seq_int_to_bytes_i32_be as int_to_bytes_i32_be;
69
70#[cfg(test)]
71mod tests;