1use core::ffi::{CStr, c_char, c_int, c_void};
19
20use asdf_core::block::header::CHECKSUM_SIZE;
21use asdf_core::compression::Compression;
22use asdf_core::{ChecksumStatus, PendingBlock};
23
24use crate::ffi::write_out;
25use crate::file_ffi::{AsdfFile, file_blocks_mut, file_reader};
26use crate::panic::guard;
27
28enum BlockData {
30 Empty,
32 Borrowed { ptr: *const u8, len: usize },
34 Owned(Vec<u8>),
36}
37
38impl BlockData {
39 fn as_slice(&self) -> &[u8] {
40 match self {
41 BlockData::Empty => &[],
42 BlockData::Borrowed { ptr, len } => unsafe { core::slice::from_raw_parts(*ptr, *len) },
45 BlockData::Owned(v) => v,
46 }
47 }
48
49 fn len(&self) -> usize {
50 match self {
51 BlockData::Empty => 0,
52 BlockData::Borrowed { len, .. } => *len,
53 BlockData::Owned(v) => v.len(),
54 }
55 }
56}
57
58pub struct AsdfBlock {
60 file: *mut AsdfFile,
62 index: Option<usize>,
64 detached: bool,
66 data: BlockData,
67 data_is_compressed: bool,
70 declared_data_size: u64,
72 compression: Compression,
73 compression_name: [u8; 5],
80 allocated_size: u64,
81 decompressed: Option<Vec<u8>>,
83}
84
85impl core::fmt::Debug for AsdfBlock {
86 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
87 f.debug_struct("AsdfBlock")
88 .field("index", &self.index)
89 .field("detached", &self.detached)
90 .field("len", &self.data.len())
91 .field("compression", &self.compression)
92 .finish()
93 }
94}
95
96impl AsdfBlock {
97 fn detached(data: BlockData) -> Self {
98 Self {
99 file: core::ptr::null_mut(),
100 index: None,
101 detached: true,
102 data,
103 data_is_compressed: false,
104 declared_data_size: 0,
105 compression: Compression::None,
106 compression_name: [0; 5],
107 allocated_size: 0,
108 decompressed: None,
109 }
110 }
111
112 fn to_pending(&self) -> PendingBlock {
114 PendingBlock {
115 data: self.data.as_slice().to_vec(),
116 compression: self.compression,
117 allocated_size: self.allocated_size,
118 already_compressed: self.data_is_compressed,
121 uncompressed_size: self.declared_data_size,
122 }
123 }
124}
125
126fn block_ref<'a>(block: *mut AsdfBlock) -> Option<&'a mut AsdfBlock> {
127 unsafe { crate::ffi::as_mut(block) }
128}
129
130#[unsafe(no_mangle)]
136pub unsafe extern "C" fn asdf_block_open(file: *mut AsdfFile, index: usize) -> *mut AsdfBlock {
137 guard("asdf_block_open", core::ptr::null_mut(), || {
138 let Some(reader) = file_reader(file) else {
139 return core::ptr::null_mut();
140 };
141 let Ok(location) = reader.block(index) else {
142 return core::ptr::null_mut();
143 };
144 let header = location.header.clone();
145 let compression =
146 Compression::from_name(header.compression_name()).unwrap_or(Compression::None);
147
148 let mut block = AsdfBlock {
149 file,
150 index: Some(index),
151 detached: false,
152 data: BlockData::Empty,
153 data_is_compressed: compression != Compression::None,
154 declared_data_size: header.data_size,
155 compression,
156 compression_name: {
157 let mut name = [0u8; 5];
158 name[..4].copy_from_slice(&header.compression);
159 name
160 },
161 allocated_size: header.allocated_size,
162 decompressed: None,
163 };
164 if let Ok(raw) = reader.block_raw(index) {
167 block.data = BlockData::Borrowed { ptr: raw.as_ptr(), len: raw.len() };
168 }
169 Box::into_raw(Box::new(block))
170 })
171}
172
173#[unsafe(no_mangle)]
179pub unsafe extern "C" fn asdf_block_close(block: *mut AsdfBlock) {
180 guard("asdf_block_close", (), || {
181 if !block.is_null() {
182 drop(unsafe { Box::from_raw(block) });
183 }
184 })
185}
186
187#[unsafe(no_mangle)]
199pub unsafe extern "C" fn asdf_block_create(data: *const c_void, size: usize) -> *mut AsdfBlock {
200 guard("asdf_block_create", core::ptr::null_mut(), || {
201 let payload = if !data.is_null() {
202 BlockData::Borrowed { ptr: data.cast::<u8>(), len: size }
203 } else if size > 0 {
204 let mut buf = Vec::new();
209 if buf.try_reserve_exact(size).is_err() {
210 return core::ptr::null_mut();
211 }
212 buf.resize(size, 0);
213 BlockData::Owned(buf)
214 } else {
215 BlockData::Empty
216 };
217 Box::into_raw(Box::new(AsdfBlock::detached(payload)))
218 })
219}
220
221#[unsafe(no_mangle)]
226pub unsafe extern "C" fn asdf_block_destroy(block: *mut AsdfBlock) {
227 guard("asdf_block_destroy", (), || {
228 if !block.is_null() {
229 drop(unsafe { Box::from_raw(block) });
230 }
231 })
232}
233
234#[unsafe(no_mangle)]
244pub unsafe extern "C" fn asdf_block_data_alloc(block: *mut AsdfBlock, size: usize) -> *mut c_void {
245 guard("asdf_block_data_alloc", core::ptr::null_mut(), || {
246 let Some(block) = block_ref(block) else {
247 return core::ptr::null_mut();
248 };
249 match &mut block.data {
250 BlockData::Owned(existing) if existing.len() == size => {
251 existing.as_mut_ptr().cast::<c_void>()
252 }
253 _ => {
254 block.data = BlockData::Owned(vec![0u8; size]);
255 let BlockData::Owned(buffer) = &mut block.data else {
256 unreachable!("just assigned")
257 };
258 buffer.as_mut_ptr().cast::<c_void>()
259 }
260 }
261 })
262}
263
264#[unsafe(no_mangle)]
272pub unsafe extern "C" fn asdf_block_data_set(
273 block: *mut AsdfBlock,
274 data: *const c_void,
275 size: usize,
276) -> c_int {
277 guard("asdf_block_data_set", -1, || {
278 let Some(block) = block_ref(block) else { return -1 };
279 if data.is_null() && size > 0 {
280 return -1;
281 }
282 block.data = if data.is_null() {
283 BlockData::Empty
284 } else {
285 BlockData::Borrowed { ptr: data.cast::<u8>(), len: size }
286 };
287 block.data_is_compressed = false;
288 block.decompressed = None;
289 0
290 })
291}
292
293#[unsafe(no_mangle)]
302pub unsafe extern "C" fn asdf_block_data_set_compressed(
303 block: *mut AsdfBlock,
304 data: *const c_void,
305 size: usize,
306 data_size: u64,
307 compression: *const c_char,
308) -> c_int {
309 guard("asdf_block_data_set_compressed", -1, || {
310 let Some(block) = block_ref(block) else { return -1 };
311 if data.is_null() && size > 0 {
312 return -1;
313 }
314 let name = if compression.is_null() {
315 String::new()
316 } else {
317 unsafe { CStr::from_ptr(compression) }.to_string_lossy().into_owned()
318 };
319 if name.len() > 4 {
320 return -1;
321 }
322
323 block.data = if data.is_null() {
324 BlockData::Empty
325 } else {
326 BlockData::Borrowed { ptr: data.cast::<u8>(), len: size }
327 };
328 block.data_is_compressed = true;
329 block.declared_data_size = data_size;
330 block.compression = Compression::from_name(&name).unwrap_or(Compression::None);
331 block.compression_name = [0; 5];
332 block.compression_name[..name.len()].copy_from_slice(name.as_bytes());
333 block.decompressed = None;
334 0
335 })
336}
337
338#[unsafe(no_mangle)]
346pub unsafe extern "C" fn asdf_block_allocated_size_set(
347 block: *mut AsdfBlock,
348 allocated_size: u64,
349) -> c_int {
350 guard("asdf_block_allocated_size_set", -1, || {
351 let Some(block) = block_ref(block) else { return -1 };
352 block.allocated_size = allocated_size;
353 0
354 })
355}
356
357#[unsafe(no_mangle)]
368pub unsafe extern "C" fn asdf_block_append(
369 file: *mut AsdfFile,
370 block: *mut AsdfBlock,
371) -> *mut AsdfBlock {
372 guard("asdf_block_append", core::ptr::null_mut(), || {
373 let Some(handle) = block_ref(block) else {
374 return core::ptr::null_mut();
375 };
376 if !handle.detached {
377 return core::ptr::null_mut();
378 }
379 let Some(blocks) = file_blocks_mut(file) else {
380 return core::ptr::null_mut();
381 };
382 blocks.push(handle.to_pending());
383
384 handle.file = file;
385 handle.index = Some(blocks.len() - 1);
386 handle.detached = false;
387 block
388 })
389}
390
391#[unsafe(no_mangle)]
396pub unsafe extern "C" fn asdf_block_data_size(block: *mut AsdfBlock) -> usize {
397 guard("asdf_block_data_size", 0, || {
398 let Some(block) = block_ref(block) else { return 0 };
399 if block.data_is_compressed {
400 return usize::try_from(block.declared_data_size).unwrap_or(0);
401 }
402 block.data.len()
403 })
404}
405
406#[unsafe(no_mangle)]
411pub unsafe extern "C" fn asdf_block_compression(block: *mut AsdfBlock) -> *const c_char {
412 guard("asdf_block_compression", core::ptr::null(), || {
413 let Some(block) = block_ref(block) else {
414 return core::ptr::null();
415 };
416 block.compression_name.as_ptr().cast::<c_char>()
418 })
419}
420
421#[unsafe(no_mangle)]
426pub unsafe extern "C" fn asdf_block_compression_set(
427 block: *mut AsdfBlock,
428 compression: *const c_char,
429) -> c_int {
430 guard("asdf_block_compression_set", -1, || {
431 let Some(block) = block_ref(block) else { return -1 };
432 let name = if compression.is_null() {
433 String::new()
434 } else {
435 unsafe { CStr::from_ptr(compression) }.to_string_lossy().into_owned()
436 };
437 let Ok(method) = Compression::from_name(&name) else {
439 return -1;
440 };
441 if !method.is_available() {
442 return -1;
443 }
444 block.compression = method;
445 block.compression_name = [0; 5];
446 block.compression_name[..name.len()].copy_from_slice(name.as_bytes());
447 0
448 })
449}
450
451#[unsafe(no_mangle)]
456pub unsafe extern "C" fn asdf_block_checksum(block: *mut AsdfBlock) -> *const u8 {
457 guard("asdf_block_checksum", core::ptr::null(), || {
458 let Some(handle) = block_ref(block) else {
459 return core::ptr::null();
460 };
461 let (Some(reader), Some(index)) = (file_reader(handle.file), handle.index) else {
462 return core::ptr::null();
463 };
464 match reader.block(index) {
465 Ok(location) => location.header.checksum.as_ptr(),
466 Err(_) => core::ptr::null(),
467 }
468 })
469}
470
471#[unsafe(no_mangle)]
480pub unsafe extern "C" fn asdf_block_checksum_verify(
481 block: *mut AsdfBlock,
482 expected: *mut u8,
483) -> bool {
484 guard("asdf_block_checksum_verify", false, || {
485 let Some(handle) = block_ref(block) else { return false };
486 let (Some(reader), Some(index)) = (file_reader(handle.file), handle.index) else {
487 return false;
488 };
489 let Ok((status, computed)) = reader.verify_block_checksum(index) else {
490 return false;
491 };
492 if !expected.is_null() {
493 unsafe {
494 core::ptr::copy_nonoverlapping(computed.as_ptr(), expected, CHECKSUM_SIZE);
495 }
496 }
497 matches!(status, ChecksumStatus::Valid | ChecksumStatus::Absent)
498 })
499}
500
501#[unsafe(no_mangle)]
507pub unsafe extern "C" fn asdf_block_data(block: *mut AsdfBlock, size: *mut usize) -> *const c_void {
508 guard("asdf_block_data", core::ptr::null(), || {
509 let Some(handle) = block_ref(block) else {
510 if !size.is_null() {
511 unsafe { write_out(size, 0) };
512 }
513 return core::ptr::null();
514 };
515
516 if handle.compression == Compression::None {
517 let slice = handle.data.as_slice();
518 if !size.is_null() {
519 unsafe { write_out(size, slice.len()) };
520 }
521 return if slice.is_empty() {
522 core::ptr::null()
523 } else {
524 slice.as_ptr().cast::<c_void>()
525 };
526 }
527
528 if handle.decompressed.is_none() {
530 let expected = usize::try_from(handle.declared_data_size).unwrap_or(0);
531 match handle.compression.decompress(handle.data.as_slice(), expected) {
532 Ok(bytes) => handle.decompressed = Some(bytes),
533 Err(_) => {
534 if !size.is_null() {
535 unsafe { write_out(size, 0) };
536 }
537 return core::ptr::null();
538 }
539 }
540 }
541 let bytes = handle.decompressed.as_ref().expect("just decompressed");
542 if !size.is_null() {
543 unsafe { write_out(size, bytes.len()) };
544 }
545 bytes.as_ptr().cast::<c_void>()
546 })
547}
548
549#[unsafe(no_mangle)]
554pub unsafe extern "C" fn asdf_block_data_raw(
555 block: *mut AsdfBlock,
556 size: *mut usize,
557) -> *const c_void {
558 guard("asdf_block_data_raw", core::ptr::null(), || {
559 let Some(handle) = block_ref(block) else {
560 if !size.is_null() {
561 unsafe { write_out(size, 0) };
562 }
563 return core::ptr::null();
564 };
565 let slice = handle.data.as_slice();
566 if !size.is_null() {
567 unsafe { write_out(size, slice.len()) };
568 }
569 if slice.is_empty() { core::ptr::null() } else { slice.as_ptr().cast::<c_void>() }
570 })
571}
572
573#[cfg(test)]
574mod tests {
575 use super::*;
576 use crate::file_ffi::{asdf_block_count, asdf_close, asdf_open_mem_ex, asdf_write_to_mem};
577 use alloc::ffi::CString;
578 use asdf_core::{Writer, writer::PendingBlock as CorePending};
579
580 struct Handle(*mut AsdfFile);
581 impl Drop for Handle {
582 fn drop(&mut self) {
583 unsafe { asdf_close(self.0) };
584 }
585 }
586
587 fn sample_file() -> Vec<u8> {
589 let doc = asdf_core::yaml::parse_document(
590 "%YAML 1.1\n%TAG ! tag:stsci.edu:asdf/\n--- !core/asdf-1.1.0\na: 1\n...\n",
591 )
592 .unwrap();
593 let mut writer = Writer::from_document(doc);
594 writer.add_block(CorePending::new((0..=255u8).collect()));
595 writer.add_block(CorePending::compressed(vec![7u8; 2048], Compression::Zlib));
596 writer.to_bytes().unwrap()
597 }
598
599 fn open_sample() -> (Handle, Vec<u8>) {
600 let bytes = sample_file();
601 let f =
602 unsafe { asdf_open_mem_ex(bytes.as_ptr().cast(), bytes.len(), core::ptr::null_mut()) };
603 assert!(!f.is_null());
604 (Handle(f), bytes)
605 }
606
607 #[test]
608 fn opens_a_block_and_reads_its_data() {
609 let (h, _bytes) = open_sample();
610 assert_eq!(unsafe { asdf_block_count(h.0) }, 2);
611
612 let block = unsafe { asdf_block_open(h.0, 0) };
613 assert!(!block.is_null());
614
615 let mut size = 0usize;
616 let data = unsafe { asdf_block_data(block, &mut size) };
617 assert!(!data.is_null());
618 assert_eq!(size, 256);
619 let slice = unsafe { core::slice::from_raw_parts(data.cast::<u8>(), size) };
620 assert_eq!(slice[0], 0);
621 assert_eq!(slice[255], 255);
622
623 unsafe { asdf_block_close(block) };
624 }
625
626 #[test]
627 fn a_compressed_block_inflates_on_demand() {
628 let (h, _bytes) = open_sample();
629 let block = unsafe { asdf_block_open(h.0, 1) };
630
631 let name = unsafe { CStr::from_ptr(asdf_block_compression(block)) };
632 assert_eq!(name.to_str().unwrap(), "zlib");
633
634 let mut raw_size = 0usize;
636 let raw = unsafe { asdf_block_data_raw(block, &mut raw_size) };
637 assert!(!raw.is_null());
638 assert!(raw_size < 2048, "raw should be the compressed form");
639
640 let mut size = 0usize;
642 let data = unsafe { asdf_block_data(block, &mut size) };
643 assert_eq!(size, 2048);
644 let slice = unsafe { core::slice::from_raw_parts(data.cast::<u8>(), size) };
645 assert!(slice.iter().all(|b| *b == 7));
646
647 let again = unsafe { asdf_block_data(block, &mut size) };
649 assert_eq!(again, data);
650
651 unsafe { asdf_block_close(block) };
652 }
653
654 #[test]
655 fn checksums_verify_through_the_c_api() {
656 let (h, _bytes) = open_sample();
657 for index in 0..2 {
658 let block = unsafe { asdf_block_open(h.0, index) };
659 let checksum = unsafe { asdf_block_checksum(block) };
660 assert!(!checksum.is_null());
661
662 let mut computed = [0u8; CHECKSUM_SIZE];
663 assert!(
664 unsafe { asdf_block_checksum_verify(block, computed.as_mut_ptr()) },
665 "block {index}"
666 );
667 assert!(computed.iter().any(|b| *b != 0), "digest was not written out");
668 unsafe { asdf_block_close(block) };
669 }
670 }
671
672 #[test]
673 fn out_of_range_indices_return_null() {
674 let (h, _bytes) = open_sample();
675 assert!(unsafe { asdf_block_open(h.0, 99) }.is_null());
676 assert!(unsafe { asdf_block_open(core::ptr::null_mut(), 0) }.is_null());
677 }
678
679 #[test]
680 fn a_created_block_borrows_the_callers_buffer() {
681 let payload: Vec<u8> = (0..64u8).collect();
682 let block = unsafe { asdf_block_create(payload.as_ptr().cast(), payload.len()) };
683 assert!(!block.is_null());
684 assert_eq!(unsafe { asdf_block_data_size(block) }, 64);
685
686 let mut size = 0usize;
687 let data = unsafe { asdf_block_data_raw(block, &mut size) };
688 assert_eq!(size, 64);
689 assert_eq!(data.cast::<u8>(), payload.as_ptr());
691
692 unsafe { asdf_block_destroy(block) };
693 }
694
695 #[test]
696 fn creating_with_a_null_buffer_allocates_one() {
697 let block = unsafe { asdf_block_create(core::ptr::null(), 128) };
698 assert_eq!(unsafe { asdf_block_data_size(block) }, 128);
699
700 let first = unsafe { asdf_block_data_alloc(block, 128) };
703 let second = unsafe { asdf_block_data_alloc(block, 128) };
704 assert!(!first.is_null());
705 assert_eq!(first, second, "an existing buffer of the same size is reused");
706
707 unsafe { core::ptr::write_bytes(first.cast::<u8>(), 0xAB, 128) };
709 let mut size = 0usize;
710 let data = unsafe { asdf_block_data_raw(block, &mut size) };
711 let slice = unsafe { core::slice::from_raw_parts(data.cast::<u8>(), size) };
712 assert!(slice.iter().all(|b| *b == 0xAB));
713
714 unsafe { asdf_block_destroy(block) };
715 }
716
717 #[test]
718 fn a_different_size_reallocates() {
719 let block = unsafe { asdf_block_create(core::ptr::null(), 16) };
720 let _ = unsafe { asdf_block_data_alloc(block, 32) };
721 assert_eq!(unsafe { asdf_block_data_size(block) }, 32);
722 unsafe { asdf_block_destroy(block) };
723 }
724
725 #[test]
726 fn appending_transfers_the_block_to_the_file() {
727 let f = unsafe { asdf_open_mem_ex(core::ptr::null(), 0, core::ptr::null_mut()) };
728 let h = Handle(f);
729
730 let payload = b"appended data".to_vec();
731 let block = unsafe { asdf_block_create(payload.as_ptr().cast(), payload.len()) };
732 let appended = unsafe { asdf_block_append(h.0, block) };
733 assert_eq!(appended, block, "append returns the same handle as a view");
734 assert_eq!(unsafe { asdf_block_count(h.0) }, 1);
735
736 assert!(unsafe { asdf_block_append(h.0, block) }.is_null());
739 unsafe { asdf_block_close(appended) };
740
741 let mut buf: *mut c_void = core::ptr::null_mut();
743 let mut size = 0usize;
744 assert_eq!(unsafe { asdf_write_to_mem(h.0, &mut buf, &mut size) }, 0);
745 let written = unsafe { core::slice::from_raw_parts(buf.cast::<u8>(), size) }.to_vec();
746 unsafe { libc::free(buf) };
747
748 let reader = asdf_core::Reader::from_bytes(written).unwrap();
749 assert_eq!(reader.block_count(), 1);
750 assert_eq!(&*reader.block_data(0).unwrap(), b"appended data");
751 }
752
753 #[test]
754 fn compression_can_be_set_and_unknown_names_refused() {
755 let block = unsafe { asdf_block_create(core::ptr::null(), 8) };
756
757 let zlib = CString::new("zlib").unwrap();
758 assert_eq!(unsafe { asdf_block_compression_set(block, zlib.as_ptr()) }, 0);
759 assert_eq!(
760 unsafe { CStr::from_ptr(asdf_block_compression(block)) }.to_str().unwrap(),
761 "zlib"
762 );
763
764 let bogus = CString::new("zstd").unwrap();
766 assert_eq!(unsafe { asdf_block_compression_set(block, bogus.as_ptr()) }, -1);
767 assert_eq!(
768 unsafe { CStr::from_ptr(asdf_block_compression(block)) }.to_str().unwrap(),
769 "zlib",
770 "a rejected name must not change the setting"
771 );
772
773 assert_eq!(unsafe { asdf_block_compression_set(block, core::ptr::null()) }, 0);
775 assert_eq!(unsafe { CStr::from_ptr(asdf_block_compression(block)) }.to_str().unwrap(), "");
776
777 unsafe { asdf_block_destroy(block) };
778 }
779
780 #[test]
781 fn an_appended_block_is_compressed_on_write() {
782 let f = unsafe { asdf_open_mem_ex(core::ptr::null(), 0, core::ptr::null_mut()) };
783 let h = Handle(f);
784
785 let payload = vec![3u8; 4096];
786 let block = unsafe { asdf_block_create(payload.as_ptr().cast(), payload.len()) };
787 let zlib = CString::new("zlib").unwrap();
788 unsafe { asdf_block_compression_set(block, zlib.as_ptr()) };
789 unsafe { asdf_block_append(h.0, block) };
790 unsafe { asdf_block_close(block) };
791
792 let mut buf: *mut c_void = core::ptr::null_mut();
793 let mut size = 0usize;
794 unsafe { asdf_write_to_mem(h.0, &mut buf, &mut size) };
795 let written = unsafe { core::slice::from_raw_parts(buf.cast::<u8>(), size) }.to_vec();
796 unsafe { libc::free(buf) };
797
798 let reader = asdf_core::Reader::from_bytes(written).unwrap();
799 assert_eq!(reader.block_compression(0).unwrap(), Compression::Zlib);
800 assert_eq!(&*reader.block_data(0).unwrap(), &payload[..]);
801 assert!(reader.block_raw(0).unwrap().len() < payload.len());
802 }
803
804 #[test]
805 fn allocated_size_is_honoured_on_write() {
806 let f = unsafe { asdf_open_mem_ex(core::ptr::null(), 0, core::ptr::null_mut()) };
807 let h = Handle(f);
808
809 let payload = [1u8; 32];
810 let block = unsafe { asdf_block_create(payload.as_ptr().cast(), payload.len()) };
811 assert_eq!(unsafe { asdf_block_allocated_size_set(block, 1024) }, 0);
812 unsafe { asdf_block_append(h.0, block) };
813 unsafe { asdf_block_close(block) };
814
815 let mut buf: *mut c_void = core::ptr::null_mut();
816 let mut size = 0usize;
817 unsafe { asdf_write_to_mem(h.0, &mut buf, &mut size) };
818 let written = unsafe { core::slice::from_raw_parts(buf.cast::<u8>(), size) }.to_vec();
819 unsafe { libc::free(buf) };
820
821 let reader = asdf_core::Reader::from_bytes(written).unwrap();
822 assert_eq!(reader.block(0).unwrap().header.allocated_size, 1024);
823 assert_eq!(reader.block(0).unwrap().header.used_size, 32);
824 }
825
826 #[test]
827 fn data_set_replaces_the_buffer() {
828 let block = unsafe { asdf_block_create(core::ptr::null(), 0) };
829 let payload = b"replacement".to_vec();
830 assert_eq!(
831 unsafe { asdf_block_data_set(block, payload.as_ptr().cast(), payload.len()) },
832 0
833 );
834 assert_eq!(unsafe { asdf_block_data_size(block) }, payload.len());
835 unsafe { asdf_block_destroy(block) };
836 }
837
838 #[test]
839 fn precompressed_data_records_its_uncompressed_size() {
840 let raw = vec![9u8; 1000];
841 let stored = Compression::Zlib.compress(&raw).unwrap();
842 let block = unsafe { asdf_block_create(core::ptr::null(), 0) };
843 let zlib = CString::new("zlib").unwrap();
844 assert_eq!(
845 unsafe {
846 asdf_block_data_set_compressed(
847 block,
848 stored.as_ptr().cast(),
849 stored.len(),
850 raw.len() as u64,
851 zlib.as_ptr(),
852 )
853 },
854 0
855 );
856 assert_eq!(unsafe { asdf_block_data_size(block) }, 1000);
858
859 let mut size = 0usize;
861 let data = unsafe { asdf_block_data(block, &mut size) };
862 assert_eq!(size, 1000);
863 let slice = unsafe { core::slice::from_raw_parts(data.cast::<u8>(), size) };
864 assert!(slice.iter().all(|b| *b == 9));
865
866 unsafe { asdf_block_destroy(block) };
867 }
868
869 #[test]
870 fn null_handles_are_tolerated() {
871 let null = core::ptr::null_mut();
872 assert_eq!(unsafe { asdf_block_data_size(null) }, 0);
873 assert!(unsafe { asdf_block_compression(null) }.is_null());
874 assert_eq!(unsafe { asdf_block_compression_set(null, core::ptr::null()) }, -1);
875 assert!(unsafe { asdf_block_checksum(null) }.is_null());
876 assert!(!unsafe { asdf_block_checksum_verify(null, core::ptr::null_mut()) });
877 assert_eq!(unsafe { asdf_block_allocated_size_set(null, 0) }, -1);
878 assert!(unsafe { asdf_block_data_alloc(null, 8) }.is_null());
879
880 let mut size = 123usize;
881 assert!(unsafe { asdf_block_data(null, &mut size) }.is_null());
882 assert_eq!(size, 0, "size must be zeroed when there is no data");
883
884 unsafe { asdf_block_close(null) };
885 unsafe { asdf_block_destroy(null) };
886 }
887}