Skip to main content

lmflow/ffi/
packet.rs

1//! C ABI:Packet 跨界表示、内建类型构造/读取、LMFlowBuffer
2//!
3//! 由 [`super`](../index.html) 的分节拆出 —— 见 `ffi/mod.rs` 的模块头注释,
4//! 那里定义了整层的约定(catch_unwind 包裹、last_error、空指针检查、布局钉死)。
5#![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// ---------------------------------------------------------------- Packet 跨界
23
24#[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
46/// **借用**形态:不增加引用计数,调用方不得 drop。
47pub 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
63/// **移交**形态:把一份引用交给调用方,调用方须 emit/send 或 lmflow_packet_drop。
64pub 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
85/// 接管一个跨界包(消耗调用方的那份所有权)。
86///
87/// # Safety
88/// `fp` 必须是调用方拥有的包(owner 非空,或 payload+drop_fn 的自建包)。
89pub 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            // 自建包尚未进入引擎,无引用计数可增 —— 不支持克隆
146            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        // 原来的那份引用仍属调用方,不能在此释放
154        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
431// ---------------------------------------------------------------- 内建类型
432
433fn 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
485/// 借用形态的包 → 只读访问其内建 payload。
486unsafe 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// ---------------------------------------------------------------- LMFlowBuffer
557
558#[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        // 拷进一份行优先连续的缓冲,支持**任意 strides** —— 转置、带步长切片、
933        // 甚至负步长的 numpy 视图都要拷对(否则静默数据损坏)。
934        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                // 先判**整块连续**(strides 恰好是紧密行优先布局),一次拷完。
947                //
948                // 下面的通用路径以「最后一维 = 一行」为单位搬运,而 HWC 图像的最后一维
949                // 正是通道数(2/3/4)—— 于是 1920x1080x2 的帧会退化成 200 万次 2 字节
950                // 拷贝,实测 6.7ms、约 600MB/s,比 memcpy 慢约 30 倍。而 numpy 传进来的
951                // 数组绝大多数是连续的,这条快路径覆盖的正是最常见也最大的那些。
952                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                        // dims 的乘积已被 BufferData::new 验证过(它据此分配了 b.bytes),
961                        // 故此处不会溢出;仍用 checked 以防将来放宽校验。
962                        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                    // 与通用路径读取的字节区间完全相同(n_rows * row_bytes == b.bytes.len()),
974                    // 故安全性无差别。
975                    std::ptr::copy_nonoverlapping(src_base, dst_base, b.bytes.len());
976                } else {
977                    // 里程表遍历外层维度索引,按完整 strides 求每行源偏移。
978                    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                                // 最后一维连续:整行拷
990                                std::ptr::copy_nonoverlapping(
991                                    src_base.offset(source_offset),
992                                    dst_base.add(dofs),
993                                    row_bytes,
994                                );
995                            } else {
996                                // 最后一维也跳跃(转置/步长切片/负步长):逐元素拷
997                                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                        // 里程表 +1(最右外层维先进位)
1012                        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        // 取回所有权 → CoW → 再交还
1106        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}