1use crate::{BStackRawAllocator, BlockStart};
80use bstack::{
81 BStack, BStackAllocator, BStackGuardedSlice, BStackGuardedSliceSubview, BStackSlice,
82 BStackSliceReader, BStackSliceWriter,
83};
84use std::cmp::Ordering;
85use std::fmt;
86use std::hash::{Hash, Hasher};
87use std::io;
88
89pub const CHECKSUM_LENGTH: u64 = 4;
94
95pub struct BCrcBlockAllocator<A: BStackAllocator> {
120 inner: A,
121}
122
123impl<A: BStackAllocator> BCrcBlockAllocator<A> {
124 pub fn new(inner: A) -> Self {
126 Self { inner }
127 }
128
129 pub fn inner(&self) -> &A {
131 &self.inner
132 }
133
134 pub fn into_inner(self) -> A {
136 self.inner
137 }
138}
139
140#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
175pub struct BCrcBlock<'a, A: BStackAllocator> {
176 slice: BStackSlice<'a, A>,
177 len: u64,
178}
179
180impl<'a, A: BStackAllocator> Copy for BCrcBlock<'a, A> {}
181
182impl<'a, A: BStackAllocator> Clone for BCrcBlock<'a, A> {
183 fn clone(&self) -> Self {
184 *self
185 }
186}
187
188impl<'a, A: BStackAllocator> BCrcBlock<'a, A> {
189 pub fn to_bytes(&self) -> [u8; 16] {
194 let mut out = [0u8; 16];
195 out[..8].copy_from_slice(&self.slice.start().to_le_bytes());
196 out[8..].copy_from_slice(&self.len.to_le_bytes());
197 out
198 }
199
200 pub fn from_bytes(allocator: &'a A, bytes: [u8; 16]) -> Self {
203 let offset = u64::from_le_bytes(bytes[..8].try_into().unwrap());
204 let len = u64::from_le_bytes(bytes[8..].try_into().unwrap());
205 BCrcBlock {
206 slice: unsafe { BStackSlice::from_raw_parts(allocator, offset, len + CHECKSUM_LENGTH) },
207 len,
208 }
209 }
210
211 pub fn checksum(&self) -> io::Result<u32> {
213 let mut buf = [0u8; 4];
214 unsafe { self.checksum_slice() }.read_into(&mut buf)?;
215 Ok(u32::from_le_bytes(buf))
216 }
217
218 pub fn verify(&self) -> io::Result<bool> {
221 let data = unsafe { self.data_slice() }.read()?;
222 let stored = self.checksum()?;
223 Ok(crc32fast::hash(&data) == stored)
224 }
225
226 pub fn view(&self) -> BCrcBlockView<'a, A> {
231 BCrcBlockView {
232 slice: self.slice,
233 full_len: self.len,
234 start: 0,
235 end: self.len,
236 }
237 }
238
239 pub fn reader(&self) -> BCrcBlockReader<'a, A> {
241 BCrcBlockReader {
242 inner: unsafe { self.data_slice() }.reader(),
243 }
244 }
245
246 pub fn reader_at(&self, offset: u64) -> BCrcBlockReader<'a, A> {
248 BCrcBlockReader {
249 inner: unsafe { self.data_slice() }.reader_at(offset),
250 }
251 }
252
253 pub fn writer(&self) -> BCrcBlockWriter<'a, A> {
258 let full_data = unsafe { self.data_slice() };
259 BCrcBlockWriter {
260 inner: full_data.writer(),
261 full_data,
262 checksum: unsafe { self.checksum_slice() },
263 }
264 }
265
266 pub fn writer_at(&self, offset: u64) -> BCrcBlockWriter<'a, A> {
271 let full_data = unsafe { self.data_slice() };
272 BCrcBlockWriter {
273 inner: full_data.writer_at(offset),
274 full_data,
275 checksum: unsafe { self.checksum_slice() },
276 }
277 }
278
279 pub unsafe fn into_slice(self) -> BStackSlice<'a, A> {
287 self.slice
288 }
289
290 unsafe fn data_slice(&self) -> BStackSlice<'a, A> {
291 self.slice.subslice(0, self.len)
292 }
293
294 unsafe fn checksum_slice(&self) -> BStackSlice<'a, A> {
295 self.slice.subslice(self.len, self.len + CHECKSUM_LENGTH)
296 }
297}
298
299#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
337pub struct BCrcBlockView<'a, A: BStackAllocator> {
338 slice: BStackSlice<'a, A>,
340 full_len: u64,
342 start: u64,
344 end: u64,
346}
347
348impl<'a, A: BStackAllocator> Copy for BCrcBlockView<'a, A> {}
349
350impl<'a, A: BStackAllocator> Clone for BCrcBlockView<'a, A> {
351 fn clone(&self) -> Self {
352 *self
353 }
354}
355
356impl<'a, A: BStackAllocator> From<BCrcBlock<'a, A>> for [u8; 16] {
357 fn from(block: BCrcBlock<'a, A>) -> [u8; 16] {
358 block.to_bytes()
359 }
360}
361
362impl<'a, A: BStackAllocator> BCrcBlockView<'a, A> {
363 pub fn new(block: &BCrcBlock<'a, A>) -> Self {
365 Self {
366 slice: block.slice,
367 full_len: block.len,
368 start: 0,
369 end: block.len,
370 }
371 }
372
373 pub fn subview(&self, start: u64, end: u64) -> Self {
380 BCrcBlockView {
381 slice: self.slice,
382 full_len: self.full_len,
383 start: self.start + start,
384 end: self.start + end,
385 }
386 }
387
388 pub fn read_into(&self, buf: &mut [u8]) -> io::Result<()> {
390 unsafe { self.data_slice() }.read_into(buf)
391 }
392
393 pub fn read_range_into(&self, start: u64, buf: &mut [u8]) -> io::Result<()> {
395 unsafe { self.data_slice() }.read_range_into(start, buf)
396 }
397
398 pub fn checksum(&self) -> io::Result<u32> {
400 let mut buf = [0u8; 4];
401 unsafe { self.checksum_slice() }.read_into(&mut buf)?;
402 Ok(u32::from_le_bytes(buf))
403 }
404
405 pub fn verify(&self) -> io::Result<bool> {
411 let data = unsafe { self.full_data_slice() }.read()?;
412 let stored = self.checksum()?;
413 Ok(crc32fast::hash(&data) == stored)
414 }
415
416 pub fn write_range(&self, start: u64, data: &[u8]) -> io::Result<()> {
419 unsafe { self.data_slice() }.write_range(start, data)?;
420 self.update_checksum()
421 }
422
423 pub fn zero_range(&self, start: u64, n: u64) -> io::Result<()> {
426 unsafe { self.data_slice() }.zero_range(start, n)?;
427 self.update_checksum()
428 }
429
430 pub fn reader(&self) -> BCrcBlockReader<'a, A> {
432 BCrcBlockReader {
433 inner: unsafe { self.data_slice() }.reader(),
434 }
435 }
436
437 pub fn reader_at(&self, offset: u64) -> BCrcBlockReader<'a, A> {
439 BCrcBlockReader {
440 inner: unsafe { self.data_slice() }.reader_at(offset),
441 }
442 }
443
444 pub fn writer(&self) -> BCrcBlockWriter<'a, A> {
448 BCrcBlockWriter {
449 inner: unsafe { self.data_slice() }.writer(),
450 full_data: unsafe { self.full_data_slice() },
451 checksum: unsafe { self.checksum_slice() },
452 }
453 }
454
455 pub fn writer_at(&self, offset: u64) -> BCrcBlockWriter<'a, A> {
459 BCrcBlockWriter {
460 inner: unsafe { self.data_slice() }.writer_at(offset),
461 full_data: unsafe { self.full_data_slice() },
462 checksum: unsafe { self.checksum_slice() },
463 }
464 }
465
466 unsafe fn data_slice(&self) -> BStackSlice<'a, A> {
471 self.slice.subslice(self.start, self.end)
472 }
473
474 unsafe fn full_data_slice(&self) -> BStackSlice<'a, A> {
478 self.slice.subslice(0, self.full_len)
479 }
480
481 unsafe fn checksum_slice(&self) -> BStackSlice<'a, A> {
486 self.slice
487 .subslice(self.full_len, self.full_len + CHECKSUM_LENGTH)
488 }
489
490 fn update_checksum(&self) -> io::Result<()> {
491 let data = unsafe { self.full_data_slice() }.read()?;
492 let crc = crc32fast::hash(&data);
493 unsafe { self.checksum_slice() }.write(crc.to_le_bytes())
494 }
495}
496
497#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
504pub struct BCrcBlockReader<'a, A: BStackAllocator> {
505 inner: BStackSliceReader<'a, A>,
506}
507
508impl<'a, A: BStackAllocator> fmt::Debug for BCrcBlockReader<'a, A> {
509 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
510 f.debug_struct("BCrcBlockReader")
511 .field("start", &self.inner.slice().start())
512 .field("end", &self.inner.slice().end())
513 .field("len", &self.inner.slice().len())
514 .field("cursor", &self.inner.position())
515 .finish_non_exhaustive()
516 }
517}
518
519impl<'a, A: BStackAllocator> BCrcBlockReader<'a, A> {
520 pub fn position(&self) -> u64 {
522 self.inner.position()
523 }
524}
525
526impl<'a, A: BStackAllocator> PartialEq<BCrcBlockWriter<'a, A>> for BCrcBlockReader<'a, A> {
528 fn eq(&self, other: &BCrcBlockWriter<'a, A>) -> bool {
529 self.inner.slice() == other.inner.slice() && self.inner.position() == other.inner.position()
530 }
531}
532
533impl<'a, A: BStackAllocator> PartialOrd<BCrcBlockWriter<'a, A>> for BCrcBlockReader<'a, A> {
535 fn partial_cmp(&self, other: &BCrcBlockWriter<'a, A>) -> Option<Ordering> {
536 let self_pos = self.inner.slice().start() + self.inner.position();
537 let other_pos = other.inner.slice().start() + other.inner.position();
538 Some(
539 self_pos
540 .cmp(&other_pos)
541 .then(self.inner.slice().len().cmp(&other.inner.slice().len())),
542 )
543 }
544}
545
546impl<'a, A: BStackAllocator> io::Read for BCrcBlockReader<'a, A> {
547 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
548 self.inner.read(buf)
549 }
550}
551
552impl<'a, A: BStackAllocator> io::Seek for BCrcBlockReader<'a, A> {
553 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
554 self.inner.seek(pos)
555 }
556}
557
558#[derive(Clone)]
566pub struct BCrcBlockWriter<'a, A: BStackAllocator> {
567 inner: BStackSliceWriter<'a, A>,
569 full_data: BStackSlice<'a, A>,
571 checksum: BStackSlice<'a, A>,
573}
574
575impl<'a, A: BStackAllocator> fmt::Debug for BCrcBlockWriter<'a, A> {
576 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
577 f.debug_struct("BCrcBlockWriter")
578 .field("start", &self.inner.slice().start())
579 .field("end", &self.inner.slice().end())
580 .field("len", &self.inner.slice().len())
581 .field("cursor", &self.inner.position())
582 .finish_non_exhaustive()
583 }
584}
585
586impl<'a, A: BStackAllocator> BCrcBlockWriter<'a, A> {
587 pub fn position(&self) -> u64 {
589 self.inner.position()
590 }
591
592 fn update_checksum(&self) -> io::Result<()> {
593 let data = self.full_data.read()?;
594 let crc = crc32fast::hash(&data);
595 self.checksum.write(crc.to_le_bytes())
596 }
597}
598
599impl<'a, A: BStackAllocator> PartialEq for BCrcBlockWriter<'a, A> {
601 fn eq(&self, other: &Self) -> bool {
602 self.inner.slice() == other.inner.slice() && self.inner.position() == other.inner.position()
603 }
604}
605
606impl<'a, A: BStackAllocator> Eq for BCrcBlockWriter<'a, A> {}
607
608impl<'a, A: BStackAllocator> Hash for BCrcBlockWriter<'a, A> {
609 fn hash<H: Hasher>(&self, state: &mut H) {
610 self.inner.slice().hash(state);
611 self.inner.position().hash(state);
612 }
613}
614
615impl<'a, A: BStackAllocator> PartialOrd for BCrcBlockWriter<'a, A> {
617 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
618 Some(self.cmp(other))
619 }
620}
621
622impl<'a, A: BStackAllocator> Ord for BCrcBlockWriter<'a, A> {
623 fn cmp(&self, other: &Self) -> Ordering {
624 let self_pos = self.inner.slice().start() + self.inner.position();
625 let other_pos = other.inner.slice().start() + other.inner.position();
626 self_pos
627 .cmp(&other_pos)
628 .then(self.inner.slice().len().cmp(&other.inner.slice().len()))
629 }
630}
631
632impl<'a, A: BStackAllocator> PartialEq<BCrcBlockReader<'a, A>> for BCrcBlockWriter<'a, A> {
633 fn eq(&self, other: &BCrcBlockReader<'a, A>) -> bool {
634 other == self
635 }
636}
637
638impl<'a, A: BStackAllocator> PartialOrd<BCrcBlockReader<'a, A>> for BCrcBlockWriter<'a, A> {
639 fn partial_cmp(&self, other: &BCrcBlockReader<'a, A>) -> Option<Ordering> {
640 other.partial_cmp(self).map(|o| o.reverse())
641 }
642}
643
644impl<'a, A: BStackAllocator> io::Write for BCrcBlockWriter<'a, A> {
645 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
646 let n = self.inner.write(buf)?;
647 if n > 0 {
648 self.update_checksum()?;
649 }
650 Ok(n)
651 }
652
653 fn flush(&mut self) -> io::Result<()> {
654 self.inner.flush()
655 }
656}
657
658impl<'a, A: BStackAllocator> io::Seek for BCrcBlockWriter<'a, A> {
659 fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
660 self.inner.seek(pos)
661 }
662}
663
664impl<A> BStackAllocator for BCrcBlockAllocator<A>
671where
672 A: BStackAllocator<Error = io::Error> + BStackRawAllocator,
673 for<'a> A::Allocated<'a>: BlockStart + Copy,
674{
675 type Error = io::Error;
676 type Allocated<'a>
677 = BCrcBlock<'a, BCrcBlockAllocator<A>>
678 where
679 A: 'a;
680
681 fn stack(&self) -> &BStack {
682 self.inner.stack()
683 }
684
685 fn into_stack(self) -> BStack {
686 self.inner.into_stack()
687 }
688
689 fn alloc(&self, len: u64) -> io::Result<BCrcBlock<'_, BCrcBlockAllocator<A>>> {
690 let inner = self.inner.alloc(len + CHECKSUM_LENGTH)?;
691 let offset = inner.block_start();
692 let slice = unsafe { BStackSlice::from_raw_parts(self, offset, len + CHECKSUM_LENGTH) };
693 Ok(BCrcBlock { slice, len })
694 }
695
696 fn realloc<'a>(
697 &'a self,
698 block: BCrcBlock<'a, BCrcBlockAllocator<A>>,
699 new_len: u64,
700 ) -> io::Result<BCrcBlock<'a, BCrcBlockAllocator<A>>> {
701 let offset = block.slice.start();
702 let inner_old_slice = unsafe {
703 BStackSlice::from_raw_parts(&self.inner, offset, block.len + CHECKSUM_LENGTH)
704 };
705 let inner_old: A::Allocated<'_> = unsafe { A::from_raw(inner_old_slice) };
706 let inner_new = self.inner.realloc(inner_old, new_len + CHECKSUM_LENGTH)?;
707 let new_offset = inner_new.block_start();
708 let slice =
709 unsafe { BStackSlice::from_raw_parts(self, new_offset, new_len + CHECKSUM_LENGTH) };
710 Ok(BCrcBlock {
711 slice,
712 len: new_len,
713 })
714 }
715
716 fn dealloc(&self, block: BCrcBlock<'_, BCrcBlockAllocator<A>>) -> io::Result<()> {
717 let offset = block.slice.start();
718 let inner_slice = unsafe {
719 BStackSlice::from_raw_parts(&self.inner, offset, block.len + CHECKSUM_LENGTH)
720 };
721 let inner: A::Allocated<'_> = unsafe { A::from_raw(inner_slice) };
722 self.inner.dealloc(inner)
723 }
724}
725
726impl<'a, A> TryInto<BStackSlice<'a, BCrcBlockAllocator<A>>> for BCrcBlock<'a, BCrcBlockAllocator<A>>
730where
731 A: BStackAllocator<Error = io::Error> + BStackRawAllocator,
732 for<'b> A::Allocated<'b>: BlockStart + Copy,
733{
734 type Error = std::convert::Infallible;
735
736 fn try_into(self) -> Result<BStackSlice<'a, BCrcBlockAllocator<A>>, Self::Error> {
737 Ok(self.slice)
738 }
739}
740
741impl<'a, A: BStackAllocator> BlockStart for BCrcBlock<'a, A> {
742 fn block_start(&self) -> u64 {
743 self.slice.start()
744 }
745}
746
747unsafe impl<A> BStackRawAllocator for BCrcBlockAllocator<A>
748where
749 A: BStackAllocator<Error = io::Error> + BStackRawAllocator,
750 for<'a> A::Allocated<'a>: BlockStart + Copy,
751{
752 unsafe fn from_raw<'a>(
753 slice: BStackSlice<'a, BCrcBlockAllocator<A>>,
754 ) -> BCrcBlock<'a, BCrcBlockAllocator<A>> {
755 let len = slice.len() - CHECKSUM_LENGTH;
756 BCrcBlock { slice, len }
757 }
758}
759
760impl<'a, A: BStackAllocator + 'a> BStackGuardedSlice<'a, A> for BCrcBlock<'a, A> {
771 fn len(&self) -> u64 {
772 self.len
773 }
774
775 unsafe fn raw_block(&self) -> BStackSlice<'a, A> {
776 self.slice
777 }
778
779 fn as_slice(&self) -> io::Result<BStackSlice<'a, A>> {
780 Ok(unsafe { self.data_slice() })
781 }
782
783 fn write(&self, data: impl AsRef<[u8]>) -> io::Result<()> {
784 unsafe { self.data_slice() }.write(data.as_ref())?;
785 let full = unsafe { self.data_slice() }.read()?;
786 let crc = crc32fast::hash(&full);
787 unsafe { self.checksum_slice() }.write(crc.to_le_bytes())
788 }
789
790 fn zero(&self) -> io::Result<()> {
791 unsafe { self.data_slice() }.zero()?;
792 let zeros = vec![0u8; self.len as usize];
793 let crc = crc32fast::hash(&zeros);
794 unsafe { self.checksum_slice() }.write(crc.to_le_bytes())
795 }
796}
797
798impl<'a, A: BStackAllocator + 'a> BStackGuardedSliceSubview<'a, A> for BCrcBlockView<'a, A> {
799 fn subview(&self, start: u64, end: u64) -> impl BStackGuardedSliceSubview<'a, A> + '_ {
800 BCrcBlockView {
801 slice: self.slice,
802 full_len: self.full_len,
803 start: self.start + start,
804 end: self.start + end,
805 }
806 }
807}
808
809impl<'a, A: BStackAllocator + 'a> BStackGuardedSlice<'a, A> for BCrcBlockView<'a, A> {
817 fn len(&self) -> u64 {
818 self.end - self.start
819 }
820
821 unsafe fn raw_block(&self) -> BStackSlice<'a, A> {
822 unsafe { self.data_slice() }
823 }
824
825 fn as_slice(&self) -> io::Result<BStackSlice<'a, A>> {
826 Ok(unsafe { self.data_slice() })
827 }
828
829 fn write(&self, data: impl AsRef<[u8]>) -> io::Result<()> {
830 unsafe { self.data_slice() }.write(data.as_ref())?;
831 self.update_checksum()
832 }
833
834 fn zero(&self) -> io::Result<()> {
835 unsafe { self.data_slice() }.zero()?;
836 self.update_checksum()
837 }
838}
839
840#[cfg(test)]
841mod tests {
842 use super::*;
843 use bstack::{BStack, BStackGuardedSlice, LinearBStackAllocator};
844 use tempfile::NamedTempFile;
845
846 fn make_allocator() -> (BCrcBlockAllocator<LinearBStackAllocator>, NamedTempFile) {
847 let file = NamedTempFile::new().unwrap();
848 let stack = BStack::open(file.path()).unwrap();
849 let allocator = BCrcBlockAllocator::new(LinearBStackAllocator::new(stack));
850 (allocator, file)
851 }
852
853 #[test]
854 fn test_alloc_len() {
855 let (alloc, _f) = make_allocator();
856 let block = alloc.alloc(30).unwrap();
857 assert_eq!(block.len(), 30);
858 let raw_len = unsafe { block.into_slice().len() };
859 assert_eq!(raw_len, 34);
860 }
861
862 #[test]
863 fn test_write_and_verify() {
864 let (alloc, _f) = make_allocator();
865 let block = alloc.alloc(5).unwrap();
866 let view = block.view();
867 view.write(b"hello").unwrap();
868 assert!(view.verify().unwrap());
869 assert_eq!(view.read().unwrap(), b"hello");
870 }
871
872 #[test]
873 fn test_verify_fails_after_raw_write() {
874 let (alloc, _f) = make_allocator();
875 let block = alloc.alloc(5).unwrap();
876 let view = block.view();
877 view.write(b"hello").unwrap();
878 unsafe {
879 block.into_slice().write(b"world").unwrap();
880 }
881 assert!(!view.verify().unwrap());
882 }
883
884 #[test]
885 fn test_realloc() {
886 let (alloc, _f) = make_allocator();
887 let block = alloc.alloc(4).unwrap();
888 block.view().write(b"abcd").unwrap();
889 let block2 = alloc.realloc(block, 8).unwrap();
890 assert_eq!(block2.len(), 8);
891 let raw_len = unsafe { block2.into_slice().len() };
892 assert_eq!(raw_len, 12);
893 }
894
895 #[test]
896 fn test_to_from_bytes() {
897 let (alloc, _f) = make_allocator();
898 let block = alloc.alloc(8).unwrap();
899 block.view().write(&b"rustacean"[..8]).unwrap();
900 let bytes: [u8; 16] = block.into();
901 let block2 = BCrcBlock::from_bytes(alloc.inner(), bytes);
902 assert_eq!(block2.len(), 8);
903 assert!(block2.verify().unwrap());
904 assert_eq!(block2.view().read().unwrap(), &b"rustacean"[..8]);
905 }
906
907 #[test]
908 fn test_zero_clears_and_valid() {
909 let (alloc, _f) = make_allocator();
910 let block = alloc.alloc(6).unwrap();
911 let view = block.view();
912 view.write(b"foobar").unwrap();
913 view.zero().unwrap();
914 assert_eq!(view.read().unwrap(), vec![0u8; 6]);
915 assert!(view.verify().unwrap());
916 }
917
918 #[test]
919 fn test_reader() {
920 use std::io::Read;
921 let (alloc, _f) = make_allocator();
922 let block = alloc.alloc(4).unwrap();
923 block.view().write(b"abcd").unwrap();
924 let mut buf = [0u8; 4];
925 block.reader().read_exact(&mut buf).unwrap();
926 assert_eq!(&buf, b"abcd");
927 }
928
929 #[test]
930 fn test_writer_maintains_checksum() {
931 use std::io::Write;
932 let (alloc, _f) = make_allocator();
933 let block = alloc.alloc(4).unwrap();
934 block.writer().write_all(b"WXYZ").unwrap();
935 assert!(block.verify().unwrap());
936 assert_eq!(block.view().read().unwrap(), b"WXYZ");
937 }
938
939 #[test]
940 fn test_writer_seek_and_overwrite() {
941 use std::io::{Seek, SeekFrom, Write};
942 let (alloc, _f) = make_allocator();
943 let block = alloc.alloc(4).unwrap();
944 let mut w = block.writer();
945 w.write_all(b"abcd").unwrap();
946 w.seek(SeekFrom::Start(2)).unwrap();
947 w.write_all(b"XY").unwrap();
948 assert!(block.verify().unwrap());
949 assert_eq!(block.view().read().unwrap(), b"abXY");
950 }
951
952 #[test]
953 fn test_reader_writer_cmp() {
954 let (alloc, _f) = make_allocator();
955 let block = alloc.alloc(4).unwrap();
956 let r = block.reader();
957 let w = block.writer();
958 assert_eq!(r, w);
959 assert_eq!(w, r);
960 }
961
962 #[test]
963 fn test_subview_read() {
964 let (alloc, _f) = make_allocator();
965 let block = alloc.alloc(8).unwrap();
966 block.view().write(b"hello!!!").unwrap();
967 let sub = block.view().subview(0, 5);
968 assert_eq!(sub.len(), 5);
969 assert_eq!(sub.read().unwrap(), b"hello");
970 }
971
972 #[test]
973 fn test_subview_write_updates_full_checksum() {
974 let (alloc, _f) = make_allocator();
975 let block = alloc.alloc(8).unwrap();
976 block.view().write(b"hello!!!").unwrap();
977 let sub = block.view().subview(0, 5);
978 sub.write(b"world").unwrap();
979 assert!(block.verify().unwrap());
980 assert_eq!(block.view().read().unwrap(), b"world!!!");
981 }
982
983 #[test]
984 fn test_subview_writer_updates_full_checksum() {
985 use std::io::Write;
986 let (alloc, _f) = make_allocator();
987 let block = alloc.alloc(8).unwrap();
988 block.view().write(b"hello!!!").unwrap();
989 block
990 .view()
991 .subview(0, 5)
992 .writer()
993 .write_all(b"world")
994 .unwrap();
995 assert!(block.verify().unwrap());
996 assert_eq!(block.view().read().unwrap(), b"world!!!");
997 }
998
999 #[test]
1000 fn test_subview_nested() {
1001 let (alloc, _f) = make_allocator();
1002 let block = alloc.alloc(8).unwrap();
1003 block.view().write(b"abcdefgh").unwrap();
1004 let sub = block.view().subview(2, 6).subview(1, 3);
1006 assert_eq!(sub.len(), 2);
1007 assert_eq!(sub.read().unwrap(), b"de");
1008 sub.write(b"XY").unwrap();
1009 assert!(block.verify().unwrap());
1010 assert_eq!(block.view().read().unwrap(), b"abcXYfgh");
1011 }
1012}