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