1use std::sync::Arc;
4
5use thiserror::Error;
6
7use crate::{EntityId, TotalF64, Value};
8
9const BOOL: u8 = 0x10;
10const LONG: u8 = 0x20;
11const DOUBLE: u8 = 0x30;
12const INSTANT: u8 = 0x40;
13const UUID: u8 = 0x50;
14const KEYWORD: u8 = 0x60;
15const STR: u8 = 0x70;
16const BYTES: u8 = 0x80;
17const REF: u8 = 0x90;
18
19#[derive(Debug, Error, Eq, PartialEq)]
21pub enum DecodeError {
22 #[error("truncated sortable value")]
24 Truncated,
25 #[error("unknown value tag {0:#x}")]
27 UnknownTag(u8),
28 #[error("invalid escaped bytes")]
30 InvalidEscape,
31 #[error("invalid UTF-8 string")]
33 InvalidUtf8,
34 #[error("trailing bytes after sortable value")]
36 Trailing,
37}
38
39pub trait Encodable {
41 fn encode_into(&self, out: &mut Vec<u8>);
43}
44
45#[must_use]
47pub fn encode_value(value: &Value) -> Vec<u8> {
48 let mut out = Vec::new();
49 value.encode_into(&mut out);
50 out
51}
52
53impl Encodable for EntityId {
54 fn encode_into(&self, out: &mut Vec<u8>) {
55 out.extend_from_slice(&self.raw().to_be_bytes());
56 }
57}
58impl Encodable for u64 {
59 fn encode_into(&self, out: &mut Vec<u8>) {
60 out.extend_from_slice(&self.to_be_bytes());
61 }
62}
63impl Encodable for i64 {
64 fn encode_into(&self, out: &mut Vec<u8>) {
65 out.extend_from_slice(
66 &(u64::from_be_bytes(self.to_be_bytes()) ^ (1_u64 << 63)).to_be_bytes(),
67 );
68 }
69}
70
71impl Encodable for Value {
72 fn encode_into(&self, out: &mut Vec<u8>) {
73 match self {
74 Self::Bool(v) => out.extend_from_slice(&[BOOL, u8::from(*v)]),
75 Self::Long(v) => {
76 out.push(LONG);
77 v.encode_into(out);
78 }
79 Self::Double(v) => {
80 out.push(DOUBLE);
81 out.extend_from_slice(&v.sortable_bits().to_be_bytes());
82 }
83 Self::Instant(v) => {
84 out.push(INSTANT);
85 v.encode_into(out);
86 }
87 Self::Uuid(v) => {
88 out.push(UUID);
89 out.extend_from_slice(&v.to_be_bytes());
90 }
91 Self::Keyword(v) => {
92 out.push(KEYWORD);
93 v.encode_into(out);
94 }
95 Self::Str(v) => {
96 out.push(STR);
97 encode_escaped(v.as_bytes(), out);
98 }
99 Self::Bytes(v) => {
100 out.push(BYTES);
101 encode_escaped(v, out);
102 }
103 Self::Ref(v) => {
104 out.push(REF);
105 v.encode_into(out);
106 }
107 }
108 }
109}
110
111pub fn decode_value(input: &[u8]) -> Result<(Value, usize), DecodeError> {
118 let Some((&tag, rest)) = input.split_first() else {
119 return Err(DecodeError::Truncated);
120 };
121 let fixed =
122 |n: usize| -> Result<&[u8], DecodeError> { rest.get(..n).ok_or(DecodeError::Truncated) };
123 Ok(match tag {
124 BOOL => (
125 Value::Bool(*fixed(1)?.first().ok_or(DecodeError::Truncated)? != 0),
126 2,
127 ),
128 LONG => (Value::Long(decode_i64(fixed(8)?)), 9),
129 DOUBLE => (
130 Value::Double(TotalF64(f64::from_bits(decode_f64_bits(fixed(8)?)))),
131 9,
132 ),
133 INSTANT => (Value::Instant(decode_i64(fixed(8)?)), 9),
134 UUID => (Value::Uuid(u128::from_be_bytes(array_16(fixed(16)?))), 17),
135 KEYWORD => (Value::Keyword(u64::from_be_bytes(array_8(fixed(8)?))), 9),
136 REF => (
137 Value::Ref(EntityId::from_raw(u64::from_be_bytes(array_8(fixed(8)?)))),
138 9,
139 ),
140 STR | BYTES => {
141 let (bytes, used) = decode_escaped(rest)?;
142 if tag == STR {
143 (
144 Value::Str(
145 std::str::from_utf8(&bytes)
146 .map_err(|_| DecodeError::InvalidUtf8)?
147 .into(),
148 ),
149 used + 1,
150 )
151 } else {
152 (Value::Bytes(Arc::from(bytes)), used + 1)
153 }
154 }
155 other => return Err(DecodeError::UnknownTag(other)),
156 })
157}
158fn decode_i64(bytes: &[u8]) -> i64 {
159 i64::from_be_bytes((u64::from_be_bytes(array_8(bytes)) ^ (1_u64 << 63)).to_be_bytes())
160}
161fn decode_f64_bits(bytes: &[u8]) -> u64 {
162 let s = u64::from_be_bytes(array_8(bytes));
163 if (s & (1_u64 << 63)) == 0 {
164 !s
165 } else {
166 s ^ (1_u64 << 63)
167 }
168}
169fn encode_escaped(bytes: &[u8], out: &mut Vec<u8>) {
170 for b in bytes {
171 if *b == 0 {
172 out.extend_from_slice(&[0, 0xff]);
173 } else {
174 out.push(*b);
175 }
176 }
177 out.extend_from_slice(&[0, 0]);
178}
179fn decode_escaped(input: &[u8]) -> Result<(Vec<u8>, usize), DecodeError> {
180 let mut out = Vec::new();
181 let mut i = 0;
182 while i < input.len() {
183 match input[i] {
184 0 if input.get(i + 1) == Some(&0) => return Ok((out, i + 2)),
185 0 if input.get(i + 1) == Some(&0xff) => {
186 out.push(0);
187 i += 2;
188 }
189 0 => return Err(DecodeError::InvalidEscape),
190 b => {
191 out.push(b);
192 i += 1;
193 }
194 }
195 }
196 Err(DecodeError::Truncated)
197}
198
199fn array_8(bytes: &[u8]) -> [u8; 8] {
200 let mut out = [0; 8];
201 out.copy_from_slice(bytes);
202 out
203}
204
205pub fn decode_entity_id(input: &[u8]) -> Result<(EntityId, usize), DecodeError> {
210 let bytes = input.get(..8).ok_or(DecodeError::Truncated)?;
211 Ok((EntityId::from_raw(u64::from_be_bytes(array_8(bytes))), 8))
212}
213
214pub fn decode_u64(input: &[u8]) -> Result<(u64, usize), DecodeError> {
219 let bytes = input.get(..8).ok_or(DecodeError::Truncated)?;
220 Ok((u64::from_be_bytes(array_8(bytes)), 8))
221}
222
223fn array_16(bytes: &[u8]) -> [u8; 16] {
224 let mut out = [0; 16];
225 out.copy_from_slice(bytes);
226 out
227}