xrpl_wasm_stdlib/core/locator.rs
1//! Builder for nested field access locators.
2//!
3//! Locators encode a path to a nested field (sfields and array indices) in a compact
4//! binary format understood by the host. Use it to access fields like `Memos[0].MemoType`.
5//!
6//! Example
7//! ```no_run
8//! use xrpl_wasm_stdlib::core::locator::Locator;
9//! use xrpl_wasm_stdlib::sfield;
10//! let mut l = Locator::new();
11//! l.pack(sfield::Memos);
12//! l.pack(0);
13//! l.pack(sfield::MemoType);
14//! # let _ = (l.len() >= 3);
15//! ```
16
17/// The size of the buffer, in bytes, to use for any new locator
18const LOCATOR_BUFFER_SIZE: usize = 64;
19
20// /// A Locator may only pack this many levels deep in an object hierarchy (inclusive of the first
21// /// field)
22// const MAX_DEPTH: u8 = 12; // 1 byte for slot; 5 bytes for each packed object.
23
24/// A Locator allows a WASM developer located any field in any object (even nested fields) by
25/// specifying a `slot_num` (1 byte); a `locator_field_type` (1 byte); then one of an `sfield` (4
26/// bytes) or an `index` (4 bytes).
27#[derive(Debug, Clone, Copy, Eq, PartialEq)]
28#[repr(C)]
29pub struct Locator {
30 // The first packed value is 6 bytes; All nested/packed values are 5 bytes; so 64 bytes allow
31 // 12 nested levels of access.
32 buffer: [u8; LOCATOR_BUFFER_SIZE],
33
34 /// An index into `buffer` where the next packing operation can be stored.
35 cur_buffer_index: usize,
36}
37
38impl Default for Locator {
39 fn default() -> Self {
40 Self::new()
41 }
42}
43
44impl Locator {
45 /// Create a new Locator using an unsigned 8-bit slot number. Valid slots are 0 to 255.
46 pub fn new_with_slot(slot_num: u8) -> Locator {
47 let mut buffer: [u8; 64] = [0; 64];
48 buffer[0] = slot_num;
49 Self {
50 buffer,
51 cur_buffer_index: 1,
52 }
53 }
54
55 /// Create a new Locator. Valid slots are 0 to 255.
56 pub fn new() -> Locator {
57 Self {
58 buffer: [0; 64],
59 cur_buffer_index: 0,
60 }
61 }
62
63 pub fn pack(&mut self, sfield_or_index: i32) -> bool {
64 if self.cur_buffer_index + 4 > LOCATOR_BUFFER_SIZE {
65 return false;
66 }
67
68 let value_bytes: [u8; 4] = sfield_or_index.to_le_bytes();
69
70 for byte in value_bytes.iter() {
71 match self.buffer.get_mut(self.cur_buffer_index) {
72 Some(b) => *b = *byte,
73 None => return false,
74 }
75 self.cur_buffer_index += 1;
76 }
77
78 true
79 }
80
81 pub fn get_addr(&self) -> *const u8 {
82 self.buffer.as_ptr()
83 }
84
85 pub fn as_ptr(&self) -> *const u8 {
86 self.buffer.as_ptr()
87 }
88
89 pub fn num_packed_bytes(&self) -> usize {
90 self.cur_buffer_index
91 }
92
93 pub fn len(&self) -> usize {
94 self.cur_buffer_index
95 }
96
97 pub fn is_empty(&self) -> bool {
98 self.cur_buffer_index == 0
99 }
100
101 pub fn repack_last(&mut self, sfield_or_index: i32) -> bool {
102 self.cur_buffer_index -= 4;
103
104 let value_bytes: [u8; 4] = sfield_or_index.to_le_bytes();
105
106 for byte in value_bytes.iter() {
107 match self.buffer.get_mut(self.cur_buffer_index) {
108 Some(b) => *b = *byte,
109 None => return false,
110 }
111 self.cur_buffer_index += 1;
112 }
113
114 true
115 }
116}