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;