Skip to main content

nifty_rt/abi/
lists.rs

1//! Lists: building, reading, and changing them in place.
2
3use std::slice;
4
5use nifty_runtime::Failure;
6
7use super::{borrow_context, count};
8use crate::context::Context;
9use crate::value::{self, Slot, TypeDesc};
10
11/// A list of type `ty` holding `len` items from `items`, which it takes ownership of.
12///
13/// # Safety
14/// `ty` is a list type and `items` points to `len` slots of its element type.
15#[unsafe(no_mangle)]
16pub(crate) unsafe extern "C" fn nf_list_new(
17    ty: &'static TypeDesc,
18    items: *const Slot,
19    len: usize,
20) -> Slot {
21    let items = if len == 0 {
22        Vec::new()
23    } else {
24        // SAFETY: by the caller's contract.
25        unsafe { slice::from_raw_parts(items, len) }.to_vec()
26    };
27    value::list_slot(ty, items)
28}
29
30/// How many items the borrowed list holds.
31#[unsafe(no_mangle)]
32pub(crate) extern "C" fn nf_list_len(list: Slot) -> i64 {
33    count(value::as_list(list).items.len())
34}
35
36/// Writes the item at `index` of the borrowed list to `out`, borrowed from the list.
37///
38/// # Safety
39/// See the module's contract.
40#[unsafe(no_mangle)]
41pub(crate) unsafe extern "C" fn nf_list_get(
42    context: *const Context,
43    list: Slot,
44    index: i64,
45    span: u32,
46    out: *mut Slot,
47) -> bool {
48    // SAFETY: by the caller's contract.
49    let context = unsafe { borrow_context(context) };
50    let items = &value::as_list(list).items;
51    match position(context, index, items.len(), span) {
52        Some(position) => {
53            // SAFETY: by the caller's contract.
54            unsafe { out.write(items[position]) };
55            true
56        }
57        None => false,
58    }
59}
60
61/// Appends `item`, which it takes ownership of, to the list in `place`.
62///
63/// # Safety
64/// `place` holds a list whose element type `item` has.
65#[unsafe(no_mangle)]
66pub(crate) unsafe extern "C" fn nf_list_push(place: *mut Slot, item: Slot) {
67    // SAFETY: by the caller's contract.
68    value::list_mut(unsafe { &mut *place }).items.push(item);
69}
70
71/// Removes the item at `index` from the list in `place`.
72///
73/// # Safety
74/// See the module's contract.
75#[unsafe(no_mangle)]
76pub(crate) unsafe extern "C" fn nf_list_remove(
77    context: *const Context,
78    place: *mut Slot,
79    index: i64,
80    span: u32,
81) -> bool {
82    // SAFETY: by the caller's contract.
83    let (context, place) = unsafe { (borrow_context(context), &mut *place) };
84    let len = value::as_list(*place).items.len();
85    let Some(position) = position(context, index, len, span) else {
86        return false;
87    };
88    let list = value::list_mut(place);
89    value::release(list.items.remove(position), list.element);
90    true
91}
92
93/// Writes to `out` the slot of the item at `index` in the list in `place`, which becomes
94/// its only owner's so the item can be changed. The slot is valid until the list next
95/// changes.
96///
97/// # Safety
98/// See the module's contract.
99#[unsafe(no_mangle)]
100pub(crate) unsafe extern "C" fn nf_list_item_mut(
101    context: *const Context,
102    place: *mut Slot,
103    index: i64,
104    span: u32,
105    out: *mut *mut Slot,
106) -> bool {
107    // SAFETY: by the caller's contract.
108    let (context, place) = unsafe { (borrow_context(context), &mut *place) };
109    let len = value::as_list(*place).items.len();
110    let Some(position) = position(context, index, len, span) else {
111        return false;
112    };
113    let item = &raw mut value::list_mut(place).items[position];
114    // SAFETY: by the caller's contract.
115    unsafe { out.write(item) };
116    true
117}
118
119/// The item position `index` names in a list of `len`, or `None` after recording why
120/// there's no such item.
121fn position(context: &Context, index: i64, len: usize, span: u32) -> Option<usize> {
122    let Ok(position) = usize::try_from(index) else {
123        context.fail(Failure::NegativePosition(index), span);
124        return None;
125    };
126    if position >= len {
127        context.fail(Failure::NoItem { position, len }, span);
128        return None;
129    }
130    Some(position)
131}