istring-0.3.3/.cargo_vcs_info.json0000644000000001360000000000100125160ustar { "git": { "sha1": "7213e69a85351818c2aec8215c9dd66e4656a048" }, "path_in_vcs": "" }istring-0.3.3/.gitignore000064400000000000000000000000371046102023000132760ustar 00000000000000/target/ **/*.rs.bk Cargo.lock istring-0.3.3/.travis.yml000064400000000000000000000000411046102023000134120ustar 00000000000000language: rust rust: - nightly istring-0.3.3/Cargo.toml0000644000000020650000000000100105170ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "istring" version = "0.3.3" authors = ["Sebastian Köln "] description = "A replacement for String that allows storing short strings of length up to sizeof() - 1 without a heap allocation" homepage = "https://github.com/s3bk/istring" keywords = [ "string", "inline", "no_std", ] categories = [] license = "MIT" repository = "https://github.com/s3bk/istring" [dependencies.datasize] version = "0.2.13" optional = true [dependencies.serde] version = "1.0" optional = true [features] serialize = ["serde"] size = ["datasize"] std = [] istring-0.3.3/Cargo.toml.orig000064400000000000000000000011231046102023000141720ustar 00000000000000[package] name = "istring" version = "0.3.3" edition = "2021" authors = ["Sebastian Köln "] description = "A replacement for String that allows storing short strings of length up to sizeof() - 1 without a heap allocation" categories = [] license = "MIT" homepage = "https://github.com/s3bk/istring" repository = "https://github.com/s3bk/istring" keywords = ["string", "inline", "no_std"] [features] size = ["datasize"] serialize = ["serde"] std = [] [dependencies.datasize] version = "0.2.13" optional = true [dependencies.serde] version = "1.0" optional = true istring-0.3.3/LICENSE000064400000000000000000000020571046102023000123170ustar 00000000000000Copyright 2017 Sebastian Köln Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. istring-0.3.3/src/common.rs000064400000000000000000000233251046102023000137400ustar 00000000000000macro_rules! define_common_bytes { ($name:ident, $union:ident) => { impl $name { /// view as Inline. /// /// Panics if the string isn't inlined #[inline(always)] pub unsafe fn as_inline(&mut self) -> &mut Inline { debug_assert!(self.is_inline()); &mut self.union.inline } /// view as Heap. /// /// Panics if the string isn't on the Heap #[inline(always)] pub unsafe fn as_heap(&mut self) -> &mut Heap { debug_assert!(!self.is_inline()); &mut self.union.heap } //#[inline] //pub fn as_inline_or_heap(self) #[inline(always)] pub fn is_inline(&self) -> bool { unsafe { (self.union.inline.len & IS_INLINE) != 0 } } #[inline(always)] pub fn len(&self) -> usize { unsafe { if self.is_inline() { (self.union.inline.len & LEN_MASK) as usize } else { self.union.heap.len } } } #[inline(always)] pub fn as_mut_ptr(&mut self) -> *mut u8 { unsafe { if self.is_inline() { &mut self.union.inline.data as *mut u8 } else { self.union.heap.ptr } } } #[inline(always)] pub fn as_slice(&self) -> &[u8] { let len = self.len(); unsafe { if self.is_inline() { &self.union.inline.data[.. len] } else { slice::from_raw_parts(self.union.heap.ptr, len) } } } #[inline(always)] pub fn as_mut_slice(&mut self) -> &mut [u8] { unsafe { let len = self.len(); if self.is_inline() { &mut self.union.inline.data[.. len] } else { slice::from_raw_parts_mut(self.union.heap.ptr, len) } } } /// Deconstruct into the Inline part and the allocator /// /// Assumes the string is inlined and panics otherwhise. #[inline(always)] pub fn to_inline(self) -> Inline { assert_eq!(self.is_inline(), true); unsafe { let mut inline = self.union.inline; mem::forget(self); inline.len &= !IS_INLINE; // clear the bit inline } } pub unsafe fn from_heap(heap: Heap) -> Self { let union = $union { heap: heap }; assert_eq!(union.inline.len & IS_INLINE, 0); $name { union: union } } pub unsafe fn from_inline(mut inline: Inline) -> Self { assert!(inline.len as usize <= INLINE_CAPACITY); inline.len |= IS_INLINE; // set inline bit $name { union: $union { inline: inline }, } } /// Deconstruct into the Heap part and the allocator /// /// Assumes it is heap-state, panics otherwhise. (you may want to call move_to_heap before this.) /// The caller is responsible to adequatly dispose the owned memory. (for example by calling $name::from_heap) #[inline(always)] pub fn to_heap(self) -> Heap { assert_eq!(self.is_inline(), false); unsafe { let heap = self.union.heap; mem::forget(self); heap } } } impl ops::Deref for $name { type Target = [u8]; #[inline(always)] fn deref(&self) -> &[u8] { self.as_slice() } } impl ops::DerefMut for $name { #[inline(always)] fn deref_mut(&mut self) -> &mut [u8] { self.as_mut_slice() } } impl fmt::Debug for $name { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { <[u8] as fmt::Debug>::fmt(&*self, f) } } impl PartialEq<[u8]> for $name { #[inline(always)] fn eq(&self, rhs: &[u8]) -> bool { self.as_slice() == rhs } } impl PartialEq for $name { fn eq(&self, rhs: &Self) -> bool { self.as_slice().eq(rhs.as_slice()) } } impl Eq for $name {} impl core::hash::Hash for $name { fn hash(&self, state: &mut H) { self.as_slice().hash(state); } } impl cmp::PartialOrd for $name { #[inline(always)] fn partial_cmp(&self, rhs: &Self) -> Option { self.as_slice().partial_cmp(rhs.as_slice()) } #[inline(always)] fn lt(&self, rhs: &Self) -> bool { self.as_slice().lt(rhs.as_slice()) } #[inline(always)] fn le(&self, rhs: &Self) -> bool { self.as_slice().le(rhs.as_slice()) } #[inline(always)] fn gt(&self, rhs: &Self) -> bool { self.as_slice().gt(rhs.as_slice()) } #[inline(always)] fn ge(&self, rhs: &Self) -> bool { self.as_slice().ge(rhs.as_slice()) } } impl cmp::Ord for $name { #[inline(always)] fn cmp(&self, other: &$name) -> cmp::Ordering { self.as_slice().cmp(other.as_slice()) } } impl ops::Index> for $name { type Output = [u8]; #[inline] fn index(&self, index: ops::Range) -> &[u8] { &self[..][index] } } impl ops::Index> for $name { type Output = [u8]; #[inline] fn index(&self, index: ops::RangeTo) -> &[u8] { &self[..][index] } } impl ops::Index> for $name { type Output = [u8]; #[inline] fn index(&self, index: ops::RangeFrom) -> &[u8] { &self[..][index] } } impl ops::Index for $name { type Output = [u8]; #[inline] fn index(&self, _index: ops::RangeFull) -> &[u8] { self.as_slice() } } impl ops::Index> for $name { type Output = [u8]; #[inline] fn index(&self, index: ops::RangeInclusive) -> &[u8] { Index::index(&**self, index) } } impl ops::Index> for $name { type Output = [u8]; #[inline] fn index(&self, index: ops::RangeToInclusive) -> &[u8] { Index::index(&**self, index) } } impl Borrow<[u8]> for $name { fn borrow(&self) -> &[u8] { self.as_slice() } } } } macro_rules! define_common_string { ($name:ident, $union:ident) => { impl $name { #[inline(always)] pub fn as_str(&self) -> &str { unsafe { str::from_utf8_unchecked(self.bytes.as_slice()) } } #[inline(always)] pub fn as_mut_str(&mut self) -> &mut str { unsafe { str::from_utf8_unchecked_mut(self.bytes.as_mut_slice()) } } } impl $name { #[inline(always)] pub fn into_bytes(self) -> Vec { let s: String = self.into(); s.into_bytes() } } impl<'a> Into for &'a $name { #[inline(always)] fn into(self) -> String { String::from(self.as_str()) } } impl ops::Deref for $name { type Target = str; #[inline(always)] fn deref(&self) -> &str { self.as_str() } } impl fmt::Debug for $name { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { ::fmt(&*self, f) } } impl fmt::Display for $name { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { ::fmt(&*self, f) } } impl PartialEq for $name { #[inline(always)] fn eq(&self, rhs: &str) -> bool { self.as_str() == rhs } } impl<'a> PartialEq<&'a str> for $name { #[inline(always)] fn eq(&self, rhs: &&'a str) -> bool { self.as_str() == *rhs } } impl PartialEq for $name { #[inline(always)] fn eq(&self, rhs: &String) -> bool { self.as_str() == rhs } } impl PartialEq for $name { fn eq(&self, rhs: &Self) -> bool { self.as_str().eq(rhs.as_str()) } } impl Eq for $name {} impl core::hash::Hash for $name { fn hash(&self, state: &mut H) { self.as_str().hash(state); } } impl core::cmp::PartialOrd for $name { #[inline(always)] fn partial_cmp(&self, rhs: &Self) -> Option { self.as_str().partial_cmp(rhs.as_str()) } #[inline(always)] fn lt(&self, rhs: &Self) -> bool { self.as_str().lt(rhs.as_str()) } #[inline(always)] fn le(&self, rhs: &Self) -> bool { self.as_str().le(rhs.as_str()) } #[inline(always)] fn gt(&self, rhs: &Self) -> bool { self.as_str().gt(rhs.as_str()) } #[inline(always)] fn ge(&self, rhs: &Self) -> bool { self.as_str().ge(rhs.as_str()) } } impl core::cmp::Ord for $name { #[inline(always)] fn cmp(&self, other: &$name) -> core::cmp::Ordering { self.as_str().cmp(other.as_str()) } } impl ops::Index> for $name { type Output = str; #[inline] fn index(&self, index: ops::Range) -> &str { &self[..][index] } } impl ops::Index> for $name { type Output = str; #[inline] fn index(&self, index: ops::RangeTo) -> &str { &self[..][index] } } impl ops::Index> for $name { type Output = str; #[inline] fn index(&self, index: ops::RangeFrom) -> &str { &self[..][index] } } impl ops::Index for $name { type Output = str; #[inline] fn index(&self, _index: ops::RangeFull) -> &str { self.as_str() } } impl ops::Index> for $name { type Output = str; #[inline] fn index(&self, index: ops::RangeInclusive) -> &str { Index::index(&**self, index) } } impl ops::Index> for $name { type Output = str; #[inline] fn index(&self, index: ops::RangeToInclusive) -> &str { Index::index(&**self, index) } } impl Borrow for $name { fn borrow(&self) -> &str { self.as_str() } } } }istring-0.3.3/src/ibytes.rs000064400000000000000000000222301046102023000137410ustar 00000000000000use alloc::vec::Vec; use core::{ptr, mem, slice, convert, ops, hash, cmp, fmt}; use core::ops::{Index}; use core::borrow::Borrow; const IS_INLINE: u8 = 1 << 7; const LEN_MASK: u8 = !IS_INLINE; #[cfg(target_pointer_width="64")] const INLINE_CAPACITY: usize = 23; #[cfg(target_pointer_width="32")] const INLINE_CAPACITY: usize = 11; #[cfg(target_pointer_width="64")] const MAX_CAPACITY: usize = (1 << 63) - 1; #[cfg(target_pointer_width="32")] const MAX_CAPACITY: usize = (1 << 31) - 1; // use the MSG of heap.len to encode the variant // which is also MSB of inline.len #[cfg(target_endian = "little")] #[derive(Copy, Clone)] #[repr(C)] pub struct Inline { pub data: [u8; INLINE_CAPACITY], pub len: u8 } #[cfg(target_endian = "little")] #[derive(Copy, Clone)] #[repr(C)] pub struct Heap { pub ptr: *mut u8, pub cap: usize, pub len: usize } #[cfg(target_endian = "big")] #[derive(Copy, Clone)] #[repr(C)] pub struct Inline { pub len: u8, pub data: [u8; INLINE_CAPACITY], } #[cfg(target_endian = "big")] #[derive(Copy, Clone)] #[repr(C)] pub struct Heap { pub len: usize, pub ptr: *mut u8, pub cap: usize } pub enum InlineOrHeap { Inline(Inline), Heap(Heap) } pub union IBytesUnion { inline: Inline, heap: Heap } pub struct IBytes { union: IBytesUnion, } unsafe impl Send for IBytes {} unsafe impl Sync for IBytes {} #[test] fn test_layout() { let s = IBytesUnion { inline: Inline { data: [0; INLINE_CAPACITY], len: IS_INLINE } }; let heap = unsafe { s.heap }; assert_eq!(heap.len, MAX_CAPACITY + 1); } #[inline] fn vec_into_raw_parts(mut s: Vec) -> (*mut u8, usize, usize) { let len = s.len(); let cap = s.capacity(); let ptr = s.as_mut_ptr(); mem::forget(s); (ptr, len, cap) } define_common_bytes!(IBytes, IBytesUnion); impl IBytes { #[inline] pub fn new() -> IBytes { IBytes { union: IBytesUnion { inline: Inline { data: [0; INLINE_CAPACITY], len: IS_INLINE } }, } } #[inline] pub fn with_capacity(capacity: usize) -> IBytes { assert!(capacity < MAX_CAPACITY); if capacity > INLINE_CAPACITY { let (ptr, len, cap) = vec_into_raw_parts(Vec::with_capacity(capacity)); IBytes { union: IBytesUnion { heap: Heap { ptr, len, cap } } } } else { IBytes { union: IBytesUnion { inline: Inline { data: [0; INLINE_CAPACITY], len: IS_INLINE } }, } } } #[inline(always)] pub unsafe fn set_len(&mut self, new_len: usize) { assert!(new_len <= self.capacity()); if self.is_inline() { self.union.inline.len = new_len as u8 | IS_INLINE; } else { self.union.heap.len = new_len; } } #[inline(always)] pub fn capacity(&self) -> usize { if self.is_inline() { INLINE_CAPACITY } else { unsafe { self.union.heap.cap } } } /// un-inline the string and expand the capacity to `cap`. /// /// does nothing if it isn't inlined. /// panics, if `cap` < `self.len()` pub fn move_to_heap(&mut self, cap: usize) { if self.is_inline() { // keep check here. the heap-bit is known to be zero, which makes len() trivial assert!(cap >= self.len()); unsafe { let len = self.len(); let (ptr, _, cap) = vec_into_raw_parts(Vec::with_capacity(cap)); ptr::copy_nonoverlapping(self.union.inline.data.as_ptr(), ptr, len); self.union.heap = Heap { ptr, len, cap }; } } } /// if the strings fits inline, make it inline, /// otherwhise shrink the capacity to the `self.len()`. pub fn shrink(&mut self) { let len = self.len(); if len <= INLINE_CAPACITY { unsafe { let heap = self.union.heap; self.union.inline.len = len as u8 | IS_INLINE; ptr::copy_nonoverlapping(heap.ptr, self.union.inline.data.as_mut_ptr(), len); Vec::from_raw_parts(heap.ptr, len, heap.cap); } } else { self.resize(len); } } pub (crate) fn resize(&mut self, new_cap: usize) { assert_eq!(self.is_inline(), false); assert!(new_cap >= self.len()); unsafe { let len = self.len(); let mut data = Vec::from_raw_parts(self.union.heap.ptr, len, self.union.heap.cap); self.union.heap.ptr = ptr::null_mut(); data.reserve(new_cap - len); let (ptr, _, cap) = vec_into_raw_parts(data); self.union.heap.ptr = ptr; self.union.heap.cap = cap; } } #[inline] pub fn reserve(&mut self, additional: usize) { let new_cap = self.capacity() + additional; if self.is_inline() { if new_cap > INLINE_CAPACITY { self.move_to_heap(new_cap); } } else { self.resize(new_cap); } } #[inline] pub fn reserve_exact(&mut self, additional: usize) { let new_cap = self.capacity() + additional; if self.is_inline() { self.move_to_heap(new_cap); } else { self.resize(new_cap); } } #[inline] pub fn push(&mut self, byte: u8) { self.extend_from_slice(&[byte]); } pub fn extend_from_slice(&mut self, bytes: &[u8]) { let old_len = self.len(); let new_len = old_len + bytes.len(); if self.is_inline() { if new_len > INLINE_CAPACITY { self.move_to_heap(new_len.next_power_of_two()); } } else { if new_len > self.capacity() { self.resize(new_len.next_power_of_two()); } } unsafe { ptr::copy_nonoverlapping(bytes.as_ptr(), self.as_mut_ptr().offset(old_len as isize), bytes.len()); self.set_len(new_len); } } } impl Drop for IBytes { #[inline] fn drop(&mut self) { if !self.is_inline() { unsafe { let len = self.len(); Vec::from_raw_parts(self.union.heap.ptr, len, self.union.heap.cap); } } } } impl<'a> convert::From<&'a [u8]> for IBytes { #[inline] fn from(s: &'a [u8]) -> IBytes { if s.len() > INLINE_CAPACITY { let (ptr, len, cap) = vec_into_raw_parts(Vec::from(s)); let heap = Heap { ptr, len, cap, }; IBytes { union: IBytesUnion { heap: heap }, } } else { unsafe { let mut data = [0; INLINE_CAPACITY]; data[..s.len()].copy_from_slice(s); IBytes::from_inline(Inline { data, len: s.len() as u8 }) } } } } impl<'a> convert::From<&'a str> for IBytes { #[inline] fn from(s: &'a str) -> IBytes { IBytes::from(s.as_bytes()) } } impl convert::From> for IBytes { #[inline] fn from(s: Vec) -> IBytes { if s.capacity() != 0 { let (ptr, len, cap) = vec_into_raw_parts(s); let heap = Heap { ptr, len, cap, }; IBytes { union: IBytesUnion { heap: heap }, } } else { IBytes::new() } } } impl convert::From for IBytes { #[inline] fn from(s: alloc::string::String) -> IBytes { IBytes::from(s.into_bytes()) } } impl convert::Into> for IBytes { #[inline] fn into(mut self) -> Vec { if self.is_inline() { let len = self.len(); self.move_to_heap(len); } unsafe { let s = Vec::from_raw_parts(self.union.heap.ptr, self.union.heap.len, self.union.heap.cap); // the IBytes must not drop mem::forget(self); s } } } impl Clone for IBytes { #[inline] fn clone(&self) -> IBytes { unsafe { if self.is_inline() { // simple case IBytes { union: IBytesUnion { inline: self.union.inline }, } } else { let len = self.len(); let mut s = IBytes::with_capacity(len); s.extend_from_slice(slice::from_raw_parts(self.union.heap.ptr, len)); s } } } } #[cfg(feature="size")] impl datasize::DataSize for IBytes { const IS_DYNAMIC: bool = true; const STATIC_HEAP_SIZE: usize = core::mem::size_of::(); fn estimate_heap_size(&self) -> usize { if self.is_inline() { Self::STATIC_HEAP_SIZE } else { Self::STATIC_HEAP_SIZE + self.capacity() } } } istring-0.3.3/src/istring.rs000064400000000000000000000123021046102023000141200ustar 00000000000000use core::{fmt, str, convert, str::Utf8Error}; use core::clone::Clone; use core::iter::{FromIterator, IntoIterator, Extend}; use core::ops::{self, Index, Add, AddAssign}; use core::borrow::Borrow; use alloc::{string::String, vec::Vec}; use alloc::borrow::Cow; use crate::ibytes::IBytes; use crate::FromUtf8Error; #[derive(Clone)] #[cfg_attr(feature="size", derive(datasize::DataSize))] pub struct IString { pub (crate) bytes: IBytes, } impl IString { #[inline] pub fn new() -> IString { IString { bytes: IBytes::new() } } #[inline] pub fn with_capacity(capacity: usize) -> IString { IString { bytes: IBytes::with_capacity(capacity) } } #[inline(always)] pub unsafe fn set_len(&mut self, new_len: usize) { self.bytes.set_len(new_len); } #[inline(always)] pub fn capacity(&self) -> usize { self.bytes.capacity() } /// un-inline the string and expand the capacity to `cap`. /// /// does nothing if it isn't inlined. /// panics, if `cap` < `self.len()` #[inline(always)] pub fn move_to_heap(&mut self, cap: usize) { self.bytes.move_to_heap(cap); } /// if the strings fits inline, make it inline, /// otherwhise shrink the capacity to the `self.len()`. #[inline(always)] pub fn shrink(&mut self) { self.bytes.shrink(); } #[inline] pub fn push_str(&mut self, s: &str) { self.bytes.extend_from_slice(s.as_bytes()); } #[inline(always)] pub unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> IString { String::from_raw_parts(buf, length, capacity).into() } #[inline] pub fn reserve(&mut self, additional: usize) { self.bytes.reserve(additional); } #[inline] pub fn reserve_exact(&mut self, additional: usize) { self.bytes.reserve_exact(additional); } #[inline] pub fn push(&mut self, ch: char) { let mut buf = [0; 4]; self.push_str(ch.encode_utf8(&mut buf)); } #[inline] pub fn truncate(&mut self, new_len: usize) { if new_len < self.len() { unsafe { self.set_len(new_len) } } } pub fn from_utf8(bytes: IBytes) -> Result> { match str::from_utf8(bytes.as_slice()) { Ok(_) => Ok(IString { bytes }), Err(error) => Err(FromUtf8Error { bytes, error }) } } } impl<'a> convert::From<&'a str> for IString { #[inline] fn from(s: &'a str) -> IString { let mut istring = IString::with_capacity(s.len()); istring.push_str(s); istring } } impl convert::From for IString { #[inline] fn from(s: String) -> IString { IString { bytes: IBytes::from(s.into_bytes()) } } } impl<'a> convert::From> for IString { #[inline] fn from(s: Cow<'a, str>) -> IString { match s { Cow::Borrowed(s) => IString::from(s), Cow::Owned(s) => IString::from(s) } } } impl convert::Into for IString { #[inline] fn into(self) -> String { unsafe { String::from_utf8_unchecked(self.bytes.into()) } } } impl fmt::Write for IString { #[inline(always)] fn write_str(&mut self, s: &str) -> fmt::Result { self.push_str(s); Ok(()) } } impl Extend for IString { #[inline] fn extend>(&mut self, iter: I) { let iterator = iter.into_iter(); let (lower_bound, _) = iterator.size_hint(); self.reserve(lower_bound); for ch in iterator { self.push(ch) } } } impl<'a> Extend<&'a char> for IString { #[inline(always)] fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } } impl<'a> Extend<&'a str> for IString { #[inline(always)] fn extend>(&mut self, iter: I) { for s in iter { self.push_str(s) } } } impl<'a> Extend> for IString { #[inline(always)] fn extend>>(&mut self, iter: I) { for s in iter { self.push_str(&s) } } } impl Default for IString { #[inline(always)] fn default() -> IString { IString::new() } } impl<'a> Add<&'a str> for IString { type Output = IString; #[inline(always)] fn add(mut self, other: &str) -> IString { self.push_str(other); self } } impl<'a> AddAssign<&'a str> for IString { #[inline] fn add_assign(&mut self, other: &str) { self.push_str(other); } } impl FromIterator for IString { fn from_iter(iter: T) -> Self where T: IntoIterator { let mut s = IString::new(); s.extend(iter); s } } impl<'a> FromIterator<&'a str> for IString { fn from_iter(iter: T) -> Self where T: IntoIterator { let mut s = IString::new(); s.extend(iter); s } } define_common_string!(IString, IStringUnion);istring-0.3.3/src/lib.rs000064400000000000000000000060311046102023000132110ustar 00000000000000// Copyright 2022 Sebastian Köln // Licensed under the MIT license // // The trait impls contains large chunks from alloc/string.rs, // with the following copyright notice: // Copyright 2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. #![no_std] /*! A replacement for String that allows storing strings of length up to sizeof() - 1 without a heap allocation That means on 32bit machines: size_of::() == 12 bytes, inline capacity: 11 bytes on 64bit machines: size_of::() == 24 bytes, inline capacity: 23 bytes */ extern crate alloc; #[cfg(feature="std")] extern crate std; #[macro_use] mod common; pub mod istring; pub mod small; pub mod ibytes; pub use crate::istring::{IString}; pub use crate::ibytes::IBytes; pub use crate::small::{SmallBytes, SmallString}; #[derive(Debug)] pub struct FromUtf8Error { bytes: T, error: core::str::Utf8Error, } impl> FromUtf8Error { pub fn as_bytes(&self) -> &[u8] { &*self.bytes } pub fn into_bytes(self) -> T { self.bytes } pub fn utf8_error(&self) -> core::str::Utf8Error { self.error } } #[cfg(feature="std")] impl std::fmt::Display for FromUtf8Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { self.error.fmt(f) } } #[cfg(feature="std")] impl std::error::Error for FromUtf8Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { self.error.source() } } #[cfg(feature="serialize")] use serde::{Serialize, Serializer, Deserialize, Deserializer}; #[cfg(feature="serialize")] impl Serialize for IString { fn serialize(&self, serializer: S) -> Result { self.as_str().serialize(serializer) } } #[cfg(feature="serialize")] impl<'de> Deserialize<'de> for IString { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { let s = alloc::string::String::deserialize(deserializer)?; let mut s = IString::from(s); s.shrink(); Ok(s) } } #[cfg(feature="serialize")] impl Serialize for SmallString { fn serialize(&self, serializer: S) -> Result { self.as_str().serialize(serializer) } } #[cfg(feature="serialize")] impl<'de> Deserialize<'de> for SmallString { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { let s = alloc::string::String::deserialize(deserializer)?; Ok(SmallString::from(s)) } } istring-0.3.3/src/small.rs000064400000000000000000000166151046102023000135640ustar 00000000000000use core::{fmt, slice, str, convert, mem, cmp, hash}; use core::clone::Clone; use core::ops::{self, Index}; use core::borrow::Borrow; use alloc::{string::String, vec::Vec}; use alloc::boxed::Box; use crate::FromUtf8Error; const IS_INLINE: u8 = 1 << 7; const LEN_MASK: u8 = !IS_INLINE; #[cfg(target_pointer_width="64")] const INLINE_CAPACITY: usize = 15; #[cfg(target_pointer_width="32")] const INLINE_CAPACITY: usize = 7; #[cfg(target_pointer_width="64")] const MAX_CAPACITY: usize = (1 << 63) - 1; #[cfg(target_pointer_width="32")] const MAX_CAPACITY: usize = (1 << 31) - 1; // use the MSG of heap.len to encode the variant // which is also MSB of inline.len #[cfg(target_endian = "little")] #[derive(Copy, Clone)] #[repr(C)] pub struct Inline { pub data: [u8; INLINE_CAPACITY], pub len: u8 } #[cfg(target_endian = "little")] #[derive(Copy, Clone)] #[repr(C)] pub struct Heap { pub ptr: *mut u8, pub len: usize } #[cfg(target_endian = "big")] #[derive(Copy, Clone)] #[repr(C)] pub struct Inline { pub len: u8, pub data: [u8; INLINE_CAPACITY], } #[cfg(target_endian = "big")] #[derive(Copy, Clone)] #[repr(C)] pub struct Heap { pub len: usize, pub ptr: *mut u8, } union SmallBytesUnion { inline: Inline, heap: Heap } pub struct SmallBytes { union: SmallBytesUnion, } unsafe impl Send for SmallBytes {} unsafe impl Sync for SmallBytes {} #[derive(Clone)] #[cfg_attr(feature="size", derive(datasize::DataSize))] pub struct SmallString { bytes: SmallBytes, } #[test] fn test_layout() { let s = SmallBytesUnion { inline: Inline { data: [0; INLINE_CAPACITY], len: IS_INLINE } }; let heap = unsafe { s.heap }; assert_eq!(heap.len, MAX_CAPACITY + 1); } #[inline(always)] fn box_slice(s: &[u8]) -> Box<[u8]> { Box::from(s) } #[inline(always)] fn box_slice_into_raw_parts(mut s: Box<[u8]>) -> (*mut u8, usize) { let len = s.len(); let ptr = s.as_mut_ptr(); mem::forget(s); (ptr, len) } #[inline(always)] unsafe fn box_slice_from_raw_parts(ptr: *mut u8, len: usize) -> Box<[u8]> { let ptr = slice::from_raw_parts_mut(ptr, len) as *mut [u8]; Box::from_raw(ptr) } impl SmallBytes { #[inline(always)] pub fn new() -> SmallBytes { unsafe { SmallBytes::from_inline( Inline { data: [0; INLINE_CAPACITY], len: 0 }, ) } } } impl<'a> From<&'a [u8]> for SmallBytes { #[inline] fn from(s: &[u8]) -> SmallBytes { let len = s.len(); unsafe { if len > INLINE_CAPACITY { let s = box_slice(s); let (ptr, len) = box_slice_into_raw_parts(s); SmallBytes::from_heap( Heap { ptr, len }, ) } else { let mut data = [0; INLINE_CAPACITY]; data[.. len].copy_from_slice(s); SmallBytes::from_inline( Inline { data, len: len as u8 }, ) } } } } impl SmallString { #[inline(always)] pub fn new() -> SmallString { SmallString { bytes: SmallBytes::new() } } pub fn from_utf8(bytes: SmallBytes) -> Result> { match str::from_utf8(bytes.as_slice()) { Ok(_) => Ok(SmallString { bytes }), Err(error) => Err(FromUtf8Error { bytes, error }) } } } impl Drop for SmallBytes { #[inline] fn drop(&mut self) { if !self.is_inline() { unsafe { box_slice_from_raw_parts(self.union.heap.ptr, self.union.heap.len); } } } } impl<'a> convert::From<&'a str> for SmallString { #[inline] fn from(s: &'a str) -> SmallString { SmallString { bytes: SmallBytes::from(s.as_bytes()) } } } impl convert::From> for SmallBytes { #[inline] fn from(s: Vec) -> SmallBytes { let len = s.len(); if len <= INLINE_CAPACITY { return SmallBytes::from(s.as_slice()); } unsafe { let s = s.into_boxed_slice(); let (ptr, len) = box_slice_into_raw_parts(s); let heap = Heap { ptr, len, }; SmallBytes::from_heap( heap, ) } } } impl convert::From for SmallString { #[inline] fn from(s: String) -> SmallString { SmallString { bytes: SmallBytes::from(s.into_bytes()) } } } impl Into> for SmallBytes { #[inline] fn into(self) -> Vec { let len = self.len(); if self.is_inline() { self.as_slice().into() } else { unsafe { let s = box_slice_from_raw_parts(self.union.heap.ptr, len); // the SmallString must not drop mem::forget(self); Vec::from(s) } } } } impl Into for SmallString { #[inline] fn into(self) -> String { unsafe { String::from_utf8_unchecked(self.bytes.into()) } } } impl Clone for SmallBytes { #[inline] fn clone(&self) -> SmallBytes { unsafe { if self.is_inline() { // simple case SmallBytes { union: SmallBytesUnion { inline: self.union.inline }, } } else { let len = self.len(); let bytes = slice::from_raw_parts(self.union.heap.ptr, len); let (ptr, len) = box_slice_into_raw_parts(box_slice(bytes)); SmallBytes::from_heap( Heap { ptr, len }, ) } } } } impl FromIterator for SmallString { fn from_iter>(iter: T) -> Self { let mut buf = [0; INLINE_CAPACITY]; let mut pos = 0; let mut iter = iter.into_iter(); while let Some(c) = iter.next() { if pos + c.len_utf8() > INLINE_CAPACITY { let mut s = String::with_capacity(32); s.push_str(unsafe { str::from_utf8_unchecked(&buf[..pos]) }); s.push(c); s.extend(iter); return s.into(); } pos += c.encode_utf8(&mut buf[pos..]).len(); } let bytes = unsafe { SmallBytes::from_inline( Inline { data: buf, len: pos as u8 }, ) }; SmallString { bytes } } } impl From for SmallString { fn from(c: char) -> SmallString { let mut buf = [0; INLINE_CAPACITY]; let len = c.encode_utf8(&mut buf).len(); let bytes = unsafe { SmallBytes::from_inline( Inline { data: buf, len: len as u8 }, ) }; SmallString { bytes } } } #[cfg(feature="size")] impl datasize::DataSize for SmallBytes { const IS_DYNAMIC: bool = true; const STATIC_HEAP_SIZE: usize = core::mem::size_of::(); fn estimate_heap_size(&self) -> usize { if self.is_inline() { Self::STATIC_HEAP_SIZE } else { Self::STATIC_HEAP_SIZE + self.len() } } } define_common_string!(SmallString, SmallStringUnion); define_common_bytes!(SmallBytes, SmallBytesUnion); istring-0.3.3/tests/misc.rs000064400000000000000000000011501046102023000137460ustar 00000000000000use istring::{IString, SmallString}; #[test] fn test_misc_istring() { let p1 = "Hello World!"; let p2 = "Hello World! .........xyz"; let p3 = " .........xyz"; let s1 = IString::from(p1); assert_eq!(s1, p1); let s2 = IString::from(p2); assert_eq!(s2, p2); let mut s3 = s1.clone(); s3.push_str(p3); assert_eq!(s3, p2); } #[test] fn test_misc_smallstring() { let p1 = "Hello World!"; let p2 = "Hello World! .........xyz"; let s1 = SmallString::from(p1); assert_eq!(s1, p1); let s2 = SmallString::from(p2); assert_eq!(s2, p2); } istring-0.3.3/tests/thread.rs000064400000000000000000000005671046102023000142750ustar 00000000000000extern crate istring; use istring::IString; use std::thread; use std::fmt::Write; #[test] fn test_thread() { let mut s = IString::from("Hello"); write!(s, " world").unwrap(); let s2 = thread::spawn(move || { let mut s = s; s += " from another thread!"; s }).join().unwrap(); assert_eq!(s2, "Hello world from another thread!"); }