1#![allow(clippy::missing_safety_doc)]
6
7use std::alloc::Layout;
8use std::ffi::{c_char, c_void};
9use std::sync::Arc;
10
11use crate::buffer_pool::{self, BufferAllocation, BufferPool};
12use crate::metadata::MetadataValue;
13use crate::packet::{
14 self, BufferData, BufferView, Builtin, ExternalBuffer, Packet, PacketBody, Payload,
15};
16use crate::runtime::{self, last_error};
17use crate::status::{code, Error};
18use crate::timestamp::Timestamp;
19
20use super::*;
21
22#[repr(C)]
25#[derive(Clone, Copy)]
26pub struct LMFlowPacket {
27 pub payload: *mut c_void,
28 pub type_id: u64,
29 pub timestamp: i64,
30 pub owner: *mut c_void,
31 pub drop_fn: Option<unsafe extern "C" fn(*mut c_void)>,
32}
33
34impl Default for LMFlowPacket {
35 fn default() -> Self {
36 Self {
37 payload: std::ptr::null_mut(),
38 type_id: 0,
39 timestamp: Timestamp::unset().0,
40 owner: std::ptr::null_mut(),
41 drop_fn: None,
42 }
43 }
44}
45
46pub fn borrow_packet(p: &Packet) -> LMFlowPacket {
48 match p.arc_ref() {
49 Some(arc) => LMFlowPacket {
50 payload: arc.payload.data_ptr(),
51 type_id: arc.payload.type_id(),
52 timestamp: p.timestamp().0,
53 owner: Arc::as_ptr(arc) as *mut c_void,
54 drop_fn: None,
55 },
56 None => LMFlowPacket {
57 timestamp: p.timestamp().0,
58 ..Default::default()
59 },
60 }
61}
62
63pub fn own_packet(p: Packet) -> LMFlowPacket {
65 let ts = p.timestamp().0;
66 match p.into_arc() {
67 Some(arc) => {
68 let payload = arc.payload.data_ptr();
69 let type_id = arc.payload.type_id();
70 LMFlowPacket {
71 payload,
72 type_id,
73 timestamp: ts,
74 owner: Arc::into_raw(arc) as *mut c_void,
75 drop_fn: None,
76 }
77 }
78 None => LMFlowPacket {
79 timestamp: ts,
80 ..Default::default()
81 },
82 }
83}
84
85pub unsafe fn take_packet(fp: LMFlowPacket) -> Packet {
90 let ts = Timestamp(fp.timestamp);
91 if !fp.owner.is_null() {
92 let arc = Arc::from_raw(fp.owner as *const PacketBody);
93 Packet::from_arc(arc, ts)
94 } else if !fp.payload.is_null() {
95 Packet::from_foreign(fp.payload, fp.type_id, fp.drop_fn).at(ts)
96 } else {
97 Packet::empty().at(ts)
98 }
99}
100
101#[no_mangle]
102pub extern "C" fn lmflow_abi_version() -> u32 {
103 ABI_VERSION
104}
105
106#[no_mangle]
107pub extern "C" fn lmflow_last_error() -> *const c_char {
108 last_error::get()
109}
110
111#[no_mangle]
112pub extern "C" fn lmflow_set_log_callback(
113 cb: Option<unsafe extern "C" fn(*mut c_void, i32, *const c_char)>,
114 user: *mut c_void,
115) {
116 guard_val((), || runtime::set_log_callback(cb, user));
117}
118
119#[no_mangle]
120pub unsafe extern "C" fn lmflow_packet_drop(pkt: *mut LMFlowPacket) {
121 guard_val((), || {
122 if pkt.is_null() {
123 return;
124 }
125 let p = &mut *pkt;
126 if !p.owner.is_null() {
127 drop(Arc::from_raw(p.owner as *const PacketBody));
128 } else if !p.payload.is_null() {
129 if let Some(f) = p.drop_fn {
130 f(p.payload);
131 }
132 }
133 *p = LMFlowPacket::default();
134 });
135}
136
137#[no_mangle]
138pub unsafe extern "C" fn lmflow_packet_clone(pkt: *const LMFlowPacket) -> LMFlowPacket {
139 guard_val(LMFlowPacket::default(), || {
140 if pkt.is_null() {
141 return LMFlowPacket::default();
142 }
143 let src = &*pkt;
144 if src.owner.is_null() {
145 last_error::set(
147 "lmflow_packet_clone can only be used on engine-held packets (owner non-null)",
148 );
149 return LMFlowPacket::default();
150 }
151 let arc = Arc::from_raw(src.owner as *const PacketBody);
152 let cloned = arc.clone();
153 let _ = Arc::into_raw(arc);
155 LMFlowPacket {
156 payload: cloned.payload.data_ptr(),
157 type_id: cloned.payload.type_id(),
158 timestamp: src.timestamp,
159 owner: Arc::into_raw(cloned) as *mut c_void,
160 drop_fn: None,
161 }
162 })
163}
164
165unsafe fn packet_body<'a>(pkt: *const LMFlowPacket) -> Option<&'a PacketBody> {
166 let packet = pkt.as_ref()?;
167 (!packet.owner.is_null()).then(|| &*(packet.owner as *const PacketBody))
168}
169
170unsafe fn set_metadata(pkt: *mut LMFlowPacket, key: *const c_char, value: MetadataValue) -> i32 {
171 let Some(packet) = pkt.as_mut() else {
172 return fail(Error::InvalidArg("packet must not be null".into()));
173 };
174 let Some(key) = cstr(key).filter(|key| !key.is_empty()) else {
175 return fail(Error::InvalidArg("metadata key must not be empty".into()));
176 };
177 if packet.owner.is_null() {
178 return fail(Error::InvalidArg(
179 "metadata can only be set on an owned engine packet".into(),
180 ));
181 }
182 let old = std::mem::take(packet);
183 let mut owned = take_packet(old);
184 owned.set_metadata(key, value);
185 *packet = own_packet(owned);
186 code::OK
187}
188
189#[no_mangle]
190pub unsafe extern "C" fn lmflow_packet_set_metadata_i64(
191 pkt: *mut LMFlowPacket,
192 key: *const c_char,
193 value: i64,
194) -> i32 {
195 guard(|| set_metadata(pkt, key, MetadataValue::I64(value)))
196}
197
198#[no_mangle]
199pub unsafe extern "C" fn lmflow_packet_set_metadata_f64(
200 pkt: *mut LMFlowPacket,
201 key: *const c_char,
202 value: f64,
203) -> i32 {
204 guard(|| set_metadata(pkt, key, MetadataValue::F64(value)))
205}
206
207#[no_mangle]
208pub unsafe extern "C" fn lmflow_packet_set_metadata_bool(
209 pkt: *mut LMFlowPacket,
210 key: *const c_char,
211 value: bool,
212) -> i32 {
213 guard(|| set_metadata(pkt, key, MetadataValue::Bool(value)))
214}
215
216#[no_mangle]
217pub unsafe extern "C" fn lmflow_packet_set_metadata_str(
218 pkt: *mut LMFlowPacket,
219 key: *const c_char,
220 value: *const c_char,
221) -> i32 {
222 guard(|| {
223 let Some(value) = cstr(value) else {
224 return fail(Error::InvalidArg(
225 "metadata string must be valid UTF-8".into(),
226 ));
227 };
228 set_metadata(pkt, key, MetadataValue::String(value.to_string()))
229 })
230}
231
232macro_rules! metadata_scalar {
233 ($name:ident, $variant:ident, $ty:ty) => {
234 #[no_mangle]
235 pub unsafe extern "C" fn $name(
236 pkt: *const LMFlowPacket,
237 key: *const c_char,
238 out: *mut $ty,
239 ) -> bool {
240 guard_val(false, || {
241 let Some(key) = cstr(key) else { return false };
242 let Some(body) = packet_body(pkt) else {
243 return false;
244 };
245 let Some(MetadataValue::$variant(value)) = body.metadata.get(key) else {
246 return false;
247 };
248 if !out.is_null() {
249 *out = *value;
250 }
251 true
252 })
253 }
254 };
255}
256
257metadata_scalar!(lmflow_packet_metadata_i64, I64, i64);
258metadata_scalar!(lmflow_packet_metadata_f64, F64, f64);
259metadata_scalar!(lmflow_packet_metadata_bool, Bool, bool);
260
261#[no_mangle]
262pub unsafe extern "C" fn lmflow_packet_metadata_str(
263 pkt: *const LMFlowPacket,
264 key: *const c_char,
265 out: *mut *const c_char,
266) -> bool {
267 guard_val(false, || {
268 let Some(key) = cstr(key) else { return false };
269 let Some(body) = packet_body(pkt) else {
270 return false;
271 };
272 let Some(MetadataValue::String(value)) = body.metadata.get(key) else {
273 return false;
274 };
275 thread_local! {
276 static METADATA_STRING: std::cell::RefCell<std::ffi::CString> =
277 std::cell::RefCell::new(std::ffi::CString::default());
278 }
279 METADATA_STRING.with(|buffer| {
280 *buffer.borrow_mut() = std::ffi::CString::new(value.as_str()).unwrap_or_default();
281 if !out.is_null() {
282 *out = buffer.borrow().as_ptr();
283 }
284 });
285 true
286 })
287}
288
289#[no_mangle]
290pub unsafe extern "C" fn lmflow_packet_has_metadata(
291 pkt: *const LMFlowPacket,
292 key: *const c_char,
293) -> bool {
294 guard_val(false, || {
295 let Some(key) = cstr(key) else { return false };
296 packet_body(pkt).is_some_and(|body| body.metadata.contains_key(key))
297 })
298}
299
300#[no_mangle]
301pub unsafe extern "C" fn lmflow_packet_remove_metadata(
302 pkt: *mut LMFlowPacket,
303 key: *const c_char,
304) -> bool {
305 guard_val(false, || {
306 let Some(packet) = pkt.as_mut() else {
307 return false;
308 };
309 let Some(key) = cstr(key) else { return false };
310 if packet.owner.is_null() {
311 last_error::set("metadata can only be removed from an owned engine packet");
312 return false;
313 }
314 let old = std::mem::take(packet);
315 let mut owned = take_packet(old);
316 let removed = owned.remove_metadata(key).is_some();
317 *packet = own_packet(owned);
318 removed
319 })
320}
321
322#[no_mangle]
323pub unsafe extern "C" fn lmflow_packet_metadata_count(pkt: *const LMFlowPacket) -> usize {
324 guard_val(0, || packet_body(pkt).map_or(0, |body| body.metadata.len()))
325}
326
327#[no_mangle]
328pub unsafe extern "C" fn lmflow_packet_metadata_key_at(
329 pkt: *const LMFlowPacket,
330 index: usize,
331) -> *const c_char {
332 guard_val(std::ptr::null(), || {
333 let Some(body) = packet_body(pkt) else {
334 return std::ptr::null();
335 };
336 let Some(key) = body.metadata.keys().nth(index) else {
337 return std::ptr::null();
338 };
339 thread_local! {
340 static METADATA_KEY: std::cell::RefCell<std::ffi::CString> =
341 std::cell::RefCell::new(std::ffi::CString::default());
342 }
343 METADATA_KEY.with(|buffer| {
344 *buffer.borrow_mut() = std::ffi::CString::new(key.as_str()).unwrap_or_default();
345 buffer.borrow().as_ptr()
346 })
347 })
348}
349
350#[no_mangle]
351pub unsafe extern "C" fn lmflow_packet_debug_string(pkt: *const LMFlowPacket) -> *const c_char {
352 guard_val(c"".as_ptr(), || {
353 thread_local! {
354 static BUF: std::cell::RefCell<std::ffi::CString> =
355 std::cell::RefCell::new(std::ffi::CString::default());
356 }
357 let s = if pkt.is_null() {
358 "Packet{null}".to_string()
359 } else {
360 let p = &*pkt;
361 let ty = if p.payload.is_null() {
362 "Empty".to_string()
363 } else {
364 format!("type#{}", p.type_id)
365 };
366 format!("Packet{{type={ty}, ts={}}}", Timestamp(p.timestamp))
367 };
368 BUF.with(|b| {
369 *b.borrow_mut() = std::ffi::CString::new(s).unwrap_or_default();
370 b.borrow().as_ptr()
371 })
372 })
373}
374
375#[no_mangle]
376pub unsafe extern "C" fn lmflow_register_type_descriptor(
377 type_id: u64,
378 name: *const c_char,
379 size: usize,
380 align: usize,
381) -> i32 {
382 guard(|| {
383 let Some(name) = cstr(name) else {
384 return fail(Error::InvalidArg("type name is empty or not UTF-8".into()));
385 };
386 match packet::register_type_descriptor(type_id, name, size, align) {
387 Ok(()) => code::OK,
388 Err(error) => fail(error),
389 }
390 })
391}
392
393#[no_mangle]
394pub unsafe extern "C" fn lmflow_type_id(name: *const c_char) -> u64 {
395 guard_val(0, || {
396 let Some(name) = cstr(name) else {
397 last_error::set("type name is empty or not UTF-8");
398 return 0;
399 };
400 if name.is_empty() {
401 last_error::set("type name must not be empty");
402 return 0;
403 }
404 packet::fnv1a_type_id(name)
405 })
406}
407
408#[no_mangle]
409pub extern "C" fn lmflow_type_size(type_id: u64) -> usize {
410 guard_val(0, || {
411 packet::type_descriptor(type_id).map_or(0, |descriptor| descriptor.size)
412 })
413}
414
415#[no_mangle]
416pub extern "C" fn lmflow_type_align(type_id: u64) -> usize {
417 guard_val(0, || {
418 packet::type_descriptor(type_id).map_or(0, |descriptor| descriptor.align)
419 })
420}
421
422#[no_mangle]
423pub extern "C" fn lmflow_type_name(type_id: u64) -> *const c_char {
424 guard_val(c"".as_ptr(), || {
425 static A: std::sync::LazyLock<runtime::CStrArena> =
426 std::sync::LazyLock::new(runtime::CStrArena::default);
427 A.intern(&packet::type_name(type_id))
428 })
429}
430
431fn own_builtin(b: Builtin, ts: i64) -> LMFlowPacket {
434 own_packet(Packet::from_builtin(b).at(Timestamp(ts)))
435}
436
437#[no_mangle]
438pub unsafe extern "C" fn lmflow_packet_from_bytes(
439 data: *const c_void,
440 len: usize,
441 ts: i64,
442) -> LMFlowPacket {
443 guard_val(LMFlowPacket::default(), || {
444 let v = if data.is_null() || len == 0 {
445 Vec::new()
446 } else {
447 std::slice::from_raw_parts(data as *const u8, len).to_vec()
448 };
449 own_builtin(Builtin::Bytes(v), ts)
450 })
451}
452
453#[no_mangle]
454pub extern "C" fn lmflow_packet_from_i64(value: i64, ts: i64) -> LMFlowPacket {
455 guard_val(LMFlowPacket::default(), || {
456 own_builtin(Builtin::I64(value), ts)
457 })
458}
459
460#[no_mangle]
461pub extern "C" fn lmflow_packet_from_f64(value: f64, ts: i64) -> LMFlowPacket {
462 guard_val(LMFlowPacket::default(), || {
463 own_builtin(Builtin::F64(value), ts)
464 })
465}
466
467#[no_mangle]
468pub extern "C" fn lmflow_packet_from_bool(value: bool, ts: i64) -> LMFlowPacket {
469 guard_val(LMFlowPacket::default(), || {
470 own_builtin(Builtin::Bool(value), ts)
471 })
472}
473
474#[no_mangle]
475pub unsafe extern "C" fn lmflow_packet_from_str(utf8: *const c_char, ts: i64) -> LMFlowPacket {
476 guard_val(LMFlowPacket::default(), || {
477 let s = cstr(utf8).unwrap_or("");
478 own_builtin(
479 Builtin::Str(std::ffi::CString::new(s).unwrap_or_default()),
480 ts,
481 )
482 })
483}
484
485unsafe fn peek_builtin<'a>(pkt: *const LMFlowPacket) -> Option<&'a Builtin> {
487 if pkt.is_null() {
488 return None;
489 }
490 let p = &*pkt;
491 if p.owner.is_null() {
492 return None;
493 }
494 match (*(p.owner as *const PacketBody)).payload.as_ref() {
495 Payload::Builtin(b) => Some(b),
496 _ => None,
497 }
498}
499
500#[no_mangle]
501pub unsafe extern "C" fn lmflow_packet_as_bytes(
502 pkt: *const LMFlowPacket,
503 data: *mut *const c_void,
504 len: *mut usize,
505) -> bool {
506 guard_val(false, || match peek_builtin(pkt) {
507 Some(Builtin::Bytes(v)) => {
508 if !data.is_null() {
509 *data = v.as_ptr() as *const c_void;
510 }
511 if !len.is_null() {
512 *len = v.len();
513 }
514 true
515 }
516 _ => false,
517 })
518}
519
520macro_rules! as_scalar {
521 ($name:ident, $variant:ident, $ty:ty) => {
522 #[no_mangle]
523 pub unsafe extern "C" fn $name(pkt: *const LMFlowPacket, out: *mut $ty) -> bool {
524 guard_val(false, || match peek_builtin(pkt) {
525 Some(Builtin::$variant(v)) => {
526 if !out.is_null() {
527 *out = *v;
528 }
529 true
530 }
531 _ => false,
532 })
533 }
534 };
535}
536as_scalar!(lmflow_packet_as_i64, I64, i64);
537as_scalar!(lmflow_packet_as_f64, F64, f64);
538as_scalar!(lmflow_packet_as_bool, Bool, bool);
539
540#[no_mangle]
541pub unsafe extern "C" fn lmflow_packet_as_str(
542 pkt: *const LMFlowPacket,
543 out: *mut *const c_char,
544) -> bool {
545 guard_val(false, || match peek_builtin(pkt) {
546 Some(Builtin::Str(s)) => {
547 if !out.is_null() {
548 *out = s.as_ptr();
549 }
550 true
551 }
552 _ => false,
553 })
554}
555
556#[repr(C)]
559#[derive(Clone, Copy)]
560pub struct LMFlowBuffer {
561 pub data: *mut c_void,
562 pub shape: [i64; packet::MAX_DIMS],
563 pub strides: [i64; packet::MAX_DIMS],
564 pub ndim: i32,
565 pub dtype: i32,
566 pub flags: u32,
567 pub device: i32,
568 pub reserved: [i64; 2],
569}
570
571impl Default for LMFlowBuffer {
572 fn default() -> Self {
573 Self {
574 data: std::ptr::null_mut(),
575 shape: [0; packet::MAX_DIMS],
576 strides: [0; packet::MAX_DIMS],
577 ndim: 0,
578 dtype: 0,
579 flags: 0,
580 device: 0,
581 reserved: [0; 2],
582 }
583 }
584}
585
586pub const BUF_FLAG_READONLY: u32 = 1;
587pub const DEVICE_CPU: i32 = 0;
588const BUF_FLAG_MASK: u32 = BUF_FLAG_READONLY;
589
590struct ValidatedBuffer<'a> {
591 dims: &'a [i64],
592 strides: &'a [i64],
593 element_size: usize,
594 element_count: usize,
595 byte_size: u64,
596}
597
598fn validate_buffer_descriptor(src: &LMFlowBuffer) -> crate::status::Result<ValidatedBuffer<'_>> {
599 let ndim = usize::try_from(src.ndim).map_err(|_| {
600 Error::InvalidArg(format!(
601 "buffer ndim must be in 1..={}, got {}",
602 packet::MAX_DIMS,
603 src.ndim
604 ))
605 })?;
606 if !(1..=packet::MAX_DIMS).contains(&ndim) {
607 return Err(Error::InvalidArg(format!(
608 "buffer ndim must be in 1..={}, got {}",
609 packet::MAX_DIMS,
610 src.ndim
611 )));
612 }
613 if src.device != DEVICE_CPU {
614 return Err(Error::InvalidArg(format!(
615 "buffer device {} is not supported; only LMFLOW_DEVICE_CPU (0) is available",
616 src.device
617 )));
618 }
619 if src.flags & !BUF_FLAG_MASK != 0 {
620 return Err(Error::InvalidArg(format!(
621 "buffer flags contain unknown bits: 0x{:x}",
622 src.flags & !BUF_FLAG_MASK
623 )));
624 }
625 if src.reserved != [0; 2] {
626 return Err(Error::InvalidArg(
627 "buffer reserved fields must be zero".into(),
628 ));
629 }
630 if src.shape[ndim..].iter().any(|&value| value != 0) {
631 return Err(Error::InvalidArg(
632 "buffer shape entries after ndim must be zero".into(),
633 ));
634 }
635 if src.strides[ndim..].iter().any(|&value| value != 0) {
636 return Err(Error::InvalidArg(
637 "buffer stride entries after ndim must be zero".into(),
638 ));
639 }
640
641 let dims = &src.shape[..ndim];
642 let strides = &src.strides[..ndim];
643 let element_size = packet::dtype_size(src.dtype);
644 if element_size == 0 {
645 return Err(Error::InvalidArg(format!(
646 "buffer dtype {} is unknown",
647 src.dtype
648 )));
649 }
650
651 let mut element_count = 1usize;
652 let mut min_offset = 0i128;
653 let mut max_offset = 0i128;
654 for (axis, (&dim, &stride)) in dims.iter().zip(strides).enumerate() {
655 if dim < 0 {
656 return Err(Error::InvalidArg(format!(
657 "buffer shape[{axis}] must not be negative, got {dim}"
658 )));
659 }
660 let dim = usize::try_from(dim)
661 .map_err(|_| Error::InvalidArg(format!("buffer shape[{axis}] is too large")))?;
662 element_count = element_count
663 .checked_mul(dim)
664 .ok_or_else(|| Error::InvalidArg("buffer element count overflow".into()))?;
665 let extent = (dim.saturating_sub(1) as i128)
666 .checked_mul(i128::from(stride))
667 .ok_or_else(|| Error::InvalidArg("buffer stride extent overflow".into()))?;
668 if extent < 0 {
669 min_offset = min_offset
670 .checked_add(extent)
671 .ok_or_else(|| Error::InvalidArg("buffer minimum offset overflow".into()))?;
672 } else {
673 max_offset = max_offset
674 .checked_add(extent)
675 .ok_or_else(|| Error::InvalidArg("buffer maximum offset overflow".into()))?;
676 }
677 }
678 let last_byte = max_offset
679 .checked_add(element_size.saturating_sub(1) as i128)
680 .ok_or_else(|| Error::InvalidArg("buffer byte extent overflow".into()))?;
681 if min_offset < isize::MIN as i128 || last_byte > isize::MAX as i128 {
682 return Err(Error::InvalidArg(format!(
683 "buffer address range [{min_offset}, {last_byte}] exceeds platform pointer offsets"
684 )));
685 }
686 if element_count > 0 && src.data.is_null() {
687 return Err(Error::InvalidArg(
688 "buffer data must be non-null for a non-empty shape".into(),
689 ));
690 }
691 if element_count > 0 {
692 let base = src.data as usize as i128;
693 let first_address = base
694 .checked_add(min_offset)
695 .ok_or_else(|| Error::InvalidArg("buffer start address overflow".into()))?;
696 let last_address = base
697 .checked_add(last_byte)
698 .ok_or_else(|| Error::InvalidArg("buffer end address overflow".into()))?;
699 if first_address < 0 || last_address > usize::MAX as i128 {
700 return Err(Error::InvalidArg(format!(
701 "buffer address range [{first_address}, {last_address}] is outside the platform address space"
702 )));
703 }
704 }
705 let byte_size = element_count
706 .checked_mul(element_size)
707 .ok_or_else(|| Error::InvalidArg("buffer logical byte count overflow".into()))?
708 .try_into()
709 .map_err(|_| Error::InvalidArg("buffer logical byte count exceeds u64".into()))?;
710
711 Ok(ValidatedBuffer {
712 dims,
713 strides,
714 element_size,
715 element_count,
716 byte_size,
717 })
718}
719
720fn fill_buffer_view(out: *mut LMFlowBuffer, view: BufferView) {
721 if out.is_null() {
722 return;
723 }
724 let value = LMFlowBuffer {
725 data: view.data,
726 shape: view.shape,
727 strides: view.strides,
728 ndim: view.ndim,
729 dtype: view.dtype,
730 flags: if view.readonly { BUF_FLAG_READONLY } else { 0 },
731 device: DEVICE_CPU,
732 reserved: [0; 2],
733 };
734 unsafe { std::ptr::write(out, value) };
735}
736
737struct BufferAllocationOwner {
738 allocation: Option<BufferAllocation>,
739 pool: Option<Arc<BufferPool>>,
740}
741
742unsafe extern "C" fn release_uninitialized_allocation(user_data: *mut c_void) {
743 if user_data.is_null() {
744 return;
745 }
746 let mut owner = Box::from_raw(user_data as *mut BufferAllocationOwner);
747 if let Some(allocation) = owner.allocation.take() {
748 if let Some(pool) = owner.pool.take() {
749 pool.release(allocation);
750 } else if !allocation.data.is_null() {
751 unsafe { std::alloc::dealloc(allocation.data, allocation.layout) };
752 }
753 }
754}
755
756fn allocate_buffer(layout: Layout) -> Option<(BufferAllocation, Option<Arc<BufferPool>>)> {
757 let pool = buffer_pool::active();
758 let allocation = pool.as_ref().map_or_else(
759 || BufferAllocation {
760 data: unsafe { std::alloc::alloc(layout) },
761 layout,
762 },
763 |pool| pool.take(layout),
764 );
765 (!allocation.data.is_null()).then_some((allocation, pool))
766}
767
768#[no_mangle]
769pub extern "C" fn lmflow_dtype_size(dt: i32) -> usize {
770 packet::dtype_size(dt)
771}
772
773#[no_mangle]
774pub unsafe extern "C" fn lmflow_packet_new_buffer(
775 ndim: i32,
776 shape: *const i64,
777 dtype: i32,
778 ts: i64,
779 out: *mut LMFlowBuffer,
780) -> LMFlowPacket {
781 guard_val(LMFlowPacket::default(), || {
782 if !(1..=packet::MAX_DIMS as i32).contains(&ndim) || shape.is_null() {
783 last_error::set(&format!(
784 "lmflow_packet_new_buffer: ndim must be in 1..={}, and shape must be non-null",
785 packet::MAX_DIMS
786 ));
787 return LMFlowPacket::default();
788 }
789 let dims = std::slice::from_raw_parts(shape, ndim as usize);
790 let (shape_array, strides, total) = match BufferData::checked_layout(dims, dtype) {
791 Ok(layout) => layout,
792 Err(error) => {
793 last_error::set(&error.to_string());
794 return LMFlowPacket::default();
795 }
796 };
797 let layout = match Layout::from_size_align(total.max(1), 1) {
798 Ok(layout) => layout,
799 Err(_) => {
800 last_error::set("lmflow_packet_new_buffer: invalid allocation layout");
801 return LMFlowPacket::default();
802 }
803 };
804 let Some((allocation, pool)) = allocate_buffer(layout) else {
805 last_error::set(&format!(
806 "lmflow_packet_new_buffer: cannot allocate {total} buffer bytes"
807 ));
808 return LMFlowPacket::default();
809 };
810 unsafe { std::ptr::write_bytes(allocation.data, 0, total) };
811 let owner = Box::new(BufferAllocationOwner {
812 allocation: Some(allocation),
813 pool,
814 });
815 let source = LMFlowBuffer {
816 data: owner.allocation.as_ref().unwrap().data as *mut c_void,
817 shape: shape_array,
818 strides,
819 ndim,
820 dtype,
821 flags: 0,
822 device: DEVICE_CPU,
823 reserved: [0; 2],
824 };
825 let user_data = Box::into_raw(owner) as *mut c_void;
826 let packet = lmflow_packet_adopt_buffer(
827 &source,
828 ts,
829 Some(release_uninitialized_allocation),
830 user_data,
831 );
832 if packet.owner.is_null() {
833 release_uninitialized_allocation(user_data);
834 } else if !out.is_null() {
835 std::ptr::write(out, source);
836 }
837 packet
838 })
839}
840
841#[no_mangle]
842pub unsafe extern "C" fn lmflow_packet_new_buffer_uninit(
843 ndim: i32,
844 shape: *const i64,
845 dtype: i32,
846 ts: i64,
847 out: *mut LMFlowBuffer,
848) -> LMFlowPacket {
849 guard_val(LMFlowPacket::default(), || {
850 if !(1..=packet::MAX_DIMS as i32).contains(&ndim) || shape.is_null() {
851 last_error::set(&format!(
852 "lmflow_packet_new_buffer_uninit: ndim must be in 1..={}, and shape must be non-null",
853 packet::MAX_DIMS
854 ));
855 return LMFlowPacket::default();
856 }
857 let dims = std::slice::from_raw_parts(shape, ndim as usize);
858 let (shape_array, strides, total) = match BufferData::checked_layout(dims, dtype) {
859 Ok(layout) => layout,
860 Err(error) => {
861 last_error::set(&error.to_string());
862 return LMFlowPacket::default();
863 }
864 };
865 let layout = match Layout::from_size_align(total.max(1), 1) {
866 Ok(layout) => layout,
867 Err(_) => {
868 last_error::set("lmflow_packet_new_buffer_uninit: invalid allocation layout");
869 return LMFlowPacket::default();
870 }
871 };
872 let Some((allocation, pool)) = allocate_buffer(layout) else {
873 last_error::set(&format!(
874 "lmflow_packet_new_buffer_uninit: cannot allocate {total} buffer bytes"
875 ));
876 return LMFlowPacket::default();
877 };
878 let data = allocation.data;
879 let allocation = Box::new(BufferAllocationOwner {
880 allocation: Some(allocation),
881 pool,
882 });
883 let user_data = Box::into_raw(allocation) as *mut c_void;
884 let source = LMFlowBuffer {
885 data: data as *mut c_void,
886 shape: shape_array,
887 strides,
888 ndim,
889 dtype,
890 flags: 0,
891 device: DEVICE_CPU,
892 reserved: [0; 2],
893 };
894 let packet = lmflow_packet_adopt_buffer(
895 &source,
896 ts,
897 Some(release_uninitialized_allocation),
898 user_data,
899 );
900 if packet.owner.is_null() {
901 release_uninitialized_allocation(user_data);
902 } else if !out.is_null() {
903 std::ptr::write(out, source);
904 }
905 packet
906 })
907}
908
909#[no_mangle]
910pub unsafe extern "C" fn lmflow_packet_from_buffer(
911 src: *const LMFlowBuffer,
912 ts: i64,
913) -> LMFlowPacket {
914 guard_val(LMFlowPacket::default(), || {
915 if src.is_null() {
916 last_error::set("lmflow_packet_from_buffer: src is null");
917 return LMFlowPacket::default();
918 }
919 let s = &*src;
920 let validated = match validate_buffer_descriptor(s) {
921 Ok(validated) => validated,
922 Err(error) => {
923 last_error::set(&format!("lmflow_packet_from_buffer: {error}"));
924 return LMFlowPacket::default();
925 }
926 };
927 let dims = validated.dims;
928 let Ok(mut b) = BufferData::new(dims, s.dtype) else {
929 last_error::set("lmflow_packet_from_buffer: invalid shape/dtype");
930 return LMFlowPacket::default();
931 };
932 if validated.element_count > 0 {
935 let esz = validated.element_size;
936 let ndim = dims.len();
937 if ndim >= 1 && esz > 0 {
938 let last = *dims.last().unwrap();
939 let last_stride = validated.strides[ndim - 1];
940 let row_bytes = (last as usize)
941 .checked_mul(esz)
942 .expect("validated buffer byte count");
943 let n_rows = validated.element_count / last.max(1) as usize;
944 let src_base = s.data as *const u8;
945 let dst_base = b.bytes.as_mut_ptr();
946 let packed = {
953 let mut expected = esz as i64;
954 let mut ok = true;
955 for d in (0..ndim).rev() {
956 if validated.strides[d] != expected {
957 ok = false;
958 break;
959 }
960 match expected.checked_mul(dims[d]) {
963 Some(next) => expected = next,
964 None => {
965 ok = false;
966 break;
967 }
968 }
969 }
970 ok
971 };
972 if packed {
973 std::ptr::copy_nonoverlapping(src_base, dst_base, b.bytes.len());
976 } else {
977 let mut idx = vec![0i64; ndim - 1];
979 for r in 0..n_rows {
980 let mut source_offset = 0i128;
981 for (d, &ix) in idx.iter().enumerate() {
982 source_offset += i128::from(ix) * i128::from(validated.strides[d]);
983 }
984 let source_offset =
985 isize::try_from(source_offset).expect("validated source offset");
986 let dofs = r * row_bytes;
987 if dofs + row_bytes <= b.bytes.len() {
988 if last_stride == esz as i64 {
989 std::ptr::copy_nonoverlapping(
991 src_base.offset(source_offset),
992 dst_base.add(dofs),
993 row_bytes,
994 );
995 } else {
996 for k in 0..last {
998 let element_offset = source_offset as i128
999 + i128::from(k) * i128::from(last_stride);
1000 std::ptr::copy_nonoverlapping(
1001 src_base.offset(
1002 isize::try_from(element_offset)
1003 .expect("validated element offset"),
1004 ),
1005 dst_base.add(dofs + (k as usize) * esz),
1006 esz,
1007 );
1008 }
1009 }
1010 }
1011 for d in (0..ndim - 1).rev() {
1013 idx[d] += 1;
1014 if idx[d] < dims[d] {
1015 break;
1016 }
1017 idx[d] = 0;
1018 }
1019 }
1020 }
1021 }
1022 }
1023 own_packet(Packet::from_builtin(Builtin::Buffer(b)).at(Timestamp(ts)))
1024 })
1025}
1026
1027#[no_mangle]
1028pub unsafe extern "C" fn lmflow_packet_adopt_buffer(
1029 src: *const LMFlowBuffer,
1030 ts: i64,
1031 release_fn: Option<unsafe extern "C" fn(*mut c_void)>,
1032 user_data: *mut c_void,
1033) -> LMFlowPacket {
1034 guard_val(LMFlowPacket::default(), || {
1035 if src.is_null() {
1036 last_error::set("lmflow_packet_adopt_buffer: src is null");
1037 return LMFlowPacket::default();
1038 }
1039 let Some(release_fn) = release_fn else {
1040 last_error::set("lmflow_packet_adopt_buffer: release_fn is null");
1041 return LMFlowPacket::default();
1042 };
1043 let source = &*src;
1044 let validated = match validate_buffer_descriptor(source) {
1045 Ok(validated) => validated,
1046 Err(error) => {
1047 last_error::set(&format!("lmflow_packet_adopt_buffer: {error}"));
1048 return LMFlowPacket::default();
1049 }
1050 };
1051 let packet = Packet::from_external_buffer(ExternalBuffer {
1052 data: source.data,
1053 shape: source.shape,
1054 strides: source.strides,
1055 ndim: source.ndim,
1056 dtype: source.dtype,
1057 readonly: source.flags & BUF_FLAG_READONLY != 0,
1058 element_count: validated.element_count,
1059 byte_size: validated.byte_size,
1060 release_fn,
1061 user_data,
1062 })
1063 .at(Timestamp(ts));
1064 own_packet(packet)
1065 })
1066}
1067
1068#[no_mangle]
1069pub unsafe extern "C" fn lmflow_packet_as_buffer(
1070 pkt: *const LMFlowPacket,
1071 out: *mut LMFlowBuffer,
1072) -> bool {
1073 guard_val(false, || {
1074 if pkt.is_null() {
1075 return false;
1076 }
1077 let packet = &*pkt;
1078 if packet.owner.is_null() {
1079 return false;
1080 }
1081 let payload = (*(packet.owner as *const PacketBody)).payload.as_ref();
1082 let Some(view) = payload.buffer_view() else {
1083 return false;
1084 };
1085 fill_buffer_view(out, view);
1086 true
1087 })
1088}
1089
1090#[no_mangle]
1091pub unsafe extern "C" fn lmflow_packet_make_mutable_buffer(
1092 pkt: *mut LMFlowPacket,
1093 out: *mut LMFlowBuffer,
1094) -> i32 {
1095 guard(|| {
1096 if pkt.is_null() {
1097 return fail(Error::InvalidArg("pkt is null".into()));
1098 }
1099 let fp = &mut *pkt;
1100 if fp.owner.is_null() {
1101 return fail(Error::InvalidArg(
1102 "make_mutable requires a caller-owned packet (owner non-null); for a borrowed input packet, take_input first".into(),
1103 ));
1104 }
1105 let mut p = take_packet(*fp);
1107 let r = match p.make_mutable_buffer() {
1108 Ok(view) => {
1109 fill_buffer_view(out, view);
1110 code::OK
1111 }
1112 Err(e) => fail(e),
1113 };
1114 *fp = own_packet(p);
1115 r
1116 })
1117}
1118
1119#[no_mangle]
1120pub unsafe extern "C" fn lmflow_packet_make_mutable_bytes(
1121 pkt: *mut LMFlowPacket,
1122 data: *mut *mut c_void,
1123 len: *mut usize,
1124) -> i32 {
1125 guard(|| {
1126 if pkt.is_null() {
1127 return fail(Error::InvalidArg("pkt is null".into()));
1128 }
1129 let fp = &mut *pkt;
1130 if fp.owner.is_null() {
1131 return fail(Error::InvalidArg(
1132 "make_mutable requires a caller-owned packet".into(),
1133 ));
1134 }
1135 let mut p = take_packet(*fp);
1136 let r = match p.make_mutable_builtin() {
1137 Ok(Builtin::Bytes(v)) => {
1138 if !data.is_null() {
1139 *data = v.as_mut_ptr() as *mut c_void;
1140 }
1141 if !len.is_null() {
1142 *len = v.len();
1143 }
1144 code::OK
1145 }
1146 Ok(_) => fail(Error::InvalidArg("this packet is not BYTES".into())),
1147 Err(e) => fail(e),
1148 };
1149 *fp = own_packet(p);
1150 r
1151 })
1152}