1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
36
37use super::types::{read_varint, write_varint, DataType, Value, ValueError};
38
39mod limit;
40
41pub type ValueKind = DataType;
46
47#[inline]
53pub fn type_tag(value: &Value) -> u8 {
54 match value {
55 Value::Null => 0,
56 other => other.data_type().to_byte(),
57 }
58}
59
60#[inline]
63pub fn type_for_tag(tag: u8) -> Option<ValueKind> {
64 if tag == 0 {
65 Some(DataType::Nullable)
66 } else {
67 DataType::from_byte(tag)
68 }
69}
70
71pub(super) const TOAST_THRESHOLD: usize = 2048;
75
76pub(super) const TOAST_ZSTD_LEVEL: i32 = 3;
79
80pub fn encode(value: &Value, out: &mut Vec<u8>) {
85 match value {
86 Value::Null => {
87 out.push(0); }
89 Value::Integer(v) => {
90 out.push(DataType::Integer.to_byte());
91 out.extend_from_slice(&v.to_le_bytes());
92 }
93 Value::UnsignedInteger(v) => {
94 out.push(DataType::UnsignedInteger.to_byte());
95 out.extend_from_slice(&v.to_le_bytes());
96 }
97 Value::Float(v) => {
98 out.push(DataType::Float.to_byte());
99 out.extend_from_slice(&v.to_le_bytes());
100 }
101 Value::Text(s) => {
102 let bytes = s.as_bytes();
103 if bytes.len() > TOAST_THRESHOLD {
108 if let Ok(compressed) = zstd::bulk::compress(bytes, TOAST_ZSTD_LEVEL) {
109 if compressed.len() < bytes.len() {
110 out.push(DataType::TextZstd.to_byte());
111 write_varint(out, bytes.len() as u64);
113 write_varint(out, compressed.len() as u64);
114 out.extend_from_slice(&compressed);
115 return;
116 }
117 }
118 }
119 out.push(DataType::Text.to_byte());
120 write_varint(out, bytes.len() as u64);
121 out.extend_from_slice(bytes);
122 }
123 Value::Blob(data) => {
124 if data.len() > TOAST_THRESHOLD {
126 if let Ok(compressed) = zstd::bulk::compress(data, TOAST_ZSTD_LEVEL) {
127 if compressed.len() < data.len() {
128 out.push(DataType::BlobZstd.to_byte());
129 write_varint(out, data.len() as u64);
130 write_varint(out, compressed.len() as u64);
131 out.extend_from_slice(&compressed);
132 return;
133 }
134 }
135 }
136 out.push(DataType::Blob.to_byte());
137 write_varint(out, data.len() as u64);
138 out.extend_from_slice(data);
139 }
140 Value::Boolean(v) => {
141 out.push(DataType::Boolean.to_byte());
142 out.push(if *v { 1 } else { 0 });
143 }
144 Value::Timestamp(v) => {
145 out.push(DataType::Timestamp.to_byte());
146 out.extend_from_slice(&v.to_le_bytes());
147 }
148 Value::Duration(v) => {
149 out.push(DataType::Duration.to_byte());
150 out.extend_from_slice(&v.to_le_bytes());
151 }
152 Value::IpAddr(addr) => {
153 out.push(DataType::IpAddr.to_byte());
154 match addr {
155 IpAddr::V4(v4) => {
156 out.push(4); out.extend_from_slice(&v4.octets());
158 }
159 IpAddr::V6(v6) => {
160 out.push(6); out.extend_from_slice(&v6.octets());
162 }
163 }
164 }
165 Value::MacAddr(mac) => {
166 out.push(DataType::MacAddr.to_byte());
167 out.extend_from_slice(mac);
168 }
169 Value::Vector(vec) => {
170 out.push(DataType::Vector.to_byte());
171 write_varint(out, vec.len() as u64);
172 for v in vec {
173 out.extend_from_slice(&v.to_le_bytes());
174 }
175 }
176 Value::Json(data) => {
177 out.push(DataType::Json.to_byte());
178 write_varint(out, data.len() as u64);
179 out.extend_from_slice(data);
180 }
181 Value::Uuid(uuid) => {
182 out.push(DataType::Uuid.to_byte());
183 out.extend_from_slice(uuid);
184 }
185 Value::NodeRef(node_id) => {
186 out.push(DataType::NodeRef.to_byte());
187 let bytes = node_id.as_bytes();
188 write_varint(out, bytes.len() as u64);
189 out.extend_from_slice(bytes);
190 }
191 Value::EdgeRef(edge_id) => {
192 out.push(DataType::EdgeRef.to_byte());
193 let bytes = edge_id.as_bytes();
194 write_varint(out, bytes.len() as u64);
195 out.extend_from_slice(bytes);
196 }
197 Value::VectorRef(collection, vector_id) => {
198 out.push(DataType::VectorRef.to_byte());
199 let coll_bytes = collection.as_bytes();
200 write_varint(out, coll_bytes.len() as u64);
201 out.extend_from_slice(coll_bytes);
202 out.extend_from_slice(&vector_id.to_le_bytes());
203 }
204 Value::RowRef(table, row_id) => {
205 out.push(DataType::RowRef.to_byte());
206 let table_bytes = table.as_bytes();
207 write_varint(out, table_bytes.len() as u64);
208 out.extend_from_slice(table_bytes);
209 out.extend_from_slice(&row_id.to_le_bytes());
210 }
211 Value::Color(rgb) => {
212 out.push(DataType::Color.to_byte());
213 out.extend_from_slice(rgb);
214 }
215 Value::Email(s) => {
216 out.push(DataType::Email.to_byte());
217 let bytes = s.as_bytes();
218 write_varint(out, bytes.len() as u64);
219 out.extend_from_slice(bytes);
220 }
221 Value::Url(s) => {
222 out.push(DataType::Url.to_byte());
223 let bytes = s.as_bytes();
224 write_varint(out, bytes.len() as u64);
225 out.extend_from_slice(bytes);
226 }
227 Value::Phone(n) => {
228 out.push(DataType::Phone.to_byte());
229 out.extend_from_slice(&n.to_le_bytes());
230 }
231 Value::Semver(packed) => {
232 out.push(DataType::Semver.to_byte());
233 out.extend_from_slice(&packed.to_le_bytes());
234 }
235 Value::Cidr(ip, prefix) => {
236 out.push(DataType::Cidr.to_byte());
237 out.extend_from_slice(&ip.to_le_bytes());
238 out.push(*prefix);
239 }
240 Value::Date(days) => {
241 out.push(DataType::Date.to_byte());
242 out.extend_from_slice(&days.to_le_bytes());
243 }
244 Value::Time(ms) => {
245 out.push(DataType::Time.to_byte());
246 out.extend_from_slice(&ms.to_le_bytes());
247 }
248 Value::Decimal(v) => {
249 out.push(DataType::Decimal.to_byte());
250 out.extend_from_slice(&v.to_le_bytes());
251 }
252 Value::DecimalText(s) => {
253 out.push(DataType::DecimalText.to_byte());
254 let bytes = s.as_bytes();
255 write_varint(out, bytes.len() as u64);
256 out.extend_from_slice(bytes);
257 }
258 Value::EnumValue(idx) => {
259 out.push(DataType::Enum.to_byte());
260 out.push(*idx);
261 }
262 Value::Array(elements) => {
263 out.push(DataType::Array.to_byte());
264 write_varint(out, elements.len() as u64);
265 for elem in elements {
266 encode(elem, out);
267 }
268 }
269 Value::TimestampMs(v) => {
270 out.push(DataType::TimestampMs.to_byte());
271 out.extend_from_slice(&v.to_le_bytes());
272 }
273 Value::Ipv4(v) => {
274 out.push(DataType::Ipv4.to_byte());
275 out.extend_from_slice(&v.to_le_bytes());
276 }
277 Value::Ipv6(bytes) => {
278 out.push(DataType::Ipv6.to_byte());
279 out.extend_from_slice(bytes);
280 }
281 Value::Subnet(ip, mask) => {
282 out.push(DataType::Subnet.to_byte());
283 out.extend_from_slice(&ip.to_le_bytes());
284 out.extend_from_slice(&mask.to_le_bytes());
285 }
286 Value::Port(v) => {
287 out.push(DataType::Port.to_byte());
288 out.extend_from_slice(&v.to_le_bytes());
289 }
290 Value::Latitude(v) => {
291 out.push(DataType::Latitude.to_byte());
292 out.extend_from_slice(&v.to_le_bytes());
293 }
294 Value::Longitude(v) => {
295 out.push(DataType::Longitude.to_byte());
296 out.extend_from_slice(&v.to_le_bytes());
297 }
298 Value::GeoPoint(lat, lon) => {
299 out.push(DataType::GeoPoint.to_byte());
300 out.extend_from_slice(&lat.to_le_bytes());
301 out.extend_from_slice(&lon.to_le_bytes());
302 }
303 Value::Country2(c) => {
304 out.push(DataType::Country2.to_byte());
305 out.extend_from_slice(c);
306 }
307 Value::Country3(c) => {
308 out.push(DataType::Country3.to_byte());
309 out.extend_from_slice(c);
310 }
311 Value::Lang2(c) => {
312 out.push(DataType::Lang2.to_byte());
313 out.extend_from_slice(c);
314 }
315 Value::Lang5(c) => {
316 out.push(DataType::Lang5.to_byte());
317 out.extend_from_slice(c);
318 }
319 Value::Currency(c) => {
320 out.push(DataType::Currency.to_byte());
321 out.extend_from_slice(c);
322 }
323 Value::AssetCode(code) => {
324 out.push(DataType::AssetCode.to_byte());
325 let bytes = code.as_bytes();
326 write_varint(out, bytes.len() as u64);
327 out.extend_from_slice(bytes);
328 }
329 Value::Money {
330 asset_code,
331 minor_units,
332 scale,
333 } => {
334 out.push(DataType::Money.to_byte());
335 let bytes = asset_code.as_bytes();
336 write_varint(out, bytes.len() as u64);
337 out.extend_from_slice(bytes);
338 out.push(*scale);
339 out.extend_from_slice(&minor_units.to_le_bytes());
340 }
341 Value::ColorAlpha(rgba) => {
342 out.push(DataType::ColorAlpha.to_byte());
343 out.extend_from_slice(rgba);
344 }
345 Value::BigInt(v) => {
346 out.push(DataType::BigInt.to_byte());
347 out.extend_from_slice(&v.to_le_bytes());
348 }
349 Value::KeyRef(col, key) => {
350 out.push(DataType::KeyRef.to_byte());
351 let col_bytes = col.as_bytes();
352 write_varint(out, col_bytes.len() as u64);
353 out.extend_from_slice(col_bytes);
354 let key_bytes = key.as_bytes();
355 write_varint(out, key_bytes.len() as u64);
356 out.extend_from_slice(key_bytes);
357 }
358 Value::DocRef(col, id) => {
359 out.push(DataType::DocRef.to_byte());
360 let col_bytes = col.as_bytes();
361 write_varint(out, col_bytes.len() as u64);
362 out.extend_from_slice(col_bytes);
363 out.extend_from_slice(&id.to_le_bytes());
364 }
365 Value::TableRef(name) => {
366 out.push(DataType::TableRef.to_byte());
367 let name_bytes = name.as_bytes();
368 write_varint(out, name_bytes.len() as u64);
369 out.extend_from_slice(name_bytes);
370 }
371 Value::PageRef(page_id) => {
372 out.push(DataType::PageRef.to_byte());
373 out.extend_from_slice(&page_id.to_le_bytes());
374 }
375 Value::Secret(bytes) => {
376 out.push(DataType::Secret.to_byte());
377 write_varint(out, bytes.len() as u64);
378 out.extend_from_slice(bytes);
379 }
380 Value::Password(hash) => {
381 out.push(DataType::Password.to_byte());
382 let bytes = hash.as_bytes();
383 write_varint(out, bytes.len() as u64);
384 out.extend_from_slice(bytes);
385 }
386 }
387}
388
389pub fn decode_with_limit(data: &[u8], limit_bytes: usize) -> Result<(Value, usize), ValueError> {
392 let mut remaining_bytes = limit_bytes;
393 decode_with_budget(data, &mut remaining_bytes)
394}
395
396pub fn decode_with_budget(
398 data: &[u8],
399 remaining_bytes: &mut usize,
400) -> Result<(Value, usize), ValueError> {
401 let (expected, allocation_bytes) = limit::preflight(data, *remaining_bytes)?;
402 let (value, consumed) = decode(&data[..expected])?;
403 assert_eq!(
404 consumed, expected,
405 "invariant: value preflight and typed decoder agree"
406 );
407 *remaining_bytes -= allocation_bytes;
408 Ok((value, consumed))
409}
410
411pub fn decode(data: &[u8]) -> Result<(Value, usize), ValueError> {
413 if data.is_empty() {
414 return Err(ValueError::EmptyData);
415 }
416
417 let type_byte = data[0];
418 let mut offset = 1;
419
420 if type_byte == 0 {
422 return Ok((Value::Null, 1));
423 }
424
425 let data_type = DataType::from_byte(type_byte).ok_or(ValueError::InvalidType(type_byte))?;
426
427 let value = match data_type {
428 DataType::Integer => {
429 if data.len() < offset + 8 {
430 return Err(ValueError::TruncatedData);
431 }
432 let v = i64::from_le_bytes(data[offset..offset + 8].try_into().unwrap());
433 offset += 8;
434 Value::Integer(v)
435 }
436 DataType::UnsignedInteger => {
437 if data.len() < offset + 8 {
438 return Err(ValueError::TruncatedData);
439 }
440 let v = u64::from_le_bytes(data[offset..offset + 8].try_into().unwrap());
441 offset += 8;
442 Value::UnsignedInteger(v)
443 }
444 DataType::Float => {
445 if data.len() < offset + 8 {
446 return Err(ValueError::TruncatedData);
447 }
448 let v = f64::from_le_bytes(data[offset..offset + 8].try_into().unwrap());
449 offset += 8;
450 Value::Float(v)
451 }
452 DataType::Text => {
453 let (len, varint_size) = read_varint(&data[offset..])?;
454 offset += varint_size;
455 if data.len() < offset + len as usize {
456 return Err(ValueError::TruncatedData);
457 }
458 let s = String::from_utf8(data[offset..offset + len as usize].to_vec())
459 .map_err(|_| ValueError::InvalidUtf8)?;
460 offset += len as usize;
461 Value::text(s)
462 }
463 DataType::Blob => {
464 let (len, varint_size) = read_varint(&data[offset..])?;
465 offset += varint_size;
466 if data.len() < offset + len as usize {
467 return Err(ValueError::TruncatedData);
468 }
469 let blob = data[offset..offset + len as usize].to_vec();
470 offset += len as usize;
471 Value::Blob(blob)
472 }
473 DataType::Boolean => {
474 if data.len() < offset + 1 {
475 return Err(ValueError::TruncatedData);
476 }
477 let v = data[offset] != 0;
478 offset += 1;
479 Value::Boolean(v)
480 }
481 DataType::Timestamp => {
482 if data.len() < offset + 8 {
483 return Err(ValueError::TruncatedData);
484 }
485 let v = i64::from_le_bytes(data[offset..offset + 8].try_into().unwrap());
486 offset += 8;
487 Value::Timestamp(v)
488 }
489 DataType::Duration => {
490 if data.len() < offset + 8 {
491 return Err(ValueError::TruncatedData);
492 }
493 let v = i64::from_le_bytes(data[offset..offset + 8].try_into().unwrap());
494 offset += 8;
495 Value::Duration(v)
496 }
497 DataType::IpAddr => {
498 if data.len() < offset + 1 {
499 return Err(ValueError::TruncatedData);
500 }
501 let version = data[offset];
502 offset += 1;
503 match version {
504 4 => {
505 if data.len() < offset + 4 {
506 return Err(ValueError::TruncatedData);
507 }
508 let octets: [u8; 4] = data[offset..offset + 4].try_into().unwrap();
509 offset += 4;
510 Value::IpAddr(IpAddr::V4(Ipv4Addr::from(octets)))
511 }
512 6 => {
513 if data.len() < offset + 16 {
514 return Err(ValueError::TruncatedData);
515 }
516 let octets: [u8; 16] = data[offset..offset + 16].try_into().unwrap();
517 offset += 16;
518 Value::IpAddr(IpAddr::V6(Ipv6Addr::from(octets)))
519 }
520 _ => return Err(ValueError::InvalidIpVersion(version)),
521 }
522 }
523 DataType::MacAddr => {
524 if data.len() < offset + 6 {
525 return Err(ValueError::TruncatedData);
526 }
527 let mac: [u8; 6] = data[offset..offset + 6].try_into().unwrap();
528 offset += 6;
529 Value::MacAddr(mac)
530 }
531 DataType::Vector => {
532 let (len, varint_size) = read_varint(&data[offset..])?;
533 offset += varint_size;
534 let float_count = len as usize;
535 if data.len() < offset + float_count * 4 {
536 return Err(ValueError::TruncatedData);
537 }
538 let mut vec = Vec::with_capacity(float_count);
539 for _ in 0..float_count {
540 let v = f32::from_le_bytes(data[offset..offset + 4].try_into().unwrap());
541 offset += 4;
542 vec.push(v);
543 }
544 Value::Vector(vec)
545 }
546 DataType::Json => {
547 let (len, varint_size) = read_varint(&data[offset..])?;
548 offset += varint_size;
549 if data.len() < offset + len as usize {
550 return Err(ValueError::TruncatedData);
551 }
552 let json = data[offset..offset + len as usize].to_vec();
553 offset += len as usize;
554 Value::Json(json)
555 }
556 DataType::Uuid => {
557 if data.len() < offset + 16 {
558 return Err(ValueError::TruncatedData);
559 }
560 let uuid: [u8; 16] = data[offset..offset + 16].try_into().unwrap();
561 offset += 16;
562 Value::Uuid(uuid)
563 }
564 DataType::NodeRef => {
565 let (len, len_bytes) = read_varint(&data[offset..])?;
566 offset += len_bytes;
567 if data.len() < offset + len as usize {
568 return Err(ValueError::TruncatedData);
569 }
570 let node_id = String::from_utf8_lossy(&data[offset..offset + len as usize]).to_string();
571 offset += len as usize;
572 Value::NodeRef(node_id)
573 }
574 DataType::EdgeRef => {
575 let (len, len_bytes) = read_varint(&data[offset..])?;
576 offset += len_bytes;
577 if data.len() < offset + len as usize {
578 return Err(ValueError::TruncatedData);
579 }
580 let edge_id = String::from_utf8_lossy(&data[offset..offset + len as usize]).to_string();
581 offset += len as usize;
582 Value::EdgeRef(edge_id)
583 }
584 DataType::VectorRef => {
585 let (len, len_bytes) = read_varint(&data[offset..])?;
586 offset += len_bytes;
587 if data.len() < offset + len as usize + 8 {
588 return Err(ValueError::TruncatedData);
589 }
590 let collection =
591 String::from_utf8_lossy(&data[offset..offset + len as usize]).to_string();
592 offset += len as usize;
593 let vector_id = u64::from_le_bytes(data[offset..offset + 8].try_into().unwrap());
594 offset += 8;
595 Value::VectorRef(collection, vector_id)
596 }
597 DataType::RowRef => {
598 let (len, len_bytes) = read_varint(&data[offset..])?;
599 offset += len_bytes;
600 if data.len() < offset + len as usize + 8 {
601 return Err(ValueError::TruncatedData);
602 }
603 let table = String::from_utf8_lossy(&data[offset..offset + len as usize]).to_string();
604 offset += len as usize;
605 let row_id = u64::from_le_bytes(data[offset..offset + 8].try_into().unwrap());
606 offset += 8;
607 Value::RowRef(table, row_id)
608 }
609 DataType::Color => {
610 if data.len() < offset + 3 {
611 return Err(ValueError::TruncatedData);
612 }
613 let rgb: [u8; 3] = data[offset..offset + 3].try_into().unwrap();
614 offset += 3;
615 Value::Color(rgb)
616 }
617 DataType::Email => {
618 let (len, varint_size) = read_varint(&data[offset..])?;
619 offset += varint_size;
620 if data.len() < offset + len as usize {
621 return Err(ValueError::TruncatedData);
622 }
623 let s = String::from_utf8(data[offset..offset + len as usize].to_vec())
624 .map_err(|_| ValueError::InvalidUtf8)?;
625 offset += len as usize;
626 Value::Email(s)
627 }
628 DataType::Url => {
629 let (len, varint_size) = read_varint(&data[offset..])?;
630 offset += varint_size;
631 if data.len() < offset + len as usize {
632 return Err(ValueError::TruncatedData);
633 }
634 let s = String::from_utf8(data[offset..offset + len as usize].to_vec())
635 .map_err(|_| ValueError::InvalidUtf8)?;
636 offset += len as usize;
637 Value::Url(s)
638 }
639 DataType::Phone => {
640 if data.len() < offset + 8 {
641 return Err(ValueError::TruncatedData);
642 }
643 let v = u64::from_le_bytes(data[offset..offset + 8].try_into().unwrap());
644 offset += 8;
645 Value::Phone(v)
646 }
647 DataType::Semver => {
648 if data.len() < offset + 4 {
649 return Err(ValueError::TruncatedData);
650 }
651 let v = u32::from_le_bytes(data[offset..offset + 4].try_into().unwrap());
652 offset += 4;
653 Value::Semver(v)
654 }
655 DataType::Cidr => {
656 if data.len() < offset + 5 {
657 return Err(ValueError::TruncatedData);
658 }
659 let ip = u32::from_le_bytes(data[offset..offset + 4].try_into().unwrap());
660 offset += 4;
661 let prefix = data[offset];
662 offset += 1;
663 Value::Cidr(ip, prefix)
664 }
665 DataType::Date => {
666 if data.len() < offset + 4 {
667 return Err(ValueError::TruncatedData);
668 }
669 let v = i32::from_le_bytes(data[offset..offset + 4].try_into().unwrap());
670 offset += 4;
671 Value::Date(v)
672 }
673 DataType::Time => {
674 if data.len() < offset + 4 {
675 return Err(ValueError::TruncatedData);
676 }
677 let v = u32::from_le_bytes(data[offset..offset + 4].try_into().unwrap());
678 offset += 4;
679 Value::Time(v)
680 }
681 DataType::Decimal => {
682 if data.len() < offset + 8 {
683 return Err(ValueError::TruncatedData);
684 }
685 let v = i64::from_le_bytes(data[offset..offset + 8].try_into().unwrap());
686 offset += 8;
687 Value::Decimal(v)
688 }
689 DataType::DecimalText => {
690 let (len, varint_size) = read_varint(&data[offset..])?;
691 offset += varint_size;
692 if data.len() < offset + len as usize {
693 return Err(ValueError::TruncatedData);
694 }
695 let value = String::from_utf8(data[offset..offset + len as usize].to_vec())
696 .map_err(|_| ValueError::InvalidUtf8)?;
697 offset += len as usize;
698 Value::DecimalText(value)
699 }
700 DataType::Enum => {
701 if data.len() < offset + 1 {
702 return Err(ValueError::TruncatedData);
703 }
704 let idx = data[offset];
705 offset += 1;
706 Value::EnumValue(idx)
707 }
708 DataType::Array => {
709 let (len, varint_size) = read_varint(&data[offset..])?;
710 offset += varint_size;
711 let count = len as usize;
712 let mut elements = Vec::with_capacity(count);
713 for _ in 0..count {
714 let (elem, elem_size) = decode(&data[offset..])?;
715 offset += elem_size;
716 elements.push(elem);
717 }
718 Value::Array(elements)
719 }
720 DataType::TimestampMs => {
721 if data.len() < offset + 8 {
722 return Err(ValueError::TruncatedData);
723 }
724 let v = i64::from_le_bytes(data[offset..offset + 8].try_into().unwrap());
725 offset += 8;
726 Value::TimestampMs(v)
727 }
728 DataType::Ipv4 => {
729 if data.len() < offset + 4 {
730 return Err(ValueError::TruncatedData);
731 }
732 let v = u32::from_le_bytes(data[offset..offset + 4].try_into().unwrap());
733 offset += 4;
734 Value::Ipv4(v)
735 }
736 DataType::Ipv6 => {
737 if data.len() < offset + 16 {
738 return Err(ValueError::TruncatedData);
739 }
740 let bytes: [u8; 16] = data[offset..offset + 16].try_into().unwrap();
741 offset += 16;
742 Value::Ipv6(bytes)
743 }
744 DataType::Subnet => {
745 if data.len() < offset + 8 {
746 return Err(ValueError::TruncatedData);
747 }
748 let ip = u32::from_le_bytes(data[offset..offset + 4].try_into().unwrap());
749 offset += 4;
750 let mask = u32::from_le_bytes(data[offset..offset + 4].try_into().unwrap());
751 offset += 4;
752 Value::Subnet(ip, mask)
753 }
754 DataType::Port => {
755 if data.len() < offset + 2 {
756 return Err(ValueError::TruncatedData);
757 }
758 let v = u16::from_le_bytes(data[offset..offset + 2].try_into().unwrap());
759 offset += 2;
760 Value::Port(v)
761 }
762 DataType::Latitude => {
763 if data.len() < offset + 4 {
764 return Err(ValueError::TruncatedData);
765 }
766 let v = i32::from_le_bytes(data[offset..offset + 4].try_into().unwrap());
767 offset += 4;
768 Value::Latitude(v)
769 }
770 DataType::Longitude => {
771 if data.len() < offset + 4 {
772 return Err(ValueError::TruncatedData);
773 }
774 let v = i32::from_le_bytes(data[offset..offset + 4].try_into().unwrap());
775 offset += 4;
776 Value::Longitude(v)
777 }
778 DataType::GeoPoint => {
779 if data.len() < offset + 8 {
780 return Err(ValueError::TruncatedData);
781 }
782 let lat = i32::from_le_bytes(data[offset..offset + 4].try_into().unwrap());
783 offset += 4;
784 let lon = i32::from_le_bytes(data[offset..offset + 4].try_into().unwrap());
785 offset += 4;
786 Value::GeoPoint(lat, lon)
787 }
788 DataType::Country2 => {
789 if data.len() < offset + 2 {
790 return Err(ValueError::TruncatedData);
791 }
792 let c: [u8; 2] = data[offset..offset + 2].try_into().unwrap();
793 offset += 2;
794 Value::Country2(c)
795 }
796 DataType::Country3 => {
797 if data.len() < offset + 3 {
798 return Err(ValueError::TruncatedData);
799 }
800 let c: [u8; 3] = data[offset..offset + 3].try_into().unwrap();
801 offset += 3;
802 Value::Country3(c)
803 }
804 DataType::Lang2 => {
805 if data.len() < offset + 2 {
806 return Err(ValueError::TruncatedData);
807 }
808 let c: [u8; 2] = data[offset..offset + 2].try_into().unwrap();
809 offset += 2;
810 Value::Lang2(c)
811 }
812 DataType::Lang5 => {
813 if data.len() < offset + 5 {
814 return Err(ValueError::TruncatedData);
815 }
816 let c: [u8; 5] = data[offset..offset + 5].try_into().unwrap();
817 offset += 5;
818 Value::Lang5(c)
819 }
820 DataType::Currency => {
821 if data.len() < offset + 3 {
822 return Err(ValueError::TruncatedData);
823 }
824 let c: [u8; 3] = data[offset..offset + 3].try_into().unwrap();
825 offset += 3;
826 Value::Currency(c)
827 }
828 DataType::AssetCode => {
829 let (len, len_bytes) = read_varint(&data[offset..])?;
830 offset += len_bytes;
831 if data.len() < offset + len as usize {
832 return Err(ValueError::TruncatedData);
833 }
834 let code = String::from_utf8(data[offset..offset + len as usize].to_vec())
835 .map_err(|_| ValueError::InvalidUtf8)?;
836 offset += len as usize;
837 Value::AssetCode(code)
838 }
839 DataType::Money => {
840 let (len, len_bytes) = read_varint(&data[offset..])?;
841 offset += len_bytes;
842 if data.len() < offset + len as usize + 1 + 8 {
843 return Err(ValueError::TruncatedData);
844 }
845 let asset_code = String::from_utf8(data[offset..offset + len as usize].to_vec())
846 .map_err(|_| ValueError::InvalidUtf8)?;
847 offset += len as usize;
848 let scale = data[offset];
849 offset += 1;
850 let minor_units = i64::from_le_bytes(data[offset..offset + 8].try_into().unwrap());
851 offset += 8;
852 Value::Money {
853 asset_code,
854 minor_units,
855 scale,
856 }
857 }
858 DataType::ColorAlpha => {
859 if data.len() < offset + 4 {
860 return Err(ValueError::TruncatedData);
861 }
862 let rgba: [u8; 4] = data[offset..offset + 4].try_into().unwrap();
863 offset += 4;
864 Value::ColorAlpha(rgba)
865 }
866 DataType::BigInt => {
867 if data.len() < offset + 8 {
868 return Err(ValueError::TruncatedData);
869 }
870 let v = i64::from_le_bytes(data[offset..offset + 8].try_into().unwrap());
871 offset += 8;
872 Value::BigInt(v)
873 }
874 DataType::KeyRef => {
875 let (col_len, col_varint) = read_varint(&data[offset..])?;
876 offset += col_varint;
877 if data.len() < offset + col_len as usize {
878 return Err(ValueError::TruncatedData);
879 }
880 let col = String::from_utf8(data[offset..offset + col_len as usize].to_vec())
881 .map_err(|_| ValueError::InvalidUtf8)?;
882 offset += col_len as usize;
883 let (key_len, key_varint) = read_varint(&data[offset..])?;
884 offset += key_varint;
885 if data.len() < offset + key_len as usize {
886 return Err(ValueError::TruncatedData);
887 }
888 let key = String::from_utf8(data[offset..offset + key_len as usize].to_vec())
889 .map_err(|_| ValueError::InvalidUtf8)?;
890 offset += key_len as usize;
891 Value::KeyRef(col, key)
892 }
893 DataType::DocRef => {
894 let (col_len, col_varint) = read_varint(&data[offset..])?;
895 offset += col_varint;
896 if data.len() < offset + col_len as usize + 8 {
897 return Err(ValueError::TruncatedData);
898 }
899 let col = String::from_utf8(data[offset..offset + col_len as usize].to_vec())
900 .map_err(|_| ValueError::InvalidUtf8)?;
901 offset += col_len as usize;
902 let id = u64::from_le_bytes(data[offset..offset + 8].try_into().unwrap());
903 offset += 8;
904 Value::DocRef(col, id)
905 }
906 DataType::TableRef => {
907 let (len, varint_size) = read_varint(&data[offset..])?;
908 offset += varint_size;
909 if data.len() < offset + len as usize {
910 return Err(ValueError::TruncatedData);
911 }
912 let name = String::from_utf8(data[offset..offset + len as usize].to_vec())
913 .map_err(|_| ValueError::InvalidUtf8)?;
914 offset += len as usize;
915 Value::TableRef(name)
916 }
917 DataType::PageRef => {
918 if data.len() < offset + 4 {
919 return Err(ValueError::TruncatedData);
920 }
921 let page_id = u32::from_le_bytes(data[offset..offset + 4].try_into().unwrap());
922 offset += 4;
923 Value::PageRef(page_id)
924 }
925 DataType::Secret => {
926 let (len, varint_size) = read_varint(&data[offset..])?;
927 offset += varint_size;
928 if data.len() < offset + len as usize {
929 return Err(ValueError::TruncatedData);
930 }
931 let bytes = data[offset..offset + len as usize].to_vec();
932 offset += len as usize;
933 Value::Secret(bytes)
934 }
935 DataType::Password => {
936 let (len, varint_size) = read_varint(&data[offset..])?;
937 offset += varint_size;
938 if data.len() < offset + len as usize {
939 return Err(ValueError::TruncatedData);
940 }
941 let hash = String::from_utf8(data[offset..offset + len as usize].to_vec())
942 .map_err(|_| ValueError::InvalidUtf8)?;
943 offset += len as usize;
944 Value::Password(hash)
945 }
946 DataType::Nullable => {
947 Value::Null
949 }
950 DataType::Unknown => {
951 Value::Null
955 }
956 DataType::TextZstd => {
960 let (orig_len, vs1) = read_varint(&data[offset..])?;
961 offset += vs1;
962 let (comp_len, vs2) = read_varint(&data[offset..])?;
963 offset += vs2;
964 if data.len() < offset + comp_len as usize {
965 return Err(ValueError::TruncatedData);
966 }
967 let compressed = &data[offset..offset + comp_len as usize];
968 let mut decompressed = vec![0u8; orig_len as usize];
969 let written = zstd::bulk::decompress_to_buffer(compressed, &mut decompressed)
970 .map_err(|_| ValueError::InvalidUtf8)?;
971 if written != decompressed.len() {
972 return Err(ValueError::TruncatedData);
973 }
974 offset += comp_len as usize;
975 let s = String::from_utf8(decompressed).map_err(|_| ValueError::InvalidUtf8)?;
976 Value::text(s)
977 }
978 DataType::BlobZstd => {
980 let (orig_len, vs1) = read_varint(&data[offset..])?;
981 offset += vs1;
982 let (comp_len, vs2) = read_varint(&data[offset..])?;
983 offset += vs2;
984 if data.len() < offset + comp_len as usize {
985 return Err(ValueError::TruncatedData);
986 }
987 let compressed = &data[offset..offset + comp_len as usize];
988 let mut decompressed = vec![0u8; orig_len as usize];
989 let written = zstd::bulk::decompress_to_buffer(compressed, &mut decompressed)
990 .map_err(|_| ValueError::InvalidUtf8)?;
991 if written != decompressed.len() {
992 return Err(ValueError::TruncatedData);
993 }
994 offset += comp_len as usize;
995 Value::Blob(decompressed)
996 }
997 };
998
999 Ok((value, offset))
1000}
1001
1002#[cfg(test)]
1003mod tests {
1004 use super::*;
1005 use proptest::prelude::*;
1006 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
1007
1008 #[test]
1017 fn pinned_bytes() {
1018 let mut buf = Vec::new();
1020 encode(&Value::Null, &mut buf);
1021 assert_eq!(buf, vec![0x00], "Value::Null layout drifted");
1022
1023 let mut buf = Vec::new();
1025 encode(&Value::Integer(-1), &mut buf);
1026 assert_eq!(
1027 buf,
1028 vec![0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
1029 "Value::Integer layout drifted"
1030 );
1031
1032 let mut buf = Vec::new();
1034 encode(&Value::text("hi"), &mut buf);
1035 assert_eq!(
1036 buf,
1037 vec![0x04, 0x02, b'h', b'i'],
1038 "Value::Text layout drifted"
1039 );
1040
1041 let mut buf = Vec::new();
1043 encode(&Value::Boolean(true), &mut buf);
1044 assert_eq!(buf, vec![0x06, 0x01], "Value::Boolean layout drifted");
1045
1046 let mut buf = Vec::new();
1048 encode(&Value::Blob(vec![0x01, 0x02, 0x03]), &mut buf);
1049 assert_eq!(
1050 buf,
1051 vec![0x05, 0x03, 0x01, 0x02, 0x03],
1052 "Value::Blob layout drifted"
1053 );
1054 }
1055
1056 #[test]
1060 fn type_tag_matches_data_type_byte() {
1061 let samples: &[Value] = &[
1062 Value::Null,
1063 Value::Integer(0),
1064 Value::UnsignedInteger(0),
1065 Value::Float(0.0),
1066 Value::text(""),
1067 Value::Blob(Vec::new()),
1068 Value::Boolean(false),
1069 Value::Timestamp(0),
1070 Value::Duration(0),
1071 Value::Uuid([0; 16]),
1072 ];
1073 for v in samples {
1074 let tag = type_tag(v);
1075 if matches!(v, Value::Null) {
1076 assert_eq!(tag, 0);
1077 } else {
1078 assert_eq!(tag, v.data_type().to_byte());
1079 let kind = type_for_tag(tag).expect("registered tag");
1080 assert_eq!(kind, v.data_type());
1081 }
1082 }
1083 }
1084
1085 #[test]
1089 fn rejects_unknown_type_tag() {
1090 let buf = [0xFFu8];
1092 let err = decode(&buf).expect_err("unknown tag must error");
1093 assert!(matches!(err, ValueError::InvalidType(0xFF)));
1094 }
1095
1096 #[test]
1100 fn rejects_truncated_buffer() {
1101 assert!(matches!(decode(&[]), Err(ValueError::EmptyData)));
1103
1104 let mut buf = vec![DataType::Integer.to_byte()];
1106 buf.extend_from_slice(&[0x01, 0x02, 0x03]);
1107 assert!(matches!(decode(&buf), Err(ValueError::TruncatedData)));
1108
1109 let mut buf = vec![DataType::Text.to_byte()];
1111 write_varint(&mut buf, 5);
1112 buf.extend_from_slice(b"ab");
1113 assert!(matches!(decode(&buf), Err(ValueError::TruncatedData)));
1114 }
1115
1116 #[test]
1117 fn type_for_tag_handles_null_registered_and_unknown_tags() {
1118 assert_eq!(type_for_tag(0), Some(DataType::Nullable));
1119 assert_eq!(
1120 type_for_tag(DataType::Money.to_byte()),
1121 Some(DataType::Money)
1122 );
1123 assert_eq!(type_for_tag(0xFF), None);
1124 }
1125
1126 #[test]
1127 fn round_trip_every_value_variant_directly_through_registry() {
1128 let values = vec![
1129 Value::Null,
1130 Value::Integer(-1),
1131 Value::UnsignedInteger(2),
1132 Value::Float(3.5),
1133 Value::text("hello"),
1134 Value::Blob(vec![1, 2, 3]),
1135 Value::Boolean(true),
1136 Value::Timestamp(4),
1137 Value::Duration(5),
1138 Value::IpAddr(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
1139 Value::IpAddr(IpAddr::V6(Ipv6Addr::LOCALHOST)),
1140 Value::MacAddr([1, 2, 3, 4, 5, 6]),
1141 Value::Vector(vec![1.0, 2.0]),
1142 Value::Json(br#"{"ok":true}"#.to_vec()),
1143 Value::Uuid([7; 16]),
1144 Value::NodeRef("node".to_string()),
1145 Value::EdgeRef("edge".to_string()),
1146 Value::VectorRef("vectors".to_string(), 8),
1147 Value::RowRef("rows".to_string(), 9),
1148 Value::Color([0xAA, 0xBB, 0xCC]),
1149 Value::Email("a@example.com".to_string()),
1150 Value::Url("https://example.com".to_string()),
1151 Value::Phone(5511999),
1152 Value::Semver(1_002_003),
1153 Value::Cidr(10 << 24, 8),
1154 Value::Date(20_000),
1155 Value::Time(43_200_000),
1156 Value::Decimal(123_456),
1157 Value::DecimalText("18446744073709551616.00000000000000000001".to_string()),
1158 Value::EnumValue(3),
1159 Value::Array(vec![Value::Integer(1), Value::text("two")]),
1160 Value::TimestampMs(123_456),
1161 Value::Ipv4(0x7f000001),
1162 Value::Ipv6([1; 16]),
1163 Value::Subnet(10 << 24, 0xff000000),
1164 Value::Port(5432),
1165 Value::Latitude(-23_550_520),
1166 Value::Longitude(-46_633_308),
1167 Value::GeoPoint(-23_550_520, -46_633_308),
1168 Value::Country2(*b"BR"),
1169 Value::Country3(*b"BRA"),
1170 Value::Lang2(*b"pt"),
1171 Value::Lang5(*b"pt-BR"),
1172 Value::Currency(*b"USD"),
1173 Value::AssetCode("BTC".to_string()),
1174 Value::Money {
1175 asset_code: "USD".to_string(),
1176 minor_units: 1234,
1177 scale: 2,
1178 },
1179 Value::ColorAlpha([1, 2, 3, 4]),
1180 Value::BigInt(-10),
1181 Value::KeyRef("kv".to_string(), "key".to_string()),
1182 Value::DocRef("docs".to_string(), 42),
1183 Value::TableRef("users".to_string()),
1184 Value::PageRef(99),
1185 Value::Secret(vec![9, 8, 7]),
1186 Value::Password("$argon2id$v=19$hash".to_string()),
1187 ];
1188
1189 for original in values {
1190 let mut bytes = Vec::new();
1191 encode(&original, &mut bytes);
1192 let (decoded, consumed) = decode(&bytes).expect("decode");
1193 assert_eq!(consumed, bytes.len());
1194 assert_eq!(decoded, original, "{bytes:?}");
1195 }
1196 }
1197
1198 #[test]
1199 fn compressed_text_and_blob_decode_to_plain_values() {
1200 let text = Value::text("reddb ".repeat(700));
1201 let mut bytes = Vec::new();
1202 encode(&text, &mut bytes);
1203 assert_eq!(bytes[0], DataType::TextZstd.to_byte());
1204 let (decoded, consumed) = decode(&bytes).unwrap();
1205 assert_eq!(consumed, bytes.len());
1206 assert_eq!(decoded, text);
1207
1208 let blob = Value::Blob(vec![0xAB; TOAST_THRESHOLD + 512]);
1209 let mut bytes = Vec::new();
1210 encode(&blob, &mut bytes);
1211 assert_eq!(bytes[0], DataType::BlobZstd.to_byte());
1212 let (decoded, consumed) = decode(&bytes).unwrap();
1213 assert_eq!(consumed, bytes.len());
1214 assert_eq!(decoded, blob);
1215 }
1216
1217 #[test]
1218 fn decode_rejects_short_payload_for_registered_tags() {
1219 let truncated_tags = [
1220 DataType::Integer,
1221 DataType::UnsignedInteger,
1222 DataType::Float,
1223 DataType::Text,
1224 DataType::Blob,
1225 DataType::Boolean,
1226 DataType::Timestamp,
1227 DataType::Duration,
1228 DataType::IpAddr,
1229 DataType::MacAddr,
1230 DataType::Vector,
1231 DataType::Json,
1232 DataType::Uuid,
1233 DataType::NodeRef,
1234 DataType::EdgeRef,
1235 DataType::VectorRef,
1236 DataType::RowRef,
1237 DataType::Color,
1238 DataType::Email,
1239 DataType::Url,
1240 DataType::Phone,
1241 DataType::Semver,
1242 DataType::Cidr,
1243 DataType::Date,
1244 DataType::Time,
1245 DataType::Decimal,
1246 DataType::DecimalText,
1247 DataType::Enum,
1248 DataType::Array,
1249 DataType::TimestampMs,
1250 DataType::Ipv4,
1251 DataType::Ipv6,
1252 DataType::Subnet,
1253 DataType::Port,
1254 DataType::Latitude,
1255 DataType::Longitude,
1256 DataType::GeoPoint,
1257 DataType::Country2,
1258 DataType::Country3,
1259 DataType::Lang2,
1260 DataType::Lang5,
1261 DataType::Currency,
1262 DataType::AssetCode,
1263 DataType::Money,
1264 DataType::ColorAlpha,
1265 DataType::BigInt,
1266 DataType::KeyRef,
1267 DataType::DocRef,
1268 DataType::TableRef,
1269 DataType::PageRef,
1270 DataType::Secret,
1271 DataType::Password,
1272 DataType::TextZstd,
1273 DataType::BlobZstd,
1274 ];
1275
1276 for data_type in truncated_tags {
1277 let err = decode(&[data_type.to_byte()]).expect_err("short payload must error");
1278 assert_eq!(err, ValueError::TruncatedData, "{data_type:?}");
1279 }
1280
1281 assert_eq!(
1282 decode(&[DataType::Nullable.to_byte()]).unwrap().0,
1283 Value::Null
1284 );
1285 }
1286
1287 #[test]
1288 fn decode_rejects_invalid_embedded_tags_and_utf8_payloads() {
1289 assert_eq!(
1290 decode(&[DataType::IpAddr.to_byte(), 5]).expect_err("bad ip version"),
1291 ValueError::InvalidIpVersion(5)
1292 );
1293
1294 let invalid_text = [DataType::Text.to_byte(), 1, 0xff];
1295 assert_eq!(
1296 decode(&invalid_text).expect_err("invalid utf8"),
1297 ValueError::InvalidUtf8
1298 );
1299
1300 let invalid_email = [DataType::Email.to_byte(), 1, 0xff];
1301 assert_eq!(
1302 decode(&invalid_email).expect_err("invalid utf8"),
1303 ValueError::InvalidUtf8
1304 );
1305
1306 let invalid_url = [DataType::Url.to_byte(), 1, 0xff];
1307 assert_eq!(
1308 decode(&invalid_url).expect_err("invalid utf8"),
1309 ValueError::InvalidUtf8
1310 );
1311
1312 let invalid_asset = [DataType::AssetCode.to_byte(), 1, 0xff];
1313 assert_eq!(
1314 decode(&invalid_asset).expect_err("invalid utf8"),
1315 ValueError::InvalidUtf8
1316 );
1317 }
1318
1319 #[test]
1322 fn round_trip_canonical_variants() {
1323 let cases = vec![
1324 Value::Null,
1325 Value::Integer(-12345),
1326 Value::text("hello"),
1327 Value::Boolean(true),
1328 Value::Blob(vec![1, 2, 3, 4, 5]),
1329 ];
1330 for original in cases {
1331 let mut bytes = Vec::new();
1332 encode(&original, &mut bytes);
1333 let (recovered, consumed) = decode(&bytes).expect("decode");
1334 assert_eq!(consumed, bytes.len());
1335 assert_eq!(original, recovered);
1336 }
1337 }
1338
1339 fn value_variant_strategy() -> impl Strategy<Value = Value> {
1340 prop_oneof![
1341 Just(Value::Null),
1342 any::<bool>().prop_map(Value::Boolean),
1343 any::<i64>().prop_map(Value::Integer),
1344 prop_oneof![
1345 any::<f64>(),
1346 Just(f64::NAN),
1347 Just(f64::INFINITY),
1348 Just(f64::NEG_INFINITY),
1349 Just(f64::MIN_POSITIVE),
1350 Just(f64::from_bits(1)),
1351 ]
1352 .prop_map(Value::Float),
1353 "[1-9][0-9]{18,36}\\.[0-9]{1,24}".prop_map(Value::DecimalText),
1354 proptest::collection::vec(any::<u8>(), 0..4096).prop_map(Value::Blob),
1355 prop_oneof![
1356 Just(br#"{"a":null,"b":[1,true]}"#.to_vec()),
1357 Just(br#"[]"#.to_vec()),
1358 Just(br#"{"deep":{"nest":{"leaf":[false]}}}"#.to_vec()),
1359 ]
1360 .prop_map(Value::Json),
1361 any::<i64>().prop_map(Value::Timestamp),
1362 any::<[u8; 16]>().prop_map(Value::Uuid),
1363 ]
1364 }
1365
1366 proptest! {
1367 #![proptest_config(ProptestConfig::with_cases(256))]
1368
1369 #[test]
1370 fn prop_value_codec_round_trips_remaining_variants(original in value_variant_strategy()) {
1371 let mut bytes = Vec::new();
1372 encode(&original, &mut bytes);
1373 let (recovered, consumed) = decode(&bytes).expect("decode");
1374 prop_assert_eq!(consumed, bytes.len());
1375 prop_assert!(
1376 values_equivalent(&recovered, &original),
1377 "recovered={recovered:?} original={original:?}"
1378 );
1379 }
1380 }
1381
1382 fn values_equivalent(left: &Value, right: &Value) -> bool {
1383 match (left, right) {
1384 (Value::Float(a), Value::Float(b)) => a.to_bits() == b.to_bits(),
1385 _ => left == right,
1386 }
1387 }
1388}