Repository: cberner/redb
Pull request #235 (2022-03-23 19:26:50)

Use little endian


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index cc7c8ce..623bbdd 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -17,6 +17,9 @@ jobs:
     steps:
       - uses: actions/checkout@v2
 
+      - name: Check for forbidden words
+        run: "! grep --include='*.rs' -RE 'to_be_bytes|from_be_bytes|dbg!' ."
+
       - name: Install packages
         if: startsWith(matrix.os, 'ubuntu')
         run: |
diff --git a/benches/lmdb_benchmark.rs b/benches/lmdb_benchmark.rs
index 90d6e7d..17fb4cf 100644
--- a/benches/lmdb_benchmark.rs
+++ b/benches/lmdb_benchmark.rs
@@ -35,7 +35,7 @@ fn benchmark<T: BenchDatabase>(mut db: T) -> Vec<(&'static str, Duration)> {
         for _ in 0..ELEMENTS {
             let len = pairs.len();
             let (key, value) = &mut pairs[written % len];
-            key[16..].copy_from_slice(&(written as u64).to_be_bytes());
+            key[16..].copy_from_slice(&(written as u64).to_le_bytes());
             inserter.insert(key, value).unwrap();
             written += 1;
         }
@@ -61,7 +61,7 @@ fn benchmark<T: BenchDatabase>(mut db: T) -> Vec<(&'static str, Duration)> {
             let mut inserter = txn.get_inserter();
             let len = pairs.len();
             let (key, value) = &mut pairs[written % len];
-            key[16..].copy_from_slice(&(written as u64).to_be_bytes());
+            key[16..].copy_from_slice(&(written as u64).to_le_bytes());
             inserter.insert(key, value).unwrap();
             drop(inserter);
             txn.commit().unwrap();
@@ -88,7 +88,7 @@ fn benchmark<T: BenchDatabase>(mut db: T) -> Vec<(&'static str, Duration)> {
             for _ in 0..batch_size {
                 let len = pairs.len();
                 let (key, value) = &mut pairs[written % len];
-                key[16..].copy_from_slice(&(written as u64).to_be_bytes());
+                key[16..].copy_from_slice(&(written as u64).to_le_bytes());
                 inserter.insert(key, value).unwrap();
                 written += 1;
             }
@@ -120,7 +120,7 @@ fn benchmark<T: BenchDatabase>(mut db: T) -> Vec<(&'static str, Duration)> {
             for i in &key_order {
                 let len = pairs.len();
                 let (key, value) = &mut pairs[i % len];
-                key[16..].copy_from_slice(&(*i as u64).to_be_bytes());
+                key[16..].copy_from_slice(&(*i as u64).to_le_bytes());
                 let result = txn.get(key).unwrap();
                 checksum += result.as_ref()[0] as u64;
                 expected_checksum += value[0] as u64;
@@ -142,7 +142,7 @@ fn benchmark<T: BenchDatabase>(mut db: T) -> Vec<(&'static str, Duration)> {
             for i in &key_order {
                 let len = pairs.len();
                 let (key, _) = &mut pairs[i % len];
-                key[16..].copy_from_slice(&(*i as u64).to_be_bytes());
+                key[16..].copy_from_slice(&(*i as u64).to_le_bytes());
                 txn.exists_after(key);
             }
             let end = Instant::now();
@@ -165,7 +165,7 @@ fn benchmark<T: BenchDatabase>(mut db: T) -> Vec<(&'static str, Duration)> {
         for i in 0..deletes {
             let len = pairs.len();
             let (key, _) = &mut pairs[i % len];
-            key[16..].copy_from_slice(&(i as u64).to_be_bytes());
+            key[16..].copy_from_slice(&(i as u64).to_le_bytes());
             inserter.remove(key).unwrap();
         }
         drop(inserter);
diff --git a/benches/syscall_benchmark.rs b/benches/syscall_benchmark.rs
index 5684c32..67f2e2b 100644
--- a/benches/syscall_benchmark.rs
+++ b/benches/syscall_benchmark.rs
@@ -77,7 +77,7 @@ fn lmdb_zero_bench(path: &str) {
             for i in 0..ELEMENTS {
                 let (key, value) = &pairs[i % pairs.len()];
                 let mut mut_key = key.clone();
-                mut_key[0..8].copy_from_slice(&(i as u64).to_be_bytes());
+                mut_key[0..8].copy_from_slice(&(i as u64).to_le_bytes());
                 access
                     .put(&db, &mut_key, value, lmdb_zero::put::Flags::empty())
                     .unwrap();
@@ -102,7 +102,7 @@ fn lmdb_zero_bench(path: &str) {
                 for &i in &key_order {
                     let (key, value) = &pairs[i % pairs.len()];
                     let mut mut_key = key.clone();
-                    mut_key[0..8].copy_from_slice(&(i as u64).to_be_bytes());
+                    mut_key[0..8].copy_from_slice(&(i as u64).to_le_bytes());
                     let result: &[u8] = access.get(&db, &mut_key).unwrap();
                     checksum += result[0] as u64;
                     expected_checksum += value[0] as u64;
@@ -136,7 +136,7 @@ fn uring_bench(path: &Path) {
         for i in 0..ELEMENTS {
             let (key, value) = &pairs[i % pairs.len()];
             let mut mut_key = key.clone();
-            mut_key[0..8].copy_from_slice(&(i as u64).to_be_bytes());
+            mut_key[0..8].copy_from_slice(&(i as u64).to_le_bytes());
             file.write_all(&mut_key).unwrap();
             file.write_all(value).unwrap();
         }
@@ -163,7 +163,7 @@ fn uring_bench(path: &Path) {
             for (uring_counter, &i) in key_order.iter().enumerate() {
                 let (key, value) = &pairs[i % pairs.len()];
                 let mut mut_key = key.clone();
-                mut_key[0..8].copy_from_slice(&(i as u64).to_be_bytes());
+                mut_key[0..8].copy_from_slice(&(i as u64).to_le_bytes());
                 let offset = i * (mut_key.len() + value.len()) + mut_key.len();
 
                 expected_checksum += value[0] as u64;
@@ -220,7 +220,7 @@ fn readwrite_bench(path: &Path) {
         for i in 0..ELEMENTS {
             let (key, value) = &pairs[i % pairs.len()];
             let mut mut_key = key.clone();
-            mut_key[0..8].copy_from_slice(&(i as u64).to_be_bytes());
+            mut_key[0..8].copy_from_slice(&(i as u64).to_le_bytes());
             file.write_all(&mut_key).unwrap();
             file.write_all(value).unwrap();
         }
@@ -243,7 +243,7 @@ fn readwrite_bench(path: &Path) {
             for &i in &key_order {
                 let (key, value) = &pairs[i % pairs.len()];
                 let mut mut_key = key.clone();
-                mut_key[0..8].copy_from_slice(&(i as u64).to_be_bytes());
+                mut_key[0..8].copy_from_slice(&(i as u64).to_le_bytes());
                 let offset = i * (mut_key.len() + value.len()) + mut_key.len();
 
                 file.seek(SeekFrom::Start(offset as u64)).unwrap();
@@ -284,7 +284,7 @@ fn mmap_bench(path: &Path) {
         for i in 0..ELEMENTS {
             let (key, value) = &pairs[i % pairs.len()];
             let mut mut_key = key.clone();
-            mut_key[0..8].copy_from_slice(&(i as u64).to_be_bytes());
+            mut_key[0..8].copy_from_slice(&(i as u64).to_le_bytes());
             mmap[write_index..(write_index + mut_key.len())].copy_from_slice(&mut_key);
             write_index += mut_key.len();
             mmap[write_index..(write_index + value.len())].copy_from_slice(value);
@@ -308,7 +308,7 @@ fn mmap_bench(path: &Path) {
             for &i in &key_order {
                 let (key, value) = &pairs[i % pairs.len()];
                 let mut mut_key = key.clone();
-                mut_key[0..8].copy_from_slice(&(i as u64).to_be_bytes());
+                mut_key[0..8].copy_from_slice(&(i as u64).to_le_bytes());
                 let offset = i * (mut_key.len() + value.len()) + mut_key.len();
                 let buffer = &mmap[offset..(offset + value.len())];
                 checksum += buffer[0] as u64;
diff --git a/docs/design.md b/docs/design.md
index 973d26e..b80233d 100644
--- a/docs/design.md
+++ b/docs/design.md
@@ -37,12 +37,12 @@ inactive copy, which is then atomically promoted to the primary via the `field_m
 * magic number (immutable, 4 bytes): 32-bit magic number
 * version (immutable, 1 byte): single byte which must be `1`
 * page_size (immutable, 1 byte): single byte, x, where `2^x` represents the size of pages
-* db_size: (immutable, 8 bytes): 64-bit unsigned big-endian integer, indicating the number of valid
+* db_size: (immutable, 8 bytes): 64-bit unsigned little-endian integer, indicating the number of valid
   bytes in the database file
-* root1 (mutable, 8 bytes): 64-bit unsigned big-endian integer, indicating the offset
+* root1 (mutable, 8 bytes): 64-bit unsigned little-endian integer, indicating the offset
   to the root page of the B-tree
 * root2 (mutable, 8 bytes): double buffer for `root1`
-* transaction_id1 (mutable, 16 bytes): 128-bit unsigned big-endian integer, monotonically
+* transaction_id1 (mutable, 16 bytes): 128-bit unsigned little-endian integer, monotonically
   increasing id indicating the currently committed transaction
 * transaction_id2 (mutable, 16 bytes): double buffer for `transaction_id1`
 * field_mutex (mutable, 1 byte): either `1` indicating that the first version of each
diff --git a/src/multimap_table.rs b/src/multimap_table.rs
index cd7a78f..7f9d3bc 100644
--- a/src/multimap_table.rs
+++ b/src/multimap_table.rs
@@ -148,7 +148,7 @@ impl<'a, K: RedbKey + ?Sized + 'a, V: RedbKey + ?Sized + 'a> MultimapKVPairAcces
     }
 
     fn key_len(&self) -> usize {
-        u32::from_be_bytes(self.data[1..5].try_into().unwrap()) as usize
+        u32::from_le_bytes(self.data[1..5].try_into().unwrap()) as usize
     }
 
     fn key_bytes(&self) -> &'a [u8] {
@@ -162,7 +162,7 @@ impl<'a, K: RedbKey + ?Sized + 'a, V: RedbKey + ?Sized + 'a> MultimapKVPairAcces
 
 fn make_serialized_kv<K: RedbKey + ?Sized, V: RedbKey + ?Sized>(key: &K, value: &V) -> Vec<u8> {
     let mut result = vec![MultimapKeyCompareOp::KeyAndValue.serialize()];
-    result.extend_from_slice(&(key.as_bytes().as_ref().len() as u32).to_be_bytes());
+    result.extend_from_slice(&(key.as_bytes().as_ref().len() as u32).to_le_bytes());
     result.extend_from_slice(key.as_bytes().as_ref());
     result.extend_from_slice(value.as_bytes().as_ref());
 
@@ -171,7 +171,7 @@ fn make_serialized_kv<K: RedbKey + ?Sized, V: RedbKey + ?Sized>(key: &K, value:
 
 fn make_serialized_key_with_op<K: RedbKey + ?Sized>(key: &K, op: MultimapKeyCompareOp) -> Vec<u8> {
     let mut result = vec![op.serialize()];
-    result.extend_from_slice(&(key.as_bytes().as_ref().len() as u32).to_be_bytes());
+    result.extend_from_slice(&(key.as_bytes().as_ref().len() as u32).to_le_bytes());
     result.extend_from_slice(key.as_bytes().as_ref());
 
     result
diff --git a/src/tree_store/btree_base.rs b/src/tree_store/btree_base.rs
index f062ca0..f1a5b12 100644
--- a/src/tree_store/btree_base.rs
+++ b/src/tree_store/btree_base.rs
@@ -165,7 +165,7 @@ impl<'a: 'b, 'b, T: Page + 'a> LeafAccessor<'a, 'b, T> {
             None
         } else {
             let offset = 4 + size_of::<u32>() * n;
-            let end = u32::from_be_bytes(
+            let end = u32::from_le_bytes(
                 self.page.memory()[offset..(offset + size_of::<u32>())]
                     .try_into()
                     .unwrap(),
@@ -187,7 +187,7 @@ impl<'a: 'b, 'b, T: Page + 'a> LeafAccessor<'a, 'b, T> {
             None
         } else {
             let offset = 4 + size_of::<u32>() * self.num_pairs() + size_of::<u32>() * n;
-            let end = u32::from_be_bytes(
+            let end = u32::from_le_bytes(
                 self.page.memory()[offset..(offset + size_of::<u32>())]
                     .try_into()
                     .unwrap(),
@@ -197,7 +197,7 @@ impl<'a: 'b, 'b, T: Page + 'a> LeafAccessor<'a, 'b, T> {
     }
 
     pub(in crate::tree_store) fn num_pairs(&self) -> usize {
-        u16::from_be_bytes(self.page.memory()[2..4].try_into().unwrap()) as usize
+        u16::from_le_bytes(self.page.memory()[2..4].try_into().unwrap()) as usize
     }
 
     pub(in crate::tree_store) fn offset_of_first_value(&self) -> usize {
@@ -301,7 +301,7 @@ impl<'a: 'b, 'b> LeafBuilder<'a, 'b> {
         key_bytes: usize,
     ) -> Self {
         page.memory_mut()[0] = LEAF;
-        page.memory_mut()[2..4].copy_from_slice(&(num_pairs as u16).to_be_bytes());
+        page.memory_mut()[2..4].copy_from_slice(&(num_pairs as u16).to_le_bytes());
         #[cfg(debug_assertions)]
         {
             // Poison all the key & value offsets, in case the caller forgets to write them
@@ -320,7 +320,7 @@ impl<'a: 'b, 'b> LeafBuilder<'a, 'b> {
 
     fn value_end(&self, n: usize) -> usize {
         let offset = 4 + size_of::<u32>() * self.num_pairs + size_of::<u32>() * n;
-        u32::from_be_bytes(
+        u32::from_le_bytes(
             self.page.memory()[offset..(offset + size_of::<u32>())]
                 .try_into()
                 .unwrap(),
@@ -329,7 +329,7 @@ impl<'a: 'b, 'b> LeafBuilder<'a, 'b> {
 
     fn key_end(&self, n: usize) -> usize {
         let offset = 4 + size_of::<u32>() * n;
-        u32::from_be_bytes(
+        u32::from_le_bytes(
             self.page.memory()[offset..(offset + size_of::<u32>())]
                 .try_into()
                 .unwrap(),
@@ -351,14 +351,14 @@ impl<'a: 'b, 'b> LeafBuilder<'a, 'b> {
         let n = self.pairs_written;
         let offset = 4 + size_of::<u32>() * n;
         self.page.memory_mut()[offset..(offset + size_of::<u32>())]
-            .copy_from_slice(&((key_offset + key.len()) as u32).to_be_bytes());
+            .copy_from_slice(&((key_offset + key.len()) as u32).to_le_bytes());
         self.page.memory_mut()[key_offset..(key_offset + key.len())].copy_from_slice(key);
         let written_key_len = key_offset + key.len() - 4 - 2 * size_of::<u32>() * self.num_pairs;
         assert!(written_key_len <= self.provisioned_key_bytes);
 
         let offset = 4 + size_of::<u32>() * self.num_pairs + size_of::<u32>() * n;
         self.page.memory_mut()[offset..(offset + size_of::<u32>())]
-            .copy_from_slice(&((value_offset + value.len()) as u32).to_be_bytes());
+            .copy_from_slice(&((value_offset + value.len()) as u32).to_le_bytes());
         self.page.memory_mut()[value_offset..(value_offset + value.len())].copy_from_slice(value);
         self.pairs_written += 1;
     }
@@ -427,7 +427,7 @@ impl<'a: 'b, 'b, T: Page + 'a> InternalAccessor<'a, 'b, T> {
     fn key_end(&self, n: usize) -> usize {
         let offset =
             4 + PageNumber::serialized_size() * self.count_children() + size_of::<u32>() * n;
-        u32::from_be_bytes(
+        u32::from_le_bytes(
             self.page.memory()[offset..(offset + size_of::<u32>())]
                 .try_into()
                 .unwrap(),
@@ -455,7 +455,7 @@ impl<'a: 'b, 'b, T: Page + 'a> InternalAccessor<'a, 'b, T> {
         }
 
         let offset = 4 + PageNumber::serialized_size() * n;
-        Some(PageNumber::from_be_bytes(
+        Some(PageNumber::from_le_bytes(
             self.page.memory()[offset..(offset + PageNumber::serialized_size())]
                 .try_into()
                 .unwrap(),
@@ -467,7 +467,7 @@ impl<'a: 'b, 'b, T: Page + 'a> InternalAccessor<'a, 'b, T> {
     }
 
     fn num_keys(&self) -> usize {
-        u16::from_be_bytes(self.page.memory()[2..4].try_into().unwrap()) as usize
+        u16::from_le_bytes(self.page.memory()[2..4].try_into().unwrap()) as usize
     }
 }
 
@@ -499,7 +499,7 @@ impl<'a: 'b, 'b> InternalBuilder<'a, 'b> {
     // Caller MUST write num_keys values
     pub(in crate::tree_store) fn new(page: &'b mut PageMut<'a>, num_keys: usize) -> Self {
         page.memory_mut()[0] = INTERNAL;
-        page.memory_mut()[2..4].copy_from_slice(&(num_keys as u16).to_be_bytes());
+        page.memory_mut()[2..4].copy_from_slice(&(num_keys as u16).to_le_bytes());
         #[cfg(debug_assertions)]
         {
             // Poison all the child pointers & key offsets, in case the caller forgets to write them
@@ -519,12 +519,12 @@ impl<'a: 'b, 'b> InternalBuilder<'a, 'b> {
     pub(in crate::tree_store) fn write_first_page(&mut self, page_number: PageNumber) {
         let offset = 4;
         self.page.memory_mut()[offset..(offset + PageNumber::serialized_size())]
-            .copy_from_slice(&page_number.to_be_bytes());
+            .copy_from_slice(&page_number.to_le_bytes());
     }
 
     fn key_end(&self, n: usize) -> usize {
         let offset = 4 + PageNumber::serialized_size() * (self.num_keys + 1) + size_of::<u32>() * n;
-        u32::from_be_bytes(
+        u32::from_le_bytes(
             self.page.memory()[offset..(offset + size_of::<u32>())]
                 .try_into()
                 .unwrap(),
@@ -544,7 +544,7 @@ impl<'a: 'b, 'b> InternalBuilder<'a, 'b> {
         self.keys_written += 1;
         let offset = 4 + PageNumber::serialized_size() * (n + 1);
         self.page.memory_mut()[offset..(offset + PageNumber::serialized_size())]
-            .copy_from_slice(&page_number.to_be_bytes());
+            .copy_from_slice(&page_number.to_le_bytes());
 
         let data_offset = if n > 0 {
             self.key_end(n - 1)
@@ -554,7 +554,7 @@ impl<'a: 'b, 'b> InternalBuilder<'a, 'b> {
         };
         let offset = 4 + PageNumber::serialized_size() * (self.num_keys + 1) + size_of::<u32>() * n;
         self.page.memory_mut()[offset..(offset + size_of::<u32>())]
-            .copy_from_slice(&((data_offset + key.len()) as u32).to_be_bytes());
+            .copy_from_slice(&((data_offset + key.len()) as u32).to_le_bytes());
 
         debug_assert!(data_offset > offset);
         self.page.memory_mut()[data_offset..(data_offset + key.len())].copy_from_slice(key);
@@ -578,13 +578,13 @@ impl<'a: 'b, 'b> InternalMutator<'a, 'b> {
     }
 
     fn num_keys(&self) -> usize {
-        u16::from_be_bytes(self.page.memory()[2..4].try_into().unwrap()) as usize
+        u16::from_le_bytes(self.page.memory()[2..4].try_into().unwrap()) as usize
     }
 
     pub(in crate::tree_store) fn write_child_page(&mut self, i: usize, page_number: PageNumber) {
         debug_assert!(i <= self.num_keys());
         let offset = 4 + PageNumber::serialized_size() * i;
         self.page.memory_mut()[offset..(offset + PageNumber::serialized_size())]
-            .copy_from_slice(&page_number.to_be_bytes());
+            .copy_from_slice(&page_number.to_le_bytes());
     }
 }
diff --git a/src/tree_store/btree_utils.rs b/src/tree_store/btree_utils.rs
index 6c21904..fedda3d 100644
--- a/src/tree_store/btree_utils.rs
+++ b/src/tree_store/btree_utils.rs
@@ -1179,7 +1179,7 @@ mod test {
         {
             let mut table = txn.open_table(X).unwrap();
             for i in (0..elements).rev() {
-                table.insert(&i.to_be_bytes(), b"").unwrap();
+                table.insert(&i.to_le_bytes(), b"").unwrap();
             }
         }
         txn.commit().unwrap();
@@ -1199,7 +1199,7 @@ mod test {
         {
             let mut table = txn.open_table(X).unwrap();
             for i in 0..(elements - reduce_to) {
-                table.remove(&i.to_be_bytes()).unwrap();
+                table.remove(&i.to_le_bytes()).unwrap();
             }
         }
         txn.commit().unwrap();
diff --git a/src/tree_store/page_store/buddy_allocator.rs b/src/tree_store/page_store/buddy_allocator.rs
index bec8ee0..88d5ef3 100644
--- a/src/tree_store/page_store/buddy_allocator.rs
+++ b/src/tree_store/page_store/buddy_allocator.rs
@@ -15,7 +15,7 @@ pub(crate) struct BuddyAllocator {
 // Pages are marked free at only a single order, and it must always be the largest order
 //
 // Data structure format:
-// max_order: big endian u64
+// max_order: u64
 // order_offsets: array of (u64, u64), with offset & length for PageAllocator structure for the given order
 // ... PageAllocator structures
 impl BuddyAllocator {
@@ -41,7 +41,7 @@ impl BuddyAllocator {
         max_order: usize,
     ) -> Self {
         assert!(data.len() >= Self::required_space(max_page_capacity, max_order));
-        data[..size_of::<u64>()].copy_from_slice(&(max_order as u64).to_be_bytes());
+        data[..size_of::<u64>()].copy_from_slice(&(max_order as u64).to_le_bytes());
 
         let mut metadata_offset = size_of::<u64>();
         let mut data_offset = size_of::<u64>() + 2 * (max_order + 1) * size_of::<u64>();
@@ -51,9 +51,9 @@ impl BuddyAllocator {
         for order in 0..=max_order {
             let required = PageAllocator::required_space(pages_for_order);
             data[metadata_offset..metadata_offset + size_of::<u64>()]
-                .copy_from_slice(&(data_offset as u64).to_be_bytes());
+                .copy_from_slice(&(data_offset as u64).to_le_bytes());
             data[metadata_offset + size_of::<u64>()..metadata_offset + 2 * size_of::<u64>()]
-                .copy_from_slice(&(required as u64).to_be_bytes());
+                .copy_from_slice(&(required as u64).to_le_bytes());
             orders.push(PageAllocator::init_new(
                 Self::get_order_bytes_mut(data, order),
                 pages_for_order,
@@ -135,12 +135,12 @@ impl BuddyAllocator {
     }
 
     fn get_order_offset_and_length(data: &[u8], order: usize) -> (usize, usize) {
-        let max_order = u64::from_be_bytes(data[..size_of::<u64>()].try_into().unwrap()) as usize;
+        let max_order = u64::from_le_bytes(data[..size_of::<u64>()].try_into().unwrap()) as usize;
         assert!(order <= max_order);
         let base = size_of::<u64>() + order * 2 * size_of::<u64>();
         let offset =
-            u64::from_be_bytes(data[base..base + size_of::<u64>()].try_into().unwrap()) as usize;
-        let length = u64::from_be_bytes(
+            u64::from_le_bytes(data[base..base + size_of::<u64>()].try_into().unwrap()) as usize;
+        let length = u64::from_le_bytes(
             data[base + size_of::<u64>()..base + 2 * size_of::<u64>()]
                 .try_into()
                 .unwrap(),
diff --git a/src/tree_store/page_store/grouped_bitmap.rs b/src/tree_store/page_store/grouped_bitmap.rs
index 4adb1a5..1c4d17d 100644
--- a/src/tree_store/page_store/grouped_bitmap.rs
+++ b/src/tree_store/page_store/grouped_bitmap.rs
@@ -39,24 +39,24 @@ impl<'a> U64GroupedBitMapMut<'a> {
     // Returns true iff the bit's group is all set
     pub(crate) fn set(&mut self, bit: usize) -> bool {
         let (index, bit_index) = self.data_index_of(bit);
-        let mut group = u64::from_be_bytes(self.data[index..(index + 8)].try_into().unwrap());
+        let mut group = u64::from_le_bytes(self.data[index..(index + 8)].try_into().unwrap());
         group |= Self::select_mask(bit_index);
-        self.data[index..(index + 8)].copy_from_slice(&group.to_be_bytes());
+        self.data[index..(index + 8)].copy_from_slice(&group.to_le_bytes());
 
         group == u64::MAX
     }
 
     pub(crate) fn get(&self, bit: usize) -> bool {
         let (index, bit_index) = self.data_index_of(bit);
-        let group = u64::from_be_bytes(self.data[index..(index + 8)].try_into().unwrap());
+        let group = u64::from_le_bytes(self.data[index..(index + 8)].try_into().unwrap());
         group & Self::select_mask(bit_index) != 0
     }
 
     pub(crate) fn clear(&mut self, bit: usize) {
         let (index, bit_index) = self.data_index_of(bit);
-        let mut group = u64::from_be_bytes(self.data[index..(index + 8)].try_into().unwrap());
+        let mut group = u64::from_le_bytes(self.data[index..(index + 8)].try_into().unwrap());
         group &= !Self::select_mask(bit_index);
-        self.data[index..(index + 8)].copy_from_slice(&group.to_be_bytes());
+        self.data[index..(index + 8)].copy_from_slice(&group.to_le_bytes());
     }
 
     pub(crate) fn first_unset(&self, start_bit: usize, end_bit: usize) -> Option<usize> {
@@ -64,7 +64,7 @@ impl<'a> U64GroupedBitMapMut<'a> {
         assert_eq!(end_bit, start_bit + 64);
 
         let (index, _) = self.data_index_of(start_bit);
-        let group = u64::from_be_bytes(self.data[index..(index + 8)].try_into().unwrap());
+        let group = u64::from_le_bytes(self.data[index..(index + 8)].try_into().unwrap());
         match group.trailing_ones() {
             64 => None,
             x => Some(start_bit + x as usize),
diff --git a/src/tree_store/page_store/page_allocator.rs b/src/tree_store/page_store/page_allocator.rs
index d56963e..0d656c1 100644
--- a/src/tree_store/page_store/page_allocator.rs
+++ b/src/tree_store/page_store/page_allocator.rs
@@ -14,7 +14,7 @@ pub(crate) struct PageAllocator {
 // borrowing the data array
 //
 // Data structure format:
-// num_pages: big endian u64
+// num_pages: u64
 // root: u64
 // subtree layer: 2-64 u64s
 // ...consecutive layers. Except for the last level, all sub-trees of the root must be complete
@@ -67,7 +67,7 @@ impl PageAllocator {
             *value = 0xFF;
         }
 
-        data[..8].copy_from_slice(&(num_pages as u64).to_be_bytes());
+        data[..8].copy_from_slice(&(num_pages as u64).to_le_bytes());
 
         let result = Self::new(num_pages);
 
@@ -323,14 +323,14 @@ mod test {
         while allocator.alloc(&mut data).is_ok() {}
         // The last u64 must be used, since the leaf layer is compact
         let l = data.len();
-        assert_ne!(0, u64::from_be_bytes(data[(l - 8)..].try_into().unwrap()));
+        assert_ne!(0, u64::from_le_bytes(data[(l - 8)..].try_into().unwrap()));
     }
 
     #[test]
     fn random_pattern() {
         let seed = rand::thread_rng().gen();
         // Print the seed to debug for reproducibility, in case this test fails
-        dbg!(seed);
+        println!("seed={}", seed);
         let mut rng = StdRng::seed_from_u64(seed);
 
         let num_pages = rng.gen_range(2..10000);
diff --git a/src/tree_store/page_store/page_manager.rs b/src/tree_store/page_store/page_manager.rs
index e47e535..9156b64 100644
--- a/src/tree_store/page_store/page_manager.rs
+++ b/src/tree_store/page_store/page_manager.rs
@@ -80,12 +80,20 @@ fn ceil_log2(x: usize) -> usize {
     }
 }
 
+fn round_up_to_multiple_of(value: usize, multiple: usize) -> usize {
+    if value % multiple == 0 {
+        value
+    } else {
+        value + multiple - value % multiple
+    }
+}
+
 pub(crate) fn get_db_size(path: impl AsRef<Path>) -> Result<usize, io::Error> {
     let mut db_size = [0u8; size_of::<u64>()];
     let mut file = File::open(path)?;
     file.seek(SeekFrom::Start(DB_SIZE_OFFSET as u64))?;
     file.read_exact(&mut db_size)?;
-    Ok(u64::from_be_bytes(db_size) as usize)
+    Ok(u64::from_le_bytes(db_size) as usize)
 }
 
 // Marker struct for the mutex guarding the metadata (header & allocators)
@@ -155,7 +163,7 @@ impl<'a> MetadataAccessor<'a> {
     }
 
     fn get_max_capacity(&self) -> usize {
-        u64::from_be_bytes(
+        u64::from_le_bytes(
             self.header[DB_SIZE_OFFSET..DB_SIZE_OFFSET + size_of::<u64>()]
                 .try_into()
                 .unwrap(),
@@ -164,7 +172,7 @@ impl<'a> MetadataAccessor<'a> {
 
     fn set_max_capacity(&mut self, max_size: usize) {
         self.header[DB_SIZE_OFFSET..DB_SIZE_OFFSET + size_of::<u64>()]
-            .copy_from_slice(&(max_size as u64).to_be_bytes());
+            .copy_from_slice(&(max_size as u64).to_le_bytes());
     }
 
     fn get_magic_number(&self) -> [u8; MAGICNUMBER.len()] {
@@ -294,15 +302,15 @@ impl PageNumber {
         }
     }
 
-    pub(crate) fn to_be_bytes(self) -> [u8; 8] {
+    pub(crate) fn to_le_bytes(self) -> [u8; 8] {
         let mut temp = (0x000F_FFFF & self.page_index) as u64;
         temp |= (0x000F_FFFF & self.region as u64) << 20;
         temp |= (0b0001_1111 & self.page_order as u64) << 59;
-        temp.to_be_bytes()
+        temp.to_le_bytes()
     }
 
-    pub(crate) fn from_be_bytes(bytes: [u8; 8]) -> Self {
-        let temp = u64::from_be_bytes(bytes);
+    pub(crate) fn from_le_bytes(bytes: [u8; 8]) -> Self {
+        let temp = u64::from_le_bytes(bytes);
         let index = (temp & 0x000F_FFFF) as u32;
         let region = ((temp >> 20) & 0x000F_FFFF) as u32;
         let order = (temp >> 59) as u8;
@@ -342,7 +350,7 @@ impl<'a> TransactionAccessor<'a> {
     }
 
     fn get_root_page(&self) -> Option<PageNumber> {
-        let num = PageNumber::from_be_bytes(
+        let num = PageNumber::from_le_bytes(
             self.mem[ROOT_PAGE_OFFSET..(ROOT_PAGE_OFFSET + 8)]
                 .try_into()
                 .unwrap(),
@@ -355,7 +363,7 @@ impl<'a> TransactionAccessor<'a> {
     }
 
     fn get_last_committed_transaction_id(&self) -> u64 {
-        u64::from_be_bytes(
+        u64::from_le_bytes(
             self.mem[TRANSACTION_ID_OFFSET..(TRANSACTION_ID_OFFSET + size_of::<u64>())]
                 .try_into()
                 .unwrap(),
@@ -363,7 +371,7 @@ impl<'a> TransactionAccessor<'a> {
     }
 
     fn get_data_section_layout(&self) -> DatabaseLayout {
-        DatabaseLayout::from_be_bytes(
+        DatabaseLayout::from_le_bytes(
             self.mem[DATA_LAYOUT_OFFSET..(DATA_LAYOUT_OFFSET + DatabaseLayout::serialized_size())]
                 .try_into()
                 .unwrap(),
@@ -382,17 +390,17 @@ impl<'a> TransactionMutator<'a> {
 
     fn set_root_page(&mut self, page_number: PageNumber) {
         self.mem[ROOT_PAGE_OFFSET..(ROOT_PAGE_OFFSET + 8)]
-            .copy_from_slice(&page_number.to_be_bytes());
+            .copy_from_slice(&page_number.to_le_bytes());
     }
 
     fn set_last_committed_transaction_id(&mut self, transaction_id: u64) {
         self.mem[TRANSACTION_ID_OFFSET..(TRANSACTION_ID_OFFSET + size_of::<u64>())]
-            .copy_from_slice(&transaction_id.to_be_bytes());
+            .copy_from_slice(&transaction_id.to_le_bytes());
     }
 
     fn set_data_section_layout(&mut self, layout: &DatabaseLayout) {
         self.mem[DATA_LAYOUT_OFFSET..(DATA_LAYOUT_OFFSET + DatabaseLayout::serialized_size())]
-            .copy_from_slice(&layout.to_be_bytes());
+            .copy_from_slice(&layout.to_le_bytes());
     }
 }
 
@@ -553,25 +561,25 @@ impl RegionLayout {
         size_of::<u32>() + 2 * size_of::<u64>()
     }
 
-    fn to_be_bytes(&self) -> [u8; Self::serialized_size()] {
+    fn to_le_bytes(&self) -> [u8; Self::serialized_size()] {
         let mut result = [0; Self::serialized_size()];
-        result[..size_of::<u32>()].copy_from_slice(&(self.num_pages as u32).to_be_bytes());
+        result[..size_of::<u32>()].copy_from_slice(&(self.num_pages as u32).to_le_bytes());
         result[size_of::<u32>()..size_of::<u32>() + size_of::<u64>()]
-            .copy_from_slice(&(self.allocator_state_len as u64).to_be_bytes());
+            .copy_from_slice(&(self.allocator_state_len as u64).to_le_bytes());
         result[size_of::<u32>() + size_of::<u64>()..]
-            .copy_from_slice(&(self.page_size as u64).to_be_bytes());
+            .copy_from_slice(&(self.page_size as u64).to_le_bytes());
 
         result
     }
 
-    fn from_be_bytes(data: [u8; Self::serialized_size()]) -> Self {
-        let num_pages = u32::from_be_bytes(data[..size_of::<u32>()].try_into().unwrap()) as usize;
-        let allocator_state_len = u64::from_be_bytes(
+    fn from_le_bytes(data: [u8; Self::serialized_size()]) -> Self {
+        let num_pages = u32::from_le_bytes(data[..size_of::<u32>()].try_into().unwrap()) as usize;
+        let allocator_state_len = u64::from_le_bytes(
             data[size_of::<u32>()..size_of::<u32>() + size_of::<u64>()]
                 .try_into()
                 .unwrap(),
         ) as usize;
-        let page_size = u64::from_be_bytes(
+        let page_size = u64::from_le_bytes(
             data[size_of::<u32>() + size_of::<u64>()..]
                 .try_into()
                 .unwrap(),
@@ -590,6 +598,7 @@ impl RegionLayout {
 #[derive(Clone)]
 struct DatabaseLayout {
     db_header_bytes: usize,
+    region_allocator_range: Range<usize>,
     full_region_layout: RegionLayout,
     num_full_regions: usize,
     trailing_partial_region: Option<RegionLayout>,
@@ -604,10 +613,13 @@ impl DatabaseLayout {
         desired_usable_bytes = min(desired_usable_bytes, db_capacity);
         let full_region_layout = RegionLayout::full_region_layout(page_size);
         let min_header_size = DB_HEADER_SIZE + U64GroupedBitMapMut::required_bytes(1);
+        let region_allocator_range = DB_HEADER_SIZE..min_header_size;
+        // Pad to be page aligned
+        let min_header_size = round_up_to_multiple_of(min_header_size, page_size);
         if db_capacity < min_header_size + MIN_USABLE_PAGES * page_size {
             return Err(Error::OutOfSpace);
         }
-        if db_capacity - min_header_size <= full_region_layout.len() {
+        let result = if db_capacity - min_header_size <= full_region_layout.len() {
             // Single region layout
             let region_layout = RegionLayout::calculate(
                 db_capacity - min_header_size,
@@ -615,17 +627,21 @@ impl DatabaseLayout {
                 page_size,
             )
             .ok_or(Error::OutOfSpace)?;
-            Ok(DatabaseLayout {
+            DatabaseLayout {
                 db_header_bytes: min_header_size,
+                region_allocator_range,
                 full_region_layout,
                 num_full_regions: 0,
                 trailing_partial_region: Some(region_layout),
-            })
+            }
         } else {
             // Multi region layout
             let max_regions = (db_capacity - min_header_size + full_region_layout.len() - 1)
                 / full_region_layout.len();
             let db_header_bytes = DB_HEADER_SIZE + U64GroupedBitMapMut::required_bytes(max_regions);
+            let region_allocator_range = DB_HEADER_SIZE..db_header_bytes;
+            // Pad to be page aligned
+            let db_header_bytes = round_up_to_multiple_of(db_header_bytes, page_size);
             let max_full_regions = (db_capacity - db_header_bytes) / full_region_layout.len();
             let desired_full_regions = desired_usable_bytes / MAX_USABLE_REGION_SPACE;
             let num_full_regions = min(max_full_regions, desired_full_regions);
@@ -633,9 +649,9 @@ impl DatabaseLayout {
                 db_capacity - db_header_bytes - num_full_regions * full_region_layout.len();
             let remaining_desired =
                 desired_usable_bytes - num_full_regions * MAX_USABLE_REGION_SPACE;
-            // TODO: make sure db_header_bytes is page aligned
-            Ok(DatabaseLayout {
+            DatabaseLayout {
                 db_header_bytes,
+                region_allocator_range,
                 full_region_layout,
                 num_full_regions,
                 trailing_partial_region: RegionLayout::calculate(
@@ -643,8 +659,11 @@ impl DatabaseLayout {
                     remaining_desired,
                     page_size,
                 ),
-            })
-        }
+            }
+        };
+
+        assert!(result.db_header_bytes % page_size == 0);
+        Ok(result)
     }
 
     fn num_regions(&self) -> usize {
@@ -670,7 +689,7 @@ impl DatabaseLayout {
     }
 
     fn region_allocator_address_range(&self) -> Range<usize> {
-        DB_HEADER_SIZE..self.db_header_bytes
+        self.region_allocator_range.clone()
     }
 
     fn region_base_address(&self, region: usize) -> usize {
@@ -689,25 +708,32 @@ impl DatabaseLayout {
     }
 
     const fn serialized_size() -> usize {
-        2 * size_of::<u64>() + 2 * RegionLayout::serialized_size() + 1
+        4 * size_of::<u64>() + 2 * RegionLayout::serialized_size() + 1
     }
 
-    // TODO: use le instead of be for everything? ARM is le by default, and x86 is always le
-    fn to_be_bytes(&self) -> [u8; Self::serialized_size()] {
+    fn to_le_bytes(&self) -> [u8; Self::serialized_size()] {
         let mut result = [0; Self::serialized_size()];
         let mut offset = 0;
         result[offset..offset + size_of::<u64>()]
-            .copy_from_slice(&(self.db_header_bytes as u64).to_be_bytes());
+            .copy_from_slice(&(self.db_header_bytes as u64).to_le_bytes());
         offset += size_of::<u64>();
         result[offset..offset + size_of::<u64>()]
-            .copy_from_slice(&(self.num_full_regions as u64).to_be_bytes());
+            .copy_from_slice(&(self.num_full_regions as u64).to_le_bytes());
         offset += size_of::<u64>();
+
+        result[offset..offset + size_of::<u64>()]
+            .copy_from_slice(&(self.region_allocator_range.start as u64).to_le_bytes());
+        offset += size_of::<u64>();
+        result[offset..offset + size_of::<u64>()]
+            .copy_from_slice(&(self.region_allocator_range.end as u64).to_le_bytes());
+        offset += size_of::<u64>();
+
         result[offset..offset + RegionLayout::serialized_size()]
-            .copy_from_slice(&self.full_region_layout.to_be_bytes());
+            .copy_from_slice(&self.full_region_layout.to_le_bytes());
         offset += RegionLayout::serialized_size();
         if let Some(trailing) = self.trailing_partial_region.as_ref() {
             result[offset..offset + RegionLayout::serialized_size()]
-                .copy_from_slice(&trailing.to_be_bytes());
+                .copy_from_slice(&trailing.to_le_bytes());
             offset += RegionLayout::serialized_size();
             result[offset] = 1;
         } else {
@@ -719,17 +745,27 @@ impl DatabaseLayout {
         result
     }
 
-    fn from_be_bytes(data: [u8; Self::serialized_size()]) -> Self {
+    fn from_le_bytes(data: [u8; Self::serialized_size()]) -> Self {
         let mut offset = 0;
         let db_header_bytes =
-            u64::from_be_bytes(data[offset..offset + size_of::<u64>()].try_into().unwrap())
+            u64::from_le_bytes(data[offset..offset + size_of::<u64>()].try_into().unwrap())
                 as usize;
         offset += size_of::<u64>();
         let num_full_regions =
-            u64::from_be_bytes(data[offset..offset + size_of::<u64>()].try_into().unwrap())
+            u64::from_le_bytes(data[offset..offset + size_of::<u64>()].try_into().unwrap())
+                as usize;
+        offset += size_of::<u64>();
+
+        let region_allocator_start =
+            u64::from_le_bytes(data[offset..offset + size_of::<u64>()].try_into().unwrap())
                 as usize;
         offset += size_of::<u64>();
-        let full_region_layout = RegionLayout::from_be_bytes(
+        let region_allocator_end =
+            u64::from_le_bytes(data[offset..offset + size_of::<u64>()].try_into().unwrap())
+                as usize;
+        offset += size_of::<u64>();
+
+        let full_region_layout = RegionLayout::from_le_bytes(
             data[offset..offset + RegionLayout::serialized_size()]
                 .try_into()
                 .unwrap(),
@@ -738,7 +774,7 @@ impl DatabaseLayout {
         let trailing_partial_region = if data[Self::serialized_size() - 1] == 0 {
             None
         } else {
-            Some(RegionLayout::from_be_bytes(
+            Some(RegionLayout::from_le_bytes(
                 data[offset..offset + RegionLayout::serialized_size()]
                     .try_into()
                     .unwrap(),
@@ -747,6 +783,7 @@ impl DatabaseLayout {
 
         Self {
             db_header_bytes,
+            region_allocator_range: region_allocator_start..region_allocator_end,
             full_region_layout,
             num_full_regions,
             trailing_partial_region,
diff --git a/src/tree_store/storage.rs b/src/tree_store/storage.rs
index dd1bc27..eff35ba 100644
--- a/src/tree_store/storage.rs
+++ b/src/tree_store/storage.rs
@@ -6,7 +6,9 @@ use crate::tree_store::btree_utils::{
 };
 use crate::tree_store::page_store::{Page, PageMut, PageNumber, TransactionalMemory};
 use crate::tree_store::{btree_base, AccessGuardMut, BtreeRangeIter};
-use crate::types::{RedbKey, RedbValue, WithLifetime};
+use crate::types::{
+    AsBytesWithLifetime, OwnedAsBytesLifetime, OwnedLifetime, RedbKey, RedbValue, WithLifetime,
+};
 use crate::Error;
 use crate::Result;
 use std::borrow::Borrow;
@@ -20,13 +22,56 @@ use std::panic;
 use std::sync::atomic::{AtomicU64, Ordering};
 use std::sync::Mutex;
 
-// The table of freed pages by transaction. (transaction id, pagination counter) -> binary.
-// The binary blob is a length-prefixed array of big endian PageNumber
+// The table of freed pages by transaction. FreedTableKey -> binary.
+// The binary blob is a length-prefixed array of PageNumber
 pub(crate) const FREED_TABLE: &str = "$$internal$$freed";
 
 pub(crate) type TransactionId = u64;
 type AtomicTransactionId = AtomicU64;
 
+struct FreedTableKey {
+    transaction_id: u64,
+    pagination_id: u64,
+}
+
+impl RedbValue for FreedTableKey {
+    type View = OwnedLifetime<FreedTableKey>;
+    type ToBytes = OwnedAsBytesLifetime<[u8; 2 * size_of::<u64>()]>;
+
+    fn from_bytes(data: &[u8]) -> <Self::View as WithLifetime>::Out {
+        let transaction_id = u64::from_le_bytes(data[..size_of::<u64>()].try_into().unwrap());
+        let pagination_id = u64::from_le_bytes(data[size_of::<u64>()..].try_into().unwrap());
+        Self {
+            transaction_id,
+            pagination_id,
+        }
+    }
+
+    fn as_bytes(&self) -> <Self::ToBytes as AsBytesWithLifetime>::Out {
+        let mut result = [0u8; 2 * size_of::<u64>()];
+        result[..size_of::<u64>()].copy_from_slice(&self.transaction_id.to_le_bytes());
+        result[size_of::<u64>()..].copy_from_slice(&self.pagination_id.to_le_bytes());
+        result
+    }
+
+    fn redb_type_name() -> &'static str {
+        "FreedTableKey"
+    }
+}
+
+impl RedbKey for FreedTableKey {
+    fn compare(data1: &[u8], data2: &[u8]) -> std::cmp::Ordering {
+        let value1 = Self::from_bytes(data1);
+        let value2 = Self::from_bytes(data2);
+
+        match value1.transaction_id.cmp(&value2.transaction_id) {
+            std::cmp::Ordering::Greater => std::cmp::Ordering::Greater,
+            std::cmp::Ordering::Equal => value1.pagination_id.cmp(&value2.pagination_id),
+            std::cmp::Ordering::Less => std::cmp::Ordering::Less,
+        }
+    }
+}
+
 #[derive(Debug)]
 pub struct DatabaseStats {
     pub(crate) tree_height: usize,
@@ -119,9 +164,9 @@ impl TableHeader {
             &self
                 .table_root
                 .unwrap_or_else(PageNumber::null)
-                .to_be_bytes(),
+                .to_le_bytes(),
         );
-        result.extend_from_slice(&(self.key_type.as_bytes().len() as u32).to_be_bytes());
+        result.extend_from_slice(&(self.key_type.as_bytes().len() as u32).to_le_bytes());
         result.extend_from_slice(self.key_type.as_bytes());
         result.extend_from_slice(self.value_type.as_bytes());
 
@@ -131,13 +176,13 @@ impl TableHeader {
     fn from_bytes(value: &[u8]) -> Self {
         debug_assert!(value.len() > 14);
         let table_type = TableType::from(value[0]);
-        let table_root = PageNumber::from_be_bytes(value[1..9].try_into().unwrap());
+        let table_root = PageNumber::from_le_bytes(value[1..9].try_into().unwrap());
         let table_root = if table_root == PageNumber::null() {
             None
         } else {
             Some(table_root)
         };
-        let key_type_len = u32::from_be_bytes(value[9..13].try_into().unwrap()) as usize;
+        let key_type_len = u32::from_le_bytes(value[9..13].try_into().unwrap()) as usize;
         let key_type = std::str::from_utf8(&value[13..(13 + key_type_len)])
             .unwrap()
             .to_string();
@@ -269,7 +314,7 @@ impl Storage {
             let freed_table = TableHeader {
                 table_root: None,
                 table_type: TableType::Normal,
-                key_type: <[u8]>::redb_type_name().to_string(),
+                key_type: FreedTableKey::redb_type_name().to_string(),
                 value_type: <[u8]>::redb_type_name().to_string(),
             };
             let (new_root, _) =
@@ -604,25 +649,27 @@ impl Storage {
             transaction_id,
             self.live_write_transaction.lock().unwrap().unwrap()
         );
-        assert_eq!(PageNumber::null().to_be_bytes().len(), 8); // We assume below that PageNumber is length 8
-        let mut lookup_key = [0u8; size_of::<TransactionId>() + size_of::<u64>()]; // (oldest_live_read, 0)
-        lookup_key[0..size_of::<TransactionId>()].copy_from_slice(&oldest_live_read.to_be_bytes());
-        // second element of pair is already zero
+        // We assume below that PageNumber is length 8
+        assert_eq!(PageNumber::null().to_le_bytes().len(), 8);
+        let lookup_key = FreedTableKey {
+            transaction_id: oldest_live_read,
+            pagination_id: 0,
+        };
 
         let mut to_remove = vec![];
         let mut freed_table = self
-            .get_table::<[u8], [u8]>(FREED_TABLE, TableType::Normal, master_root)?
+            .get_table::<FreedTableKey, [u8]>(FREED_TABLE, TableType::Normal, master_root)?
             .unwrap();
         #[allow(clippy::type_complexity)]
-        let mut iter: BtreeRangeIter<'_, [u8], [u8]> =
-            self.get_range(..lookup_key.as_ref(), freed_table.get_root())?;
+        let mut iter: BtreeRangeIter<'_, FreedTableKey, [u8]> =
+            self.get_range(..lookup_key, freed_table.get_root())?;
         while let Some(entry) = iter.next() {
-            to_remove.push(entry.key().to_vec());
+            to_remove.push(FreedTableKey::from_bytes(entry.key()));
             let value = entry.value();
-            let length = u64::from_be_bytes(value[..size_of::<u64>()].try_into().unwrap()) as usize;
+            let length = u64::from_le_bytes(value[..size_of::<u64>()].try_into().unwrap()) as usize;
             // 1..=length because the array is length prefixed
             for i in 1..=length {
-                let page = PageNumber::from_be_bytes(value[i * 8..(i + 1) * 8].try_into().unwrap());
+                let page = PageNumber::from_le_bytes(value[i * 8..(i + 1) * 8].try_into().unwrap());
                 // Safety: we free only pages that were marked to be freed before the oldest live transaction,
                 // therefore no one can have a reference to this page still
                 unsafe {
@@ -636,7 +683,12 @@ impl Storage {
         for key in to_remove {
             // Safety: all references to the freed table above have already been dropped
             let (new_root, _) = unsafe {
-                self.remove::<[u8], [u8]>(&key, transaction_id, true, freed_table.table_root)?
+                self.remove::<FreedTableKey, [u8]>(
+                    key.as_bytes().as_slice(),
+                    transaction_id,
+                    true,
+                    freed_table.table_root,
+                )?
             };
             freed_table.table_root = new_root;
         }
@@ -662,24 +714,25 @@ impl Storage {
             transaction_id,
             self.live_write_transaction.lock().unwrap().unwrap()
         );
-        assert_eq!(PageNumber::null().to_be_bytes().len(), 8); // We assume below that PageNumber is length 8
+        assert_eq!(PageNumber::null().to_le_bytes().len(), 8); // We assume below that PageNumber is length 8
 
         let mut pagination_counter = 0u64;
         let mut freed_table = self
-            .get_table::<[u8], [u8]>(FREED_TABLE, TableType::Normal, master_root)?
+            .get_table::<FreedTableKey, [u8]>(FREED_TABLE, TableType::Normal, master_root)?
             .unwrap();
         while !self.pending_freed_pages.lock().unwrap().is_empty() {
             let chunk_size = 100;
             let buffer_size = size_of::<u64>() + 8 * chunk_size;
-            let mut key = [0u8; size_of::<TransactionId>() + size_of::<u64>()];
-            key[0..size_of::<TransactionId>()].copy_from_slice(&transaction_id.to_be_bytes());
-            key[size_of::<u64>()..].copy_from_slice(&pagination_counter.to_be_bytes());
+            let key = FreedTableKey {
+                transaction_id,
+                pagination_id: pagination_counter,
+            };
             // Safety: The freed table is only accessed from the writer, so only this function
             // is using it. The only reference retrieved, access_guard, is dropped before the next call
             // to this method
             let (r, mut access_guard) = unsafe {
-                self.insert_reserve::<[u8]>(
-                    key.as_ref(),
+                self.insert_reserve::<FreedTableKey>(
+                    key.as_bytes().as_ref(),
                     buffer_size,
                     transaction_id,
                     freed_table.table_root,
@@ -703,7 +756,7 @@ impl Storage {
 
             let len = self.pending_freed_pages.lock().unwrap().len();
             access_guard.as_mut()[..8]
-                .copy_from_slice(&min(len as u64, chunk_size as u64).to_be_bytes());
+                .copy_from_slice(&min(len as u64, chunk_size as u64).to_le_bytes());
             for (i, page) in self
                 .pending_freed_pages
                 .lock()
@@ -712,7 +765,7 @@ impl Storage {
                 .enumerate()
             {
                 access_guard.as_mut()[(i + 1) * 8..(i + 2) * 8]
-                    .copy_from_slice(&page.to_be_bytes());
+                    .copy_from_slice(&page.to_le_bytes());
             }
             drop(access_guard);
 
diff --git a/src/types.rs b/src/types.rs
index 18d0753..11c11ac 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -113,11 +113,11 @@ macro_rules! be_value {
             type ToBytes = OwnedAsBytesLifetime<[u8; std::mem::size_of::<$t>()]>;
 
             fn from_bytes(data: &[u8]) -> <Self::View as WithLifetime>::Out {
-                <$t>::from_be_bytes(data.try_into().unwrap())
+                <$t>::from_le_bytes(data.try_into().unwrap())
             }
 
             fn as_bytes(&self) -> <Self::ToBytes as AsBytesWithLifetime>::Out {
-                self.to_be_bytes()
+                self.to_le_bytes()
             }
 
             fn redb_type_name() -> &'static str {


Test patch:
diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs
index 652a8d6..42906ce 100644
--- a/tests/integration_tests.rs
+++ b/tests/integration_tests.rs
@@ -171,7 +171,7 @@ fn free() {
         let mut table = txn.open_table(SLICE_TABLE).unwrap();
         for i in 0..num_writes {
             let mut mut_key = key.clone();
-            mut_key.extend_from_slice(&i.to_be_bytes());
+            mut_key.extend_from_slice(&i.to_le_bytes());
             table.insert(&mut_key, &value).unwrap();
         }
     }
@@ -186,7 +186,7 @@ fn free() {
                 let mut table = txn.open_table(SLICE_TABLE).unwrap();
                 for i in chunk {
                     let mut mut_key = key.clone();
-                    mut_key.extend_from_slice(&(*i as u64).to_be_bytes());
+                    mut_key.extend_from_slice(&(*i as u64).to_le_bytes());
                     table.remove(&mut_key).unwrap();
                 }
             }