pub const MAX_LENGTH: usize = 255;
#[repr(packed)]
pub struct SmallString {
pub len: u8,
pub str: [u8; MAX_LENGTH],
}
pub struct SmallStr {
len: u8,
_str: [u8],
}
impl std::hash::Hash for SmallStr {
fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
self.as_bytes().hash(hasher)
}
}
impl Clone for SmallString {
fn clone(&self) -> Self {
Self::from_str(self.as_str())
}
}
impl std::fmt::Debug for SmallString {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
use std::ops::Deref;
self.deref().fmt(fmt)
}
}
impl PartialEq for SmallStr {
fn eq(&self, x: &SmallStr) -> bool {
self.as_str().eq(x.as_str())
}
}
impl std::ops::Deref for SmallString {
type Target = SmallStr;
fn deref(&self) -> &Self::Target {
let len = self.len as usize;
unsafe {
std::mem::transmute(std::slice::from_raw_parts(
self as *const Self as *const u8,
1 + len,
))
}
}
}
impl AsRef<SmallStr> for SmallString {
fn as_ref(&self) -> &SmallStr {
let len = self.len as usize;
unsafe {
std::mem::transmute(std::slice::from_raw_parts(
self as *const Self as *const u8,
1 + len,
))
}
}
}
impl AsMut<SmallStr> for SmallString {
fn as_mut(&mut self) -> &mut SmallStr {
let len = self.len as usize;
unsafe {
std::mem::transmute(std::slice::from_raw_parts_mut(
self as *mut Self as *mut u8,
1 + len,
))
}
}
}
impl std::ops::DerefMut for SmallString {
fn deref_mut(&mut self) -> &mut Self::Target {
let len = self.len as usize;
unsafe {
std::mem::transmute(std::slice::from_raw_parts_mut(
self as *mut Self as *mut u8,
1 + len,
))
}
}
}
#[test]
fn eq() {
let s0 = SmallString::from_str("blabla");
let s1 = SmallString::from_str("blabla");
assert_eq!(s0, s1);
assert_eq!(s0, s1);
assert_eq!(s0, s1);
assert_eq!(s0, s0);
assert_eq!(s1, s1);
}
#[test]
fn debug() {
let s = SmallString::from_str("blabla");
assert_eq!(format!("{:?}", s), "\"blabla\"");
}
impl Eq for SmallStr {}
impl PartialEq for SmallString {
fn eq(&self, x: &SmallString) -> bool {
self.as_str().eq(x.as_str())
}
}
impl Eq for SmallString {}
impl std::hash::Hash for SmallString {
fn hash<H: std::hash::Hasher>(&self, x: &mut H) {
self.as_str().hash(x)
}
}
impl PartialOrd for SmallStr {
fn partial_cmp(&self, x: &SmallStr) -> Option<std::cmp::Ordering> {
self.as_str().partial_cmp(x.as_str())
}
}
impl Ord for SmallStr {
fn cmp(&self, x: &SmallStr) -> std::cmp::Ordering {
self.as_str().cmp(x.as_str())
}
}
impl PartialOrd for SmallString {
fn partial_cmp(&self, x: &SmallString) -> Option<std::cmp::Ordering> {
self.as_str().partial_cmp(x.as_str())
}
}
impl Ord for SmallString {
fn cmp(&self, x: &SmallString) -> std::cmp::Ordering {
self.as_str().cmp(x.as_str())
}
}
#[test]
fn ord() {
let s0 = SmallString::from_str("1234");
let s1 = SmallString::from_str("5678");
assert!(s0 < s1);
assert!(s0 < s1);
assert_eq!(s0.cmp(&s1), std::cmp::Ordering::Less);
}
impl std::fmt::Debug for SmallStr {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
self.as_str().fmt(fmt)
}
}
impl Default for SmallString {
fn default() -> Self {
Self {
len: 0,
str: [0; MAX_LENGTH],
}
}
}
impl SmallString {
pub fn new() -> Self {
Self::default()
}
pub fn len(&self) -> usize {
self.len as usize
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn from_str(s: &str) -> Self {
let mut b = SmallString {
len: s.len() as u8,
str: [0; MAX_LENGTH],
};
b.clone_from_str(s);
b
}
pub fn clone_from_str(&mut self, s: &str) {
self.len = s.len() as u8;
(&mut self.str[..s.len()]).copy_from_slice(s.as_bytes());
}
pub fn clear(&mut self) {
self.len = 0;
}
pub fn push_str(&mut self, s: &str) {
let l = self.len as usize;
assert!(l + s.len() <= 0xff);
(&mut self.str[l..l + s.len()]).copy_from_slice(s.as_bytes());
self.len += s.len() as u8;
}
pub fn as_str(&self) -> &str {
use std::ops::Deref;
self.deref().as_str()
}
pub fn as_bytes(&self) -> &[u8] {
use std::ops::Deref;
self.deref().as_bytes()
}
}
impl SmallStr {
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn len(&self) -> usize {
self.len as usize
}
pub fn as_str(&self) -> &str {
unsafe { std::str::from_utf8_unchecked(self.as_bytes()) }
}
pub fn as_bytes(&self) -> &[u8] {
let s: &[u8] = unsafe { std::mem::transmute(self) };
&s[1..]
}
pub fn to_owned(&self) -> SmallString {
SmallString::from_str(self.as_str())
}
}
#[test]
fn all_doc_tests() {
{
let s = SmallString::from_str("blah!");
assert_eq!(s.len(), s.as_str().len());
}
{
let mut s = SmallString::from_str("blah");
s.clear();
assert_eq!(s.as_str(), "");
assert!(s.is_empty());
}
{
let mut s = SmallString::from_str("blah");
s.clear();
assert!(s.is_empty());
}
{
let mut s = SmallString::from_str("");
assert!(s.is_empty());
s.push_str("blah");
assert!(!s.is_empty());
}
{
let s = SmallString::from_str("blah");
assert_eq!(s.len(), "blah".len())
}
}
impl sanakirja::UnsizedStorable for SmallStr {
const ALIGN: usize = 1;
fn size(&self) -> usize {
1 + self.len as usize
}
unsafe fn write_to_page(&self, p: *mut u8) {
std::ptr::copy(&self.len, p, 1 + self.len as usize);
debug!(
"writing {:?}",
std::slice::from_raw_parts(p, 1 + self.len as usize)
);
}
unsafe fn from_raw_ptr<'a, T>(_: &T, p: *const u8) -> &'a Self {
smallstr_from_raw_ptr(p)
}
unsafe fn onpage_size(p: *const u8) -> usize {
let len = *p as usize;
debug!(
"onpage_size {:?}",
std::slice::from_raw_parts(p, 1 + len as usize)
);
1 + len
}
}
impl sanakirja::Storable for SmallStr {
fn compare<T>(&self, _: &T, x: &Self) -> std::cmp::Ordering {
self.cmp(x)
}
type PageReferences = std::iter::Empty<u64>;
fn page_references(&self) -> Self::PageReferences {
std::iter::empty()
}
}
impl ::sanakirja::debug::Check for SmallStr {}
unsafe fn smallstr_from_raw_ptr<'a>(p: *const u8) -> &'a SmallStr {
let len = *p as usize;
std::mem::transmute(std::slice::from_raw_parts(p, 1 + len as usize))
}
#[test]
fn smallstr_repr() {
use sanakirja::UnsizedStorable;
let o = SmallString::from_str("blablabla");
let mut x = vec![0u8; 200];
unsafe {
o.write_to_page(x.as_mut_ptr());
let p = smallstr_from_raw_ptr(x.as_ptr());
assert_eq!(p.as_str(), "blablabla")
}
}