| 75 | } |
| 76 | |
| 77 | pub fn from_slice(mc: &Mutation<'gc>, s: impl AsRef<[u8]>) -> InternedString<'gc> { |
| 78 | // TODO: This is an extremely silly way to allocate a dynamically sized, inline string. |
| 79 | // Since gc-arena does not support variable sized allocations, we try a set of static |
| 80 | // sizes to inline small strings. All larger strings are instead allocated with an indirect |
| 81 | // buffer. This can be improved when gc-arena learns to allocate variable sizes. |
| 82 | |
| 83 | fn create<'gc, const N: usize>(mc: &Mutation<'gc>, s: &[u8]) -> InternedString<'gc> { |
| 84 | #[derive(Collect)] |
| 85 | #[collect(require_static)] |
| 86 | #[repr(C)] |
| 87 | struct InlineString<const N: usize> { |
| 88 | header: StringInner, |
| 89 | array: [u8; N], |
| 90 | } |
| 91 | |
| 92 | assert!(s.len() <= N); |
| 93 | let mut string = InlineString { |
| 94 | header: StringInner { |
| 95 | hash: str_hash(s), |
| 96 | buffer: Buffer::Inline(s.len()), |
| 97 | }, |
| 98 | array: [0; N], |
| 99 | }; |
| 100 | string.array[0..s.len()].copy_from_slice(s); |
| 101 | |
| 102 | let string = Gc::new(mc, string); |
| 103 | // SAFETY: We know we can cast to `StringInner` because `InlineString` is `#[repr(C)]` |
| 104 | // and `header` is the first field. |
| 105 | unsafe { InternedString(Gc::cast::<StringInner>(string)) } |
| 106 | } |
| 107 | |
| 108 | let s = s.as_ref(); |
| 109 | |
| 110 | macro_rules! try_sizes { |
| 111 | ($($size:expr),*) => { |
| 112 | $(if s.len() <= $size { |
| 113 | return create::<$size>(mc, s); |
| 114 | })* |
| 115 | }; |
| 116 | } |
| 117 | try_sizes!(0, 2, 4, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256); |
| 118 | |
| 119 | Self::from_buffer(mc, s.into()) |
| 120 | } |
| 121 | |
| 122 | pub fn from_static<S: ?Sized + AsRef<[u8]>>( |
| 123 | mc: &Mutation<'gc>, |