use crate::arena;
use std::fmt;
pub struct SeqString {
ptr: *const u8,
len: usize,
capacity: usize, global: bool,
}
impl PartialEq for SeqString {
fn eq(&self, other: &Self) -> bool {
self.as_str() == other.as_str()
}
}
impl Eq for SeqString {}
unsafe impl Send for SeqString {}
unsafe impl Sync for SeqString {}
impl SeqString {
pub fn as_str(&self) -> &str {
unsafe { std::str::from_utf8_unchecked(std::slice::from_raw_parts(self.ptr, self.len)) }
}
#[allow(dead_code)]
pub fn is_global(&self) -> bool {
self.global
}
pub fn len(&self) -> usize {
self.len
}
#[allow(dead_code)]
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn is_interned(&self) -> bool {
self.capacity == 0 && self.global
}
pub fn as_ptr(&self) -> *const u8 {
self.ptr
}
pub fn into_raw_parts(self) -> (*const u8, usize, usize, bool) {
let parts = (self.ptr, self.len, self.capacity, self.global);
std::mem::forget(self); parts
}
pub unsafe fn from_raw_parts(
ptr: *const u8,
len: usize,
capacity: usize,
global: bool,
) -> Self {
SeqString {
ptr,
len,
capacity,
global,
}
}
}
impl Clone for SeqString {
fn clone(&self) -> Self {
let s = self.as_str().to_string();
global_string(s)
}
}
impl Drop for SeqString {
fn drop(&mut self) {
if self.global && self.capacity > 0 {
unsafe {
let _s = String::from_raw_parts(
self.ptr as *mut u8,
self.len,
self.capacity, );
}
}
}
}
impl fmt::Debug for SeqString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SeqString({:?}, global={})", self.as_str(), self.global)
}
}
impl fmt::Display for SeqString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
pub fn arena_string(s: &str) -> SeqString {
arena::with_arena(|arena| {
let arena_str = arena.alloc_str(s);
SeqString {
ptr: arena_str.as_ptr(),
len: arena_str.len(),
capacity: 0, global: false,
}
})
}
pub fn global_string(s: String) -> SeqString {
let len = s.len();
let capacity = s.capacity();
let ptr = s.as_ptr();
std::mem::forget(s);
SeqString {
ptr,
len,
capacity, global: true,
}
}
impl From<&str> for SeqString {
fn from(s: &str) -> Self {
arena_string(s)
}
}
impl From<String> for SeqString {
fn from(s: String) -> Self {
global_string(s)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arena_string() {
let s = arena_string("Hello, arena!");
assert_eq!(s.as_str(), "Hello, arena!");
assert_eq!(s.len(), 13);
assert!(!s.is_global());
}
#[test]
fn test_global_string() {
let s = global_string("Hello, global!".to_string());
assert_eq!(s.as_str(), "Hello, global!");
assert_eq!(s.len(), 14);
assert!(s.is_global());
}
#[test]
fn test_clone_creates_global() {
let s1 = arena_string("test");
let s2 = s1.clone();
assert_eq!(s1.as_str(), s2.as_str());
assert!(!s1.is_global());
assert!(s2.is_global()); }
#[test]
fn test_clone_global() {
let s1 = global_string("test".to_string());
let s2 = s1.clone();
assert_eq!(s1.as_str(), s2.as_str());
assert!(s1.is_global());
assert!(s2.is_global());
}
#[test]
fn test_drop_global() {
{
let s = global_string("Will be dropped".to_string());
assert_eq!(s.as_str(), "Will be dropped");
}
}
#[test]
fn test_drop_arena() {
{
let s = arena_string("Will be dropped (no-op)");
assert_eq!(s.as_str(), "Will be dropped (no-op)");
}
}
#[test]
fn test_equality() {
let s1 = arena_string("test");
let s2 = arena_string("test");
let s3 = global_string("test".to_string());
let s4 = arena_string("different");
assert_eq!(s1, s2); assert_eq!(s1, s3); assert_ne!(s1, s4); }
#[test]
fn test_from_str() {
let s: SeqString = "test".into();
assert_eq!(s.as_str(), "test");
assert!(!s.is_global()); }
#[test]
fn test_from_string() {
let s: SeqString = "test".to_string().into();
assert_eq!(s.as_str(), "test");
assert!(s.is_global()); }
#[test]
fn test_debug_format() {
let s = arena_string("debug");
let debug_str = format!("{:?}", s);
assert!(debug_str.contains("debug"));
assert!(debug_str.contains("global=false"));
}
#[test]
fn test_display_format() {
let s = global_string("display".to_string());
let display_str = format!("{}", s);
assert_eq!(display_str, "display");
}
#[test]
fn test_empty_string() {
let s = arena_string("");
assert_eq!(s.len(), 0);
assert!(s.is_empty());
assert_eq!(s.as_str(), "");
}
#[test]
fn test_unicode() {
let s = arena_string("Hello, δΈη! π¦");
assert_eq!(s.as_str(), "Hello, δΈη! π¦");
assert!(s.len() > 10); }
#[test]
fn test_global_string_preserves_capacity() {
let mut s = String::with_capacity(100);
s.push_str("hi");
assert_eq!(s.len(), 2);
assert_eq!(s.capacity(), 100);
let cem = global_string(s);
assert_eq!(cem.len(), 2);
assert_eq!(cem.capacity, 100); assert_eq!(cem.as_str(), "hi");
assert!(cem.is_global());
drop(cem);
}
#[test]
fn test_arena_string_capacity_zero() {
let s = arena_string("test");
assert_eq!(s.capacity, 0); assert!(!s.is_global());
}
}