|
2 | 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. |
4 | 4 |
|
5 | | -use alloc::string::String; |
6 | | -use core::cmp::Ordering; |
| 5 | +use alloc::{alloc::Layout, string::String}; |
| 6 | +use core::{ |
| 7 | + mem::forget, |
| 8 | + ops::{Deref, DerefMut}, |
| 9 | + ptr::NonNull, |
| 10 | +}; |
7 | 11 |
|
8 | | -#[allow(unreachable_pub)] |
9 | | -pub trait BoxedString { |
10 | | - fn string(&self) -> &String; |
11 | | - fn string_mut(&mut self) -> &mut String; |
12 | | - fn into_string(self) -> String; |
| 12 | +use crate::{ops::GenericString, MAX_INLINE}; |
13 | 13 |
|
14 | | - fn cmp_with_str(&self, other: &str) -> Ordering; |
15 | | - fn cmp_with_self(&self, other: &Self) -> Ordering; |
16 | | - fn eq_with_str(&self, other: &str) -> bool; |
17 | | - fn eq_with_self(&self, other: &Self) -> bool; |
| 14 | +#[cfg(not(endian = "big"))] |
| 15 | +#[repr(C)] |
| 16 | +pub(crate) struct BoxedString { |
| 17 | + ptr: NonNull<u8>, |
| 18 | + cap: usize, |
| 19 | + len: usize, |
| 20 | +} |
| 21 | + |
| 22 | +#[cfg(endian = "big")] |
| 23 | +#[repr(C)] |
| 24 | +pub(crate) struct BoxedString { |
| 25 | + length: usize, |
| 26 | + cap: usize, |
| 27 | + ptr: NunNull<u8>, |
| 28 | +} |
18 | 29 |
|
19 | | - fn len(&self) -> usize { |
20 | | - self.string().len() |
| 30 | +impl GenericString for BoxedString { |
| 31 | + fn set_size(&mut self, size: usize) { |
| 32 | + self.len = size; |
| 33 | + debug_assert!(self.len <= self.cap); |
| 34 | + } |
| 35 | + |
| 36 | + fn as_mut_capacity_slice(&mut self) -> &mut [u8] { |
| 37 | + #[allow(unsafe_code)] |
| 38 | + unsafe { |
| 39 | + core::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.capacity()) |
| 40 | + } |
21 | 41 | } |
22 | 42 | } |
23 | 43 |
|
24 | | -impl BoxedString for String { |
25 | | - #[inline(always)] |
26 | | - fn string(&self) -> &String { |
27 | | - self |
| 44 | +impl BoxedString { |
| 45 | + const MINIMAL_CAPACITY: usize = MAX_INLINE * 2; |
| 46 | + |
| 47 | + fn layout_for(cap: usize) -> Layout { |
| 48 | + let layout = Layout::array::<u8>(cap).unwrap(); |
| 49 | + assert!( |
| 50 | + layout.size() <= isize::MAX as usize, |
| 51 | + "allocation too large!" |
| 52 | + ); |
| 53 | + layout |
| 54 | + } |
| 55 | + |
| 56 | + fn alloc(cap: usize) -> NonNull<u8> { |
| 57 | + let layout = Self::layout_for(cap); |
| 58 | + #[allow(unsafe_code)] |
| 59 | + let ptr = unsafe { alloc::alloc::alloc(layout) }; |
| 60 | + match NonNull::new(ptr) { |
| 61 | + Some(ptr) => ptr, |
| 62 | + None => alloc::alloc::handle_alloc_error(layout), |
| 63 | + } |
| 64 | + } |
| 65 | + |
| 66 | + fn realloc(&mut self, cap: usize) { |
| 67 | + let layout = Self::layout_for(cap); |
| 68 | + let old_layout = Self::layout_for(self.cap); |
| 69 | + let old_ptr = self.ptr.as_ptr(); |
| 70 | + #[allow(unsafe_code)] |
| 71 | + let ptr = unsafe { alloc::alloc::realloc(old_ptr, old_layout, layout.size()) }; |
| 72 | + self.ptr = match NonNull::new(ptr) { |
| 73 | + Some(ptr) => ptr, |
| 74 | + None => alloc::alloc::handle_alloc_error(layout), |
| 75 | + }; |
| 76 | + self.cap = cap; |
28 | 77 | } |
29 | 78 |
|
30 | | - #[inline(always)] |
31 | | - fn string_mut(&mut self) -> &mut String { |
32 | | - self |
| 79 | + pub(crate) fn ensure_capacity(&mut self, target_cap: usize) { |
| 80 | + let mut cap = self.cap; |
| 81 | + while cap < target_cap { |
| 82 | + cap *= 2; |
| 83 | + } |
| 84 | + self.realloc(cap) |
33 | 85 | } |
34 | 86 |
|
35 | | - fn into_string(self) -> String { |
36 | | - self |
| 87 | + pub(crate) fn new(cap: usize) -> Self { |
| 88 | + let cap = cap.max(Self::MINIMAL_CAPACITY); |
| 89 | + Self { |
| 90 | + cap, |
| 91 | + len: 0, |
| 92 | + ptr: Self::alloc(cap), |
| 93 | + } |
37 | 94 | } |
38 | 95 |
|
39 | | - #[inline(always)] |
40 | | - fn cmp_with_str(&self, other: &str) -> Ordering { |
41 | | - self.as_str().cmp(other) |
| 96 | + pub(crate) fn from_str(cap: usize, src: &str) -> Self { |
| 97 | + let mut out = Self::new(cap); |
| 98 | + out.len = src.len(); |
| 99 | + out.as_mut_capacity_slice()[..src.len()].copy_from_slice(src.as_bytes()); |
| 100 | + out |
42 | 101 | } |
43 | 102 |
|
44 | | - #[inline(always)] |
45 | | - fn cmp_with_self(&self, other: &Self) -> Ordering { |
46 | | - self.cmp(other) |
| 103 | + pub(crate) fn capacity(&self) -> usize { |
| 104 | + self.cap |
47 | 105 | } |
48 | 106 |
|
49 | | - #[inline(always)] |
50 | | - fn eq_with_str(&self, other: &str) -> bool { |
51 | | - self == other |
| 107 | + pub(crate) fn shrink_to_fit(&mut self) { |
| 108 | + self.realloc(self.len); |
52 | 109 | } |
| 110 | +} |
| 111 | + |
| 112 | +impl Drop for BoxedString { |
| 113 | + fn drop(&mut self) { |
| 114 | + #[allow(unsafe_code)] |
| 115 | + unsafe { |
| 116 | + alloc::alloc::dealloc(self.ptr.as_ptr(), Self::layout_for(self.cap)) |
| 117 | + } |
| 118 | + } |
| 119 | +} |
| 120 | + |
| 121 | +impl Clone for BoxedString { |
| 122 | + fn clone(&self) -> Self { |
| 123 | + Self::from_str(self.capacity(), self.deref()) |
| 124 | + } |
| 125 | +} |
| 126 | + |
| 127 | +impl Deref for BoxedString { |
| 128 | + type Target = str; |
| 129 | + |
| 130 | + fn deref(&self) -> &Self::Target { |
| 131 | + #[allow(unsafe_code)] |
| 132 | + unsafe { |
| 133 | + core::str::from_utf8_unchecked(core::slice::from_raw_parts(self.ptr.as_ptr(), self.len)) |
| 134 | + } |
| 135 | + } |
| 136 | +} |
| 137 | + |
| 138 | +impl DerefMut for BoxedString { |
| 139 | + fn deref_mut(&mut self) -> &mut Self::Target { |
| 140 | + #[allow(unsafe_code)] |
| 141 | + unsafe { |
| 142 | + core::str::from_utf8_unchecked_mut(core::slice::from_raw_parts_mut( |
| 143 | + self.ptr.as_ptr(), |
| 144 | + self.len, |
| 145 | + )) |
| 146 | + } |
| 147 | + } |
| 148 | +} |
| 149 | + |
| 150 | +impl From<String> for BoxedString { |
| 151 | + fn from(mut s: String) -> Self { |
| 152 | + if s.is_empty() { |
| 153 | + Self::new(s.capacity()) |
| 154 | + } else { |
| 155 | + // TODO: Use String::into_raw_parts when stabilised, meanwhile let's get unsafe |
| 156 | + let len = s.len(); |
| 157 | + let cap = s.capacity(); |
| 158 | + #[allow(unsafe_code)] |
| 159 | + let ptr = unsafe { NonNull::new_unchecked(s.as_mut_ptr()) }; |
| 160 | + forget(s); |
| 161 | + Self { cap, len, ptr } |
| 162 | + } |
| 163 | + } |
| 164 | +} |
53 | 165 |
|
54 | | - #[inline(always)] |
55 | | - fn eq_with_self(&self, other: &Self) -> bool { |
56 | | - self == other |
| 166 | +impl From<BoxedString> for String { |
| 167 | + fn from(s: BoxedString) -> Self { |
| 168 | + #[allow(unsafe_code)] |
| 169 | + let out = unsafe { String::from_raw_parts(s.ptr.as_ptr(), s.len(), s.capacity()) }; |
| 170 | + forget(s); |
| 171 | + out |
57 | 172 | } |
58 | 173 | } |
0 commit comments