Skip to main content

siglus_scene_vm/runtime/forms/
int_list.rs

1use crate::runtime::{CommandContext, Value};
2use anyhow::Result;
3
4fn ensure_len(v: &mut Vec<i64>, idx: usize) {
5    if v.len() <= idx {
6        v.resize(idx + 1, 0);
7    }
8}
9
10fn bit_unit(bit: i32) -> i32 {
11    if bit <= 0 {
12        32
13    } else {
14        bit
15    }
16}
17
18fn get_bit_width(ctx: &mut CommandContext, form_id: u32, op: i32) -> i32 {
19    if op == ctx.ids.elm_array {
20        return 32;
21    }
22    if let Some(&w) = ctx.globals.intlist_bit_widths.get(&(form_id, op)) {
23        return w;
24    }
25    let order = ctx.globals.intlist_bit_order.entry(form_id).or_default();
26    if !order.contains(&op) {
27        order.push(op);
28    }
29    let w = match order.iter().position(|&x| x == op).unwrap_or(0) {
30        0 => 1,
31        1 => 2,
32        2 => 4,
33        3 => 8,
34        4 => 16,
35        _ => 32,
36    };
37    ctx.globals.intlist_bit_widths.insert((form_id, op), w);
38    w
39}
40
41fn bit_get(list: &mut Vec<i64>, bit_width: i32, index: i64) -> i64 {
42    let bit_width = bit_unit(bit_width) as u32;
43    if bit_width >= 32 {
44        let idx = index.max(0) as usize;
45        ensure_len(list, idx);
46        return list[idx];
47    }
48    let per = 32 / bit_width;
49    let idx = (index.max(0) as u32 / per) as usize;
50    let shift = (index.max(0) as u32 % per) * bit_width;
51    ensure_len(list, idx);
52    let mask = (1u32 << bit_width) - 1;
53    let raw = list[idx] as u32;
54    ((raw >> shift) & mask) as i64
55}
56
57fn bit_set(list: &mut Vec<i64>, bit_width: i32, index: i64, value: i64) {
58    let bit_width = bit_unit(bit_width) as u32;
59    if bit_width >= 32 {
60        let idx = index.max(0) as usize;
61        ensure_len(list, idx);
62        list[idx] = value;
63        return;
64    }
65    let per = 32 / bit_width;
66    let idx = (index.max(0) as u32 / per) as usize;
67    let shift = (index.max(0) as u32 % per) * bit_width;
68    ensure_len(list, idx);
69    let mask = ((1u32 << bit_width) - 1) << shift;
70    let raw = list[idx] as u32;
71    let v = (value as u32) & ((1u32 << bit_width) - 1);
72    let next = (raw & !mask) | (v << shift);
73    list[idx] = next as i64;
74}
75
76/// Generic handler for global int-list forms (`tnm_command_proc_int_list`).
77///
78/// We implement the runtime subset:
79/// - array indexing get: LIST[i]
80/// - array indexing set: LIST[i] = value
81///
82/// More exotic sub-ops (bit ops, init, copy, etc.) can be added later.
83pub fn dispatch(ctx: &mut CommandContext, form_id: u32, args: &[Value]) -> Result<bool> {
84    // Find element chain from the current VM call first, matching the str-list path.
85    let mut chain_pos: Option<usize> = None;
86    let mut chain: Option<&[i32]> = None;
87    if let Some(vm_call) = ctx.vm_call.as_ref() {
88        if vm_call.element.first().copied() == Some(form_id as i32) {
89            chain_pos = Some(args.len());
90            chain = Some(vm_call.element.as_slice());
91        }
92    }
93    if chain.is_none() {
94        for (i, v) in args.iter().enumerate() {
95            if let Value::Element(c) = v {
96                if !c.is_empty() && c[0] == form_id as i32 {
97                    chain_pos = Some(i);
98                    chain = Some(c);
99                    break;
100                }
101            }
102        }
103    }
104
105    let params = if let Some(pos) = chain_pos {
106        if pos == args.len() {
107            args
108        } else if pos > 1 {
109            &args[1..pos]
110        } else {
111            &[]
112        }
113    } else {
114        &[][..]
115    };
116
117    // Property-assign call shape: [op_id, al_id, rhs, Element(chain)]
118    if chain_pos == Some(3) {
119        if let (Some(al_id), Some(rhs)) = (
120            args.get(1).and_then(|v| v.as_i64()),
121            args.get(2).and_then(|v| v.as_i64()),
122        ) {
123            if al_id == 1 {
124                if let Some(c_ref) = chain {
125                    let c = c_ref.to_vec();
126                    if c.len() >= 3 && c[1] == ctx.ids.elm_array {
127                        let idx = c[2] as i64;
128                        let list = ctx
129                            .globals
130                            .int_lists
131                            .entry(form_id)
132                            .or_insert_with(|| vec![0; 32]);
133                        bit_set(list, 32, idx, rhs);
134                    } else if c.len() >= 4 && c[2] == ctx.ids.elm_array {
135                        let bit_width = get_bit_width(ctx, form_id, c[1]);
136                        let idx = c[3] as i64;
137                        let list = ctx
138                            .globals
139                            .int_lists
140                            .entry(form_id)
141                            .or_insert_with(|| vec![0; 32]);
142                        bit_set(list, bit_width, idx, rhs);
143                    }
144                }
145            }
146            ctx.push(Value::Int(0));
147            return Ok(true);
148        }
149    }
150
151    // Command call shape: [rhs?, Element(chain), al_id, ret_form]
152    if let (Some(pos), Some(c_ref)) = (chain_pos, chain) {
153        let c = c_ref.to_vec();
154        let meta_al_id = ctx.vm_call.as_ref().map(|m| m.al_id).unwrap_or(0);
155        let meta_ret_form = ctx.vm_call.as_ref().map(|m| m.ret_form).unwrap_or(0);
156        let al_id = args
157            .get(pos + 1)
158            .and_then(|v| v.as_i64())
159            .unwrap_or(meta_al_id);
160        let _ret_form = args
161            .get(pos + 2)
162            .and_then(|v| v.as_i64())
163            .unwrap_or(meta_ret_form);
164
165        if c.len() >= 3 && c[1] == ctx.ids.elm_array {
166            let idx = c[2] as i64;
167            let out = {
168                let list = ctx
169                    .globals
170                    .int_lists
171                    .entry(form_id)
172                    .or_insert_with(|| vec![0; 32]);
173                if al_id == 1 {
174                    if let Some(rhs) = args.get(0).and_then(|v| v.as_i64()) {
175                        bit_set(list, 32, idx, rhs);
176                    }
177                    0
178                } else {
179                    bit_get(list, 32, idx)
180                }
181            };
182            ctx.push(Value::Int(out));
183            return Ok(true);
184        }
185
186        if c.len() >= 4 && c[2] == ctx.ids.elm_array {
187            let bit_width = get_bit_width(ctx, form_id, c[1]);
188            let idx = c[3] as i64;
189            let out = {
190                let list = ctx
191                    .globals
192                    .int_lists
193                    .entry(form_id)
194                    .or_insert_with(|| vec![0; 32]);
195                if al_id == 1 {
196                    if let Some(rhs) = args.get(0).and_then(|v| v.as_i64()) {
197                        bit_set(list, bit_width, idx, rhs);
198                    }
199                    0
200                } else {
201                    bit_get(list, bit_width, idx)
202                }
203            };
204            ctx.push(Value::Int(out));
205            return Ok(true);
206        }
207
208        if c.len() == 3 && c[1] != ctx.ids.elm_array && c[2] != ctx.ids.elm_array {
209            let bit_width = get_bit_width(ctx, form_id, c[1]);
210            let list = ctx
211                .globals
212                .int_lists
213                .entry(form_id)
214                .or_insert_with(|| vec![0; 32]);
215
216            if _ret_form != 0 && params.is_empty() {
217                let unit = bit_unit(bit_width) as i64;
218                let per = if unit >= 32 { 1 } else { 32 / unit };
219                let size = list.len() as i64 * per as i64;
220                ctx.push(Value::Int(size));
221                return Ok(true);
222            }
223
224            if _ret_form == 0 {
225                if params.is_empty() {
226                    list.clear();
227                    ctx.push(Value::Int(0));
228                    return Ok(true);
229                }
230                if params.len() == 1 {
231                    let n = params[0].as_i64().unwrap_or(0).max(0) as usize;
232                    list.resize(n, 0);
233                    ctx.push(Value::Int(0));
234                    return Ok(true);
235                }
236                if params.len() >= 2 {
237                    let start = params[0].as_i64().unwrap_or(0);
238                    let end = params[1].as_i64().unwrap_or(start);
239                    let value = if params.len() >= 3 {
240                        params[2].as_i64().unwrap_or(0)
241                    } else {
242                        0
243                    };
244                    for i in start..=end {
245                        bit_set(list, bit_width, i, value);
246                    }
247                    ctx.push(Value::Int(0));
248                    return Ok(true);
249                }
250            }
251        }
252
253        if c.len() == 2 {
254            if _ret_form != 0 && params.is_empty() {
255                let size = {
256                    let list = ctx
257                        .globals
258                        .int_lists
259                        .entry(form_id)
260                        .or_insert_with(|| vec![0; 32]);
261                    list.len() as i64
262                };
263                ctx.push(Value::Int(size));
264                return Ok(true);
265            }
266
267            let list = ctx
268                .globals
269                .int_lists
270                .entry(form_id)
271                .or_insert_with(|| vec![0; 32]);
272
273            if _ret_form == 0 {
274                if params.is_empty() {
275                    list.clear();
276                    ctx.push(Value::Int(0));
277                    return Ok(true);
278                }
279                if params.len() == 1 {
280                    let n = params[0].as_i64().unwrap_or(0).max(0) as usize;
281                    list.resize(n, 0);
282                    ctx.push(Value::Int(0));
283                    return Ok(true);
284                }
285                if params.len() >= 2 {
286                    let start = params[0].as_i64().unwrap_or(0);
287                    let end = params[1].as_i64().unwrap_or(start);
288                    let value = if params.len() >= 3 {
289                        params[2].as_i64().unwrap_or(0)
290                    } else {
291                        0
292                    };
293                    for i in start..=end {
294                        bit_set(list, 32, i, value);
295                    }
296                    ctx.push(Value::Int(0));
297                    return Ok(true);
298                }
299            }
300        }
301    }
302
303    ctx.push(Value::Int(0));
304    Ok(true)
305}