frozen_hashbrown/
frozen.rs1use core::{alloc::Layout, ptr::NonNull};
2use std::fmt::Debug;
3
4pub const RANDOM_STATE_TYPE_NAME: &str = "std::collections::hash::map::RandomState";
5pub const GLOBAL_ALLOC_TYPE_NAME: &str = "alloc::alloc::Global";
6
7#[derive(Clone)]
8pub struct FrozenHashMap<S = RandomState> {
9 pub table_layout: TableLayout,
10 pub hashmap: HashMap<S>,
11 pub memory: Vec<u8>,
12}
13
14impl<S: Debug> Debug for FrozenHashMap<S> {
15 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16 f.debug_struct("FrozenHashMap")
17 .field("table_layout", &self.table_layout)
18 .field("hashmap", &self.hashmap)
19 .field(
20 "memory",
21 &format!("<binary data of size {}>", self.memory.len()),
22 )
23 .finish()
24 }
25}
26
27#[derive(Debug, Clone)]
28pub struct HashMap<S = RandomState> {
29 pub hash_builder: S,
30 pub table: RawTable,
31}
32
33#[derive(Debug, Clone)]
34pub struct RandomState {
35 pub k0: u64,
36 pub k1: u64,
37}
38
39#[derive(Debug, Clone)]
40pub struct RawTable {
41 pub table: RawTableInner,
42}
43
44#[derive(Debug, Clone)]
45pub struct RawTableInner {
46 pub bucket_mask: usize,
47 pub ctrl: NonNull<u8>,
48 pub growth_left: usize,
49 pub items: usize,
50}
51
52#[derive(Debug, Copy, Clone)]
53pub struct TableLayout {
54 pub size: usize,
55 pub ctrl_align: usize,
56}
57
58impl TableLayout {
59 pub fn new(layout: Layout) -> Self {
60 Self {
61 size: layout.size(),
62 ctrl_align: if layout.align() > crate::Group::WIDTH {
63 layout.align()
64 } else {
65 crate::Group::WIDTH
66 },
67 }
68 }
69
70 pub fn calculate_layout_for(&self, buckets: usize) -> Option<(Layout, usize)> {
71 assert!(buckets.is_power_of_two());
72
73 let TableLayout { size, ctrl_align } = *self;
74 let ctrl_offset =
76 size.checked_mul(buckets)?.checked_add(ctrl_align - 1)? & !(ctrl_align - 1);
77 let len = ctrl_offset.checked_add(buckets + crate::Group::WIDTH)?;
78
79 Some((
80 unsafe { Layout::from_size_align_unchecked(len, ctrl_align) },
81 ctrl_offset,
82 ))
83 }
84}
85
86impl RawTableInner {
87 pub fn allocation(&self, table_layout: &TableLayout) -> Option<(*const u8, Layout)> {
88 if self.is_empty_singleton() {
89 None
90 } else {
91 let (layout, ctrl_offset) = table_layout.calculate_layout_for(self.buckets())?;
92 Some((unsafe { self.ctrl.as_ptr().sub(ctrl_offset) }, layout))
93 }
94 }
95
96 pub fn reallocation(&self, table_layout: &TableLayout) -> Option<(usize, Layout)> {
97 if self.is_empty_singleton() {
98 None
99 } else {
100 let (layout, ctrl_offset) = table_layout.calculate_layout_for(self.buckets())?;
101 Some((ctrl_offset, layout))
102 }
103 }
104
105 fn buckets(&self) -> usize {
106 self.bucket_mask + 1
107 }
108
109 fn is_empty_singleton(&self) -> bool {
110 self.bucket_mask == 0
111 }
112}
113
114impl FrozenHashMap<RandomState> {
115 pub fn construct<K, V>(hashmap: &std::collections::HashMap<K, V>) -> Self {
116 Self::construct_with(
117 unsafe {
118 core::slice::from_raw_parts(
119 std::mem::transmute(hashmap as *const _),
120 std::mem::size_of::<std::collections::HashMap<K, V>>(),
121 )
122 },
123 TableLayout::new(Layout::new::<(K, V)>()),
124 )
125 }
126
127 pub fn construct_with(hashmap: &[u8], table_layout: TableLayout) -> Self {
128 assert_eq!(std::mem::size_of::<HashMap<RandomState>>(), hashmap.len());
129 let hashmap: HashMap<RandomState> =
130 unsafe { std::ptr::read_unaligned(hashmap.as_ptr() as *const _) };
131 let memory = if let Some((location, layout)) = hashmap.table.table.allocation(&table_layout)
132 {
133 let location: &[u8] =
134 unsafe { core::slice::from_raw_parts(location as *const u8, layout.size()) };
135 location.to_vec()
136 } else {
137 vec![]
138 };
139 Self {
140 table_layout,
141 hashmap,
142 memory,
143 }
144 }
145
146 pub fn reconstruct<K, V>(&mut self) -> Option<&std::collections::HashMap<K, V>> {
147 assert_eq!(
148 std::mem::size_of::<HashMap<RandomState>>(),
149 std::mem::size_of::<std::collections::HashMap<K, V>>()
150 );
151 if self.memory.is_empty() {
152 return None;
153 }
154 if let Some((offset, layout)) = self.hashmap.table.table.reallocation(&self.table_layout) {
155 assert_eq!(layout.size(), self.memory.len());
156 let address = self.memory.as_ptr() as usize + offset;
157 if address == 0 {
158 return None;
159 }
160 self.hashmap.table.table.ctrl = unsafe { NonNull::new_unchecked(address as *mut u8) };
161 unsafe {
162 Some(std::mem::transmute(&self.hashmap))
164 }
165 } else {
166 None
167 }
168 }
169
170 pub fn store(&self) -> Vec<u8> {
171 let mut bytes = Vec::new();
172 bytes.extend_from_slice(unsafe {
173 core::slice::from_raw_parts(
174 std::mem::transmute(&self.table_layout as *const _),
175 std::mem::size_of::<TableLayout>(),
176 )
177 });
178 bytes.extend_from_slice(unsafe {
179 core::slice::from_raw_parts(
180 std::mem::transmute(&self.hashmap as *const _),
181 std::mem::size_of::<HashMap<RandomState>>(),
182 )
183 });
184 bytes.extend_from_slice(&self.memory.len().to_ne_bytes());
185 bytes.extend_from_slice(&self.memory);
186 bytes
187 }
188
189 pub fn load(bytes: &[u8]) -> Option<Self> {
191 let mut cursor = 0;
192 let chunk = std::mem::size_of::<TableLayout>();
193 if cursor + chunk > bytes.len() {
194 return None;
195 }
196 let table_layout: TableLayout =
197 unsafe { std::ptr::read_unaligned(bytes.as_ptr() as *const _) };
198 cursor += chunk;
199 let chunk = std::mem::size_of::<HashMap<RandomState>>();
200 if cursor + chunk > bytes.len() {
201 return None;
202 }
203 let hashmap: HashMap<RandomState> =
204 unsafe { std::ptr::read_unaligned(bytes.as_ptr().add(cursor) as *const _) };
205 cursor += chunk;
206 let chunk = 8;
207 if cursor + chunk > bytes.len() {
208 return None;
209 }
210 let ll = [
211 bytes[cursor],
212 bytes[cursor + 1],
213 bytes[cursor + 2],
214 bytes[cursor + 3],
215 bytes[cursor + 4],
216 bytes[cursor + 5],
217 bytes[cursor + 6],
218 bytes[cursor + 7],
219 ];
220 cursor += chunk;
221 let length = usize::from_ne_bytes(ll);
222 if cursor + length != bytes.len() {
223 return None;
224 }
225 let memory = bytes[cursor..].to_vec();
226 Some(Self {
227 table_layout,
228 hashmap,
229 memory,
230 })
231 }
232
233 pub fn len(&self) -> usize {
234 self.hashmap.len()
235 }
236}
237
238impl<S> HashMap<S> {
239 pub fn len(&self) -> usize {
240 self.table.table.items
241 }
242}