1use std::{io::Cursor, str::Utf8Error};
47
48use roaring::{RoaringBitmap, RoaringTreemap};
49use snafu::{ResultExt, Snafu};
50
51use crate::coverage::{Coverage, EntityCoverage, EntityIdentity, EntityIdentityError, EntityValue};
52
53const ENTITY_COVERAGE_MAGIC: &[u8; 8] = b"TSTECOV2";
54const ENTITY_VALUE_UTF8: u8 = 1;
55const ENTITY_VALUE_INT32: u8 = 2;
56const ENTITY_VALUE_INT64: u8 = 3;
57const ENTITY_VALUE_UINT64: u8 = 4;
58
59#[derive(Debug, Snafu)]
65pub enum CoverageSerdeError {
66 #[snafu(display("Failed to serialize roaring bitmap: {source}"))]
68 Serialize {
69 source: std::io::Error,
71 },
72
73 #[snafu(display("Failed to deserialize roaring bitmap: {source}"))]
75 Deserialize {
76 source: std::io::Error,
78 },
79}
80
81#[derive(Debug, Snafu)]
83pub enum EntityCoverageSerdeError {
84 #[snafu(display("Entity coverage {field} is too large to serialize"))]
86 LengthOverflow {
87 field: &'static str,
89 },
90
91 #[snafu(display("Failed to serialize nested entity coverage: {source}"))]
93 SerializeCoverage {
94 source: CoverageSerdeError,
96 },
97
98 #[snafu(display("Invalid entity coverage payload identifier"))]
100 InvalidMagic,
101
102 #[snafu(display("Truncated entity coverage payload"))]
104 Truncated,
105
106 #[snafu(display("Invalid entity coverage {field}"))]
108 InvalidLength {
109 field: &'static str,
111 },
112
113 #[snafu(display("Invalid entity identity string: {source}"))]
115 InvalidString {
116 source: Utf8Error,
118 },
119
120 #[snafu(display("Unknown entity identity value type tag: {tag}"))]
122 UnknownValueType {
123 tag: u8,
125 },
126
127 #[snafu(display("Invalid entity identity: {source}"))]
129 InvalidIdentity {
130 source: EntityIdentityError,
132 },
133
134 #[snafu(display("Duplicate entity identity in coverage payload: {identity:?}"))]
136 DuplicateIdentity {
137 identity: EntityIdentity,
139 },
140
141 #[snafu(display("Malformed nested entity coverage: {source}"))]
143 MalformedCoverage {
144 source: CoverageSerdeError,
146 },
147
148 #[snafu(display("Trailing bytes after entity coverage payload"))]
150 TrailingBytes,
151}
152
153pub fn coverage_to_bytes(cov: &Coverage) -> Result<Vec<u8>, CoverageSerdeError> {
170 let mut out = Vec::new();
171 {
172 let mut w = Cursor::new(&mut out);
173 cov.present()
174 .serialize_into(&mut w)
175 .context(SerializeSnafu)?;
176 }
177 Ok(out)
178}
179
180pub fn coverage_from_bytes(bytes: &[u8]) -> Result<Coverage, CoverageSerdeError> {
198 let mut r = Cursor::new(bytes);
199 let present = RoaringTreemap::deserialize_from(&mut r).context(DeserializeSnafu)?;
200
201 if r.position() != bytes.len() as u64 {
202 return Err(CoverageSerdeError::Deserialize {
203 source: std::io::Error::new(
204 std::io::ErrorKind::InvalidData,
205 "trailing bytes after roaring bitmap",
206 ),
207 });
208 }
209
210 Ok(Coverage::from_treemap(present))
211}
212
213pub fn entity_coverage_to_bytes(
220 coverage: &EntityCoverage,
221) -> Result<Vec<u8>, EntityCoverageSerdeError> {
222 let entity_count = u32::try_from(coverage.identity_count()).map_err(|_| {
223 EntityCoverageSerdeError::LengthOverflow {
224 field: "entity count",
225 }
226 })?;
227
228 let mut out = Vec::new();
229 out.extend_from_slice(ENTITY_COVERAGE_MAGIC);
230 out.extend_from_slice(&entity_count.to_be_bytes());
231
232 for (identity, nested) in coverage.iter() {
233 let component_count = u32::try_from(identity.components().len()).map_err(|_| {
234 EntityCoverageSerdeError::LengthOverflow {
235 field: "identity component count",
236 }
237 })?;
238 out.extend_from_slice(&component_count.to_be_bytes());
239
240 for component in identity.components() {
241 match component {
242 EntityValue::Utf8(value) => {
243 out.push(ENTITY_VALUE_UTF8);
244 let component_len = u64::try_from(value.len()).map_err(|_| {
245 EntityCoverageSerdeError::LengthOverflow {
246 field: "identity component length",
247 }
248 })?;
249 out.extend_from_slice(&component_len.to_be_bytes());
250 out.extend_from_slice(value.as_bytes());
251 }
252 EntityValue::Int32(value) => {
253 out.push(ENTITY_VALUE_INT32);
254 out.extend_from_slice(&value.to_be_bytes());
255 }
256 EntityValue::Int64(value) => {
257 out.push(ENTITY_VALUE_INT64);
258 out.extend_from_slice(&value.to_be_bytes());
259 }
260 EntityValue::UInt64(value) => {
261 out.push(ENTITY_VALUE_UINT64);
262 out.extend_from_slice(&value.to_be_bytes());
263 }
264 }
265 }
266
267 let nested_bytes = canonical_nested_coverage_to_bytes(nested)
268 .map_err(|source| EntityCoverageSerdeError::SerializeCoverage { source })?;
269 let nested_len = u64::try_from(nested_bytes.len()).map_err(|_| {
270 EntityCoverageSerdeError::LengthOverflow {
271 field: "nested coverage length",
272 }
273 })?;
274 out.extend_from_slice(&nested_len.to_be_bytes());
275 out.extend_from_slice(&nested_bytes);
276 }
277
278 Ok(out)
279}
280
281pub fn entity_coverage_from_bytes(
288 bytes: &[u8],
289) -> Result<EntityCoverage, EntityCoverageSerdeError> {
290 let mut remaining = bytes;
291 if take(&mut remaining, ENTITY_COVERAGE_MAGIC.len())? != ENTITY_COVERAGE_MAGIC {
292 return Err(EntityCoverageSerdeError::InvalidMagic);
293 }
294
295 let entity_count = read_u32(&mut remaining)? as usize;
296 let mut coverage = EntityCoverage::empty();
297 for _ in 0..entity_count {
298 let component_count = read_u32(&mut remaining)? as usize;
299 if component_count > remaining.len().saturating_sub(8) / 5 {
300 return Err(EntityCoverageSerdeError::InvalidLength {
301 field: "identity component count",
302 });
303 }
304
305 let mut components = Vec::new();
306 for _ in 0..component_count {
307 let tag = take(&mut remaining, 1)?[0];
308 let component = match tag {
309 ENTITY_VALUE_UTF8 => {
310 let component_len = read_u64(&mut remaining)?;
311 let component_bytes =
312 take_declared(&mut remaining, component_len, "identity component length")?;
313 let component = std::str::from_utf8(component_bytes)
314 .map_err(|source| EntityCoverageSerdeError::InvalidString { source })?;
315 EntityValue::Utf8(component.to_owned())
316 }
317 ENTITY_VALUE_INT32 => EntityValue::Int32(read_i32(&mut remaining)?),
318 ENTITY_VALUE_INT64 => EntityValue::Int64(read_i64(&mut remaining)?),
319 ENTITY_VALUE_UINT64 => EntityValue::UInt64(read_u64(&mut remaining)?),
320 tag => return Err(EntityCoverageSerdeError::UnknownValueType { tag }),
321 };
322 components.push(component);
323 }
324
325 let identity = EntityIdentity::try_new(components)
326 .map_err(|source| EntityCoverageSerdeError::InvalidIdentity { source })?;
327 if coverage.get(&identity).is_some() {
328 return Err(EntityCoverageSerdeError::DuplicateIdentity { identity });
329 }
330
331 let nested_len = read_u64(&mut remaining)?;
332 let nested_bytes = take_declared(&mut remaining, nested_len, "nested coverage length")?;
333 let nested = coverage_from_bytes(nested_bytes)
334 .map_err(|source| EntityCoverageSerdeError::MalformedCoverage { source })?;
335 coverage.union_coverage(identity, nested);
336 }
337
338 if !remaining.is_empty() {
339 return Err(EntityCoverageSerdeError::TrailingBytes);
340 }
341 Ok(coverage)
342}
343
344fn canonical_nested_coverage_to_bytes(coverage: &Coverage) -> Result<Vec<u8>, CoverageSerdeError> {
347 let present = RoaringTreemap::from_bitmaps(
348 coverage
349 .present()
350 .bitmaps()
351 .filter(|(_, bitmap)| !bitmap.is_empty())
352 .map(|(key, bitmap)| {
353 let mut canonical = RoaringBitmap::new();
354 let mut ranges = bitmap.iter();
355 while let Some(range) = ranges.next_range() {
356 canonical.insert_range(range);
357 }
358 canonical.optimize();
359 (key, canonical)
360 }),
361 );
362 coverage_to_bytes(&Coverage::from_treemap(present))
363}
364
365fn take<'a>(remaining: &mut &'a [u8], len: usize) -> Result<&'a [u8], EntityCoverageSerdeError> {
366 if remaining.len() < len {
367 return Err(EntityCoverageSerdeError::Truncated);
368 }
369 let (value, rest) = remaining.split_at(len);
370 *remaining = rest;
371 Ok(value)
372}
373
374fn take_declared<'a>(
375 remaining: &mut &'a [u8],
376 len: u64,
377 field: &'static str,
378) -> Result<&'a [u8], EntityCoverageSerdeError> {
379 let len =
380 usize::try_from(len).map_err(|_| EntityCoverageSerdeError::InvalidLength { field })?;
381 if len > remaining.len() {
382 return Err(EntityCoverageSerdeError::InvalidLength { field });
383 }
384 take(remaining, len)
385}
386
387fn read_u32(remaining: &mut &[u8]) -> Result<u32, EntityCoverageSerdeError> {
388 let mut encoded = [0; 4];
389 encoded.copy_from_slice(take(remaining, 4)?);
390 Ok(u32::from_be_bytes(encoded))
391}
392
393fn read_i32(remaining: &mut &[u8]) -> Result<i32, EntityCoverageSerdeError> {
394 let mut encoded = [0; 4];
395 encoded.copy_from_slice(take(remaining, 4)?);
396 Ok(i32::from_be_bytes(encoded))
397}
398
399fn read_i64(remaining: &mut &[u8]) -> Result<i64, EntityCoverageSerdeError> {
400 let mut encoded = [0; 8];
401 encoded.copy_from_slice(take(remaining, 8)?);
402 Ok(i64::from_be_bytes(encoded))
403}
404
405fn read_u64(remaining: &mut &[u8]) -> Result<u64, EntityCoverageSerdeError> {
406 let mut encoded = [0; 8];
407 encoded.copy_from_slice(take(remaining, 8)?);
408 Ok(u64::from_be_bytes(encoded))
409}
410
411#[cfg(test)]
412mod tests {
413 use super::*;
414
415 const ROARING_ZERO: &[u8] = &[
416 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3a, 0x30, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, ];
424 const ROARING_MAX: &[u8] = &[
425 1, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0x3a, 0x30, 0, 0, 1, 0, 0, 0, 0xff, 0xff, 0, 0, 16, 0, 0, 0, 0xff, 0xff, ];
433
434 fn identity(components: &[&str]) -> EntityIdentity {
435 EntityIdentity::try_new(
436 components
437 .iter()
438 .map(|component| EntityValue::from(*component))
439 .collect(),
440 )
441 .unwrap()
442 }
443
444 #[test]
445 fn round_trip_empty_and_non_empty() {
446 let cov_empty = Coverage::empty();
448 let bytes = coverage_to_bytes(&cov_empty).expect("serialize empty");
449 let restored = coverage_from_bytes(&bytes).expect("deserialize empty");
450 assert_eq!(cov_empty.cardinality(), restored.cardinality());
451
452 let cov = Coverage::from_iter(vec![1u64, 2, 3, u64::MAX]);
454 let bytes = coverage_to_bytes(&cov).expect("serialize non-empty");
455 let restored = coverage_from_bytes(&bytes).expect("deserialize non-empty");
456 assert_eq!(cov.present(), restored.present());
457 }
458
459 #[test]
460 fn deserialize_rejects_invalid_bytes() {
461 let bad = b"not a roaring bitmap";
462 let err = coverage_from_bytes(bad).unwrap_err();
463 match err {
464 CoverageSerdeError::Deserialize { .. } => {}
465 _ => panic!("expected deserialize error"),
466 }
467 }
468
469 #[test]
470 fn deserialize_rejects_trailing_valid_payload() {
471 let mut bytes = coverage_to_bytes(&Coverage::empty()).unwrap();
472 bytes.extend_from_slice(&coverage_to_bytes(&Coverage::from_iter([1u64])).unwrap());
473
474 let err = coverage_from_bytes(&bytes).unwrap_err();
475 assert!(matches!(err, CoverageSerdeError::Deserialize { .. }));
476 }
477
478 #[test]
479 fn serialize_reports_io_error() {
480 struct FailingWriter;
482 impl std::io::Write for FailingWriter {
483 fn write(&mut self, _buf: &[u8]) -> std::io::Result<usize> {
484 Err(std::io::Error::other("fail"))
485 }
486 fn flush(&mut self) -> std::io::Result<()> {
487 Ok(())
488 }
489 }
490
491 let cov = Coverage::from_iter(vec![1u64]);
492
493 let err = {
495 let mut w = FailingWriter;
496 cov.present()
497 .serialize_into(&mut w)
498 .map_err(|e| CoverageSerdeError::Serialize { source: e })
499 .unwrap_err()
500 };
501
502 match err {
503 CoverageSerdeError::Serialize { .. } => {}
504 _ => panic!("expected serialize error"),
505 }
506 }
507
508 #[test]
509 fn entity_coverage_round_trips_empty_and_one_identity() {
510 let empty_bytes = entity_coverage_to_bytes(&EntityCoverage::empty()).unwrap();
511 assert_eq!(
512 empty_bytes,
513 [ENTITY_COVERAGE_MAGIC.as_slice(), &[0; 4]].concat()
514 );
515 assert_eq!(
516 entity_coverage_from_bytes(&empty_bytes).unwrap(),
517 EntityCoverage::empty()
518 );
519
520 let entity = identity(&["venue", "symbol"]);
521 let mut coverage = EntityCoverage::empty();
522 coverage.union_coverage(entity.clone(), Coverage::empty());
523 let empty_identity_bytes = entity_coverage_to_bytes(&coverage).unwrap();
524 assert_ne!(empty_identity_bytes, empty_bytes);
525 assert_eq!(
526 entity_coverage_from_bytes(&empty_identity_bytes).unwrap(),
527 coverage
528 );
529
530 coverage.union_coverage(entity, [0, u64::MAX].into_iter().collect());
531 let bytes = entity_coverage_to_bytes(&coverage).unwrap();
532 assert_eq!(entity_coverage_from_bytes(&bytes).unwrap(), coverage);
533 }
534
535 #[test]
536 fn entity_coverage_keeps_composite_identities_and_buckets_independent() {
537 let first = identity(&["a", "b:c"]);
538 let second = identity(&["a:b", "c"]);
539 let mut coverage = EntityCoverage::empty();
540 coverage.union_coverage(first.clone(), [7].into_iter().collect());
541 coverage.union_coverage(second.clone(), [7].into_iter().collect());
542
543 let restored =
544 entity_coverage_from_bytes(&entity_coverage_to_bytes(&coverage).unwrap()).unwrap();
545 assert_eq!(restored.get(&first).unwrap().cardinality(), 1);
546 assert_eq!(restored.get(&second).unwrap().cardinality(), 1);
547 assert_eq!(restored.cardinality(), 2);
548 }
549
550 #[test]
551 fn entity_coverage_serialization_uses_canonical_identity_order() {
552 let first = identity(&["A"]);
553 let second = identity(&["B"]);
554 let mut forward = EntityCoverage::empty();
555 forward.union_coverage(first.clone(), [1].into_iter().collect());
556 forward.union_coverage(second.clone(), [2].into_iter().collect());
557
558 let mut reverse = EntityCoverage::empty();
559 reverse.union_coverage(second, [2].into_iter().collect());
560 reverse.union_coverage(first, [1].into_iter().collect());
561
562 assert_eq!(
563 entity_coverage_to_bytes(&forward).unwrap(),
564 entity_coverage_to_bytes(&reverse).unwrap()
565 );
566 }
567
568 #[test]
569 fn entity_coverage_serialization_canonicalizes_roaring_storage() {
570 let partition = 1u64 << 32;
571 let inserted: Coverage = (1..=3).chain(partition + 1..=partition + 5_000).collect();
572 let mut ranged = RoaringTreemap::new();
573 ranged.insert_range(1..=3);
574 ranged.insert_range(partition + 1..=partition + 5_000);
575 let ranged = Coverage::from_treemap(ranged);
576 assert_eq!(inserted, ranged);
577 assert_ne!(
578 coverage_to_bytes(&inserted).unwrap(),
579 coverage_to_bytes(&ranged).unwrap()
580 );
581
582 let entity = identity(&["A"]);
583 let mut left = EntityCoverage::empty();
584 left.union_coverage(entity.clone(), inserted);
585 let mut right = EntityCoverage::empty();
586 right.union_coverage(entity, ranged);
587
588 assert_eq!(
589 entity_coverage_to_bytes(&left).unwrap(),
590 entity_coverage_to_bytes(&right).unwrap()
591 );
592
593 let empty_partition = Coverage::from_treemap(RoaringTreemap::from_bitmaps([(
594 7,
595 roaring::RoaringBitmap::new(),
596 )]));
597 let mut logically_empty = EntityCoverage::empty();
598 logically_empty.union_coverage(identity(&["empty"]), empty_partition);
599 let mut canonical_empty = EntityCoverage::empty();
600 canonical_empty.union_coverage(identity(&["empty"]), Coverage::empty());
601 assert_eq!(
602 entity_coverage_to_bytes(&logically_empty).unwrap(),
603 entity_coverage_to_bytes(&canonical_empty).unwrap()
604 );
605 }
606
607 #[test]
608 fn entity_coverage_v2_golden_payload_is_stable() {
609 let mut coverage = EntityCoverage::empty();
610 coverage.union_coverage(
611 EntityIdentity::try_new(vec![
612 EntityValue::from("\u{6771}\u{4eac}"),
613 EntityValue::Int32(-1),
614 EntityValue::Int64(i64::MIN),
615 EntityValue::UInt64(u64::MAX),
616 ])
617 .unwrap(),
618 [0].into_iter().collect(),
619 );
620
621 let expected = [
622 b"TSTECOV2".as_slice(),
623 &[0, 0, 0, 1], &[0, 0, 0, 4], &[ENTITY_VALUE_UTF8],
626 &[0, 0, 0, 0, 0, 0, 0, 6],
627 &[0xe6, 0x9d, 0xb1, 0xe4, 0xba, 0xac],
628 &[ENTITY_VALUE_INT32],
629 &(-1i32).to_be_bytes(),
630 &[ENTITY_VALUE_INT64],
631 &i64::MIN.to_be_bytes(),
632 &[ENTITY_VALUE_UINT64],
633 &u64::MAX.to_be_bytes(),
634 &[0, 0, 0, 0, 0, 0, 0, 30],
635 ROARING_ZERO,
636 ]
637 .concat();
638
639 assert_eq!(entity_coverage_to_bytes(&coverage).unwrap(), expected);
640 assert_eq!(entity_coverage_from_bytes(&expected).unwrap(), coverage);
641 }
642
643 #[test]
644 fn entity_coverage_decoder_rejects_every_truncated_prefix() {
645 let mut coverage = EntityCoverage::empty();
646 coverage.union_coverage(identity(&["A"]), [1].into_iter().collect());
647 let bytes = entity_coverage_to_bytes(&coverage).unwrap();
648
649 for end in 0..bytes.len() {
650 assert!(entity_coverage_from_bytes(&bytes[..end]).is_err());
651 }
652 }
653
654 #[test]
655 fn entity_coverage_decoder_rejects_invalid_magic_lengths_and_strings() {
656 let mut coverage = EntityCoverage::empty();
657 coverage.union_coverage(identity(&["A"]), [1].into_iter().collect());
658 let bytes = entity_coverage_to_bytes(&coverage).unwrap();
659
660 let mut invalid_magic = bytes.clone();
661 invalid_magic[0] ^= 0xff;
662 assert!(matches!(
663 entity_coverage_from_bytes(&invalid_magic),
664 Err(EntityCoverageSerdeError::InvalidMagic)
665 ));
666
667 let mut version_one = bytes.clone();
668 version_one[..8].copy_from_slice(b"TSTECOV1");
669 assert!(matches!(
670 entity_coverage_from_bytes(&version_one),
671 Err(EntityCoverageSerdeError::InvalidMagic)
672 ));
673
674 let mut invalid_count = bytes.clone();
675 invalid_count[8..12].copy_from_slice(&u32::MAX.to_be_bytes());
676 assert!(entity_coverage_from_bytes(&invalid_count).is_err());
677
678 let mut empty_identity = bytes.clone();
679 empty_identity[12..16].copy_from_slice(&0u32.to_be_bytes());
680 assert!(matches!(
681 entity_coverage_from_bytes(&empty_identity),
682 Err(EntityCoverageSerdeError::InvalidIdentity { .. })
683 ));
684
685 let mut invalid_length = bytes.clone();
686 invalid_length[17..25].copy_from_slice(&u64::MAX.to_be_bytes());
687 assert!(matches!(
688 entity_coverage_from_bytes(&invalid_length),
689 Err(EntityCoverageSerdeError::InvalidLength { .. })
690 ));
691
692 let mut invalid_string = bytes.clone();
693 invalid_string[25] = 0xff;
694 assert!(matches!(
695 entity_coverage_from_bytes(&invalid_string),
696 Err(EntityCoverageSerdeError::InvalidString { .. })
697 ));
698
699 let mut unknown_type = bytes;
700 unknown_type[16] = u8::MAX;
701 assert!(matches!(
702 entity_coverage_from_bytes(&unknown_type),
703 Err(EntityCoverageSerdeError::UnknownValueType { tag: u8::MAX })
704 ));
705 }
706
707 #[test]
708 fn entity_coverage_decoder_rejects_duplicate_identities() {
709 let mut coverage = EntityCoverage::empty();
710 coverage.union_coverage(identity(&["A"]), Coverage::empty());
711 let mut bytes = entity_coverage_to_bytes(&coverage).unwrap();
712 let duplicate = bytes[12..].to_vec();
713 bytes[8..12].copy_from_slice(&2u32.to_be_bytes());
714 bytes.extend_from_slice(&duplicate);
715
716 assert!(matches!(
717 entity_coverage_from_bytes(&bytes),
718 Err(EntityCoverageSerdeError::DuplicateIdentity { .. })
719 ));
720 }
721
722 #[test]
723 fn entity_coverage_decoder_rejects_malformed_nested_and_trailing_bytes() {
724 let mut coverage = EntityCoverage::empty();
725 coverage.union_coverage(identity(&["A"]), [1].into_iter().collect());
726 let bytes = entity_coverage_to_bytes(&coverage).unwrap();
727
728 let mut malformed_nested = bytes.clone();
729 malformed_nested[26..34].copy_from_slice(&1u64.to_be_bytes());
730 assert!(matches!(
731 entity_coverage_from_bytes(&malformed_nested),
732 Err(EntityCoverageSerdeError::MalformedCoverage { .. })
733 ));
734
735 let mut trailing = bytes;
736 trailing.push(0);
737 assert!(matches!(
738 entity_coverage_from_bytes(&trailing),
739 Err(EntityCoverageSerdeError::TrailingBytes)
740 ));
741 }
742
743 #[test]
744 fn historical_global_coverage_codec_is_unchanged_and_distinct() {
745 let global_empty = coverage_to_bytes(&Coverage::empty()).unwrap();
746 assert_eq!(global_empty, vec![0; 8]);
747 assert!(matches!(
748 entity_coverage_from_bytes(&global_empty),
749 Err(EntityCoverageSerdeError::InvalidMagic) | Err(EntityCoverageSerdeError::Truncated)
750 ));
751
752 let global_extremes: Coverage = [0, u64::MAX].into_iter().collect();
753 let global_extremes_bytes = [
754 &[2, 0, 0, 0, 0, 0, 0, 0],
755 &ROARING_ZERO[8..],
756 &ROARING_MAX[8..],
757 ]
758 .concat();
759 assert_eq!(
760 coverage_to_bytes(&global_extremes).unwrap(),
761 global_extremes_bytes
762 );
763 assert_eq!(
764 coverage_from_bytes(&global_extremes_bytes)
765 .unwrap()
766 .present(),
767 global_extremes.present()
768 );
769
770 let entity_empty = entity_coverage_to_bytes(&EntityCoverage::empty()).unwrap();
771 assert!(coverage_from_bytes(&entity_empty).is_err());
772 }
773}