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}