xrpl/core/binarycodec/
utils.rs

1//! Utilities for binarycodec crate.
2
3use super::definitions::load_definition_map;
4use super::definitions::DefinitionHandler;
5use super::definitions::FieldHeader;
6use super::definitions::CODE_MAX_VALUE;
7use super::definitions::CODE_MIN_VALUE;
8use crate::core::binarycodec::exceptions::XRPLBinaryCodecException;
9use crate::core::exceptions::XRPLCoreResult;
10use alloc::vec;
11use alloc::vec::Vec;
12
13/// Max length that can be represented in a single byte
14/// per XRPL serialization encoding.
15pub const MAX_SINGLE_BYTE_LENGTH: usize = 192;
16/// Max length that can be represented in 2 bytes per
17/// XRPL serialization encoding.
18pub const MAX_DOUBLE_BYTE_LENGTH: usize = 12481;
19/// Max value that can be used in the second byte of a
20/// length field.
21pub const MAX_SECOND_BYTE_VALUE: usize = 240;
22/// Max value that can be represented in using two
23/// 8-bit bytes (2^16)
24pub const MAX_DOUBLE_BYTE_VALUE: usize = 65536;
25/// Maximum length that can be encoded in a length
26/// prefix per XRPL serialization encoding.
27pub const MAX_LENGTH_VALUE: usize = 918744;
28/// Max value that can be represented using one 8-bit
29/// byte (2^8)
30pub const MAX_BYTE_VALUE: usize = 256;
31
32/// See: `<https://xrpl.org/serialization.html#field-ids>`
33fn _encode_field_id(field_header: &FieldHeader) -> XRPLCoreResult<Vec<u8>> {
34    let type_code = field_header.type_code;
35    let field_code = field_header.field_code;
36    let range = CODE_MIN_VALUE..CODE_MAX_VALUE;
37
38    if !range.contains(&field_code) {
39        Err(XRPLBinaryCodecException::UnexpectedFieldCodeRange {
40            min: CODE_MIN_VALUE as usize,
41            max: CODE_MAX_VALUE as usize,
42        }
43        .into())
44    } else if !range.contains(&type_code) {
45        Err(XRPLBinaryCodecException::UnexpectedTypeCodeRange {
46            min: CODE_MIN_VALUE as usize,
47            max: CODE_MAX_VALUE as usize,
48        }
49        .into())
50    } else if type_code < 16 && field_code < 16 {
51        // high 4 bits is the type_code
52        // low 4 bits is the field code
53        let combined_code = (type_code << 4) | field_code;
54        Ok([combined_code as u8].to_vec())
55    } else if type_code >= 16 && field_code < 16 {
56        // first 4 bits are zeroes
57        // next 4 bits is field code
58        // next byte is type code
59        let mut result = vec![];
60        let byte1 = [field_code as u8];
61        let byte2 = [type_code as u8];
62
63        result.extend_from_slice(&byte1);
64        result.extend_from_slice(&byte2);
65
66        Ok(result)
67    } else if type_code < 16 && field_code >= 16 {
68        // first 4 bits is type code
69        // next 4 bits are zeroes
70        // next byte is field code
71        let mut result = vec![];
72        let byte1 = [(type_code << 4) as u8];
73        let byte2 = [field_code as u8];
74
75        result.extend_from_slice(&byte1);
76        result.extend_from_slice(&byte2);
77
78        Ok(result)
79    } else {
80        // both are >= 16
81        // first byte is all zeroes
82        // second byte is type code
83        // third byte is field code
84        let mut result = vec![];
85        let byte2 = [type_code as u8];
86        let byte3 = [field_code as u8];
87
88        result.extend_from_slice(&[0]);
89        result.extend_from_slice(&byte2);
90        result.extend_from_slice(&byte3);
91
92        Ok(result)
93    }
94}
95
96/// See: `<https://xrpl.org/serialization.html#field-ids>`
97fn _decode_field_id(field_id: &str) -> XRPLCoreResult<FieldHeader> {
98    let bytes = hex::decode(field_id)?;
99
100    match bytes.len() {
101        1 => {
102            let type_code = (bytes[0] >> 4) as i16;
103            let field_code = (bytes[0] & 0x0F) as i16;
104
105            Ok(FieldHeader {
106                type_code,
107                field_code,
108            })
109        }
110        2 => {
111            let first_byte = bytes[0];
112            let second_byte = bytes[1];
113            let first_byte_high_bits = first_byte >> 4;
114            let first_byte_low_bits = first_byte & 0x0F;
115
116            if first_byte_high_bits == 0 {
117                // Next 4 bits are field code, second byte
118                // is type code.
119                let type_code = second_byte as i16;
120                let field_code = first_byte_low_bits as i16;
121
122                Ok(FieldHeader {
123                    type_code,
124                    field_code,
125                })
126            } else {
127                // Otherwise, next 4 bits are type code,
128                // second byte is field code.
129                let type_code = first_byte_high_bits as i16;
130                let field_code = second_byte as i16;
131
132                Ok(FieldHeader {
133                    type_code,
134                    field_code,
135                })
136            }
137        }
138        3 => {
139            let type_code = bytes[1] as i16;
140            let field_code = bytes[2] as i16;
141
142            Ok(FieldHeader {
143                type_code,
144                field_code,
145            })
146        }
147        _ => Err(XRPLBinaryCodecException::UnexpectedFieldIdByteRange { min: 1, max: 3 }.into()),
148    }
149}
150
151/// Returns the unique field ID for a given field name.
152/// This field ID consists of the type code and field
153/// code, in 1 to 3 bytes depending on whether those
154/// values are "common" (<16) or "uncommon" (>=16)
155///
156/// See Field Ids:
157/// `<https://xrpl.org/serialization.html#field-ids>`
158///
159/// # Examples
160///
161/// ## Basic usage
162///
163/// ```
164/// use xrpl::core::binarycodec::utils::encode_field_name;
165/// use xrpl::core::binarycodec::exceptions::XRPLBinaryCodecException;
166/// use xrpl::core::exceptions::XRPLCoreException;
167/// extern crate alloc;
168/// use alloc::vec;
169///
170/// let field_name: &str = "LedgerSequence";
171/// let bytes: Vec<u8> = vec![38];
172///
173/// let encoding: Option<Vec<u8>> = match encode_field_name(field_name) {
174///     Ok(bytes) => Some(bytes),
175///     Err(e) => match e {
176///         XRPLCoreException::XRPLBinaryCodecError(XRPLBinaryCodecException::UnknownFieldName) => None,
177///         _ => None,
178///     }
179/// };
180///
181/// assert_eq!(Some(bytes), encoding);
182/// ```
183pub fn encode_field_name(field_name: &str) -> XRPLCoreResult<Vec<u8>> {
184    let definitions = load_definition_map();
185    let field_header = definitions.get_field_header_from_name(field_name);
186    if let Some(header) = field_header {
187        _encode_field_id(&header)
188    } else {
189        Err(XRPLBinaryCodecException::UnknownFieldName.into())
190    }
191}
192
193/// Returns the field name represented by the given field ID.
194///
195/// See Field Ids:
196/// `<https://xrpl.org/serialization.html#field-ids>`
197///
198/// # Examples
199///
200/// ## Basic usage
201///
202/// ```
203/// use xrpl::core::binarycodec::utils::decode_field_name;
204/// use xrpl::core::binarycodec::exceptions::XRPLBinaryCodecException;
205/// use xrpl::core::exceptions::XRPLCoreException;
206///
207/// let field_id: &str = "26";
208/// let field_name: &str = "LedgerSequence";
209///
210/// let decoding: Option<&str> = match decode_field_name(field_id) {
211///     Ok(field_name) => Some(field_name),
212///     Err(e) => match e {
213///         XRPLCoreException::XRPLBinaryCodecError(XRPLBinaryCodecException::UnexpectedFieldIdByteRange {
214///             min: _,
215///             max: _
216///         }) => None,
217///         _ => None,
218///     }
219/// };
220///
221/// assert_eq!(Some(field_name), decoding);
222/// ```
223pub fn decode_field_name(field_id: &str) -> XRPLCoreResult<&str> {
224    let definitions = load_definition_map();
225    let field_header = _decode_field_id(field_id)?;
226    let field_name = definitions.get_field_name_from_header(&field_header);
227
228    if let Some(name) = field_name {
229        Ok(name)
230    } else {
231        Err(XRPLBinaryCodecException::UnknownFieldName.into())
232    }
233}
234
235#[cfg(test)]
236mod test {
237    use super::*;
238    use crate::core::binarycodec::test_cases::load_field_tests;
239
240    #[test]
241    fn test_encode_field_name() {
242        for test in load_field_tests() {
243            let result = hex::encode_upper(encode_field_name(&test.name).expect(""));
244            assert_eq!(test.expected_hex, result)
245        }
246    }
247
248    #[test]
249    fn test_decode_field_name() {
250        for test in load_field_tests() {
251            assert_eq!(
252                decode_field_name(&test.expected_hex),
253                Ok(test.name.as_ref())
254            )
255        }
256    }
257}