1use ecolor::Color32;
2use egui::{CornerRadius, DragValue, Pos2, Rect, Sense, Stroke};
3use serde::{Deserialize, Serialize};
4use surfer_translation_types::VariableValue;
5
6use crate::wave_container::{ScopeRef, ScopeRefExt, VariableRef, VariableRefExt, WaveContainer};
7use crate::{Message, system_state::SystemState};
8
9#[derive(Serialize, Deserialize, Debug, Clone)]
10pub(crate) struct FrameBufferSettings {
11 pub pixels_per_row: usize,
12 pub square_pixels: bool,
13 #[serde(flatten)]
14 pub color_settings: PixelColorSettings,
15}
16
17#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
18pub(crate) struct PixelColorSettings {
19 pub rgb_mode: bool,
20 pub grayscale_bits: u8,
21 pub r_bits: u8,
22 pub g_bits: u8,
23 pub b_bits: u8,
24}
25
26impl Default for PixelColorSettings {
27 fn default() -> Self {
28 Self {
29 rgb_mode: false,
30 grayscale_bits: 1,
31 r_bits: 3,
32 g_bits: 3,
33 b_bits: 2,
34 }
35 }
36}
37
38impl Default for FrameBufferSettings {
39 fn default() -> Self {
40 Self {
41 pixels_per_row: 16,
42 square_pixels: true,
43 color_settings: PixelColorSettings::default(),
44 }
45 }
46}
47
48#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
49pub(crate) struct ArrayLevel {
50 pub min_index: i64,
51 pub max_index: i64,
52 pub first_index: i64,
53 pub last_index: i64,
54}
55
56#[derive(Debug, Clone, PartialEq, Eq)]
57pub(crate) struct FrameBufferContentCacheKey {
58 pub content: FrameBufferContent,
59 pub cursor_position: num::BigUint,
60}
61
62#[derive(Debug, Clone)]
63pub(crate) struct FrameBufferArrayCache {
64 pub key: FrameBufferContentCacheKey,
65 pub cached_value: Option<(String, u32)>,
66}
67
68#[derive(Debug, Clone, PartialEq, Eq)]
69pub(crate) struct FrameBufferPixelCacheKey {
70 pub array_key: FrameBufferContentCacheKey,
71 pub settings: PixelColorSettings,
72}
73
74#[derive(Debug, Clone)]
75pub(crate) struct FrameBufferPixelCache {
76 pub key: FrameBufferPixelCacheKey,
77 pub pixel_colors: Vec<Color32>,
78}
79
80#[derive(Debug, Clone, PartialEq, Eq)]
81pub(crate) enum FrameBufferContent {
82 Array {
83 scope_ref: ScopeRef,
84 levels: Vec<ArrayLevel>,
87 },
88 Variable(VariableRef),
89}
90
91impl SystemState {
92 pub fn draw_frame_buffer_window(&mut self, ctx: &egui::Context, msgs: &mut Vec<Message>) {
93 let mut open = true;
94 egui::Window::new("Frame Buffer")
95 .open(&mut open)
96 .resizable(true)
97 .show(ctx, |ui| {
98 let frame_buffer_value = self.selected_variable_for_frame_buffer();
99 let Some((value, word_length, variable_name)) = frame_buffer_value.as_ref() else {
100 ui.label("Place the cursor.");
101 return;
102 };
103
104 let color_settings_key = {
105 let settings = &mut self.user.frame_buffer;
106 let color_settings = &mut settings.color_settings;
107
108 ui.checkbox(&mut settings.square_pixels, "Square pixels");
109 ui.checkbox(&mut color_settings.rgb_mode, "RGB mode");
110
111 if color_settings.rgb_mode {
112 ui.horizontal(|ui| {
113 ui.label("R bits");
114 ui.add(DragValue::new(&mut color_settings.r_bits).range(0..=8));
115 ui.label("G bits");
116 ui.add(DragValue::new(&mut color_settings.g_bits).range(0..=8));
117 ui.label("B bits");
118 ui.add(DragValue::new(&mut color_settings.b_bits).range(0..=8));
119 });
120 } else {
121 ui.horizontal(|ui| {
122 ui.label("Grayscale bits");
123 ui.add(DragValue::new(&mut color_settings.grayscale_bits).range(1..=8));
124 });
125 }
126
127 color_settings.clone()
128 };
129
130 ui.separator();
131
132 let bits = frame_buffer_bits(value, *word_length as usize);
133 if bits.is_empty() {
134 ui.label("No bits available");
135 return;
136 }
137
138 let Some(pixel_cache_key) =
139 self.current_frame_buffer_array_cache_key()
140 .map(|array_key| FrameBufferPixelCacheKey {
141 array_key,
142 settings: color_settings_key.clone(),
143 })
144 else {
145 ui.label("Place the cursor.");
146 return;
147 };
148
149 let pixel_colors = if let Some(cache) = self
150 .frame_buffer_pixel_cache
151 .as_ref()
152 .filter(|cache| cache.key == pixel_cache_key)
153 {
154 cache.pixel_colors.clone()
155 } else {
156 let decoded = if color_settings_key.rgb_mode {
157 let r_bits = color_settings_key.r_bits as usize;
158 let g_bits = color_settings_key.g_bits as usize;
159 let b_bits = color_settings_key.b_bits as usize;
160 let bits_per_pixel = r_bits + g_bits + b_bits;
161 if bits_per_pixel == 0 {
162 ui.label("Set at least one RGB channel bit count above zero.");
163 return;
164 }
165 decode_rgb_pixels(&bits, r_bits, g_bits, b_bits)
166 } else {
167 let gray_bits = color_settings_key.grayscale_bits as usize;
168 decode_grayscale_pixels(&bits, gray_bits)
169 };
170
171 self.frame_buffer_pixel_cache = Some(FrameBufferPixelCache {
172 key: pixel_cache_key,
173 pixel_colors: decoded.clone(),
174 });
175 decoded
176 };
177
178 if pixel_colors.is_empty() {
179 ui.label("No pixels to draw with current bit settings.");
180 return;
181 }
182
183 let settings = &mut self.user.frame_buffer;
184 let columns = settings.pixels_per_row.min(pixel_colors.len()).max(1);
185 let rows = pixel_colors.len().div_ceil(columns);
186 ui.horizontal(|ui| {
187 ui.label(format!("Var: {variable_name} | {columns}×{rows}"));
188
189 if ui.button("Copy image").clicked() {
190 let total = columns * rows;
191 let mut padded = pixel_colors.to_vec();
192 padded.resize(total, Color32::BLACK);
193 ui.ctx().copy_image(egui::ColorImage {
194 size: [columns, rows],
195 pixels: padded,
196 source_size: egui::vec2(columns as f32, rows as f32),
197 });
198 }
199 });
200 self.draw_array_index_range(ui);
201
202 let settings = &mut self.user.frame_buffer;
203 let max_columns = pixel_colors.len().max(1);
204 settings.pixels_per_row = settings.pixels_per_row.clamp(1, max_columns);
205
206 ui.horizontal(|ui| {
207 ui.label("Pixels in x-direction");
208 ui.add(
209 egui::Slider::new(&mut settings.pixels_per_row, 1..=max_columns).integer(),
210 );
211 });
212
213 ui.separator();
214
215 let available = ui.available_size_before_wrap();
216
217 if available.x <= 0.0 || available.y <= 0.0 {
218 return;
219 }
220
221 let (pixel_width, pixel_height) = if settings.square_pixels {
222 let side = (available.x / columns as f32).min(available.y / rows as f32);
223 (side, side)
224 } else {
225 (available.x / columns as f32, available.y / rows as f32)
226 };
227
228 let image_size =
229 egui::vec2(pixel_width * columns as f32, pixel_height * rows as f32);
230 let (rect, _) = ui.allocate_exact_size(image_size, Sense::hover());
231 let painter = ui.painter_at(rect);
232
233 for (index, color) in pixel_colors.iter().copied().enumerate() {
234 let x = index % columns;
235 let y = index / columns;
236
237 let min = Pos2 {
238 x: rect.min.x + x as f32 * pixel_width,
239 y: rect.min.y + y as f32 * pixel_height,
240 };
241 let max = Pos2 {
242 x: min.x + pixel_width,
243 y: min.y + pixel_height,
244 };
245
246 painter.rect_filled(Rect { min, max }, CornerRadius::ZERO, color);
247 }
248
249 painter.rect_stroke(
250 rect,
251 CornerRadius::ZERO,
252 Stroke::new(1.0, ui.visuals().weak_text_color()),
253 egui::StrokeKind::Inside,
254 );
255 });
256
257 if !open {
258 msgs.push(Message::SetFrameBufferVisibleVariable(None));
259 }
260 }
261
262 fn draw_array_index_range(&mut self, ui: &mut egui::Ui) {
263 let Some(FrameBufferContent::Array {
264 scope_ref: _,
265 levels,
266 }) = self.frame_buffer_content.as_mut()
267 else {
268 return;
269 };
270
271 if levels.is_empty() {
272 return;
273 }
274
275 let total_levels = levels.len();
276
277 for (i, level) in levels.iter_mut().enumerate() {
278 let (min, max) = (level.min_index, level.max_index);
279 level.first_index = level.first_index.clamp(min, max);
280 level.last_index = level.last_index.clamp(min, max);
281 if level.first_index > level.last_index {
282 level.last_index = level.first_index;
283 }
284 ui.horizontal(|ui| {
285 if total_levels == 1 {
286 ui.label("First array index");
287 } else {
288 ui.label(format!("Level {} first index", i + 1));
289 }
290 ui.add(DragValue::new(&mut level.first_index).range(min..=max));
291 if total_levels == 1 {
292 ui.label("Last array index");
293 } else {
294 ui.label(format!("Level {} last index", i + 1));
295 }
296 ui.add(DragValue::new(&mut level.last_index).range(min..=max));
297 });
298 if level.first_index > level.last_index {
299 level.first_index = level.last_index;
300 }
301 }
302 }
303
304 fn selected_variable_for_frame_buffer(&mut self) -> Option<(VariableValue, u32, String)> {
305 let waves = self.user.waves.as_ref()?;
306 let cursor = waves.cursor.as_ref()?.to_biguint()?;
307 let wave_container = waves.inner.as_waves()?;
308 let content = self.frame_buffer_content.clone()?;
309 let cache_key = FrameBufferContentCacheKey {
310 content: content.clone(),
311 cursor_position: cursor.clone(),
312 };
313 let cached = self
314 .frame_buffer_array_cache
315 .as_ref()
316 .filter(|cache| cache.key == cache_key)
317 .cloned();
318
319 let cached = if let Some(cached) = cached {
320 cached
321 } else {
322 let cached = match &content {
323 FrameBufferContent::Variable(variable_ref) => build_variable_frame_buffer_cache(
324 wave_container,
325 variable_ref,
326 &cursor,
327 cache_key,
328 )?,
329 FrameBufferContent::Array { scope_ref, levels } => {
330 if levels.is_empty() {
331 return None;
332 }
333
334 let sorted_variables =
335 resolve_leaf_scopes_and_variables(wave_container, scope_ref, levels)?;
336 let cached_value =
337 build_cached_variable_value(wave_container, &sorted_variables, &cursor);
338 FrameBufferArrayCache {
339 key: cache_key,
340 cached_value,
341 }
342 }
343 };
344 self.frame_buffer_array_cache = Some(cached.clone());
345 cached
346 };
347
348 let (concat_bits, total_bits) = cached.cached_value.as_ref()?;
349
350 let variable_name = match &content {
351 FrameBufferContent::Variable(variable_ref) => variable_ref.full_path_string_no_index(),
352 FrameBufferContent::Array { scope_ref, .. } => scope_ref.full_name(),
353 };
354
355 Some((
356 VariableValue::String(concat_bits.clone()),
357 *total_bits,
358 variable_name,
359 ))
360 }
361
362 fn current_frame_buffer_array_cache_key(&self) -> Option<FrameBufferContentCacheKey> {
363 let waves = self.user.waves.as_ref()?;
364 let cursor_position = waves.cursor.as_ref()?.to_biguint()?;
365 let content = self.frame_buffer_content.clone()?;
366 Some(FrameBufferContentCacheKey {
367 content,
368 cursor_position,
369 })
370 }
371}
372
373fn build_cached_variable_value(
374 wave_container: &WaveContainer,
375 sorted_variables: &[VariableRef],
376 cursor: &num::BigUint,
377) -> Option<(String, u32)> {
378 let mut concat_bits = String::new();
379 let mut total_bits: u32 = 0;
380 for var_ref in sorted_variables {
381 let meta = wave_container.variable_meta(var_ref).ok()?;
382 let bits = meta.num_bits? as usize;
383 total_bits += bits as u32;
384 let query_result = wave_container
385 .query_variable(var_ref, cursor)
386 .ok()
387 .flatten()?;
388 let (_, value) = query_result.current?;
389 let bit_str = match &value {
390 VariableValue::BigUint(v) => format!("{v:b}"),
391 VariableValue::String(s) => s.clone(),
392 };
393 let padded = if bit_str.len() < bits {
394 format!("{bit_str:0>bits$}")
395 } else {
396 bit_str[bit_str.len() - bits..].to_string()
397 };
398 concat_bits.push_str(&padded);
399 }
400 if total_bits == 0 {
401 None
402 } else {
403 Some((concat_bits, total_bits))
404 }
405}
406
407fn build_variable_frame_buffer_cache(
408 wave_container: &WaveContainer,
409 variable_ref: &VariableRef,
410 cursor: &num::BigUint,
411 key: FrameBufferContentCacheKey,
412) -> Option<FrameBufferArrayCache> {
413 let meta = wave_container.variable_meta(variable_ref).ok()?;
414 let word_length = meta.num_bits? as usize;
415 let query_result = wave_container
416 .query_variable(variable_ref, cursor)
417 .ok()
418 .flatten()?;
419 let (_, value) = query_result.current?;
420 let bits = match value {
421 VariableValue::BigUint(v) => format!("{v:b}"),
422 VariableValue::String(s) => s,
423 };
424 let padded = if bits.len() < word_length {
425 format!("{bits:0>word_length$}")
426 } else {
427 bits[bits.len() - word_length..].to_string()
428 };
429
430 Some(FrameBufferArrayCache {
431 key,
432 cached_value: Some((padded, word_length as u32)),
433 })
434}
435
436fn resolve_leaf_scopes_and_variables(
437 wave_container: &WaveContainer,
438 scope_ref: &ScopeRef,
439 levels: &[ArrayLevel],
440) -> Option<Vec<VariableRef>> {
441 let (scope_levels, var_level) = levels.split_at(levels.len() - 1);
442 let var_level = &var_level[0];
443
444 let mut current_scopes = vec![scope_ref.clone()];
445 for level in scope_levels {
446 let clamped_first = level.first_index.clamp(level.min_index, level.max_index);
447 let clamped_last = level.last_index.clamp(level.min_index, level.max_index);
448 let mut next_scopes = Vec::new();
449 for scope in ¤t_scopes {
450 let mut selected: Vec<ScopeRef> = wave_container
451 .child_scopes(scope)
452 .unwrap_or_default()
453 .into_iter()
454 .filter(|s| {
455 let idx = scope_array_index(s);
456 idx >= clamped_first && idx <= clamped_last
457 })
458 .collect();
459 selected.sort_by_key(scope_array_index);
460 next_scopes.extend(selected);
461 }
462 current_scopes = next_scopes;
463 }
464
465 if current_scopes.is_empty() {
466 return None;
467 }
468
469 let clamped_first = var_level
470 .first_index
471 .clamp(var_level.min_index, var_level.max_index);
472 let clamped_last = var_level
473 .last_index
474 .clamp(var_level.min_index, var_level.max_index);
475 if clamped_first > clamped_last {
476 return None;
477 }
478
479 let mut sorted_variables = Vec::new();
480 for leaf_scope in ¤t_scopes {
481 let mut variables = wave_container.variables_in_scope(leaf_scope);
482 variables.sort_by_key(variable_array_index);
483 sorted_variables.extend(variables.into_iter().filter(|var_ref| {
484 let idx = variable_array_index(var_ref);
485 idx >= clamped_first && idx <= clamped_last
486 }));
487 }
488
489 Some(sorted_variables)
490}
491
492pub(crate) fn build_frame_buffer_content(
498 wave_container: &WaveContainer,
499 scope_ref: &ScopeRef,
500) -> Option<(Vec<ArrayLevel>, Vec<VariableRef>)> {
501 let mut levels: Vec<ArrayLevel> = Vec::new();
504 let mut probe = scope_ref.clone();
505 loop {
506 let children = wave_container.child_scopes(&probe).unwrap_or_default();
507 if children.is_empty() {
508 break;
509 }
510 let indices: Vec<i64> = children.iter().map(scope_array_index).collect();
511 let min_idx = *indices.iter().min().unwrap_or(&0);
512 let max_idx = *indices.iter().max().unwrap_or(&0);
513 levels.push(ArrayLevel {
514 min_index: min_idx,
515 max_index: max_idx,
516 first_index: min_idx,
517 last_index: max_idx,
518 });
519 probe = children.into_iter().min_by_key(scope_array_index).unwrap();
520 }
521
522 let leaf_vars = wave_container.variables_in_scope(&probe);
524 let var_indices: Vec<i64> = leaf_vars
525 .iter()
526 .map(variable_array_index)
527 .filter(|&i| i != i64::MAX)
528 .collect();
529 let (var_min, var_max) = if var_indices.is_empty() {
530 (0, 0)
531 } else {
532 (
533 *var_indices.iter().min().unwrap(),
534 *var_indices.iter().max().unwrap(),
535 )
536 };
537 levels.push(ArrayLevel {
538 min_index: var_min,
539 max_index: var_max,
540 first_index: var_min,
541 last_index: var_max,
542 });
543
544 let depth = levels.len().saturating_sub(1);
546 let mut leaf_scopes = vec![scope_ref.clone()];
547 for _ in 0..depth {
548 leaf_scopes = leaf_scopes
549 .iter()
550 .flat_map(|s| wave_container.child_scopes(s).unwrap_or_default())
551 .collect();
552 }
553 let all_leaf_vars: Vec<VariableRef> = leaf_scopes
554 .iter()
555 .flat_map(|s| wave_container.variables_in_scope(s))
556 .collect();
557
558 Some((levels, all_leaf_vars))
559}
560
561fn scope_array_index(scope_ref: &ScopeRef) -> i64 {
562 let name = scope_ref.name();
563 name.parse::<i64>()
564 .ok()
565 .or_else(|| {
566 name.strip_prefix('[')
567 .and_then(|s| s.strip_suffix(']'))
568 .and_then(|s| s.parse::<i64>().ok())
569 })
570 .unwrap_or(i64::MAX)
571}
572
573fn variable_array_index(var_ref: &VariableRef) -> i64 {
574 fn parse_index_name(name: &str) -> Option<i64> {
575 name.parse::<i64>().ok().or_else(|| {
576 name.strip_prefix('[')
577 .and_then(|s| s.strip_suffix(']'))
578 .and_then(|s| s.parse::<i64>().ok())
579 })
580 }
581
582 var_ref
583 .index
584 .or_else(|| parse_index_name(&var_ref.name))
585 .unwrap_or(i64::MAX)
586}
587
588fn frame_buffer_bits(value: &VariableValue, word_length: usize) -> Vec<bool> {
589 let mut bits: Vec<bool> = match value {
590 VariableValue::BigUint(v) => format!("{v:b}").chars().map(|c| c == '1').collect(),
591 VariableValue::String(v) => v.chars().map(|c| c == '1').collect(),
592 };
593
594 if bits.len() < word_length {
595 let mut padded = vec![false; word_length - bits.len()];
596 padded.extend(bits);
597 bits = padded;
598 } else if bits.len() > word_length {
599 bits = bits[bits.len() - word_length..].to_vec();
600 }
601
602 bits
603}
604
605fn decode_grayscale_pixels(bits: &[bool], grayscale_bits: usize) -> Vec<Color32> {
606 let mut out = Vec::with_capacity(bits.len().div_ceil(grayscale_bits.max(1)));
607 for start in (0..bits.len()).step_by(grayscale_bits.max(1)) {
608 let gray = scale_to_u8(
609 bits_to_u16_padded(bits, start, grayscale_bits),
610 grayscale_bits,
611 );
612 out.push(Color32::from_rgb(gray, gray, gray));
613 }
614 out
615}
616
617fn decode_rgb_pixels(bits: &[bool], r_bits: usize, g_bits: usize, b_bits: usize) -> Vec<Color32> {
618 let bits_per_pixel = r_bits + g_bits + b_bits;
619 let mut out = Vec::with_capacity(bits.len().div_ceil(bits_per_pixel.max(1)));
620 for start in (0..bits.len()).step_by(bits_per_pixel.max(1)) {
621 let red = scale_to_u8(bits_to_u16_padded(bits, start, r_bits), r_bits);
622 let green = scale_to_u8(bits_to_u16_padded(bits, start + r_bits, g_bits), g_bits);
623 let blue = scale_to_u8(
624 bits_to_u16_padded(bits, start + r_bits + g_bits, b_bits),
625 b_bits,
626 );
627 out.push(Color32::from_rgb(red, green, blue));
628 }
629 out
630}
631
632fn bits_to_u16_padded(bits: &[bool], start: usize, len: usize) -> u16 {
633 let mut value = 0u16;
634 for offset in 0..len {
635 value = (value << 1) | u16::from(bits.get(start + offset).copied().unwrap_or(false));
636 }
637 value
638}
639
640fn scale_to_u8(value: u16, bits: usize) -> u8 {
641 if bits == 0 {
642 return 0;
643 }
644 let max_in = (1u16 << bits) - 1;
645 (((value as u32) * 255) / (max_in as u32)) as u8
646}
647
648#[cfg(test)]
649mod tests {
650 use super::*;
651 use num::BigUint;
652
653 #[test]
654 fn frame_buffer_bits_pads_to_word_length() {
655 let bits = frame_buffer_bits(&VariableValue::BigUint(BigUint::from(0b101u8)), 5);
656 assert_eq!(bits, vec![false, false, true, false, true]);
657 }
658
659 #[test]
660 fn frame_buffer_bits_truncates_to_word_length() {
661 let bits = frame_buffer_bits(&VariableValue::String("101101".to_string()), 4);
662 assert_eq!(bits, vec![true, true, false, true]);
663 }
664
665 #[test]
666 fn bits_to_u16_padded_reads_and_zero_pads() {
667 let bits = vec![true, false, true];
668 assert_eq!(bits_to_u16_padded(&bits, 0, 3), 0b101);
669 assert_eq!(bits_to_u16_padded(&bits, 1, 4), 0b0100);
670 }
671
672 #[test]
673 fn scale_to_u8_scales_full_range() {
674 assert_eq!(scale_to_u8(0, 1), 0);
675 assert_eq!(scale_to_u8(1, 1), 255);
676 assert_eq!(scale_to_u8(7, 3), 255);
677 assert_eq!(scale_to_u8(4, 3), 145);
678 }
679
680 #[test]
681 fn decode_grayscale_pixels_uses_bit_groups() {
682 let bits = vec![false, false, true, true];
683 let pixels = decode_grayscale_pixels(&bits, 2);
684 assert_eq!(pixels.len(), 2);
685 assert_eq!(pixels[0], Color32::from_rgb(0, 0, 0));
686 assert_eq!(pixels[1], Color32::from_rgb(255, 255, 255));
687 }
688
689 #[test]
690 fn decode_rgb_pixels_supports_different_channel_widths() {
691 let bits = vec![
692 true, false, false, true, true, false, ];
694 let pixels = decode_rgb_pixels(&bits, 2, 2, 2);
695 assert_eq!(pixels.len(), 1);
696 assert_eq!(pixels[0], Color32::from_rgb(170, 85, 170));
697 }
698
699 #[test]
700 fn variable_array_index_parses_bracketed_name() {
701 let var_ref = VariableRef::new(ScopeRef::empty(), "[2]".to_string());
702 assert_eq!(variable_array_index(&var_ref), 2);
703 }
704
705 #[test]
706 fn variable_array_index_parses_plain_numeric_name() {
707 let var_ref = VariableRef::new(ScopeRef::empty(), "7".to_string());
708 assert_eq!(variable_array_index(&var_ref), 7);
709 }
710
711 #[test]
712 fn variable_array_index_prefers_explicit_index() {
713 let var_ref = VariableRef::new_with_id_and_index(
714 ScopeRef::empty(),
715 "[2]".to_string(),
716 Default::default(),
717 Some(9),
718 );
719 assert_eq!(variable_array_index(&var_ref), 9);
720 }
721
722 #[test]
723 fn variable_array_index_falls_back_to_max_for_non_numeric_names() {
724 let var_ref = VariableRef::new(ScopeRef::empty(), "data".to_string());
725 assert_eq!(variable_array_index(&var_ref), i64::MAX);
726 }
727}