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}