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}
35
36pub trait Encodable {
38 fn encode_into(&self, out: &mut Vec<u8>);
40}
41
42#[must_use]
44pub fn encode_value(value: &Value) -> Vec<u8> {
45 let mut out = Vec::new();
46 value.encode_into(&mut out);
47 out
48}
49
50impl Encodable for EntityId {
51 fn encode_into(&self, out: &mut Vec<u8>) {
52 out.extend_from_slice(&self.raw().to_be_bytes());
53 }
54}
55impl Encodable for u64 {
56 fn encode_into(&self, out: &mut Vec<u8>) {
57 out.extend_from_slice(&self.to_be_bytes());
58 }
59}
60impl Encodable for i64 {
61 fn encode_into(&self, out: &mut Vec<u8>) {
62 out.extend_from_slice(
63 &(u64::from_be_bytes(self.to_be_bytes()) ^ (1_u64 << 63)).to_be_bytes(),
64 );
65 }
66}
67
68impl Encodable for Value {
69 fn encode_into(&self, out: &mut Vec<u8>) {
70 match self {
71 Self::Bool(v) => out.extend_from_slice(&[BOOL, u8::from(*v)]),
72 Self::Long(v) => {
73 out.push(LONG);
74 v.encode_into(out);
75 }
76 Self::Double(v) => {
77 out.push(DOUBLE);
78 out.extend_from_slice(&v.sortable_bits().to_be_bytes());
79 }
80 Self::Instant(v) => {
81 out.push(INSTANT);
82 v.encode_into(out);
83 }
84 Self::Uuid(v) => {
85 out.push(UUID);
86 out.extend_from_slice(&v.to_be_bytes());
87 }
88 Self::Keyword(v) => {
89 out.push(KEYWORD);
90 v.encode_into(out);
91 }
92 Self::Str(v) => {
93 out.push(STR);
94 encode_escaped(v.as_bytes(), out);
95 }
96 Self::Bytes(v) => {
97 out.push(BYTES);
98 encode_escaped(v, out);
99 }
100 Self::Ref(v) => {
101 out.push(REF);
102 v.encode_into(out);
103 }
104 }
105 }
106}
107
108pub fn decode_value(input: &[u8]) -> Result<(Value, usize), DecodeError> {
115 let Some((&tag, rest)) = input.split_first() else {
116 return Err(DecodeError::Truncated);
117 };
118 let fixed =
119 |n: usize| -> Result<&[u8], DecodeError> { rest.get(..n).ok_or(DecodeError::Truncated) };
120 Ok(match tag {
121 BOOL => (
122 Value::Bool(*fixed(1)?.first().ok_or(DecodeError::Truncated)? != 0),
123 2,
124 ),
125 LONG => (Value::Long(decode_i64(fixed(8)?)), 9),
126 DOUBLE => (
127 Value::Double(TotalF64(f64::from_bits(decode_f64_bits(fixed(8)?)))),
128 9,
129 ),
130 INSTANT => (Value::Instant(decode_i64(fixed(8)?)), 9),
131 UUID => (Value::Uuid(u128::from_be_bytes(array_16(fixed(16)?))), 17),
132 KEYWORD => (Value::Keyword(u64::from_be_bytes(array_8(fixed(8)?))), 9),
133 REF => (
134 Value::Ref(EntityId::from_raw(u64::from_be_bytes(array_8(fixed(8)?)))),
135 9,
136 ),
137 STR | BYTES => {
138 let (bytes, used) = decode_escaped(rest)?;
139 if tag == STR {
140 (
141 Value::Str(
142 std::str::from_utf8(&bytes)
143 .map_err(|_| DecodeError::InvalidUtf8)?
144 .into(),
145 ),
146 used + 1,
147 )
148 } else {
149 (Value::Bytes(Arc::from(bytes)), used + 1)
150 }
151 }
152 other => return Err(DecodeError::UnknownTag(other)),
153 })
154}
155fn decode_i64(bytes: &[u8]) -> i64 {
156 i64::from_be_bytes((u64::from_be_bytes(array_8(bytes)) ^ (1_u64 << 63)).to_be_bytes())
157}
158fn decode_f64_bits(bytes: &[u8]) -> u64 {
159 let s = u64::from_be_bytes(array_8(bytes));
160 if (s & (1_u64 << 63)) == 0 {
161 !s
162 } else {
163 s ^ (1_u64 << 63)
164 }
165}
166fn encode_escaped(bytes: &[u8], out: &mut Vec<u8>) {
167 for b in bytes {
168 if *b == 0 {
169 out.extend_from_slice(&[0, 0xff]);
170 } else {
171 out.push(*b);
172 }
173 }
174 out.extend_from_slice(&[0, 0]);
175}
176fn decode_escaped(input: &[u8]) -> Result<(Vec<u8>, usize), DecodeError> {
177 let mut out = Vec::new();
178 let mut i = 0;
179 while i < input.len() {
180 match input[i] {
181 0 if input.get(i + 1) == Some(&0) => return Ok((out, i + 2)),
182 0 if input.get(i + 1) == Some(&0xff) => {
183 out.push(0);
184 i += 2;
185 }
186 0 => return Err(DecodeError::InvalidEscape),
187 b => {
188 out.push(b);
189 i += 1;
190 }
191 }
192 }
193 Err(DecodeError::Truncated)
194}
195
196fn array_8(bytes: &[u8]) -> [u8; 8] {
197 let mut out = [0; 8];
198 out.copy_from_slice(bytes);
199 out
200}
201
202fn array_16(bytes: &[u8]) -> [u8; 16] {
203 let mut out = [0; 16];
204 out.copy_from_slice(bytes);
205 out
206}