Skip to main content

any_intern/
dropless.rs

1use super::common::{Interned, RawInterned, UnsafeLock};
2use bumpalo::Bump;
3use hashbrown::{HashTable, hash_table::Entry};
4use std::{
5    alloc::Layout,
6    fmt::{self, Display, Write},
7    hash::{BuildHasher, Hash},
8    mem::MaybeUninit,
9    ptr::{self, NonNull},
10    slice,
11};
12
13/// A trait for types that can be stored in a dropless interner.
14///
15/// The `Dropless` trait provides methods for working with types that can be stored in a
16/// [`DroplessInterner`] or [`DroplessInternSet`]. It defines how instances of a type are converted
17/// to and from raw byte representations, hashed, and compared for equality. This is useful for
18/// interning values without requiring ownership.
19///
20/// # Safety
21///
22/// Implementing the `Dropless` trait requires careful attention to alignment and memory safety. The
23/// methods in this trait are marked as `unsafe` because they rely on the caller to ensure that the
24/// provided byte slices are properly aligned and valid for the type. Misuse of these methods can
25/// lead to undefined behavior.
26///
27/// # Examples
28///
29/// ```
30/// use any_intern::Dropless;
31/// use std::alloc::Layout;
32/// use std::ptr::NonNull;
33///
34/// #[derive(PartialEq, Eq, Hash, Debug)]
35/// struct MyType(u32);
36///
37/// impl Dropless for MyType {
38///     fn as_byte_ptr(&self) -> NonNull<u8> {
39///         NonNull::from_ref(self).cast::<u8>()
40///     }
41///
42///     fn layout(&self) -> Layout {
43///         Layout::for_value(self)
44///     }
45///
46///     unsafe fn from_bytes(bytes: &[u8]) -> &Self {
47///         let ptr = bytes.as_ptr().cast::<Self>();
48///         unsafe { ptr.as_ref().unwrap_unchecked() }
49///     }
50///
51///     unsafe fn hash<S: std::hash::BuildHasher>(build_hasher: &S, bytes: &[u8]) -> u64 {
52///         let this = unsafe { Self::from_bytes(bytes) };
53///         build_hasher.hash_one(this)
54///     }
55///
56///     unsafe fn eq(a: &[u8], b: &[u8]) -> bool {
57///         unsafe { Self::from_bytes(a) == Self::from_bytes(b) }
58///     }
59/// }
60/// ```
61pub trait Dropless {
62    /// Returns pointer to the instance.
63    fn as_byte_ptr(&self) -> NonNull<u8> {
64        NonNull::from_ref(self).cast::<u8>()
65    }
66
67    /// Returns layout of the type.
68    fn layout(&self) -> Layout {
69        Layout::for_value(self)
70    }
71
72    /// Converts a byte slice into a reference to the type.
73    ///
74    /// The byte slice is guaranteed to be well aligned and correct data for the type.
75    ///
76    /// # Safety
77    ///
78    /// Undefined behavior if any conditions below are not met.
79    /// * Implementation should interpret the byte slice into the type correctly.
80    /// * Caller should give well aligned data for the type.
81    unsafe fn from_bytes(bytes: &[u8]) -> &Self;
82
83    /// Computes a hash value for the type using the provided byte slice.
84    ///
85    /// The byte slice is guaranteed to be well aligned and correct data for the type.
86    ///
87    /// # Safety
88    ///
89    /// Undefined behavior if any conditions below are not met.
90    /// * Implementation should interpret the byte slice into the type correctly.
91    /// * Caller should give well aligned data for the type.
92    unsafe fn hash<S: BuildHasher>(build_hasher: &S, bytes: &[u8]) -> u64;
93
94    /// Compares two byte slices for equality as instances of the type.
95    ///
96    /// The byte slices are guaranteed to be well aligned and correct data for the type.
97    ///
98    /// # Safety
99    ///
100    /// Undefined behavior if any conditions below are not met.
101    /// * Implementation should interpret the byte slice into the type correctly.
102    /// * Caller should give well aligned data for the type.
103    unsafe fn eq(a: &[u8], b: &[u8]) -> bool;
104}
105
106macro_rules! simple_impl_dropless_fn {
107    (from_bytes) => {
108        unsafe fn from_bytes(bytes: &[u8]) -> &Self {
109            let ptr = bytes.as_ptr().cast::<Self>();
110            unsafe { ptr.as_ref().unwrap_unchecked() }
111        }
112    };
113    (hash) => {
114        unsafe fn hash<S: BuildHasher>(build_hasher: &S, bytes: &[u8]) -> u64 {
115            let this = unsafe { Self::from_bytes(bytes) };
116            build_hasher.hash_one(this)
117        }
118    };
119    (eq) => {
120        unsafe fn eq(a: &[u8], b: &[u8]) -> bool {
121            unsafe { Self::from_bytes(a) == Self::from_bytes(b) }
122        }
123    };
124}
125
126macro_rules! impl_dropless {
127    ($($ty:ty)*) => {
128        $(
129            impl Dropless for $ty {
130                simple_impl_dropless_fn!(from_bytes);
131                simple_impl_dropless_fn!(hash);
132                simple_impl_dropless_fn!(eq);
133            }
134        )*
135    };
136}
137
138impl_dropless!(i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize bool char);
139
140impl<T: Dropless + Hash + Eq> Dropless for [T] {
141    unsafe fn from_bytes(bytes: &[u8]) -> &Self {
142        let ptr = bytes.as_ptr().cast::<T>();
143        let stride = Layout::new::<T>().pad_to_align().size();
144        let num_elems = bytes.len() / stride;
145        unsafe { slice::from_raw_parts(ptr, num_elems) }
146    }
147    simple_impl_dropless_fn!(hash);
148    simple_impl_dropless_fn!(eq);
149}
150
151impl<T: Dropless + Hash + Eq, const N: usize> Dropless for [T; N] {
152    simple_impl_dropless_fn!(from_bytes);
153    simple_impl_dropless_fn!(hash);
154    simple_impl_dropless_fn!(eq);
155}
156
157impl Dropless for str {
158    unsafe fn from_bytes(bytes: &[u8]) -> &Self {
159        unsafe { str::from_utf8_unchecked(bytes) }
160    }
161    simple_impl_dropless_fn!(hash);
162    simple_impl_dropless_fn!(eq);
163}
164
165impl<T: Dropless + Hash + Eq> Dropless for Option<T> {
166    simple_impl_dropless_fn!(from_bytes);
167    simple_impl_dropless_fn!(hash);
168    simple_impl_dropless_fn!(eq);
169}
170
171impl<T: Dropless + Hash + Eq, E: Dropless + Hash + Eq> Dropless for Result<T, E> {
172    simple_impl_dropless_fn!(from_bytes);
173    simple_impl_dropless_fn!(hash);
174    simple_impl_dropless_fn!(eq);
175}
176
177/// An interner for storing and deduplicating values without requiring ownership.
178///
179/// The `DroplessInterner` is designed for interning values that implement the [`Dropless`] trait.
180/// It allows efficient storage and retrieval of values by ensuring that each unique value is stored
181/// only once. Interning with this type always copies the given value into an internal buffer,
182/// making it suitable for use cases where ownership is not required.
183///
184/// # Examples
185///
186/// ```
187/// use any_intern::DroplessInterner;
188///
189/// let mut interner = DroplessInterner::new();
190///
191/// // Interning strings
192/// let hello = interner.intern("hello");
193/// let world = interner.intern("world");
194/// let another_hello = interner.intern("hello");
195///
196/// assert_eq!(hello, another_hello); // Same value, same reference
197/// assert_ne!(hello, world); // Different values, different references
198///
199/// // Checking if a value exists
200/// assert!(interner.get("hello").is_some());
201/// assert!(interner.get("unknown").is_none());
202///
203/// // Clearing the interner
204/// interner.clear();
205/// assert!(interner.is_empty());
206/// ```
207///
208/// # Safety
209///
210/// The `DroplessInterner` relies on the `Dropless` trait for converting values to and from raw
211/// byte representations. It is the responsibility of the `Dropless` implementation to ensure
212/// memory safety and alignment when interacting with the interner.
213#[derive(Debug)]
214pub struct DroplessInterner<S = fxhash::FxBuildHasher> {
215    inner: UnsafeLock<DroplessInternSet<S>>,
216}
217
218impl DroplessInterner {
219    pub fn new() -> Self {
220        Self::default()
221    }
222}
223
224impl<S: BuildHasher> DroplessInterner<S> {
225    pub fn with_hasher(hash_builder: S) -> Self {
226        // Safety: Only one instance
227        let inner = unsafe { UnsafeLock::new(DroplessInternSet::with_hasher(hash_builder)) };
228        Self { inner }
229    }
230
231    /// Returns number of values the interner contains.
232    pub fn len(&self) -> usize {
233        self.with_inner(|set| set.len())
234    }
235
236    /// Returns true if the interner is empty.
237    pub fn is_empty(&self) -> bool {
238        self.with_inner(|set| set.is_empty())
239    }
240
241    /// Stores a value in the interner, returning a reference to the interned value.
242    ///
243    /// This method inserts the given value into the interner if it does not already exist.  If the
244    /// value already exists, a reference to the existing value is returned. The value is copied
245    /// into an internal buffer, making it suitable for use cases where ownership is not required.
246    ///
247    /// # Examples
248    ///
249    /// ```
250    /// use any_intern::DroplessInterner;
251    ///
252    /// let interner = DroplessInterner::new();
253    ///
254    /// // Interning strings
255    /// let hello = interner.intern("hello");
256    /// let world = interner.intern("world");
257    /// let another_hello = interner.intern("hello");
258    ///
259    /// assert_eq!(hello, another_hello); // Same value, same reference
260    /// assert_ne!(hello, world); // Different values, different references
261    ///
262    /// // Interning arrays
263    /// let array1 = interner.intern(&[1, 2, 3]);
264    /// let array2 = interner.intern(&[1, 2, 3]);
265    /// let array3 = interner.intern(&[4, 5, 6]);
266    ///
267    /// assert_eq!(array1, array2); // Same value, same reference
268    /// assert_ne!(array1, array3); // Different values, different references
269    /// ```
270    pub fn intern<K: Dropless + ?Sized>(&self, value: &K) -> Interned<'_, K> {
271        self.with_inner(|set| set.intern(value))
272    }
273
274    /// Stores a value in the interner as a formatted string through [`Display`], returning a
275    /// reference to the interned value.
276    ///
277    /// This method provides a buffer for making string. This will be benefit in terms of
278    /// performance when you frequently make `String` via something like `to_string()` by exploiting
279    /// chunk memory.
280    ///
281    /// This method first formats the given value using the `Display` trait and stores the resulting
282    /// string in the interner's buffer, then compares the string with existing values. If the
283    /// formatted string already exists in the interner, formatted string is discarded and reference
284    /// to the existing value is returned.
285    ///
286    /// If you give insufficient `upper_size`, then error is returned.
287    ///
288    /// # Examples
289    ///
290    /// ```
291    /// use any_intern::DroplessInterner;
292    ///
293    /// let interner = DroplessInterner::new();
294    ///
295    /// let value = 42;
296    /// let interned = interner.intern_formatted_str(&value, 10).unwrap();
297    ///
298    /// assert_eq!(&*interned, "42");
299    /// ```
300    pub fn intern_formatted_str<K: Display + ?Sized>(
301        &self,
302        value: &K,
303        upper_size: usize,
304    ) -> Result<Interned<'_, str>, fmt::Error> {
305        self.with_inner(|set| set.intern_formatted_str(value, upper_size))
306    }
307
308    /// Retrieves a reference to a value in the interner based on the provided key.
309    ///
310    /// This method checks if a value corresponding to the given key exists in the interner. If it
311    /// exists, a reference to the interned value is returned. Otherwise, `None` is returned.
312    ///
313    /// # Eaxmples
314    ///
315    /// ```
316    /// use any_intern::DroplessInterner;
317    ///
318    /// let interner = DroplessInterner::new();
319    ///
320    /// // Interning strings
321    /// let hello = interner.intern("hello");
322    ///
323    /// assert_eq!(interner.get("hello").as_deref(), Some("hello"));
324    /// assert!(interner.get("world").is_none());
325    /// ```
326    pub fn get<K: Dropless + ?Sized>(&self, value: &K) -> Option<Interned<'_, K>> {
327        self.with_inner(|set| set.get(value))
328    }
329
330    /// Removes all items in the interner.
331    ///
332    /// Although the interner support interior mutability, clear method requires mutable access
333    /// to the interner to invalidate all [`Interned`]s referencing the interner.
334    pub fn clear(&mut self) {
335        self.with_inner(|set| set.clear())
336    }
337
338    fn with_inner<'this, F, R>(&'this self, f: F) -> R
339    where
340        F: FnOnce(&'this mut DroplessInternSet<S>) -> R,
341        R: 'this,
342    {
343        // Safety: Mutex unlocking is paired with the locking.
344        unsafe {
345            let set = self.inner.lock().as_mut();
346            let ret = f(set);
347            self.inner.unlock();
348            ret
349        }
350    }
351}
352
353impl<S: Default> Default for DroplessInterner<S> {
354    fn default() -> Self {
355        // Safety: Only one instance
356        let inner = unsafe { UnsafeLock::new(DroplessInternSet::default()) };
357        Self { inner }
358    }
359}
360
361/// A dropless interner.
362///
363/// Interning on this type always copies the given value into internal buffer.
364#[derive(Debug, Default)]
365pub struct DroplessInternSet<S = fxhash::FxBuildHasher> {
366    bump: Bump,
367    str_buf: StringBuffer,
368    set: HashTable<DynInternEntry<S>>,
369    hash_builder: S,
370}
371
372impl DroplessInternSet {
373    pub fn new() -> Self {
374        Self::default()
375    }
376}
377
378impl<S: BuildHasher> DroplessInternSet<S> {
379    pub fn with_hasher(hash_builder: S) -> Self {
380        Self {
381            bump: Bump::new(),
382            str_buf: StringBuffer::default(),
383            set: HashTable::new(),
384            hash_builder,
385        }
386    }
387
388    pub fn intern<K: Dropless + ?Sized>(&mut self, value: &K) -> Interned<'_, K> {
389        let src = value.as_byte_ptr();
390        let layout = value.layout();
391
392        unsafe {
393            // Allocation for zero sized data should be avoided even if Bump allocator allows it.
394            // Plus, we change its address to make it static for equality test.
395            if layout.size() == 0 {
396                let ptr = value as *const K;
397                let ptr = ptr.with_addr(layout.align()); // Like ptr::dangling()
398                return Interned::unique(&*ptr);
399            }
400
401            let bytes = slice::from_raw_parts(src.as_ptr(), layout.size());
402            let hash = <K as Dropless>::hash(&self.hash_builder, bytes);
403            let eq = Self::table_eq::<K>(bytes, layout);
404            let hasher = Self::table_hasher(&self.hash_builder);
405            let ref_ = match self.set.entry(hash, eq, hasher) {
406                Entry::Occupied(entry) => Self::ref_from_entry(entry.get()),
407                Entry::Vacant(entry) => {
408                    // New allocation & copy
409                    let dst = self.bump.alloc_layout(layout);
410                    ptr::copy_nonoverlapping(src.as_ptr(), dst.as_ptr(), layout.size());
411
412                    // New set entry
413                    let occupied = entry.insert(DynInternEntry {
414                        data: RawInterned(dst).cast(),
415                        layout,
416                        hash: <K as Dropless>::hash,
417                    });
418
419                    Self::ref_from_entry(occupied.get())
420                }
421            };
422            Interned::unique(ref_)
423        }
424    }
425
426    pub fn intern_formatted_str<K: Display + ?Sized>(
427        &mut self,
428        value: &K,
429        upper_size: usize,
430    ) -> Result<Interned<'_, str>, fmt::Error> {
431        let mut write_buf = self.str_buf.speculative_alloc(upper_size);
432        write!(write_buf, "{value}")?;
433        let bytes = write_buf.as_bytes();
434
435        unsafe {
436            // Safety
437            // We wrote it down to the buffer through Display trait, so it is definitely UTF-8.
438            // ref: https://doc.rust-lang.org/std/fmt/index.html#fmtdisplay-vs-fmtdebug
439            let value = str::from_utf8_unchecked(bytes);
440            let layout = Layout::from_size_align_unchecked(bytes.len(), 1);
441
442            // We change address to make it static for equality test.
443            if bytes.is_empty() {
444                let ptr = value as *const str;
445                let ptr = ptr.with_addr(layout.align()); // Like ptr::dangling()
446                return Ok(Interned::unique(&*ptr));
447            }
448
449            let hash = <str as Dropless>::hash(&self.hash_builder, bytes);
450            let eq = Self::table_eq::<str>(bytes, layout);
451            let hasher = Self::table_hasher(&self.hash_builder);
452            let ref_ = match self.set.entry(hash, eq, hasher) {
453                Entry::Occupied(entry) => {
454                    // Discards the change to the string buffer by just dropping the `write_buf`
455                    // because we have the same value in the bump alloator.
456                    // drop(write_buf);
457
458                    Self::ref_from_entry(entry.get())
459                }
460                Entry::Vacant(entry) => {
461                    // We keep the change to the string buffer because we're going to return the
462                    // reference to the string buffer.
463                    let bytes = write_buf.commit();
464
465                    // Safety: Zero size was filtered out above.
466                    let ptr = NonNull::new_unchecked(bytes.as_ptr().cast_mut());
467
468                    // New set entry
469                    let occupied = entry.insert(DynInternEntry {
470                        data: RawInterned(ptr).cast(),
471                        layout,
472                        hash: <str as Dropless>::hash,
473                    });
474
475                    Self::ref_from_entry(occupied.get())
476                }
477            };
478            Ok(Interned::unique(ref_))
479        }
480    }
481
482    /// # Eaxmples
483    ///
484    /// ```
485    /// use any_intern::DroplessInternSet;
486    /// use std::hash::RandomState;
487    ///
488    /// let mut set = DroplessInternSet::with_hasher(RandomState::new());
489    /// set.intern("hello");
490    ///
491    /// assert_eq!(set.get("hello").as_deref(), Some("hello"));
492    /// assert!(set.get("hi").is_none());
493    /// ```
494    pub fn get<K: Dropless + ?Sized>(&self, value: &K) -> Option<Interned<'_, K>> {
495        let ptr = value.as_byte_ptr();
496        let layout = value.layout();
497        unsafe {
498            let bytes = slice::from_raw_parts(ptr.as_ptr(), layout.size());
499            let hash = <K as Dropless>::hash(&self.hash_builder, bytes);
500            let eq = Self::table_eq::<K>(bytes, layout);
501            let entry = self.set.find(hash, eq)?;
502            let ref_ = Self::ref_from_entry(entry);
503            Some(Interned::unique(ref_))
504        }
505    }
506
507    pub fn len(&self) -> usize {
508        self.set.len()
509    }
510
511    pub fn is_empty(&self) -> bool {
512        self.len() == 0
513    }
514
515    pub fn clear(&mut self) {
516        self.bump.reset();
517        self.set.clear();
518    }
519
520    /// Returns `eq` closure that is used for some methods on the [`HashTable`].
521    #[inline]
522    fn table_eq<K: Dropless + ?Sized>(
523        key: &[u8],
524        layout: Layout,
525    ) -> impl FnMut(&DynInternEntry<S>) -> bool {
526        move |entry: &DynInternEntry<S>| unsafe {
527            if layout == entry.layout {
528                let entry_bytes =
529                    slice::from_raw_parts(entry.data.cast::<u8>().as_ptr(), entry.layout.size());
530                <K as Dropless>::eq(key, entry_bytes)
531            } else {
532                false
533            }
534        }
535    }
536
537    /// Returns `hasher` closure that is used for some methods on the [`HashTable`].
538    #[inline]
539    fn table_hasher(hash_builder: &S) -> impl Fn(&DynInternEntry<S>) -> u64 {
540        |entry: &DynInternEntry<S>| unsafe {
541            let entry_bytes =
542                slice::from_raw_parts(entry.data.cast::<u8>().as_ptr(), entry.layout.size());
543            // Safety: `entry_bytes` is aligned for the entry hash function.
544            (entry.hash)(hash_builder, entry_bytes)
545        }
546    }
547
548    #[inline]
549    unsafe fn ref_from_entry<'a, K: Dropless + ?Sized>(entry: &DynInternEntry<S>) -> &'a K {
550        unsafe {
551            let bytes =
552                slice::from_raw_parts(entry.data.cast::<u8>().as_ptr(), entry.layout.size());
553            K::from_bytes(bytes)
554        }
555    }
556}
557
558/// A buffer for strings.
559#[derive(Debug, Default)]
560struct StringBuffer {
561    chunks: Vec<Box<[MaybeUninit<u8>]>>,
562    last_chunk_start: usize,
563}
564
565const INIT_CHUNK_SIZE: usize = 1 << 5;
566const GLOW_MAX_CHUNK_SIZE: usize = 1 << 12;
567
568impl StringBuffer {
569    fn speculative_alloc(&mut self, upper_size: usize) -> StringWriteBuffer<'_> {
570        match self.chunks.last() {
571            None => {
572                let chunk_size = INIT_CHUNK_SIZE.max(upper_size.next_power_of_two());
573                self.append_new_chunk(chunk_size)
574            }
575            Some(last_chunk) if last_chunk.len() - self.last_chunk_start < upper_size => {
576                let chunk_size = (last_chunk.len() * 2)
577                    .min(GLOW_MAX_CHUNK_SIZE)
578                    .max(upper_size.next_power_of_two());
579                self.append_new_chunk(chunk_size);
580            }
581            _ => {}
582        }
583
584        // Safety: We added the last chunk above.
585        let last_chunk = unsafe { self.chunks.last_mut().unwrap_unchecked() };
586
587        let start = self.last_chunk_start;
588        let end = self.last_chunk_start + upper_size;
589        let buf = &mut last_chunk[start..end];
590        StringWriteBuffer {
591            buf,
592            last_chuck_start: &mut self.last_chunk_start,
593            written: 0,
594        }
595    }
596
597    #[inline]
598    fn append_new_chunk(&mut self, chunk_size: usize) {
599        let chunk = Box::new_uninit_slice(chunk_size);
600        self.chunks.push(chunk);
601        self.last_chunk_start = 0;
602    }
603}
604
605struct StringWriteBuffer<'a> {
606    buf: &'a mut [MaybeUninit<u8>],
607    last_chuck_start: &'a mut usize,
608    written: usize,
609}
610
611impl<'a> StringWriteBuffer<'a> {
612    #[inline]
613    fn as_bytes(&self) -> &[u8] {
614        let ptr = self.buf.as_ptr().cast::<u8>();
615        unsafe { slice::from_raw_parts(ptr, self.written) }
616    }
617
618    #[inline]
619    fn commit(self) -> &'a [u8] {
620        *self.last_chuck_start += self.written;
621
622        let ptr = self.buf.as_ptr().cast::<u8>();
623        unsafe { slice::from_raw_parts(ptr, self.written) }
624    }
625}
626
627impl Write for StringWriteBuffer<'_> {
628    fn write_str(&mut self, s: &str) -> std::fmt::Result {
629        let size = s.len();
630
631        if self.buf.len() - self.written >= size {
632            let src = s.as_ptr();
633
634            // Safety: `written` cannot be over the buf size by the if condition
635            let buf = unsafe { self.buf.get_unchecked_mut(self.written..) };
636            let dst = buf.as_mut_ptr().cast::<u8>();
637
638            // Safety
639            // * `src` is valid for reading of `size` bytes
640            // * `dst` is valid for reading of `size` bytes
641            // * Two pointers are well aligned. Both alignments are 1
642            // * `src` and `dst` are not overlapping
643            unsafe { ptr::copy_nonoverlapping(src, dst, size) };
644
645            self.written += size;
646            Ok(())
647        } else {
648            Err(std::fmt::Error)
649        }
650    }
651}
652
653#[derive(Debug)]
654struct DynInternEntry<S> {
655    data: RawInterned,
656    layout: Layout,
657    /// Input bytes must be well aligned.
658    hash: unsafe fn(&S, &[u8]) -> u64,
659}
660
661#[cfg(test)]
662mod tests {
663    use super::*;
664    use crate::common;
665    use std::num::NonZeroU32;
666
667    #[test]
668    fn test_dropless_interner() {
669        test_dropless_interner_int();
670        test_dropless_interner_str();
671        test_dropless_interner_bytes();
672        test_dropless_interner_mixed();
673        test_dropless_interner_many();
674        test_dropless_interner_alignment_handling();
675        test_dropless_interner_complex_display_type();
676    }
677
678    fn test_dropless_interner_int() {
679        let interner = DroplessInterner::new();
680
681        let a = interner.intern(&0_u32).erased_raw();
682        let b = interner.intern(&0_u32).erased_raw();
683        let c = interner.intern(&1_u32).erased_raw();
684        // d has the same bytes and layout as c's, so memory will be shared.
685        let d = interner.intern(&1_i32).erased_raw();
686
687        let groups: [&[RawInterned]; _] = [&[a, b], &[c, d]];
688        common::assert_group_addr_eq(&groups);
689    }
690
691    fn test_dropless_interner_str() {
692        let interner = DroplessInterner::new();
693
694        // "apple"
695        let a = interner.intern("apple").erased_raw();
696        let b = interner.intern(*Box::new("apple")).erased_raw();
697        let c = interner.intern(&*String::from("apple")).erased_raw();
698
699        // "banana"
700        let d = interner.intern("banana").erased_raw();
701
702        // ""
703        let e = interner.intern("").erased_raw();
704        let f = interner.intern(&*String::from("")).erased_raw();
705
706        // "42"
707        let g = interner.intern("42").erased_raw();
708        let h = interner.intern_formatted_str(&42, 2).unwrap().erased_raw();
709
710        // "43"
711        let i = interner
712            .intern_formatted_str(&NonZeroU32::new(43).unwrap(), 2)
713            .unwrap()
714            .erased_raw();
715        let j = interner.intern(&*43.to_string()).erased_raw();
716
717        let groups: [&[RawInterned]; _] = [&[a, b, c], &[d], &[e, f], &[g, h], &[i, j]];
718        common::assert_group_addr_eq(&groups);
719    }
720
721    fn test_dropless_interner_bytes() {
722        let interner = DroplessInterner::new();
723
724        let a = interner.intern(&[0, 1]).erased_raw();
725        let boxed: Box<[i32]> = Box::new([0, 1]);
726        let b = interner.intern(&*boxed).erased_raw();
727        let c = interner.intern(&*vec![0, 1]).erased_raw();
728        let d = interner.intern(&[2, 3]).erased_raw();
729
730        let groups: [&[RawInterned]; _] = [&[a, b, c], &[d]];
731        common::assert_group_addr_eq(&groups);
732    }
733
734    fn test_dropless_interner_mixed() {
735        let interner = DroplessInterner::new();
736
737        interner.intern("apple");
738        interner.intern(&1_u32);
739        interner.intern(&[2, 3]);
740        interner.intern_formatted_str(&42, 10).unwrap();
741
742        assert_eq!(interner.get("apple").as_deref(), Some("apple"));
743        assert!(interner.get("banana").is_none());
744
745        assert_eq!(interner.get(&1_u32).as_deref(), Some(&1_u32));
746        assert!(interner.get(&2_u32).is_none());
747
748        assert_eq!(interner.get(&[2, 3]).as_deref(), Some(&[2, 3]));
749        assert!(interner.get(&[2]).is_none());
750
751        assert_eq!(interner.get("42").as_deref(), Some("42"));
752    }
753
754    fn test_dropless_interner_many() {
755        let interner = DroplessInterner::new();
756
757        let mut interned_int = Vec::new();
758        let mut interned_str = Vec::new();
759        let mut interned_bytes = Vec::new();
760
761        #[cfg(not(miri))]
762        const N: usize = 1000;
763        #[cfg(miri)]
764        const N: usize = 50;
765
766        let strs = (0..N).map(|i| (i * 10_000).to_string()).collect::<Vec<_>>();
767
768        // Interns lots of items.
769        for i in 0..N {
770            let int = &(i as u32); // 4 bytes
771            let str_ = &*strs[i]; // greater than 4 btyes
772            let bytes = &[i as u16]; // 2 bytes
773            interned_int.push(interner.intern(int).erased_raw());
774            interned_str.push(interner.intern(str_).erased_raw());
775            interned_bytes.push(interner.intern(bytes).erased_raw());
776        }
777
778        // Verifies every pointer is unique.
779        interned_int.sort_unstable();
780        interned_int.dedup();
781        interned_str.sort_unstable();
782        interned_str.dedup();
783        interned_bytes.sort_unstable();
784        interned_bytes.dedup();
785        assert_eq!(interned_int.len(), N);
786        assert_eq!(interned_str.len(), N);
787        assert_eq!(interned_bytes.len(), N);
788        let whole = interned_int
789            .iter()
790            .chain(&interned_str)
791            .chain(&interned_bytes)
792            .cloned()
793            .collect::<fxhash::FxHashSet<_>>();
794        assert_eq!(whole.len(), N * 3);
795
796        // Verifies `get` method for every item.
797        for i in 0..N {
798            let int = &(i as u32); // 4 bytes
799            let str_ = &*strs[i]; // greater than 4 btyes
800            let bytes = &[i as u16]; // 2 bytes
801            assert_eq!(interner.get(int).as_deref(), Some(int));
802            assert_eq!(interner.get(str_).as_deref(), Some(str_));
803            assert_eq!(interner.get(bytes).as_deref(), Some(bytes));
804        }
805    }
806
807    #[rustfmt::skip]
808    fn test_dropless_interner_alignment_handling() {
809        #[derive(PartialEq, Eq, Hash, Debug)] #[repr(C, align(4))]  struct T4  (u16, [u8; 2]);
810        #[derive(PartialEq, Eq, Hash, Debug)] #[repr(C, align(8))]  struct T8  (u16, [u8; 6]);
811        #[derive(PartialEq, Eq, Hash, Debug)] #[repr(C, align(16))] struct T16 (u16, [u8; 14]);
812
813        macro_rules! impl_for_first_2bytes {
814            () => {
815                unsafe fn hash<S: BuildHasher>(hash_builder: &S, bytes: &[u8]) -> u64 {
816                    hash_builder.hash_one(&bytes[0..2])
817                }
818                unsafe fn eq(a: &[u8], b: &[u8]) -> bool {
819                    a[0..2] == b[0..2]
820                }
821            };
822        }
823
824        impl Dropless for T4 { simple_impl_dropless_fn!(from_bytes); impl_for_first_2bytes!(); }
825        impl Dropless for T8 { simple_impl_dropless_fn!(from_bytes); impl_for_first_2bytes!(); }
826        impl Dropless for T16 { simple_impl_dropless_fn!(from_bytes); impl_for_first_2bytes!(); }
827
828        let interner = DroplessInterner::new();
829
830        let mut interned_4 = Vec::new();
831        let mut interned_8 = Vec::new();
832        let mut interned_16 = Vec::new();
833
834        #[cfg(not(miri))]
835        const N: usize = 1000;
836        #[cfg(miri)]
837        const N: usize = 50;
838
839        // Items being interned have the same first 2 bytes, so they will look like the smae from
840        // perspective of interner. But, they must be distinguished from each other due to their
841        // different layouts. Following bytes after the 2 bytes will be used to detect data
842        // validity.
843        for i in 0..N {
844            let t4 = T4(i as u16, [4; _]);
845            let t8 = T8(i as u16, [8; _]);
846            let t16 = T16(i as u16, [16; _]);
847            interned_4.push(interner.intern(&t4).erased_raw());
848            interned_8.push(interner.intern(&t8).erased_raw());
849            interned_16.push(interner.intern(&t16).erased_raw());
850        }
851
852        for i in 0..N {
853            let t4 = T4(i as u16, [4; _]);
854            let t8 = T8(i as u16, [8; _]);
855            let t16 = T16(i as u16, [16; _]);
856            unsafe {
857                assert_eq!(*<Interned<'_, T4>>::from_erased_raw(interned_4[i]), t4);
858                assert_eq!(*<Interned<'_, T8>>::from_erased_raw(interned_8[i]), t8);
859                assert_eq!(*<Interned<'_, T16>>::from_erased_raw(interned_16[i]), t16);
860            }
861        }
862    }
863
864    fn test_dropless_interner_complex_display_type() {
865        let interner = DroplessInterner::new();
866
867        #[allow(unused)]
868        #[derive(Debug)]
869        struct A<'a> {
870            int: i32,
871            float: f32,
872            text: &'a str,
873            bytes: &'a [u8],
874        }
875
876        impl Display for A<'_> {
877            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
878                fmt::Debug::fmt(self, f)
879            }
880        }
881
882        let a = A {
883            int: 123,
884            float: 456.789,
885            text: "this is a text",
886            bytes: &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0],
887        };
888
889        let interned = interner.intern_formatted_str(&a, 1_000).unwrap();
890
891        let mut s = String::new();
892        write!(&mut s, "{a}").unwrap();
893        assert_eq!(&*interned, s.as_str());
894    }
895
896    #[test]
897    fn test_string_buffer() {
898        test_string_buffer_chunk();
899        test_string_buffer_discard();
900        test_string_buffer_insufficient_upper_size();
901        test_string_buffer_long_string();
902    }
903
904    fn test_string_buffer_chunk() {
905        let mut buf = StringBuffer::default();
906        assert_eq!(buf.chunks.len(), 0);
907
908        // Fills the first chunk.
909        let s = "a".repeat(INIT_CHUNK_SIZE);
910        let mut write_buf = buf.speculative_alloc(s.len());
911        write_buf.write_str(&s).unwrap();
912        assert_eq!(write_buf.as_bytes(), s.as_bytes());
913        write_buf.commit();
914
915        assert_eq!(buf.chunks.len(), 1);
916        assert_eq!(buf.last_chunk_start, s.len());
917
918        // Makes another chunk.
919        let mut write_buf = buf.speculative_alloc(1);
920        write_buf.write_str("a").unwrap();
921        assert_eq!(write_buf.as_bytes(), b"a");
922        write_buf.commit();
923
924        assert_eq!(buf.chunks.len(), 2);
925        assert_eq!(buf.last_chunk_start, 1);
926
927        // Forces to make another chunk
928        let mut write_buf = buf.speculative_alloc(GLOW_MAX_CHUNK_SIZE);
929        write_buf.write_str("aa").unwrap();
930        assert_eq!(write_buf.as_bytes(), b"aa");
931        write_buf.commit();
932
933        assert_eq!(buf.chunks.len(), 3);
934        assert_eq!(buf.last_chunk_start, 2);
935    }
936
937    fn test_string_buffer_discard() {
938        let mut buf = StringBuffer::default();
939
940        // Write & discard makes no change.
941        for _ in 0..10 {
942            let s = "a".repeat(INIT_CHUNK_SIZE);
943            let mut write_buf = buf.speculative_alloc(s.len());
944            write_buf.write_str(&s).unwrap();
945            assert_eq!(write_buf.as_bytes(), s.as_bytes());
946            drop(write_buf);
947
948            assert_eq!(buf.chunks.len(), 1);
949            assert_eq!(buf.last_chunk_start, 0);
950        }
951    }
952
953    fn test_string_buffer_insufficient_upper_size() {
954        let mut buf = StringBuffer::default();
955
956        for _ in 0..10 {
957            let mut write_buf = buf.speculative_alloc(5);
958            let res = write_buf.write_str("this is longer than 5");
959            assert!(res.is_err());
960            write_buf.commit();
961
962            assert_eq!(buf.chunks.len(), 1);
963            assert_eq!(buf.last_chunk_start, 0);
964        }
965    }
966
967    fn test_string_buffer_long_string() {
968        let mut buf = StringBuffer::default();
969
970        let s = "a".repeat(GLOW_MAX_CHUNK_SIZE * 10);
971        let mut write_buf = buf.speculative_alloc(s.len());
972        write_buf.write_str(&s).unwrap();
973        assert_eq!(write_buf.as_bytes(), s.as_bytes());
974        write_buf.commit();
975
976        assert_eq!(buf.chunks.len(), 1);
977        assert_eq!(buf.last_chunk_start, s.len());
978    }
979}