123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- use std::alloc::{GlobalAlloc, Layout, System};
- use std::ptr::null_mut;
- use std::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
- use bytes::{Buf, Bytes};
- #[global_allocator]
- static LEDGER: Ledger = Ledger::new();
- const LEDGER_LENGTH: usize = 2048;
- struct Ledger {
- alloc_table: [(AtomicPtr<u8>, AtomicUsize); LEDGER_LENGTH],
- }
- impl Ledger {
- const fn new() -> Self {
- const ELEM: (AtomicPtr<u8>, AtomicUsize) =
- (AtomicPtr::new(null_mut()), AtomicUsize::new(0));
- let alloc_table = [ELEM; LEDGER_LENGTH];
- Self { alloc_table }
- }
- /// Iterate over our table until we find an open entry, then insert into said entry
- fn insert(&self, ptr: *mut u8, size: usize) {
- for (entry_ptr, entry_size) in self.alloc_table.iter() {
- // SeqCst is good enough here, we don't care about perf, i just want to be correct!
- if entry_ptr
- .compare_exchange(null_mut(), ptr, Ordering::SeqCst, Ordering::SeqCst)
- .is_ok()
- {
- entry_size.store(size, Ordering::SeqCst);
- break;
- }
- }
- }
- fn remove(&self, ptr: *mut u8) -> usize {
- for (entry_ptr, entry_size) in self.alloc_table.iter() {
- // set the value to be something that will never try and be deallocated, so that we
- // don't have any chance of a race condition
- //
- // dont worry, LEDGER_LENGTH is really long to compensate for us not reclaiming space
- if entry_ptr
- .compare_exchange(
- ptr,
- invalid_ptr(usize::MAX),
- Ordering::SeqCst,
- Ordering::SeqCst,
- )
- .is_ok()
- {
- return entry_size.load(Ordering::SeqCst);
- }
- }
- panic!("Couldn't find a matching entry for {:x?}", ptr);
- }
- }
- unsafe impl GlobalAlloc for Ledger {
- unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
- let size = layout.size();
- let ptr = System.alloc(layout);
- self.insert(ptr, size);
- ptr
- }
- unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
- let orig_size = self.remove(ptr);
- if orig_size != layout.size() {
- panic!(
- "bad dealloc: alloc size was {}, dealloc size is {}",
- orig_size,
- layout.size()
- );
- } else {
- System.dealloc(ptr, layout);
- }
- }
- }
- #[test]
- fn test_bytes_advance() {
- let mut bytes = Bytes::from(vec![10, 20, 30]);
- bytes.advance(1);
- drop(bytes);
- }
- #[test]
- fn test_bytes_truncate() {
- let mut bytes = Bytes::from(vec![10, 20, 30]);
- bytes.truncate(2);
- drop(bytes);
- }
- #[test]
- fn test_bytes_truncate_and_advance() {
- let mut bytes = Bytes::from(vec![10, 20, 30]);
- bytes.truncate(2);
- bytes.advance(1);
- drop(bytes);
- }
- /// Returns a dangling pointer with the given address. This is used to store
- /// integer data in pointer fields.
- #[inline]
- fn invalid_ptr<T>(addr: usize) -> *mut T {
- let ptr = std::ptr::null_mut::<u8>().wrapping_add(addr);
- debug_assert_eq!(ptr as usize, addr);
- ptr.cast::<T>()
- }
- #[test]
- fn test_bytes_into_vec() {
- let vec = vec![33u8; 1024];
- // Test cases where kind == KIND_VEC
- let b1 = Bytes::from(vec.clone());
- assert_eq!(Vec::from(b1), vec);
- // Test cases where kind == KIND_ARC, ref_cnt == 1
- let b1 = Bytes::from(vec.clone());
- drop(b1.clone());
- assert_eq!(Vec::from(b1), vec);
- // Test cases where kind == KIND_ARC, ref_cnt == 2
- let b1 = Bytes::from(vec.clone());
- let b2 = b1.clone();
- assert_eq!(Vec::from(b1), vec);
- // Test cases where vtable = SHARED_VTABLE, kind == KIND_ARC, ref_cnt == 1
- assert_eq!(Vec::from(b2), vec);
- // Test cases where offset != 0
- let mut b1 = Bytes::from(vec.clone());
- let b2 = b1.split_off(20);
- assert_eq!(Vec::from(b2), vec[20..]);
- assert_eq!(Vec::from(b1), vec[..20]);
- }
|