1use crate::meta::{ColumnId, PredicateValue};
4
5use super::{DocId, DocumentVersion, IngestDocument, Revision};
6
7pub const UPSERT_V1: u16 = 1;
27pub const DELETE_V1: u16 = 2;
29pub const METADATA_EDIT_V1: u16 = 3;
31pub const UPSERT_WITH_TIMESTAMP_V1: u16 = 4;
33pub const UPSERT_WITH_METADATA_V1: u16 = 5;
35pub const UPSERT_WITH_TIMESTAMP_AND_METADATA_V1: u16 = 6;
37pub const UPSERT_V2: u16 = 7;
39
40pub const UPSERT_V2_VECTOR: u32 = 1 << 0;
42pub const UPSERT_V2_TEXT: u32 = 1 << 1;
44pub const UPSERT_V2_TIMESTAMP: u32 = 1 << 2;
46pub const UPSERT_V2_METADATA: u32 = 1 << 3;
48pub const UPSERT_V2_TYPED_COLUMNS: u32 = 1 << 4;
50pub const UPSERT_V2_EPOCH_TAG_RESERVED: u32 = 1 << 5;
52
53const UPSERT_V2_KNOWN_FIELDS: u32 = UPSERT_V2_VECTOR
54 | UPSERT_V2_TEXT
55 | UPSERT_V2_TIMESTAMP
56 | UPSERT_V2_METADATA
57 | UPSERT_V2_TYPED_COLUMNS;
58
59const PAYLOAD_VERSION: u16 = 1;
60const COMMON_HEADER_LEN: usize = 4;
61const DOCUMENT_VERSION_LEN: usize = 24;
62const UPSERT_PREFIX_LEN: usize = COMMON_HEADER_LEN + DOCUMENT_VERSION_LEN + 4;
63const TIMESTAMPED_UPSERT_PREFIX_LEN: usize = COMMON_HEADER_LEN + DOCUMENT_VERSION_LEN + 8 + 4;
64const METADATA_UPSERT_PREFIX_LEN: usize = COMMON_HEADER_LEN + DOCUMENT_VERSION_LEN + 4 + 4;
65const TIMESTAMPED_METADATA_UPSERT_PREFIX_LEN: usize =
66 COMMON_HEADER_LEN + DOCUMENT_VERSION_LEN + 8 + 4 + 4;
67const DELETE_PREFIX_LEN: usize = COMMON_HEADER_LEN + 4;
68const METADATA_PREFIX_LEN: usize = COMMON_HEADER_LEN + DOCUMENT_VERSION_LEN + 4 + 1 + 3 + 4;
69
70const VALUE_NULL: u8 = 0;
71const VALUE_U64: u8 = 1;
72const VALUE_I64: u8 = 2;
73const VALUE_F64: u8 = 3;
74const VALUE_BOOL: u8 = 4;
75const VALUE_STRING: u8 = 5;
76
77#[derive(Clone, Debug, Eq, PartialEq)]
79pub enum PayloadError {
80 UnknownOperation(u16),
82 LengthOverflow,
84 Truncated,
86 Version(u16),
88 Flags(u16),
90 Reserved(u32),
92 EmptyVector,
94 EmptyDelete,
96 NonFiniteVector {
98 index: usize,
100 },
101 ValueKind(u8),
103 ValueLength {
105 kind: u8,
107 expected: usize,
109 actual: usize,
111 },
112 Boolean(u8),
114 Utf8,
116 TrailingBytes(usize),
118 FieldBitmap(u32),
120 Lexical(String),
122 Columns(String),
124}
125
126impl std::fmt::Display for PayloadError {
127 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128 match self {
129 Self::UnknownOperation(op) => {
130 write!(formatter, "WAL mutation operation {op} is unknown")
131 }
132 Self::LengthOverflow => formatter.write_str("WAL mutation payload length overflow"),
133 Self::Truncated => formatter.write_str("WAL mutation payload is truncated"),
134 Self::Version(version) => write!(
135 formatter,
136 "WAL mutation payload version {version} is unsupported"
137 ),
138 Self::Flags(flags) => write!(
139 formatter,
140 "WAL mutation payload flags {flags:#06x} are non-zero"
141 ),
142 Self::Reserved(value) => write!(
143 formatter,
144 "WAL metadata payload reserved bytes are {value:#08x}, expected zero"
145 ),
146 Self::EmptyVector => formatter.write_str("WAL upsert vector is empty"),
147 Self::EmptyDelete => formatter.write_str("WAL delete document list is empty"),
148 Self::NonFiniteVector { index } => write!(
149 formatter,
150 "WAL upsert vector coordinate {index} is non-finite"
151 ),
152 Self::ValueKind(kind) => {
153 write!(formatter, "WAL metadata value kind {kind} is unknown")
154 }
155 Self::ValueLength {
156 kind,
157 expected,
158 actual,
159 } => write!(
160 formatter,
161 "WAL metadata value kind {kind} needs {expected} bytes, got {actual}"
162 ),
163 Self::Boolean(value) => write!(
164 formatter,
165 "WAL metadata Boolean byte {value} is not canonical zero or one"
166 ),
167 Self::Utf8 => formatter.write_str("WAL metadata string is not valid UTF-8"),
168 Self::TrailingBytes(bytes) => {
169 write!(formatter, "WAL mutation payload has {bytes} trailing bytes")
170 }
171 Self::FieldBitmap(bitmap) => write!(
172 formatter,
173 "WAL upsert-v2 field bitmap {bitmap:#010x} names reserved fields"
174 ),
175 Self::Lexical(detail) => write!(formatter, "WAL lexical replay failed: {detail}"),
176 Self::Columns(detail) => write!(formatter, "WAL column replay failed: {detail}"),
177 }
178 }
179}
180
181impl std::error::Error for PayloadError {}
182
183#[derive(Clone, Debug, PartialEq)]
185pub enum MetadataValue {
186 Null,
188 U64(u64),
190 I64(i64),
192 F64(f64),
194 Bool(bool),
196 String(String),
198}
199
200#[derive(Clone, Debug, PartialEq)]
202pub struct MetadataEdit {
203 version: DocumentVersion,
204 column: ColumnId,
205 value: MetadataValue,
206}
207
208impl MetadataEdit {
209 #[must_use]
211 pub const fn new(version: DocumentVersion, column: ColumnId, value: MetadataValue) -> Self {
212 Self {
213 version,
214 column,
215 value,
216 }
217 }
218
219 #[must_use]
221 pub const fn version(&self) -> DocumentVersion {
222 self.version
223 }
224
225 #[must_use]
227 pub const fn column(&self) -> ColumnId {
228 self.column
229 }
230
231 #[must_use]
233 pub const fn value(&self) -> &MetadataValue {
234 &self.value
235 }
236}
237
238#[derive(Clone, Debug, PartialEq)]
240pub enum MutationPayload {
241 Upsert(IngestDocument),
243 Delete(Vec<DocId>),
245 MetadataEdit(MetadataEdit),
247}
248
249pub fn encode_upsert(document: &IngestDocument) -> Result<Vec<u8>, PayloadError> {
252 encode_upsert_body(document, None, false)
253}
254
255pub fn encode_upsert_with_timestamp(document: &IngestDocument) -> Result<Vec<u8>, PayloadError> {
257 encode_upsert_body(document, Some(document.timestamp()), false)
258}
259
260pub fn encode_upsert_with_metadata(document: &IngestDocument) -> Result<Vec<u8>, PayloadError> {
262 encode_upsert_body(document, None, true)
263}
264
265pub fn encode_upsert_with_timestamp_and_metadata(
267 document: &IngestDocument,
268) -> Result<Vec<u8>, PayloadError> {
269 encode_upsert_body(document, Some(document.timestamp()), true)
270}
271
272pub fn encode_upsert_v2(document: &IngestDocument) -> Result<Vec<u8>, PayloadError> {
275 let mut bitmap = UPSERT_V2_VECTOR;
276 if document.text().is_some() {
277 bitmap |= UPSERT_V2_TEXT;
278 }
279 if document.has_timestamp() {
280 bitmap |= UPSERT_V2_TIMESTAMP;
281 }
282 if document.has_metadata() {
283 bitmap |= UPSERT_V2_METADATA;
284 }
285 if document.has_columns() {
286 bitmap |= UPSERT_V2_TYPED_COLUMNS;
287 }
288 validate_vector(document.vector())?;
289 let dims = u32::try_from(document.vector().len()).map_err(|_| PayloadError::LengthOverflow)?;
290 let mut payload = Vec::new();
291 payload.extend_from_slice(&bitmap.to_le_bytes());
292 append_version(&mut payload, document.version());
293 payload.extend_from_slice(&dims.to_le_bytes());
294 for value in document.vector() {
295 payload.extend_from_slice(&value.to_bits().to_le_bytes());
296 }
297 if let Some(text) = document.text() {
298 append_length_prefixed(text.as_bytes(), &mut payload)?;
299 }
300 if document.has_timestamp() {
301 payload.extend_from_slice(&document.timestamp().to_le_bytes());
302 }
303 if document.has_metadata() {
304 append_length_prefixed(document.metadata(), &mut payload)?;
305 }
306 if document.has_columns() {
307 encode_typed_columns(document.columns(), &mut payload)?;
308 }
309 Ok(payload)
310}
311
312fn validate_vector(vector: &[f32]) -> Result<(), PayloadError> {
313 if vector.is_empty() {
314 return Err(PayloadError::EmptyVector);
315 }
316 if let Some(index) = vector.iter().position(|value| !value.is_finite()) {
317 return Err(PayloadError::NonFiniteVector { index });
318 }
319 Ok(())
320}
321
322fn append_length_prefixed(bytes: &[u8], output: &mut Vec<u8>) -> Result<(), PayloadError> {
323 let length = u32::try_from(bytes.len()).map_err(|_| PayloadError::LengthOverflow)?;
324 output.extend_from_slice(&length.to_le_bytes());
325 output.extend_from_slice(bytes);
326 Ok(())
327}
328
329fn encode_typed_columns(
330 columns: &[(ColumnId, PredicateValue)],
331 output: &mut Vec<u8>,
332) -> Result<(), PayloadError> {
333 let count = u32::try_from(columns.len()).map_err(|_| PayloadError::LengthOverflow)?;
334 output.extend_from_slice(&count.to_le_bytes());
335 for (column, value) in columns {
336 let metadata = predicate_to_metadata(value);
337 let (kind, body) = encode_metadata_value(&metadata)?;
338 output.extend_from_slice(&column.get().to_le_bytes());
339 output.push(kind);
340 output.extend_from_slice(&[0_u8; 3]);
341 append_length_prefixed(&body, output)?;
342 }
343 Ok(())
344}
345
346pub(crate) fn encode_column_values(
347 columns: &[(ColumnId, PredicateValue)],
348) -> Result<Vec<u8>, PayloadError> {
349 let mut encoded = Vec::new();
350 encode_typed_columns(columns, &mut encoded)?;
351 Ok(encoded)
352}
353
354fn predicate_to_metadata(value: &PredicateValue) -> MetadataValue {
355 match value {
356 PredicateValue::U64(value) => MetadataValue::U64(*value),
357 PredicateValue::I64(value) => MetadataValue::I64(*value),
358 PredicateValue::F64(value) => MetadataValue::F64(*value),
359 PredicateValue::Bool(value) => MetadataValue::Bool(*value),
360 PredicateValue::String(value) => MetadataValue::String(value.clone()),
361 }
362}
363
364fn encode_upsert_body(
365 document: &IngestDocument,
366 timestamp: Option<i64>,
367 include_metadata: bool,
368) -> Result<Vec<u8>, PayloadError> {
369 if document.vector().is_empty() {
370 return Err(PayloadError::EmptyVector);
371 }
372 let dims = u32::try_from(document.vector().len()).map_err(|_| PayloadError::LengthOverflow)?;
373 if let Some(index) = document
374 .vector()
375 .iter()
376 .position(|value| !value.is_finite())
377 {
378 return Err(PayloadError::NonFiniteVector { index });
379 }
380 let vector_bytes = document
381 .vector()
382 .len()
383 .checked_mul(std::mem::size_of::<f32>())
384 .ok_or(PayloadError::LengthOverflow)?;
385 let prefix = match (timestamp.is_some(), include_metadata) {
386 (false, false) => UPSERT_PREFIX_LEN,
387 (true, false) => TIMESTAMPED_UPSERT_PREFIX_LEN,
388 (false, true) => METADATA_UPSERT_PREFIX_LEN,
389 (true, true) => TIMESTAMPED_METADATA_UPSERT_PREFIX_LEN,
390 };
391 let capacity = prefix
392 .checked_add(if include_metadata {
393 document.metadata().len()
394 } else {
395 0
396 })
397 .and_then(|value| value.checked_add(vector_bytes))
398 .ok_or(PayloadError::LengthOverflow)?;
399 let mut payload = Vec::with_capacity(capacity);
400 append_header(&mut payload);
401 append_version(&mut payload, document.version());
402 if let Some(timestamp) = timestamp {
403 payload.extend_from_slice(×tamp.to_le_bytes());
404 }
405 if include_metadata {
406 let metadata_len =
407 u32::try_from(document.metadata().len()).map_err(|_| PayloadError::LengthOverflow)?;
408 payload.extend_from_slice(&metadata_len.to_le_bytes());
409 }
410 payload.extend_from_slice(&dims.to_le_bytes());
411 if include_metadata {
412 payload.extend_from_slice(document.metadata());
413 }
414 for value in document.vector() {
415 payload.extend_from_slice(&value.to_bits().to_le_bytes());
416 }
417 Ok(payload)
418}
419
420pub fn encode_delete(doc_ids: &[DocId]) -> Result<Vec<u8>, PayloadError> {
423 if doc_ids.is_empty() {
424 return Err(PayloadError::EmptyDelete);
425 }
426 let count = u32::try_from(doc_ids.len()).map_err(|_| PayloadError::LengthOverflow)?;
427 let body_bytes = doc_ids
428 .len()
429 .checked_mul(std::mem::size_of::<u128>())
430 .ok_or(PayloadError::LengthOverflow)?;
431 let capacity = DELETE_PREFIX_LEN
432 .checked_add(body_bytes)
433 .ok_or(PayloadError::LengthOverflow)?;
434 let mut payload = Vec::with_capacity(capacity);
435 append_header(&mut payload);
436 payload.extend_from_slice(&count.to_le_bytes());
437 for doc_id in doc_ids {
438 payload.extend_from_slice(&doc_id.get().to_le_bytes());
439 }
440 Ok(payload)
441}
442
443pub fn encode_metadata_edit(edit: &MetadataEdit) -> Result<Vec<u8>, PayloadError> {
446 let (kind, body) = encode_metadata_value(edit.value())?;
447 let value_len = u32::try_from(body.len()).map_err(|_| PayloadError::LengthOverflow)?;
448 let capacity = METADATA_PREFIX_LEN
449 .checked_add(body.len())
450 .ok_or(PayloadError::LengthOverflow)?;
451 let mut payload = Vec::with_capacity(capacity);
452 append_header(&mut payload);
453 append_version(&mut payload, edit.version());
454 payload.extend_from_slice(&edit.column().get().to_le_bytes());
455 payload.push(kind);
456 payload.extend_from_slice(&[0_u8; 3]);
457 payload.extend_from_slice(&value_len.to_le_bytes());
458 payload.extend_from_slice(&body);
459 Ok(payload)
460}
461
462pub fn decode_mutation(op: u16, payload: &[u8]) -> Result<MutationPayload, PayloadError> {
464 match op {
465 UPSERT_V1 => decode_upsert(payload).map(MutationPayload::Upsert),
466 DELETE_V1 => decode_delete(payload).map(MutationPayload::Delete),
467 METADATA_EDIT_V1 => decode_metadata_edit(payload).map(MutationPayload::MetadataEdit),
468 UPSERT_WITH_TIMESTAMP_V1 => {
469 decode_upsert_with_timestamp(payload).map(MutationPayload::Upsert)
470 }
471 UPSERT_WITH_METADATA_V1 => {
472 decode_upsert_with_metadata(payload).map(MutationPayload::Upsert)
473 }
474 UPSERT_WITH_TIMESTAMP_AND_METADATA_V1 => {
475 decode_upsert_with_timestamp_and_metadata(payload).map(MutationPayload::Upsert)
476 }
477 UPSERT_V2 => decode_upsert_v2(payload).map(MutationPayload::Upsert),
478 unknown => Err(PayloadError::UnknownOperation(unknown)),
479 }
480}
481
482pub fn decode_upsert_v2(payload: &[u8]) -> Result<IngestDocument, PayloadError> {
484 let mut cursor = Cursor::new(payload);
485 let bitmap = cursor.read_u32()?;
486 let reserved = bitmap & !UPSERT_V2_KNOWN_FIELDS;
487 if reserved != 0 {
488 return Err(PayloadError::FieldBitmap(reserved));
489 }
490 let version = cursor.read_version()?;
491 let vector = if bitmap & UPSERT_V2_VECTOR != 0 {
492 let dims = usize::try_from(cursor.read_u32()?).map_err(|_| PayloadError::LengthOverflow)?;
493 if dims == 0 {
494 return Err(PayloadError::EmptyVector);
495 }
496 let mut vector = Vec::with_capacity(dims);
497 for index in 0..dims {
498 let value = f32::from_bits(cursor.read_u32()?);
499 if !value.is_finite() {
500 return Err(PayloadError::NonFiniteVector { index });
501 }
502 vector.push(value);
503 }
504 vector
505 } else {
506 Vec::new()
507 };
508 let text = if bitmap & UPSERT_V2_TEXT != 0 {
509 Some(cursor.read_string()?)
510 } else {
511 None
512 };
513 let timestamp = if bitmap & UPSERT_V2_TIMESTAMP != 0 {
514 Some(i64::from_le_bytes(cursor.take()?))
515 } else {
516 None
517 };
518 let metadata = if bitmap & UPSERT_V2_METADATA != 0 {
519 Some(cursor.read_length_prefixed()?.to_vec())
520 } else {
521 None
522 };
523 let columns = if bitmap & UPSERT_V2_TYPED_COLUMNS != 0 {
524 Some(decode_typed_columns(&mut cursor)?)
525 } else {
526 None
527 };
528 cursor.finish()?;
529 let mut document = IngestDocument::new(version, vector);
530 if let Some(text) = text {
531 document = document.with_text(text);
532 }
533 if let Some(timestamp) = timestamp {
534 document = document.with_timestamp(timestamp);
535 }
536 if let Some(metadata) = metadata {
537 document = document.with_metadata(metadata);
538 }
539 if let Some(columns) = columns {
540 document = document.with_columns(columns);
541 }
542 Ok(document)
543}
544
545fn decode_typed_columns(
546 cursor: &mut Cursor<'_>,
547) -> Result<Vec<(ColumnId, PredicateValue)>, PayloadError> {
548 let count = usize::try_from(cursor.read_u32()?).map_err(|_| PayloadError::LengthOverflow)?;
549 let mut columns = Vec::with_capacity(count);
550 for _ in 0..count {
551 let column = ColumnId::new(cursor.read_u32()?);
552 let kind = cursor.read_u8()?;
553 let reserved = cursor.read_u24()?;
554 if reserved != 0 {
555 return Err(PayloadError::Reserved(reserved));
556 }
557 let body = cursor.read_length_prefixed()?;
558 let value = metadata_to_predicate(decode_metadata_value(kind, body)?)?;
559 columns.push((column, value));
560 }
561 Ok(columns)
562}
563
564pub(crate) fn decode_column_values(
565 encoded: &[u8],
566) -> Result<Vec<(ColumnId, PredicateValue)>, PayloadError> {
567 let mut cursor = Cursor::new(encoded);
568 let columns = decode_typed_columns(&mut cursor)?;
569 cursor.finish()?;
570 Ok(columns)
571}
572
573fn metadata_to_predicate(value: MetadataValue) -> Result<PredicateValue, PayloadError> {
574 match value {
575 MetadataValue::Null => Err(PayloadError::ValueKind(VALUE_NULL)),
576 MetadataValue::U64(value) => Ok(PredicateValue::U64(value)),
577 MetadataValue::I64(value) => Ok(PredicateValue::I64(value)),
578 MetadataValue::F64(value) => Ok(PredicateValue::F64(value)),
579 MetadataValue::Bool(value) => Ok(PredicateValue::Bool(value)),
580 MetadataValue::String(value) => Ok(PredicateValue::String(value)),
581 }
582}
583
584pub fn decode_upsert(payload: &[u8]) -> Result<IngestDocument, PayloadError> {
586 decode_upsert_body(payload, false, false)
587}
588
589pub fn decode_upsert_with_timestamp(payload: &[u8]) -> Result<IngestDocument, PayloadError> {
591 decode_upsert_body(payload, true, false)
592}
593
594pub fn decode_upsert_with_metadata(payload: &[u8]) -> Result<IngestDocument, PayloadError> {
596 decode_upsert_body(payload, false, true)
597}
598
599pub fn decode_upsert_with_timestamp_and_metadata(
601 payload: &[u8],
602) -> Result<IngestDocument, PayloadError> {
603 decode_upsert_body(payload, true, true)
604}
605
606fn decode_upsert_body(
607 payload: &[u8],
608 has_timestamp: bool,
609 has_metadata: bool,
610) -> Result<IngestDocument, PayloadError> {
611 let mut cursor = Cursor::new(payload);
612 cursor.require_header()?;
613 let version = cursor.read_version()?;
614 let timestamp = if has_timestamp {
615 i64::from_le_bytes(cursor.take()?)
616 } else {
617 0
618 };
619 let metadata_len = if has_metadata {
620 usize::try_from(cursor.read_u32()?).map_err(|_| PayloadError::LengthOverflow)?
621 } else {
622 0
623 };
624 let dims = usize::try_from(cursor.read_u32()?).map_err(|_| PayloadError::LengthOverflow)?;
625 if dims == 0 {
626 return Err(PayloadError::EmptyVector);
627 }
628 let body_bytes = dims
629 .checked_mul(std::mem::size_of::<f32>())
630 .ok_or(PayloadError::LengthOverflow)?;
631 let remaining = metadata_len
632 .checked_add(body_bytes)
633 .ok_or(PayloadError::LengthOverflow)?;
634 cursor.require_remaining(remaining)?;
635 let metadata = cursor.read_bytes(metadata_len)?.to_vec();
636 let mut vector = Vec::with_capacity(dims);
637 for index in 0..dims {
638 let value = f32::from_bits(cursor.read_u32()?);
639 if !value.is_finite() {
640 return Err(PayloadError::NonFiniteVector { index });
641 }
642 vector.push(value);
643 }
644 cursor.finish()?;
645 let mut document = IngestDocument::new(version, vector);
646 if has_timestamp {
647 document = document.with_timestamp(timestamp);
648 }
649 if has_metadata {
650 document = document.with_metadata(metadata);
651 }
652 Ok(document)
653}
654
655pub fn decode_delete(payload: &[u8]) -> Result<Vec<DocId>, PayloadError> {
657 let mut cursor = Cursor::new(payload);
658 cursor.require_header()?;
659 let count = usize::try_from(cursor.read_u32()?).map_err(|_| PayloadError::LengthOverflow)?;
660 if count == 0 {
661 return Err(PayloadError::EmptyDelete);
662 }
663 let body_bytes = count
664 .checked_mul(std::mem::size_of::<u128>())
665 .ok_or(PayloadError::LengthOverflow)?;
666 cursor.require_remaining(body_bytes)?;
667 let mut doc_ids = Vec::with_capacity(count);
668 for _ in 0..count {
669 doc_ids.push(DocId::new(cursor.read_u128()?));
670 }
671 cursor.finish()?;
672 Ok(doc_ids)
673}
674
675pub fn decode_metadata_edit(payload: &[u8]) -> Result<MetadataEdit, PayloadError> {
677 let mut cursor = Cursor::new(payload);
678 cursor.require_header()?;
679 let version = cursor.read_version()?;
680 let column = ColumnId::new(cursor.read_u32()?);
681 let kind = cursor.read_u8()?;
682 let reserved = cursor.read_u24()?;
683 if reserved != 0 {
684 return Err(PayloadError::Reserved(reserved));
685 }
686 let value_len =
687 usize::try_from(cursor.read_u32()?).map_err(|_| PayloadError::LengthOverflow)?;
688 let body = cursor.read_bytes(value_len)?;
689 let value = decode_metadata_value(kind, body)?;
690 cursor.finish()?;
691 Ok(MetadataEdit::new(version, column, value))
692}
693
694fn append_header(payload: &mut Vec<u8>) {
695 payload.extend_from_slice(&PAYLOAD_VERSION.to_le_bytes());
696 payload.extend_from_slice(&0_u16.to_le_bytes());
697}
698
699fn append_version(payload: &mut Vec<u8>, version: DocumentVersion) {
700 payload.extend_from_slice(&version.doc_id().get().to_le_bytes());
701 payload.extend_from_slice(&version.revision().get().to_le_bytes());
702}
703
704fn encode_metadata_value(value: &MetadataValue) -> Result<(u8, Vec<u8>), PayloadError> {
705 let encoded = match value {
706 MetadataValue::Null => (VALUE_NULL, Vec::new()),
707 MetadataValue::U64(value) => (VALUE_U64, value.to_le_bytes().to_vec()),
708 MetadataValue::I64(value) => (VALUE_I64, value.to_le_bytes().to_vec()),
709 MetadataValue::F64(value) => (VALUE_F64, value.to_bits().to_le_bytes().to_vec()),
710 MetadataValue::Bool(value) => (VALUE_BOOL, vec![u8::from(*value)]),
711 MetadataValue::String(value) => {
712 u32::try_from(value.len()).map_err(|_| PayloadError::LengthOverflow)?;
713 (VALUE_STRING, value.as_bytes().to_vec())
714 }
715 };
716 Ok(encoded)
717}
718
719fn decode_metadata_value(kind: u8, body: &[u8]) -> Result<MetadataValue, PayloadError> {
720 match kind {
721 VALUE_NULL => {
722 require_value_len(kind, body, 0)?;
723 Ok(MetadataValue::Null)
724 }
725 VALUE_U64 => {
726 require_value_len(kind, body, 8)?;
727 read_array(body)
728 .map(u64::from_le_bytes)
729 .map(MetadataValue::U64)
730 }
731 VALUE_I64 => {
732 require_value_len(kind, body, 8)?;
733 read_array(body)
734 .map(i64::from_le_bytes)
735 .map(MetadataValue::I64)
736 }
737 VALUE_F64 => {
738 require_value_len(kind, body, 8)?;
739 read_array(body)
740 .map(u64::from_le_bytes)
741 .map(f64::from_bits)
742 .map(MetadataValue::F64)
743 }
744 VALUE_BOOL => {
745 require_value_len(kind, body, 1)?;
746 match body.first().copied().ok_or(PayloadError::Truncated)? {
747 0 => Ok(MetadataValue::Bool(false)),
748 1 => Ok(MetadataValue::Bool(true)),
749 value => Err(PayloadError::Boolean(value)),
750 }
751 }
752 VALUE_STRING => std::str::from_utf8(body)
753 .map(|value| MetadataValue::String(value.to_owned()))
754 .map_err(|_| PayloadError::Utf8),
755 unknown => Err(PayloadError::ValueKind(unknown)),
756 }
757}
758
759fn require_value_len(kind: u8, body: &[u8], expected: usize) -> Result<(), PayloadError> {
760 if body.len() == expected {
761 Ok(())
762 } else {
763 Err(PayloadError::ValueLength {
764 kind,
765 expected,
766 actual: body.len(),
767 })
768 }
769}
770
771fn read_array<const N: usize>(bytes: &[u8]) -> Result<[u8; N], PayloadError> {
772 <[u8; N]>::try_from(bytes).map_err(|_| PayloadError::Truncated)
773}
774
775struct Cursor<'a> {
776 bytes: &'a [u8],
777 offset: usize,
778}
779
780impl<'a> Cursor<'a> {
781 const fn new(bytes: &'a [u8]) -> Self {
782 Self { bytes, offset: 0 }
783 }
784
785 fn require_header(&mut self) -> Result<(), PayloadError> {
786 let version = self.read_u16()?;
787 if version != PAYLOAD_VERSION {
788 return Err(PayloadError::Version(version));
789 }
790 let flags = self.read_u16()?;
791 if flags != 0 {
792 return Err(PayloadError::Flags(flags));
793 }
794 Ok(())
795 }
796
797 fn read_version(&mut self) -> Result<DocumentVersion, PayloadError> {
798 Ok(DocumentVersion::new(
799 DocId::new(self.read_u128()?),
800 Revision::new(self.read_u64()?),
801 ))
802 }
803
804 fn take<const N: usize>(&mut self) -> Result<[u8; N], PayloadError> {
805 read_array(self.read_bytes(N)?)
806 }
807
808 fn read_bytes(&mut self, length: usize) -> Result<&'a [u8], PayloadError> {
809 let end = self
810 .offset
811 .checked_add(length)
812 .ok_or(PayloadError::LengthOverflow)?;
813 let bytes = self
814 .bytes
815 .get(self.offset..end)
816 .ok_or(PayloadError::Truncated)?;
817 self.offset = end;
818 Ok(bytes)
819 }
820
821 fn require_remaining(&self, length: usize) -> Result<(), PayloadError> {
822 if self.remaining() < length {
823 Err(PayloadError::Truncated)
824 } else {
825 Ok(())
826 }
827 }
828
829 fn read_u8(&mut self) -> Result<u8, PayloadError> {
830 self.take::<1>().map(u8::from_le_bytes)
831 }
832
833 fn read_u16(&mut self) -> Result<u16, PayloadError> {
834 self.take().map(u16::from_le_bytes)
835 }
836
837 fn read_u24(&mut self) -> Result<u32, PayloadError> {
838 let [low, middle, high] = self.take::<3>()?;
839 Ok(u32::from(low) | (u32::from(middle) << 8) | (u32::from(high) << 16))
840 }
841
842 fn read_u32(&mut self) -> Result<u32, PayloadError> {
843 self.take().map(u32::from_le_bytes)
844 }
845
846 fn read_u64(&mut self) -> Result<u64, PayloadError> {
847 self.take().map(u64::from_le_bytes)
848 }
849
850 fn read_u128(&mut self) -> Result<u128, PayloadError> {
851 self.take().map(u128::from_le_bytes)
852 }
853
854 fn read_length_prefixed(&mut self) -> Result<&'a [u8], PayloadError> {
855 let length = usize::try_from(self.read_u32()?).map_err(|_| PayloadError::LengthOverflow)?;
856 self.read_bytes(length)
857 }
858
859 fn read_string(&mut self) -> Result<String, PayloadError> {
860 std::str::from_utf8(self.read_length_prefixed()?)
861 .map(str::to_owned)
862 .map_err(|_| PayloadError::Utf8)
863 }
864
865 fn remaining(&self) -> usize {
866 self.bytes.len().saturating_sub(self.offset)
867 }
868
869 fn finish(self) -> Result<(), PayloadError> {
870 let remaining = self.remaining();
871 if remaining == 0 {
872 Ok(())
873 } else {
874 Err(PayloadError::TrailingBytes(remaining))
875 }
876 }
877}