about summary refs log tree commit diff
path: root/users/tazjin/german-string
diff options
context:
space:
mode:
authorVincent Ambo <tazjin@tvl.su>2024-08-19T15·39+0300
committerclbot <clbot@tvl.fyi>2024-08-22T14·02+0000
commit012da7d0e19e272450e3a5ecd91f4f8b97a88c27 (patch)
treeef71c88b3e885391d1d78ad1b633c7acc1dd0ad2 /users/tazjin/german-string
parent95ebcc24b0f7ec1ff7113fe7ba127b3401cbd7ab (diff)
feat(tazjin/german-string): add persistent construction from statics r/8550
Makes it possible to construct persistent German Strings from data with 'static
lifetime. This doesn't leak any additional data.

Change-Id: Ifb3a36ff72b106724883302e4c2195ac54acd352
Reviewed-on: https://cl.tvl.fyi/c/depot/+/12263
Tested-by: BuildkiteCI
Reviewed-by: tazjin <tazjin@tvl.su>
Autosubmit: tazjin <tazjin@tvl.su>
Diffstat (limited to 'users/tazjin/german-string')
-rw-r--r--users/tazjin/german-string/src/lib.rs65
1 files changed, 56 insertions, 9 deletions
diff --git a/users/tazjin/german-string/src/lib.rs b/users/tazjin/german-string/src/lib.rs
index d9beb7a099cb..05e43ed103ad 100644
--- a/users/tazjin/german-string/src/lib.rs
+++ b/users/tazjin/german-string/src/lib.rs
@@ -14,16 +14,32 @@ struct GSSmall {
 struct StorageClassPtr(usize);
 
 impl StorageClassPtr {
-    fn transient(ptr: *mut u8) -> Self {
+    fn transient(ptr: *const u8) -> Self {
         debug_assert!(
-            (ptr as usize & 0b11) == 0,
-            "pointer must be at least 4-byte aligned"
+            (ptr as usize & 0b1) == 0,
+            "pointer must be at least 2-byte aligned"
         );
         Self(ptr as usize)
     }
 
-    fn as_ptr(&self) -> *mut u8 {
-        (self.0 & !0b11) as *mut u8
+    fn persistent(ptr: *const u8) -> Self {
+        debug_assert!(
+            (ptr as usize & 0b1) == 0,
+            "pointer must be at least 2-byte aligned"
+        );
+        Self((ptr as usize) | 0b1)
+    }
+
+    fn as_ptr(&self) -> *const u8 {
+        (self.0 & !0b1) as *const u8
+    }
+
+    unsafe fn as_mut_ptr(&self) -> *mut u8 {
+        (self.0 & !0b1) as *mut u8
+    }
+
+    fn is_transient(&self) -> bool {
+        (self.0 & 0b1) == 0
     }
 }
 
@@ -118,6 +134,37 @@ impl GermanString {
         }
     }
 
+    /// Creates a persistent German String from a static data buffer.
+    pub fn persistent(bytes: &'static [u8]) -> GermanString {
+        if bytes.len() > u32::MAX as usize {
+            panic!("GermanString maximum length is {} bytes", u32::MAX);
+        }
+
+        if bytes.len() <= 12 {
+            let mut s = GSSmall {
+                len: bytes.len() as u32,
+                data: [0u8; 12],
+            };
+
+            s.data[..bytes.len()].copy_from_slice(&bytes);
+            GermanString(GSRepr { small: s })
+        } else {
+            let mut large = GSLarge {
+                len: bytes.len() as u32,
+                prefix: [0u8; 4],
+                data: StorageClassPtr::persistent(bytes.as_ptr()),
+            };
+
+            large.prefix.copy_from_slice(&bytes[..4]);
+            GermanString(GSRepr { large })
+        }
+    }
+
+    /// Creates a persistent German String from a static data buffer.
+    pub fn persistent_from_str(s: &'static str) -> GermanString {
+        GermanString::persistent(s.as_bytes())
+    }
+
     pub fn len(&self) -> usize {
         // SAFETY: The length field is located in the same location for both
         // variants, reading it from either is safe.
@@ -139,10 +186,10 @@ impl GermanString {
 
 impl Drop for GermanString {
     fn drop(&mut self) {
-        if self.len() > 12 {
-            let layout = Layout::array::<u8>(self.len()).unwrap();
-            unsafe {
-                std::alloc::dealloc(self.0.large.data.as_ptr(), layout);
+        unsafe {
+            if self.len() > 12 && self.0.large.data.is_transient() {
+                let layout = Layout::array::<u8>(self.len()).unwrap();
+                std::alloc::dealloc(self.0.large.data.as_mut_ptr(), layout);
             }
         }
     }