Created
June 5, 2017 05:43
-
-
Save mhseiden/254ca2426b90f7572a02a466232e3b8a to your computer and use it in GitHub Desktop.
A basic and incomplete implementation of a compact string.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::{fmt, mem, slice, str}; | |
use std::ops::Deref; | |
const HEAP_STRING_FLAG: u8 = 1u8 << 7; | |
const MAX_INLINE_LENGTH: usize = 15; | |
#[repr(packed)] | |
#[derive(Default)] | |
pub struct CompactString { | |
flag: u8, // 1 byte | |
data: [u8; MAX_INLINE_LENGTH], // 15 bytes | |
} | |
#[repr(packed)] | |
struct CompactHeapString { | |
_flag: u8, // 1 byte | |
len: u32, // 4 bytes | |
ptr: *const u8, // 8 bytes | |
_padding: [u8; 3], // 3 bytes | |
} | |
impl CompactString { | |
fn new(s: &str) -> CompactString { | |
if s.is_empty() { | |
CompactString::default() | |
} else if s.len() <= MAX_INLINE_LENGTH { | |
let mut data = [0u8; MAX_INLINE_LENGTH]; | |
data[0..s.len()].copy_from_slice(s.as_bytes()); | |
CompactString { | |
flag: s.len() as u8, | |
data: data, | |
} | |
} else { | |
let heap_data = s.as_bytes().to_vec(); | |
let len = s.len(); | |
let ptr = heap_data.as_ptr(); | |
mem::forget(heap_data); | |
unsafe { | |
mem::transmute(CompactHeapString { | |
_flag: HEAP_STRING_FLAG, | |
len: len as u32, | |
ptr: ptr, | |
_padding: Default::default(), | |
}) | |
} | |
} | |
} | |
} | |
impl Deref for CompactString { | |
type Target = str; | |
fn deref(&self) -> &Self::Target { | |
if 0 != (self.flag & HEAP_STRING_FLAG) { | |
unsafe { | |
let chs = mem::transmute::<_, &CompactHeapString>(self); | |
let bytes = slice::from_raw_parts(chs.ptr, chs.len as usize); | |
str::from_utf8_unchecked(bytes) | |
} | |
} else { | |
unsafe { | |
let bytes = slice::from_raw_parts(self.data.as_ptr(), self.flag as usize); | |
str::from_utf8_unchecked(bytes) | |
} | |
} | |
} | |
} | |
impl fmt::Display for CompactString { | |
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { | |
write!(f, "{}", self.deref()) | |
} | |
} | |
impl fmt::Debug for CompactString { | |
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { | |
write!(f, "{:?}", self.deref()) | |
} | |
} | |
fn main() { | |
let short_string = "hello world"; | |
let short_compact_string = CompactString::new(short_string); | |
println!("{}", short_compact_string); | |
assert_eq!(short_string, &*short_compact_string); | |
let long_string = "the quick brown fox jumped over the lazy dog"; | |
let long_compact_string = CompactString::new(long_string); | |
println!("{}", long_compact_string); | |
assert_eq!(long_string, &*long_compact_string); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment