libipld_cbor/cbor.rs
1//! CBOR helper types for encoding and decoding.
2use std::convert::TryFrom;
3
4use crate::error::UnexpectedCode;
5use libipld_core::ipld::Ipld;
6
7/// Represents a major "byte". This includes both the major bits and the additional info.
8#[repr(transparent)]
9#[derive(Clone, Copy, Eq, PartialEq)]
10pub struct Major(u8);
11
12/// The constant TRUE.
13pub const FALSE: Major = Major::new(MajorKind::Other, 20);
14/// The constant FALSE.
15pub const TRUE: Major = Major::new(MajorKind::Other, 21);
16/// The constant NULL.
17pub const NULL: Major = Major::new(MajorKind::Other, 22);
18/// The major "byte" indicating that a 16 bit float follows.
19pub const F16: Major = Major::new(MajorKind::Other, 25);
20/// The major "byte" indicating that a 32 bit float follows.
21pub const F32: Major = Major::new(MajorKind::Other, 26);
22/// The major "byte" indicating that a 64 bit float follows.
23pub const F64: Major = Major::new(MajorKind::Other, 27);
24
25impl Major {
26 const fn new(kind: MajorKind, info: u8) -> Self {
27 Major(((kind as u8) << 5) | info)
28 }
29
30 /// Returns the major type.
31 #[inline(always)]
32 pub const fn kind(self) -> MajorKind {
33 // This is a 3 bit value, so value 0-7 are covered.
34 unsafe { std::mem::transmute(self.0 >> 5) }
35 }
36
37 /// Returns the additional info.
38 #[inline(always)]
39 pub const fn info(self) -> u8 {
40 self.0 & 0x1f
41 }
42
43 /// Interprets the additioanl info as a number of additional bytes that should be consumed.
44 #[inline(always)]
45 #[allow(clippy::len_without_is_empty)]
46 pub const fn len(self) -> u8 {
47 // All major types follow the same rules for "additioanl bytes".
48 // 24 -> 1, 25 -> 2, 26 -> 4, 27 -> 8
49 match self.info() {
50 info @ 24..=27 => 1 << (info - 24),
51 _ => 0,
52 }
53 }
54}
55
56impl From<Major> for u8 {
57 fn from(m: Major) -> u8 {
58 m.0
59 }
60}
61
62// This is the core of the validation logic. Every major type passes through here giving us a chance
63// to determine if it's something we allow.
64impl TryFrom<u8> for Major {
65 type Error = UnexpectedCode;
66 fn try_from(value: u8) -> Result<Self, Self::Error> {
67 // We don't allow any major types with additional info 28-31 inclusive.
68 // Or the bitmask 0b00011100 = 28.
69 if value & 28 == 28 {
70 return Err(UnexpectedCode::new::<Ipld>(value));
71 } else if (value >> 5) == MajorKind::Other as u8 {
72 match value & 0x1f {
73 // False, True, Null. TODO: Allow undefined?
74 20 | 21 | 22 => (),
75 // Floats. TODO: forbid f16 & f32?
76 25 | 26 | 27 => (),
77 // Everything is forbidden.
78 _ => {
79 return Err(UnexpectedCode::new::<Ipld>(value));
80 }
81 }
82 }
83 Ok(Major(value))
84 }
85}
86
87/// The type
88#[repr(u8)]
89#[derive(Clone, Copy, Eq, PartialEq)]
90#[allow(dead_code)]
91pub enum MajorKind {
92 /// Non-negative integer (major type 0).
93 UnsignedInt = 0,
94 /// Negative integer (major type 1).
95 NegativeInt = 1,
96 /// Byte string (major type 2).
97 ByteString = 2,
98 /// Unicode text string (major type 3).
99 TextString = 3,
100 /// Array (major type 4).
101 Array = 4,
102 /// Map (major type 5).
103 Map = 5,
104 /// Tag (major type 6).
105 Tag = 6,
106 /// Other (major type 7).
107 Other = 7,
108}