1use hidane_proto::google::firestore::v1::{Value, value::ValueType};
44
45use crate::order::{is_vector, numeric_id, vector_elements};
46
47const NULL: u8 = 0x05;
48const BOOLEAN: u8 = 0x0A;
49const NAN: u8 = 0x0F;
50const NUMBER: u8 = 0x14;
51const TIMESTAMP: u8 = 0x19;
52const STRING: u8 = 0x1E;
53const BYTES: u8 = 0x23;
54const REFERENCE: u8 = 0x28;
55const GEO_POINT: u8 = 0x2D;
56const ARRAY: u8 = 0x32;
57const VECTOR: u8 = 0x37;
58const MAP: u8 = 0x3C;
59const EXPRESSION: u8 = 0x41;
60
61const SEQUENCE_END: u8 = 0x00;
62const ESCAPE: [u8; 2] = [0x00, 0xFF];
63const TERMINATOR: [u8; 2] = [0x00, 0x01];
64const MAP_END: [u8; 2] = [0x00, 0x00];
65
66const PATH_END: u8 = 0x00;
67const PATH_NUMERIC_SEGMENT: u8 = 0x01;
68const PATH_STRING_SEGMENT: u8 = 0x02;
69const PATH_AFTER_DESCENDANTS: u8 = 0x03;
70
71pub fn encode_value(value: &Value) -> Vec<u8> {
73 let mut out = Vec::new();
74 write_value(value, &mut out);
75 out
76}
77
78pub fn encode_escaped(bytes: &[u8]) -> Vec<u8> {
81 let mut out = Vec::new();
82 write_escaped(bytes, &mut out);
83 out
84}
85
86pub fn write_value(value: &Value, out: &mut Vec<u8>) {
87 match &value.value_type {
88 None | Some(ValueType::NullValue(_)) => out.push(NULL),
89 Some(ValueType::BooleanValue(b)) => out.extend([BOOLEAN, u8::from(*b)]),
90 Some(ValueType::IntegerValue(i)) => write_integer(*i, out),
91 Some(ValueType::DoubleValue(d)) => write_double(*d, out),
92 Some(ValueType::TimestampValue(ts)) => {
93 out.push(TIMESTAMP);
94 out.extend(sortable_i64(ts.seconds));
95 out.extend(ts.nanos.to_be_bytes());
96 }
97 Some(ValueType::StringValue(s)) => {
98 out.push(STRING);
99 write_escaped(s.as_bytes(), out);
100 }
101 Some(ValueType::BytesValue(b)) => {
102 out.push(BYTES);
103 write_escaped(b, out);
104 }
105 Some(ValueType::ReferenceValue(name)) => {
106 out.push(REFERENCE);
107 write_path(name.split('/'), out);
108 }
109 Some(ValueType::GeoPointValue(point)) => {
110 out.push(GEO_POINT);
111 out.extend(sortable_f64(point.latitude));
112 out.extend(sortable_f64(point.longitude));
113 }
114 Some(ValueType::ArrayValue(array)) => {
115 out.push(ARRAY);
116 for element in &array.values {
117 write_value(element, out);
118 }
119 out.push(SEQUENCE_END);
120 }
121 Some(ValueType::MapValue(map)) if is_vector(map) => {
122 let elements = vector_elements(map);
123 out.push(VECTOR);
124 out.extend(
125 u32::try_from(elements.len())
126 .unwrap_or(u32::MAX)
127 .to_be_bytes(),
128 );
129 for element in elements {
130 write_value(element, out);
131 }
132 }
133 Some(ValueType::MapValue(map)) => {
134 out.push(MAP);
135 for (key, field) in map.fields.sorted() {
137 write_escaped(key.as_bytes(), out);
138 write_value(field, out);
139 }
140 out.extend(MAP_END);
141 }
142 Some(
143 ValueType::FieldReferenceValue(_)
144 | ValueType::VariableReferenceValue(_)
145 | ValueType::FunctionValue(_)
146 | ValueType::PipelineValue(_),
147 ) => out.push(EXPRESSION),
148 }
149}
150
151pub fn encode_path<'a>(segments: impl IntoIterator<Item = &'a str>) -> Vec<u8> {
155 let mut out = Vec::new();
156 write_path(segments, &mut out);
157 out
158}
159
160pub fn write_path<'a>(segments: impl IntoIterator<Item = &'a str>, out: &mut Vec<u8>) {
161 write_path_prefix(segments, out);
162 out.push(PATH_END);
163}
164
165pub fn encode_path_prefix<'a>(segments: impl IntoIterator<Item = &'a str>) -> Vec<u8> {
168 let mut out = Vec::new();
169 write_path_prefix(segments, &mut out);
170 out
171}
172
173pub fn path_subtree_end(prefix: &[u8]) -> Vec<u8> {
176 let mut end = prefix.to_vec();
177 end.push(PATH_AFTER_DESCENDANTS);
178 end
179}
180
181fn write_path_prefix<'a>(segments: impl IntoIterator<Item = &'a str>, out: &mut Vec<u8>) {
182 for segment in segments {
183 match numeric_id(segment) {
184 Some(id) => {
185 out.push(PATH_NUMERIC_SEGMENT);
186 out.extend(sortable_i64(id));
187 }
188 None => {
189 out.push(PATH_STRING_SEGMENT);
190 write_escaped(segment.as_bytes(), out);
191 }
192 }
193 }
194}
195
196fn write_integer(i: i64, out: &mut Vec<u8>) {
197 #[allow(clippy::cast_precision_loss)]
198 let nearest = i as f64;
199 #[allow(clippy::cast_possible_truncation)]
201 let distance = i128::from(i) - nearest as i128;
202 out.push(NUMBER);
203 out.extend(sortable_f64(nearest));
204 out.extend(sortable_i16(
205 i16::try_from(distance).expect("|i - round(i)| <= 1024"),
206 ));
207}
208
209fn write_double(d: f64, out: &mut Vec<u8>) {
210 if d.is_nan() {
211 out.push(NAN);
212 return;
213 }
214 out.push(NUMBER);
215 out.extend(sortable_f64(d));
216 out.extend(sortable_i16(0));
217}
218
219fn write_escaped(bytes: &[u8], out: &mut Vec<u8>) {
220 for &b in bytes {
221 if b == 0x00 {
222 out.extend(ESCAPE);
223 } else {
224 out.push(b);
225 }
226 }
227 out.extend(TERMINATOR);
228}
229
230fn sortable_i64(v: i64) -> [u8; 8] {
232 (v.cast_unsigned() ^ (1 << 63)).to_be_bytes()
233}
234
235fn sortable_i16(v: i16) -> [u8; 2] {
236 (v.cast_unsigned() ^ (1 << 15)).to_be_bytes()
237}
238
239fn sortable_f64(v: f64) -> [u8; 8] {
242 let v = if v == 0.0 { 0.0 } else { v };
243 let bits = v.to_bits();
244 let bits = if bits >> 63 == 1 {
245 !bits
246 } else {
247 bits ^ (1 << 63)
248 };
249 bits.to_be_bytes()
250}