diff --git a/src/lib.rs b/src/lib.rs index 850b23f..6fb3db4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -264,7 +264,7 @@ impl LeanString { /// assert_eq!(fancy_f.chars().count(), 3); /// ``` #[inline] - pub fn len(&self) -> usize { + pub const fn len(&self) -> usize { self.0.len() } @@ -281,7 +281,7 @@ impl LeanString { /// assert!(!s.is_empty()); /// ``` #[inline] - pub fn is_empty(&self) -> bool { + pub const fn is_empty(&self) -> bool { self.0.is_empty() } @@ -323,7 +323,7 @@ impl LeanString { /// assert_eq!(s.as_str(), "foo"); /// ``` #[inline] - pub fn as_str(&self) -> &str { + pub const fn as_str(&self) -> &str { self.0.as_str() } @@ -337,7 +337,7 @@ impl LeanString { /// assert_eq!(&[104, 101, 108, 108, 111], s.as_bytes()); /// ``` #[inline] - pub fn as_bytes(&self) -> &[u8] { + pub const fn as_bytes(&self) -> &[u8] { self.0.as_bytes() } diff --git a/src/repr.rs b/src/repr.rs index 999a862..26e849f 100644 --- a/src/repr.rs +++ b/src/repr.rs @@ -104,12 +104,14 @@ impl Repr { #[cfg(target_pointer_width = "64")] #[inline] - pub(crate) fn len(&self) -> usize { + pub(crate) const fn len(&self) -> usize { let last_byte = self.last_byte(); - let inline_len = (last_byte as usize) - .wrapping_sub(LastByte::MASK_1100_0000 as usize) - .min(MAX_INLINE_SIZE); + let inline_len = { + let this = (last_byte as usize).wrapping_sub(LastByte::MASK_1100_0000 as usize); + // inline Ord::min because the trait impl is not const + if MAX_INLINE_SIZE < this { MAX_INLINE_SIZE } else { this } + }; let mut len = { // SAFETY: `Repr` has the same size as `[usize; 2]` and is aligned as `usize` @@ -130,7 +132,7 @@ impl Repr { #[cfg(target_pointer_width = "32")] #[inline] - pub(crate) fn len(&self) -> usize { + pub(crate) const fn len(&self) -> usize { if self.is_heap_buffer() { // SAFETY: We just checked the discriminant to make sure we're heap allocated unsafe { self.as_heap_buffer() }.len() @@ -139,14 +141,17 @@ impl Repr { unsafe { self.as_static_buffer() }.len() } else { // Remaining is InlineBuffer - (self.last_byte() as usize) - .wrapping_sub(LastByte::MASK_1100_0000 as usize) - .min(MAX_INLINE_SIZE) + { + let this = + (self.last_byte() as usize).wrapping_sub(LastByte::MASK_1100_0000 as usize); + // inline Ord::min because the trait impl is not const + if MAX_INLINE_SIZE < this { MAX_INLINE_SIZE } else { this } + } } } #[inline] - pub(crate) fn is_empty(&self) -> bool { + pub(crate) const fn is_empty(&self) -> bool { self.len() == 0 } @@ -164,13 +169,13 @@ impl Repr { } #[inline] - pub(crate) fn as_str(&self) -> &str { + pub(crate) const fn as_str(&self) -> &str { // SAFETY: A `Repr` contains valid UTF-8 unsafe { str::from_utf8_unchecked(self.as_bytes()) } } #[inline] - pub(crate) fn as_bytes(&self) -> &[u8] { + pub(crate) const fn as_bytes(&self) -> &[u8] { let len = self.len(); let ptr = if self.last_byte() >= LastByte::HeapMarker as u8 { @@ -571,7 +576,7 @@ impl Repr { } #[inline(always)] - pub(crate) fn is_heap_buffer(&self) -> bool { + pub(crate) const fn is_heap_buffer(&self) -> bool { self.last_byte() == LastByte::HeapMarker as u8 } @@ -700,7 +705,7 @@ impl Repr { } #[inline(always)] - unsafe fn as_heap_buffer(&self) -> &HeapBuffer { + const unsafe fn as_heap_buffer(&self) -> &HeapBuffer { // SAFETY: A `Repr` is transmuted from `HeapBuffer` unsafe { &*(self as *const _ as *const HeapBuffer) } } @@ -712,7 +717,7 @@ impl Repr { } #[inline(always)] - unsafe fn as_static_buffer(&self) -> &StaticBuffer { + const unsafe fn as_static_buffer(&self) -> &StaticBuffer { // SAFETY: A `Repr` is transmuted from `StaticBuffer` unsafe { &*(self as *const _ as *const StaticBuffer) } } diff --git a/src/repr/heap_buffer.rs b/src/repr/heap_buffer.rs index 4dd029c..d4d60c9 100644 --- a/src/repr/heap_buffer.rs +++ b/src/repr/heap_buffer.rs @@ -109,9 +109,9 @@ impl HeapBuffer { self.ptr } - pub(super) fn len(&self) -> usize { + pub(super) const fn len(&self) -> usize { #[cold] - fn len_on_heap(ptr: NonNull) -> usize { + const fn len_on_heap(ptr: NonNull) -> usize { // SAFETY: We just checked that `len` is stored on the heap. unsafe { let len_ptr = ptr.sub(HeapBuffer::header_offset()).sub(size_of::()); @@ -449,7 +449,7 @@ mod internal { return self.0 == Self::ON_THE_HEAP; } - pub(super) fn as_usize(self) -> usize { + pub(super) const fn as_usize(self) -> usize { let size = self.0 ^ Self::TAG; let bytes = size.to_ne_bytes(); usize::from_le_bytes(bytes) diff --git a/src/repr/static_buffer.rs b/src/repr/static_buffer.rs index 9ea5a11..74c60d9 100644 --- a/src/repr/static_buffer.rs +++ b/src/repr/static_buffer.rs @@ -40,7 +40,7 @@ impl StaticBuffer { Ok(Self { ptr, len }) } - pub(super) fn len(&self) -> usize { + pub(super) const fn len(&self) -> usize { let len = self.len ^ Self::TAG; let bytes = len.to_ne_bytes(); usize::from_le_bytes(bytes) diff --git a/tests/const.rs b/tests/const.rs new file mode 100644 index 0000000..d579ccd --- /dev/null +++ b/tests/const.rs @@ -0,0 +1,19 @@ +use lean_string::LeanString; + +static S: LeanString = LeanString::from_static_str("hello world"); + +const STR: &str = S.as_str(); + +#[test] +fn use_const() { + assert_eq!(STR, "hello world"); +} + +#[test] +fn const_len() { + const { + let s = LeanString::from_static_str("hello"); + assert!(s.len() == 5); + s + }; +}