binary_data_schema/
integer.rs

1//! Implementation of the integer schema
2//!
3//! The integer schema allows en- and decoding of integers.
4//! BDS supports integers up to a length of 8 bytes.
5//! In order to support more dense encoding, BDS's integer schema allow for addressing [bitfields].
6//! This means that an encoded integer does not have to align with bytes.
7//!
8//! # Parameters
9//!
10//! | Key           | Type     | Default  | Comment |
11//! | ------------- | --------:| --------:| ------- |
12//! | `"byteorder"` | `string` | "bigendian" | The order in which the bytes are encoded. |
13//! | `"length"`    |   `uint` |        4 | Number of bytes of the encoded integer |
14//! | `"signed"`    |   `bool` |     true | Whether the integer is signed or not |
15//! | `"bits"`      |   `uint` | optional | Number of bits the [bitfield] covers |
16//! | `"bitoffset"` |   `uint` | optional | Number of bits the [bitfield] is shifted |
17//!
18//! ## Validation
19//!
20//! BDS only supports integers up to 64 bit.
21//! Accordingly, a `"length"` bigger than 8 is invalid.
22//!
23//! # Features
24//!
25//! ## Endianness
26//!
27//! Valid values for `"byteorder"` are `"bigendian"` and `"littleendian"`.
28//! Endianness defines the order the bytes of an integer are encoded.
29//! With big endian the Most Significant Byte (MSB) is placed first.
30//! Contrary, little endian encodes the Least Significant Byte (LSB) first.
31//!
32//! ### Example
33//!
34//! ```
35//! # use binary_data_schema::*;
36//! # use valico::json_schema;
37//! # use serde_json::{json, from_value};
38//! let schema = json!({
39//!     "type": "integer",
40//!     "byteorder": "littleendian",
41//!     "length": 2
42//! });
43//!
44//! let mut scope = json_schema::Scope::new();
45//! let j_schema = scope.compile_and_return(schema.clone(), false)?;
46//! let schema = from_value::<DataSchema>(schema)?;
47//!
48//! let value = json!(0x01_02);
49//! assert!(j_schema.validate(&value).is_valid());
50//! let mut encoded = Vec::new();
51//! schema.encode(&mut encoded, &value)?;
52//! let expected = [0x02, 0x01];
53//! assert_eq!(&expected, encoded.as_slice());
54//!
55//! let mut encoded = std::io::Cursor::new(encoded);
56//! let back = schema.decode(&mut encoded)?;
57//! assert!(j_schema.validate(&back).is_valid());
58//! assert_eq!(back, value);
59//! # Ok::<(), anyhow::Error>(())
60//! ```
61//!
62//! ## Bitfield
63//!
64//! Integer can not only be encoded into full bytes but also into a number of bits.
65//! It is common practice to save space by [merging several bitfields] into a number of bytes if the individual values do not need a whole byte.
66//! For example, when a thermometer can only display 0 °C through 60 °C with a precision of 1 °C all values fit into 6 bits (max 63).
67//!
68//! ### Recognizing an Integer Schema as Bitfield
69//!
70//! An integer schema is recognized as bitfield when either `"bits"` or `"bitoffset"` are given.
71//!
72//! Bitfields are always _unsigned_ and _big endian_.
73//! When a bitfield is recognized the values for `"signed"` and `"byteorder"` are **ignored**.
74//!
75//! ### Example
76//!
77//! ```
78//! # use binary_data_schema::*;
79//! # use valico::json_schema;
80//! # use serde_json::{json, from_value};
81//! let schema = json!({
82//!     "type": "integer",
83//!     "bits": 6,
84//!     "bitoffset": 2,
85//!     "length": 1
86//! });
87//!
88//! let mut scope = json_schema::Scope::new();
89//! let j_schema = scope.compile_and_return(schema.clone(), false)?;
90//! let schema = from_value::<DataSchema>(schema)?;
91//!
92//! let value = json!(0b0011_1001); // decimal: 57
93//! assert!(j_schema.validate(&value).is_valid());
94//! let mut encoded = Vec::new();
95//! schema.encode(&mut encoded, &value)?;
96//! let expected = [ 0b1110_0100 ];
97//! assert_eq!(&expected, encoded.as_slice());
98//!
99//! let mut encoded = std::io::Cursor::new(encoded);
100//! let back = schema.decode(&mut encoded)?;
101//! assert!(j_schema.validate(&back).is_valid());
102//! assert_eq!(back, value);
103//! # Ok::<(), anyhow::Error>(())
104//! ```
105//!
106//! ## Range and Multiples
107//!
108//! BDS does not check for [`"minimum"`, `"maximum"`] or [`"multiples"`], yet.
109//!
110//! [bitfields]: ../object/index.html#bitfields
111//! [bitfield]: ../object/index.html#bitfields
112//! [merging several bitfields]: ../object/index.html#bitfields
113//! [`"minimum"`, `"maximum"`]: https://json-schema.org/understanding-json-schema/reference/numeric.html#range
114//! [`"multiples"`]: https://json-schema.org/understanding-json-schema/reference/numeric.html#multiples
115
116use std::{convert::TryFrom, io};
117
118use byteorder::{ReadBytesExt, WriteBytesExt, BE, LE};
119use serde::{de::Error as _, Deserialize, Deserializer};
120use serde_json::Value;
121
122use crate::{ByteOrder, Decoder, Encoder};
123
124const MAX_INTEGER_SIZE: usize = 8;
125const DEFAULT_LENGTH: usize = 4;
126const DEFAULT_SIGNED: bool = true;
127
128/// Errors validating an [IntegerSchema].
129#[derive(Debug, thiserror::Error)]
130pub enum ValidationError {
131    #[error("Invalid length requested: Maximum allowed is {max} but {requested} where requested")]
132    MaxLength { max: usize, requested: usize },
133    #[error("A bitfield with offset {offset} and a width of {width} bits does not fit into a {bytes} bytes field")]
134    BitOffset {
135        bytes: usize,
136        offset: usize,
137        width: usize,
138    },
139    #[error("The requested field size of {requested} bits is insufficient. It must be between 1 and {max} bits")]
140    InvalidBitWidth { max: usize, requested: usize },
141    #[error("Invalid integer schema. Not a bitfield: {bf}; nor an integer: {int}")]
142    InvalidIntegerSchema {
143        bf: Box<ValidationError>,
144        int: Box<ValidationError>,
145    },
146    #[error(
147        "Tried to build a bitfield from a description that has neither 'bits' nor 'bitoffset'"
148    )]
149    NotABitfield,
150}
151
152/// Errors encoding a string with an [IntegerSchema].
153#[derive(Debug, thiserror::Error)]
154pub enum EncodingError {
155    #[error("The value '{value}' can not be encoded with an integer schema")]
156    InvalidValue { value: String },
157    #[error("Writing to buffer failed: {0}")]
158    WriteFail(#[from] io::Error),
159}
160
161/// Errors decoding a string with an [IntegerSchema].
162#[derive(Debug, thiserror::Error)]
163pub enum DecodingError {
164    #[error("Reading encoded data failed: {0}")]
165    ReadFail(#[from] io::Error),
166}
167
168impl DecodingError {
169    pub fn due_to_eof(&self) -> bool {
170        matches!(self, Self::ReadFail(e) if e.kind() == std::io::ErrorKind::UnexpectedEof)
171    }
172}
173
174#[derive(Debug, Clone, Deserialize)]
175#[serde(rename_all = "lowercase")]
176pub(crate) struct RawIntegerSchema {
177    #[serde(default)]
178    pub(crate) byteorder: ByteOrder,
179    #[serde(default = "IntegerSchema::default_length")]
180    pub(crate) length: usize,
181    #[serde(default = "IntegerSchema::default_signed")]
182    pub(crate) signed: bool,
183    pub(crate) bits: Option<usize>,
184    #[serde(rename = "bitoffset")]
185    pub(crate) bit_offset: Option<usize>,
186}
187
188/// A simple integer schema.
189#[derive(Debug, Clone, Copy)]
190pub struct PlainInteger {
191    byteorder: ByteOrder,
192    length: usize,
193    signed: bool,
194}
195
196/// A schema referencing some bits within a chunk of bytes.
197#[derive(Debug, Clone, Copy)]
198pub struct Bitfield {
199    pub(crate) bytes: usize,
200    pub(crate) width: usize,
201    pub(crate) offset: usize,
202}
203
204/// An integer schema. May refer to a bitfield (further information on [the module's documentation](index.html)).
205#[derive(Debug, Clone)]
206pub enum IntegerSchema {
207    Integer(PlainInteger),
208    Bitfield(Bitfield),
209}
210
211impl Default for Bitfield {
212    fn default() -> Self {
213        Self {
214            bytes: 1,
215            width: 1,
216            offset: 0,
217        }
218    }
219}
220
221impl Bitfield {
222    /// Create a new bitfield.
223    pub fn new(bytes: usize, width: usize, offset: usize) -> Result<Self, ValidationError> {
224        let max_bits = bytes * 8;
225        if bytes > MAX_INTEGER_SIZE {
226            Err(ValidationError::MaxLength {
227                max: MAX_INTEGER_SIZE,
228                requested: bytes,
229            })
230        } else if width + offset > max_bits {
231            Err(ValidationError::BitOffset {
232                bytes,
233                width,
234                offset,
235            })
236        } else if width == 0 || width > max_bits {
237            Err(ValidationError::InvalidBitWidth {
238                max: max_bits,
239                requested: width,
240            })
241        } else {
242            Ok(Self {
243                bytes,
244                width,
245                offset,
246            })
247        }
248    }
249    /// Width of the bitfield in bits.
250    pub fn bits(&self) -> usize {
251        self.width
252    }
253    pub fn bytes(&self) -> usize {
254        self.bytes
255    }
256    /// Mask to select the bits covered by the bitfield.
257    pub fn mask(&self) -> u64 {
258        ((1 << self.width) - 1) << self.offset
259    }
260    /// Read the value of the bitfield from bytes.
261    pub fn read(&self, value: u64) -> u64 {
262        (value & self.mask()) >> self.offset
263    }
264    /// Write the value to the described bitfield.
265    pub fn write(&self, value: u64, target: &mut u64) {
266        let value = (value << self.offset) & self.mask();
267        *target |= value;
268    }
269    /// An integer schema to encode the bytes of the bitfield.
270    pub(crate) fn integer(&self) -> PlainInteger {
271        PlainInteger::new(ByteOrder::BigEndian, self.bytes, false)
272            .expect("Invariants of bitfield match those of integer")
273    }
274}
275
276impl Default for PlainInteger {
277    fn default() -> Self {
278        PlainInteger {
279            length: IntegerSchema::default_length(),
280            signed: IntegerSchema::default_signed(),
281            byteorder: Default::default(),
282        }
283    }
284}
285
286impl PlainInteger {
287    pub fn new(byteorder: ByteOrder, length: usize, signed: bool) -> Result<Self, ValidationError> {
288        if length > MAX_INTEGER_SIZE {
289            Err(ValidationError::MaxLength {
290                max: MAX_INTEGER_SIZE,
291                requested: length,
292            })
293        } else {
294            Ok(Self {
295                byteorder,
296                length,
297                signed,
298            })
299        }
300    }
301    /// Default schema with a length of 8 bytes.
302    pub fn default_long() -> Self {
303        Self {
304            length: 8,
305            ..Default::default()
306        }
307    }
308    /// Default schema with a length of 1 byte.
309    pub fn signed_byte() -> Self {
310        Self {
311            length: 1,
312            ..Default::default()
313        }
314    }
315    pub fn unsigned_byte() -> Self {
316        Self {
317            length: 1,
318            signed: false,
319            ..Default::default()
320        }
321    }
322}
323
324impl IntegerSchema {
325    pub fn default_length() -> usize {
326        DEFAULT_LENGTH
327    }
328    pub fn default_signed() -> bool {
329        DEFAULT_SIGNED
330    }
331    /// Default integer schema with 8 bytes length.
332    pub fn long_int() -> Self {
333        PlainInteger::default_long().into()
334    }
335    /// Default integer schema with 1 byte length.
336    pub fn signed_byte() -> Self {
337        PlainInteger::signed_byte().into()
338    }
339    /// Default integer schema with 1 byte length.
340    pub fn unsigned_byte() -> Self {
341        PlainInteger::unsigned_byte().into()
342    }
343    /// Encoded length in bytes.
344    pub fn length(&self) -> usize {
345        match self {
346            IntegerSchema::Integer(PlainInteger { length, .. }) => *length,
347            IntegerSchema::Bitfield(Bitfield { bytes, .. }) => *bytes,
348        }
349    }
350    /// The theoretical maximal value that can be encoded.
351    pub fn max_value(&self) -> usize {
352        match self {
353            IntegerSchema::Integer(PlainInteger { length, signed, .. }) => {
354                let mut max = (1 << (length * 8)) - 1;
355                if *signed {
356                    max >>= 1;
357                }
358                max
359            }
360            IntegerSchema::Bitfield(Bitfield { width: bits, .. }) => (1 << bits) - 1,
361        }
362    }
363}
364
365impl Default for IntegerSchema {
366    fn default() -> Self {
367        PlainInteger::default().into()
368    }
369}
370
371impl From<Bitfield> for IntegerSchema {
372    fn from(bits: Bitfield) -> Self {
373        IntegerSchema::Bitfield(bits)
374    }
375}
376
377impl From<PlainInteger> for IntegerSchema {
378    fn from(integer: PlainInteger) -> Self {
379        IntegerSchema::Integer(integer)
380    }
381}
382
383impl TryFrom<RawIntegerSchema> for Bitfield {
384    type Error = ValidationError;
385
386    fn try_from(raw: RawIntegerSchema) -> Result<Self, Self::Error> {
387        if raw.bits.is_some() || raw.bit_offset.is_some() {
388            let bits = raw.bits.unwrap_or_else(|| raw.length * 8);
389            let offset = raw.bit_offset.unwrap_or_default();
390            Self::new(raw.length, bits, offset)
391        } else {
392            Err(ValidationError::NotABitfield)
393        }
394    }
395}
396
397impl TryFrom<RawIntegerSchema> for PlainInteger {
398    type Error = ValidationError;
399
400    fn try_from(raw: RawIntegerSchema) -> Result<Self, Self::Error> {
401        Self::new(raw.byteorder, raw.length, raw.signed)
402    }
403}
404
405impl TryFrom<RawIntegerSchema> for IntegerSchema {
406    type Error = ValidationError;
407
408    fn try_from(raw: RawIntegerSchema) -> Result<Self, Self::Error> {
409        match Bitfield::try_from(raw.clone()) {
410            Ok(bf) => Ok(bf.into()),
411            Err(ValidationError::NotABitfield) => PlainInteger::try_from(raw).map(Into::into),
412            Err(e) => Err(e),
413        }
414    }
415}
416
417impl<'de> Deserialize<'de> for IntegerSchema {
418    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
419    where
420        D: Deserializer<'de>,
421    {
422        let raw = RawIntegerSchema::deserialize(deserializer)?;
423        IntegerSchema::try_from(raw).map_err(D::Error::custom)
424    }
425}
426
427impl Encoder for Bitfield {
428    type Error = EncodingError;
429
430    fn encode<W>(&self, target: &mut W, value: &Value) -> Result<usize, Self::Error>
431    where
432        W: io::Write + WriteBytesExt,
433    {
434        let value = value.as_u64().ok_or_else(|| EncodingError::InvalidValue {
435            value: value.to_string(),
436        })?;
437        let mut buffer = 0;
438        self.write(value, &mut buffer);
439        let int = self.integer();
440        int.encode(target, &buffer.into())
441    }
442}
443
444impl Encoder for PlainInteger {
445    type Error = EncodingError;
446
447    fn encode<W>(&self, target: &mut W, value: &Value) -> Result<usize, Self::Error>
448    where
449        W: io::Write + WriteBytesExt,
450    {
451        let value = value.as_i64().ok_or_else(|| EncodingError::InvalidValue {
452            value: value.to_string(),
453        })?;
454        match (self.byteorder, self.signed) {
455            (ByteOrder::BigEndian, true) => target.write_int::<BE>(value, self.length)?,
456            (ByteOrder::BigEndian, false) => target.write_uint::<BE>(value as _, self.length)?,
457            (ByteOrder::LittleEndian, true) => target.write_int::<LE>(value, self.length)?,
458            (ByteOrder::LittleEndian, false) => target.write_uint::<LE>(value as _, self.length)?,
459        };
460
461        Ok(self.length)
462    }
463}
464
465impl Encoder for IntegerSchema {
466    type Error = EncodingError;
467
468    fn encode<W>(&self, target: &mut W, value: &Value) -> Result<usize, Self::Error>
469    where
470        W: io::Write + WriteBytesExt,
471    {
472        match self {
473            IntegerSchema::Integer(schema) => schema.encode(target, value),
474            IntegerSchema::Bitfield(schema) => schema.encode(target, value),
475        }
476    }
477}
478
479impl Decoder for Bitfield {
480    type Error = DecodingError;
481
482    fn decode<R>(&self, target: &mut R) -> Result<Value, Self::Error>
483    where
484        R: io::Read + ReadBytesExt,
485    {
486        let int = self.integer();
487        let int = int.decode(target)?.as_u64().expect("Is always u64.");
488        Ok(self.read(int).into())
489    }
490}
491
492impl Decoder for PlainInteger {
493    type Error = DecodingError;
494
495    fn decode<R>(&self, target: &mut R) -> Result<Value, Self::Error>
496    where
497        R: io::Read + ReadBytesExt,
498    {
499        Ok(match (self.byteorder, self.signed) {
500            (ByteOrder::BigEndian, true) => target.read_int::<BE>(self.length)?.into(),
501            (ByteOrder::BigEndian, false) => target.read_uint::<BE>(self.length)?.into(),
502            (ByteOrder::LittleEndian, true) => target.read_int::<LE>(self.length)?.into(),
503            (ByteOrder::LittleEndian, false) => target.read_uint::<LE>(self.length)?.into(),
504        })
505    }
506}
507
508impl Decoder for IntegerSchema {
509    type Error = DecodingError;
510
511    fn decode<R>(&self, target: &mut R) -> Result<Value, Self::Error>
512    where
513        R: io::Read + ReadBytesExt,
514    {
515        match self {
516            IntegerSchema::Integer(dec) => dec.decode(target),
517            IntegerSchema::Bitfield(dec) => dec.decode(target),
518        }
519    }
520}
521
522#[cfg(test)]
523mod test {
524    use super::*;
525    use anyhow::Result;
526    use serde_json::{from_value, json};
527
528    #[test]
529    fn encode_integer_4_signed() -> Result<()> {
530        let schema_msb = json!({});
531        let schema_msb: IntegerSchema = from_value(schema_msb)?;
532        let schema_lsb = json!({"byteorder": "littleendian"});
533        let schema_lsb: IntegerSchema = from_value(schema_lsb)?;
534        let value: i32 = 0x1234_5678;
535        let json: Value = value.into();
536        let mut buffer = [0; 4];
537
538        assert_eq!(4, schema_msb.encode(&mut buffer.as_mut(), &json)?);
539        let buf_value = i32::from_be_bytes(buffer);
540        assert_eq!(value, buf_value);
541
542        assert_eq!(4, schema_lsb.encode(&mut buffer.as_mut(), &json)?);
543        let buf_value = i32::from_le_bytes(buffer);
544        assert_eq!(value, buf_value);
545
546        Ok(())
547    }
548
549    #[test]
550    fn encode_integer_3_unsigned() -> Result<()> {
551        let schema = json!({
552            "length": 3,
553            "signed": false
554        });
555        let schema_msb: IntegerSchema = from_value(schema)?;
556        let schema = json!({
557            "length": 3,
558            "signed": false,
559            "byteorder": "littleendian"
560        });
561        let schema_lsb: IntegerSchema = from_value(schema)?;
562        let value: i64 = 0x123456;
563        let json: Value = value.into();
564        let mut buffer: Vec<u8> = vec![];
565
566        assert_eq!(3, schema_msb.encode(&mut buffer, &json)?);
567        assert!(matches!(buffer.as_slice(), [0x12, 0x34, 0x56, ..]));
568
569        assert_eq!(3, schema_lsb.encode(&mut buffer, &json)?);
570        assert!(matches!(
571            buffer.as_slice(),
572            [0x12, 0x34, 0x56, 0x56, 0x34, 0x12, ..]
573        ));
574
575        Ok(())
576    }
577
578    #[test]
579    fn masks() -> Result<()> {
580        let b1 = Bitfield::new(1, 1, 0)?;
581        let m1 = 1;
582        assert_eq!(b1.mask(), m1);
583        let b2 = Bitfield::new(2, 3, 9)?;
584        let m2 = 0b1110_0000_0000;
585        assert_eq!(b2.mask(), m2);
586        let b3 = Bitfield::new(4, 32, 0)?;
587        let m3 = 0xFFFF_FFFF;
588        assert_eq!(b3.mask(), m3);
589        Ok(())
590    }
591}