bipack_ru/
bipack_source.rs

1// Copyright 2023 by Sergey S. Chernov.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::fmt::{Display, Formatter};
16use std::string::FromUtf8Error;
17use crate::bipack_source::BipackError::NoDataError;
18
19/// Result of error-aware bipack function
20pub type Result<T> = std::result::Result<T, BipackError>;
21
22/// There is not enough data to fulfill the request
23#[derive(Debug, Clone, PartialEq, Eq)]
24#[non_exhaustive]
25pub enum BipackError {
26    NoDataError,
27    BadEncoding(FromUtf8Error),
28    BufferOverflow,
29    Message(String),
30    BadFormat(String),
31    Eof,
32    ExtraBytes,
33    NotSupported,
34    NotImplemented,
35    NotPossible
36}
37
38impl std::error::Error for BipackError {}
39
40impl Display for BipackError {
41    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
42        write!(f, "{:?}", self)
43    }
44}
45
46/// Data source compatible with mp_bintools serialization. It supports
47/// fixed-size integers in right order and varint ans smartint encodings
48/// separately. There is out of the box implementation for [`Vec<u8>`], and
49/// it is easy to implements your own.
50///
51/// To implement source for other type, implement just [BipackSource::get_u8],
52/// [BipackSource::eof] or maybe also
53/// [BipackSource::get_fixed_bytes] for effectiveness.
54///
55/// Unlike the [crate::bipack_sink::BipackSink] the source is returning errors. This is because
56/// it often appears when reading data do not correspond to the packed one, and this is an often
57/// case that requires proper reaction, not just a panic attack :)
58pub trait BipackSource {
59    fn get_u8(self: &mut Self) -> Result<u8>;
60
61    /// Tribute to the tradition. End Of File, if true, there is no unread data left
62    /// in the source.
63    fn eof(self: &Self) -> bool;
64
65    fn get_u16(self: &mut Self) -> Result<u16> {
66        Ok(((self.get_u8()? as u16) << 8) + (self.get_u8()? as u16))
67    }
68    fn get_u32(self: &mut Self) -> Result<u32> {
69        Ok(((self.get_u16()? as u32) << 16) + (self.get_u16()? as u32))
70    }
71
72    fn get_u64(self: &mut Self) -> Result<u64> {
73        Ok(((self.get_u32()? as u64) << 32) | (self.get_u32()? as u64))
74    }
75
76    fn get_i64(self: &mut Self) -> Result<i64> {
77        Ok(self.get_u64()? as i64)
78    }
79    fn get_i32(self: &mut Self) -> Result<i32> {
80        Ok(self.get_u32()? as i32)
81    }
82    fn get_i16(self: &mut Self) -> Result<i16> {
83        Ok(self.get_u16()? as i16)
84    }
85    fn get_i8(self: &mut Self) -> Result<i8> {
86        Ok(self.get_u8()? as i8)
87    }
88
89    /// Unpack variable-length packed unsigned value, used also internally to store size
90    /// of arrays, binary data, strings, etc. To pack use
91    /// [crate::bipack_sink::BipackSink::put_unsigned()].
92    fn get_unsigned(self: &mut Self) -> Result<u64> {
93        let mut get = || -> Result<u64> { Ok(self.get_u8()? as u64) };
94        let first = get()?;
95        let mut ty = first & 3;
96
97
98        let mut result = first >> 2;
99        if ty == 0 { return Ok(result); }
100        ty -= 1;
101
102        result = result + (get()? << 6);
103        if ty == 0 { return Ok(result); }
104        ty -= 1;
105
106        result = result + (get()? << 14);
107        if ty == 0 { return Ok(result); }
108
109        Ok(result | (self.get_varint_unsigned()? << 22))
110    }
111
112    /// Unpack variable-length signed value, packed with
113    /// [crate::bipack_sink::BipackSink::put_signed], see it for the  packing details.
114    fn get_signed(self: &mut Self) -> Result<i64> {
115        let value = self.get_unsigned()?;
116        let result = (value >> 1) as i64;
117        Ok(if value & 1 != 0 { -result } else { result } )
118    }
119
120    /// read 8-bytes varint-packed unsigned value from the source. We dont' recommend
121    /// using it directly; use [BipackSource::get_unsigned] instead.
122    fn get_varint_unsigned(self: &mut Self) -> Result<u64> {
123        let mut result = 0u64;
124        let mut count = 0;
125        loop {
126            let x = self.get_u8()? as u64;
127            result = result | ((x & 0x7F) << count);
128            if (x & 0x80) == 0 { return Ok(result); }
129            count += 7
130        }
131    }
132
133    /// read 2-bytes unsigned value from the source as smartint-encoded, same as
134    /// [BipackSource::get_unsigned] as u16
135    fn get_packed_u16(self: &mut Self) -> Result<u16> {
136        Ok(self.get_unsigned()? as u16)
137    }
138
139    /// read 4-bytes unsigned value from the source
140    /// read 2-bytes unsigned value from the source as smartint-encoded, same as
141    /// [BipackSource::get_unsigned] as u32.
142    fn get_packed_u32(self: &mut Self) -> Result<u32> { Ok(self.get_unsigned()? as u32) }
143
144    /// read exact number of bytes from the source as a vec.
145    fn get_fixed_bytes(self: &mut Self, size: usize) -> Result<Vec<u8>> {
146        let mut result = Vec::with_capacity(size);
147        for i in 0..size { result.push(self.get_u8()?); }
148        Ok(result)
149    }
150
151    /// Read variable-length byte array from the source (with packed size), created
152    /// by [crate::bipack_sink::BipackSink::put_var_bytes] or
153    /// [crate::bipack_sink::BipackSink::put_str]. The size is encoded the same way as does
154    /// [crate::bipack_sink::BipackSink::put_unsigned] and can be manually read by
155    /// [BipackSource::get_unsigned].
156    fn get_var_bytes(self: &mut Self) -> Result<Vec<u8>> {
157        let size = self.get_unsigned()? as usize;
158        self.get_fixed_bytes(size)
159    }
160
161    /// REad a variable length string from a source packed with
162    /// [crate::bipack_sink::BipackSink::put_str]. It is a variable sized array fo utf8 encoded
163    /// characters.
164    fn get_str(self: &mut Self) -> Result<String> {
165        String::from_utf8(
166            self.get_var_bytes()?
167        ).or_else(|e| Err(BipackError::BadEncoding(e)))
168    }
169}
170
171/// The bipack source capable of extracting data from a slice.
172/// use [SliceSource::from()] to create one.
173pub struct SliceSource<'a> {
174    data: &'a [u8],
175    position: usize,
176}
177
178impl<'a> SliceSource<'a> {
179    pub fn from(src: &'a [u8]) -> SliceSource<'a> {
180        SliceSource { data: src, position: 0 }
181    }
182}
183
184impl<'x> BipackSource for SliceSource<'x> {
185    fn get_u8(self: &mut Self) -> Result<u8> {
186        if self.position >= self.data.len() {
187            Err(NoDataError)
188        } else {
189            let result = self.data[self.position];
190            self.position += 1;
191            Ok(result)
192        }
193    }
194
195    fn eof(self: &Self) -> bool {
196        self.data.len() >= self.position
197    }
198}
199
200
201
202
203