il2_ilint/lib.rs
1/*
2 * BSD 3-Clause License
3 *
4 * Copyright (c) 2020, InterlockLedger Network
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * * Redistributions of source code must retain the above copyright notice, this
11 * list of conditions and the following disclaimer.
12 *
13 * * Redistributions in binary form must reproduce the above copyright notice,
14 * this list of conditions and the following disclaimer in the documentation
15 * and/or other materials provided with the distribution.
16 *
17 * * Neither the name of the copyright holder nor the names of its
18 * contributors may be used to endorse or promote products derived from
19 * this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32//! This crate provides a **no_std** implementation of the **InterlockLedger ILInt**
33//! format. This format allows the encoding of 64 bit integer values in a compact
34//! format that uses 1 to 9 bytes of space depending on the actual value. See
35//! [ILInt Specification](https://github.com/interlockledger/specification/tree/master/ILInt)
36//! for further details about the format.
37#![no_std]
38
39#[cfg(test)]
40mod tests;
41
42/// LInt base value. All values smaller than this value are encoded as
43/// a single byte.
44pub const ILINT_BASE:u8 = 0xF8;
45
46/// Value of ILINT_BASE as U64.
47pub const ILINT_BASE_U64:u64 = ILINT_BASE as u64;
48
49/// Types of errors generated by this library.
50pub enum ErrorKind {
51 /// The provided buffer is too small.
52 InsufficientBuffer,
53 /// The encoded value is larger than 26^4 - 1 (18446744073709551615).
54 Overflow,
55}
56
57/// Alias to the Result used by this library.
58pub type Result<T> = core::result::Result<T, ErrorKind>;
59
60/// Returns the size of the given value encoded as an ILInt.
61///
62/// Arguments:
63///
64/// * `value` : The value to be encoded;
65///
66/// Returns:
67///
68/// * The number of bytes required to encode the value;
69///
70pub fn encoded_size(value: u64) -> usize {
71
72 if value < ILINT_BASE_U64 {
73 1
74 } else if value <= (0xFF + ILINT_BASE_U64) {
75 2
76 } else if value <= (0xFFFF + ILINT_BASE_U64) {
77 3
78 } else if value <= (0x00FF_FFFF + ILINT_BASE_U64){
79 4
80 } else if value <= (0xFFFF_FFFF + ILINT_BASE_U64){
81 5
82 } else if value <= (0x00FF_FFFF_FFFF + ILINT_BASE_U64){
83 6
84 } else if value <= (0xFFFF_FFFF_FFFF + ILINT_BASE_U64){
85 7
86 } else if value <= (0x00FF_FFFF_FFFF_FFFF + ILINT_BASE_U64){
87 8
88 } else {
89 9
90 }
91}
92
93/// Encodes the given value into a ILInt value.
94///
95/// Arguments:
96///
97/// * `value`: The value to be encoded;
98/// * `enc`: The slice that will receive the encoded value.
99/// It must have at least encoded_size(value) bytes;
100///
101/// Returns:
102///
103/// * `Ok(size)`: The number of bytes used.
104/// * `Err(ErrorKind::InsufficientBuffer)`: If the buffer is too small
105/// to hold the encoded value.
106///
107pub fn encode(value: u64, enc: &mut[u8]) -> Result<usize> {
108
109 let size = encoded_size(value);
110 if size > enc.len() {
111 Err(ErrorKind::InsufficientBuffer)
112 } else {
113 if size == 1 {
114 enc[0] = value as u8
115 } else {
116 enc[0] = (ILINT_BASE + ((size - 2) as u8)) as u8;
117 let v = value - ILINT_BASE_U64;
118 let mut shift = 8 * (size - 1);
119 for i in enc.iter_mut().take(size).skip(1) {
120 shift -= 8;
121 *i = ((v >> shift) & 0xFF) as u8;
122 }
123 }
124 Ok(size)
125 }
126}
127
128/// Determines the size of the ILInt based on its header (the
129/// first byte of the encoded value).
130///
131/// Arguments:
132///
133/// * `header`: The header of the ILInt. It is always the first byte of
134/// the ILInt value;
135///
136/// Returns:
137///
138/// * The size of the ILInt in bytes, including the header.
139///
140pub fn decoded_size(header : u8) -> usize {
141
142 if header < ILINT_BASE {
143 1
144 } else {
145 (header - ILINT_BASE + 2) as usize
146 }
147}
148
149/// Decodes an **ILInt** value.
150///
151/// Arguments:
152///
153/// * `value`: The **ILInt** value;
154///
155/// Returns:
156///
157/// * Ok(value,size): The decoded value and the number of bytes used.
158/// * `Err(ErrorKind::InsufficientBuffer)`: If the buffer is too small
159/// to hold the encoded value.
160/// * `Err(ErrorKind::Overflow)`: If the encoded value is larger than
161/// the maximum allowed value.
162///
163pub fn decode(value: &[u8]) -> Result<(u64, usize)> {
164
165 if value.is_empty() {
166 return Err(ErrorKind::InsufficientBuffer);
167 }
168
169 let size = decoded_size(value[0]);
170 if size > value.len() {
171 return Err(ErrorKind::InsufficientBuffer);
172 }
173
174 if size == 1 {
175 Ok((value[0] as u64, 1))
176 } else {
177 let mut v:u64 = 0;
178 for i in value.iter().take(size).skip(1) {
179 v = (v << 8) + (*i as u64);
180 }
181 if v > 0xFFFF_FFFF_FFFF_FF07 {
182 Err(ErrorKind::Overflow)
183 } else {
184 Ok((v + ILINT_BASE_U64, size))
185 }
186 }
187}