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