mirror of
https://github.com/rust-embedded/heapless.git
synced 2025-09-30 14:00:29 +00:00
commit
ad5a0ee8e1
@ -48,6 +48,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- Added `truncate` to `IndexMap`.
|
||||
- Added `get_index` and `get_index_mut` to `IndexMap`.
|
||||
- Added `String::uDisplay`.
|
||||
- Added `CString`.
|
||||
- Added `LenT` generic to `Vec<T, N>` and `VecView<T>` to save memory when using a sane capacity value.
|
||||
- Added the `index_set` module.
|
||||
- Added the `index_map` module.
|
||||
|
558
src/c_string.rs
Normal file
558
src/c_string.rs
Normal file
@ -0,0 +1,558 @@
|
||||
//! A fixed capacity [`CString`](https://doc.rust-lang.org/std/ffi/struct.CString.html).
|
||||
|
||||
use crate::{len_type::DefaultLenType, vec::Vec, CapacityError, LenType};
|
||||
use core::{
|
||||
borrow::Borrow,
|
||||
cmp::Ordering,
|
||||
error::Error,
|
||||
ffi::{c_char, CStr, FromBytesWithNulError},
|
||||
fmt,
|
||||
ops::Deref,
|
||||
};
|
||||
|
||||
/// A fixed capacity [`CString`](https://doc.rust-lang.org/std/ffi/struct.CString.html).
|
||||
///
|
||||
/// It stores up to `N - 1` non-nul characters with a trailing nul terminator.
|
||||
#[derive(Clone, Hash)]
|
||||
pub struct CString<const N: usize, LenT: LenType = DefaultLenType<N>> {
|
||||
inner: Vec<u8, N, LenT>,
|
||||
}
|
||||
|
||||
impl<const N: usize, LenT: LenType> CString<N, LenT> {
|
||||
/// Creates a new C-compatible string with a terminating nul byte.
|
||||
///
|
||||
/// ```rust
|
||||
/// use heapless::CString;
|
||||
///
|
||||
/// // A fixed-size `CString` that can store up to 10 characters
|
||||
/// // including the nul terminator.
|
||||
/// let empty = CString::<10>::new();
|
||||
///
|
||||
/// assert_eq!(empty.as_c_str(), c"");
|
||||
/// assert_eq!(empty.to_str(), Ok(""));
|
||||
/// ```
|
||||
pub fn new() -> Self {
|
||||
const {
|
||||
assert!(N > 0);
|
||||
}
|
||||
|
||||
let mut inner = Vec::new();
|
||||
|
||||
// SAFETY: We just asserted that `N > 0`.
|
||||
unsafe { inner.push_unchecked(b'\0') };
|
||||
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
/// Unsafely creates a [`CString`] from a byte slice.
|
||||
///
|
||||
/// This function will copy the provided `bytes` to a [`CString`] without
|
||||
/// performing any sanity checks.
|
||||
///
|
||||
/// The function will fail if `bytes.len() > N`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The provided slice **must** be nul-terminated and not contain any interior
|
||||
/// nul bytes.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use heapless::CString;
|
||||
/// let mut c_string = unsafe { CString::<7>::from_bytes_with_nul_unchecked(b"string\0").unwrap() };
|
||||
///
|
||||
/// assert_eq!(c_string.to_str(), Ok("string"));
|
||||
/// ```
|
||||
pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> Result<Self, CapacityError> {
|
||||
let mut inner = Vec::new();
|
||||
|
||||
inner.extend_from_slice(bytes)?;
|
||||
|
||||
Ok(Self { inner })
|
||||
}
|
||||
|
||||
/// Instantiates a [`CString`] copying from the giving byte slice, assuming it is
|
||||
/// nul-terminated.
|
||||
///
|
||||
/// Fails if the given byte slice has any interior nul byte, if the slice does not
|
||||
/// end with a nul byte, or if the byte slice can't fit in `N`.
|
||||
pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<Self, ExtendError> {
|
||||
let mut string = Self::new();
|
||||
|
||||
string.extend_from_bytes(bytes)?;
|
||||
|
||||
Ok(string)
|
||||
}
|
||||
|
||||
/// Builds a [`CString`] copying from a raw C string pointer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The memory pointed to by `ptr` must contain a valid nul terminator at the
|
||||
/// end of the string.
|
||||
/// - `ptr` must be valid for reads of bytes up to and including the nul terminator.
|
||||
/// This means in particular:
|
||||
/// - The entire memory range of this `CStr` must be contained within a single allocated object!
|
||||
/// - `ptr` must be non-nul even for a zero-length `CStr`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use core::ffi::{c_char, CStr};
|
||||
/// use heapless::CString;
|
||||
///
|
||||
/// const HELLO_PTR: *const c_char = {
|
||||
/// const BYTES: &[u8] = b"Hello, world!\0";
|
||||
/// BYTES.as_ptr().cast()
|
||||
/// };
|
||||
///
|
||||
/// let copied = unsafe { CString::<14>::from_raw(HELLO_PTR) }.unwrap();
|
||||
///
|
||||
/// assert_eq!(copied.to_str(), Ok("Hello, world!"));
|
||||
/// ```
|
||||
pub unsafe fn from_raw(ptr: *const c_char) -> Result<Self, ExtendError> {
|
||||
// SAFETY: The given pointer to a string is assumed to be nul-terminated.
|
||||
Self::from_bytes_with_nul(unsafe { CStr::from_ptr(ptr).to_bytes_with_nul() })
|
||||
}
|
||||
|
||||
/// Converts the [`CString`] to a [`CStr`] slice.
|
||||
#[inline]
|
||||
pub fn as_c_str(&self) -> &CStr {
|
||||
unsafe { CStr::from_bytes_with_nul_unchecked(&self.inner) }
|
||||
}
|
||||
|
||||
/// Calculates the length of `self.inner` would have if it appended `bytes`.
|
||||
fn capacity_with_bytes(&self, bytes: &[u8]) -> Option<usize> {
|
||||
match bytes.last() {
|
||||
None => None,
|
||||
Some(0) if bytes.len() < 2 => None,
|
||||
Some(0) => {
|
||||
// `bytes` is nul-terminated and so is `self.inner`.
|
||||
// Adding up both would account for 2 nul bytes when only a single byte
|
||||
// would end up in the resulting CString.
|
||||
Some(self.inner.len() + bytes.len() - 1)
|
||||
}
|
||||
Some(_) => {
|
||||
// No terminating nul byte in `bytes` but there's one in
|
||||
// `self.inner`, so the math lines up nicely.
|
||||
//
|
||||
// In the case that `bytes` has a nul byte anywhere else, we would
|
||||
// error after `memchr` is called. So there's no problem.
|
||||
Some(self.inner.len() + bytes.len())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extends the [`CString`] with the given bytes.
|
||||
///
|
||||
/// This function fails if the [`CString`] would not have enough capacity to append the bytes or
|
||||
/// if the bytes contain an interior nul byte.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use heapless::CString;
|
||||
///
|
||||
/// let mut c_string = CString::<10>::new();
|
||||
///
|
||||
/// c_string.extend_from_bytes(b"hey").unwrap();
|
||||
/// c_string.extend_from_bytes(b" there\0").unwrap();
|
||||
///
|
||||
/// assert_eq!(c_string.to_str(), Ok("hey there"));
|
||||
/// ```
|
||||
pub fn extend_from_bytes(&mut self, bytes: &[u8]) -> Result<(), ExtendError> {
|
||||
let Some(capacity) = self.capacity_with_bytes(bytes) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if capacity > N {
|
||||
// Cannot store these bytes due to an insufficient capacity.
|
||||
return Err(CapacityError.into());
|
||||
}
|
||||
|
||||
match CStr::from_bytes_with_nul(bytes) {
|
||||
Ok(_) => {
|
||||
// SAFETY: A string is left in a valid state because appended bytes are nul-terminated.
|
||||
unsafe { self.extend_from_bytes_unchecked(bytes) }?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(FromBytesWithNulError::InteriorNul { position }) => {
|
||||
Err(ExtendError::InteriorNul { position })
|
||||
}
|
||||
Err(FromBytesWithNulError::NotNulTerminated) => {
|
||||
// Because given bytes has no nul byte anywhere, we insert the bytes and
|
||||
// then add the nul byte terminator.
|
||||
//
|
||||
// We've ensured above that we have enough space left to insert these bytes,
|
||||
// so the operations below must succeed.
|
||||
//
|
||||
// SAFETY: We append a missing nul terminator right below.
|
||||
unsafe {
|
||||
self.extend_from_bytes_unchecked(bytes).unwrap();
|
||||
self.inner.push_unchecked(0);
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the nul byte terminator from the inner buffer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Callers must ensure to add the nul terminator back after this function is called.
|
||||
#[inline]
|
||||
unsafe fn pop_terminator(&mut self) {
|
||||
debug_assert_eq!(self.inner.last(), Some(&0));
|
||||
|
||||
// SAFETY: We always have the nul terminator at the end.
|
||||
unsafe { self.inner.pop_unchecked() };
|
||||
}
|
||||
|
||||
/// Removes the existing nul terminator and then extends `self` with the given bytes.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// If `additional` is not nul-terminated, the [`CString`] is left non nul-terminated, which is
|
||||
/// an invalid state. Caller must ensure that either `additional` has a terminating nul byte
|
||||
/// or ensure to append a trailing nul terminator.
|
||||
unsafe fn extend_from_bytes_unchecked(
|
||||
&mut self,
|
||||
additional: &[u8],
|
||||
) -> Result<(), CapacityError> {
|
||||
// SAFETY: A caller is responsible for adding a nul terminator back to the inner buffer.
|
||||
unsafe { self.pop_terminator() }
|
||||
|
||||
self.inner.extend_from_slice(additional)
|
||||
}
|
||||
|
||||
/// Returns the underlying byte slice including the trailing nul terminator.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use heapless::CString;
|
||||
///
|
||||
/// let mut c_string = CString::<5>::new();
|
||||
/// c_string.extend_from_bytes(b"abc").unwrap();
|
||||
///
|
||||
/// assert_eq!(c_string.as_bytes_with_nul(), b"abc\0");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_bytes_with_nul(&self) -> &[u8] {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Returns the underlying byte slice excluding the trailing nul terminator.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use heapless::CString;
|
||||
///
|
||||
/// let mut c_string = CString::<5>::new();
|
||||
/// c_string.extend_from_bytes(b"abc").unwrap();
|
||||
///
|
||||
/// assert_eq!(c_string.as_bytes(), b"abc");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.inner[..self.inner.len() - 1]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, LenT: LenType> AsRef<CStr> for CString<N, LenT> {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &CStr {
|
||||
self.as_c_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, LenT: LenType> Borrow<CStr> for CString<N, LenT> {
|
||||
#[inline]
|
||||
fn borrow(&self) -> &CStr {
|
||||
self.as_c_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, LenT: LenType> Default for CString<N, LenT> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, LenT: LenType> Deref for CString<N, LenT> {
|
||||
type Target = CStr;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_c_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, const M: usize, LenT1: LenType, LenT2: LenType> PartialEq<CString<M, LenT2>>
|
||||
for CString<N, LenT1>
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &CString<M, LenT2>) -> bool {
|
||||
self.as_c_str() == rhs.as_c_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, LenT: LenType> Eq for CString<N, LenT> {}
|
||||
|
||||
impl<const N: usize, const M: usize, LenT1: LenType, LenT2: LenType> PartialOrd<CString<M, LenT2>>
|
||||
for CString<N, LenT1>
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, rhs: &CString<M, LenT2>) -> Option<Ordering> {
|
||||
self.as_c_str().partial_cmp(rhs.as_c_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, LenT: LenType> Ord for CString<N, LenT> {
|
||||
#[inline]
|
||||
fn cmp(&self, rhs: &Self) -> Ordering {
|
||||
self.as_c_str().cmp(rhs.as_c_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, LenT: LenType> fmt::Debug for CString<N, LenT> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.as_c_str().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// An error to extend [`CString`] with bytes.
|
||||
#[derive(Debug)]
|
||||
pub enum ExtendError {
|
||||
/// The capacity of the [`CString`] is too small.
|
||||
Capacity(CapacityError),
|
||||
/// An invalid interior nul byte found in a given byte slice.
|
||||
InteriorNul {
|
||||
/// A position of a nul byte.
|
||||
position: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl Error for ExtendError {}
|
||||
|
||||
impl fmt::Display for ExtendError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Capacity(error) => write!(f, "{error}"),
|
||||
Self::InteriorNul { position } => write!(f, "interior nul byte at {position}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CapacityError> for ExtendError {
|
||||
fn from(error: CapacityError) -> Self {
|
||||
Self::Capacity(error)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let empty = CString::<1>::new();
|
||||
|
||||
assert_eq!(empty.as_c_str(), c"");
|
||||
assert_eq!(empty.as_bytes(), &[]);
|
||||
assert_eq!(empty.to_str(), Ok(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_with_capacity_error() {
|
||||
assert!(CString::<1>::from_bytes_with_nul(b"a\0").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extend_no_byte() {
|
||||
let mut c_string = CString::<1>::new();
|
||||
|
||||
c_string.extend_from_bytes(b"").unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extend_from_bytes() {
|
||||
let mut c_string = CString::<11>::new();
|
||||
assert_eq!(c_string.to_str(), Ok(""));
|
||||
|
||||
c_string.extend_from_bytes(b"hello").unwrap();
|
||||
|
||||
assert_eq!(c_string.to_str(), Ok("hello"));
|
||||
|
||||
// Call must fail since `w\0rld` contains an interior nul byte.
|
||||
assert!(matches!(
|
||||
c_string.extend_from_bytes(b"w\0rld"),
|
||||
Err(ExtendError::InteriorNul { position: 1 })
|
||||
));
|
||||
|
||||
// However, the call above _must not_ have invalidated the state of our CString
|
||||
assert_eq!(c_string.to_str(), Ok("hello"));
|
||||
|
||||
// Call must fail since we can't store "hello world\0" in 11 bytes
|
||||
assert!(matches!(
|
||||
c_string.extend_from_bytes(b" world"),
|
||||
Err(ExtendError::Capacity(CapacityError))
|
||||
));
|
||||
|
||||
// Yet again, the call above must not have invalidated the state of our CString
|
||||
// (as it would e.g. if we pushed the bytes but then failed to push the nul terminator)
|
||||
assert_eq!(c_string.to_str(), Ok("hello"));
|
||||
|
||||
c_string.extend_from_bytes(b" Bill").unwrap();
|
||||
|
||||
assert_eq!(c_string.to_str(), Ok("hello Bill"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn calculate_capacity_with_additional_bytes() {
|
||||
const INITIAL_BYTES: &[u8] = b"abc";
|
||||
|
||||
let mut c_string = CString::<5>::new();
|
||||
|
||||
c_string.extend_from_bytes(INITIAL_BYTES).unwrap();
|
||||
|
||||
assert_eq!(c_string.to_bytes_with_nul().len(), 4);
|
||||
assert_eq!(c_string.capacity_with_bytes(b""), None);
|
||||
assert_eq!(c_string.capacity_with_bytes(b"\0"), None);
|
||||
assert_eq!(
|
||||
c_string.capacity_with_bytes(b"d"),
|
||||
Some(INITIAL_BYTES.len() + 2)
|
||||
);
|
||||
assert_eq!(
|
||||
c_string.capacity_with_bytes(b"d\0"),
|
||||
Some(INITIAL_BYTES.len() + 2)
|
||||
);
|
||||
assert_eq!(
|
||||
c_string.capacity_with_bytes(b"defg"),
|
||||
Some(INITIAL_BYTES.len() + 5)
|
||||
);
|
||||
assert_eq!(
|
||||
c_string.capacity_with_bytes(b"defg\0"),
|
||||
Some(INITIAL_BYTES.len() + 5)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn default() {
|
||||
assert_eq!(CString::<1>::default().as_c_str(), c"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deref() {
|
||||
assert_eq!(CString::<1>::new().deref(), c"");
|
||||
assert_eq!(CString::<2>::new().deref(), c"");
|
||||
assert_eq!(CString::<3>::new().deref(), c"");
|
||||
|
||||
let mut string = CString::<2>::new();
|
||||
string.extend_from_bytes(&[65]).unwrap();
|
||||
|
||||
assert_eq!(string.deref(), c"A");
|
||||
|
||||
let mut string = CString::<3>::new();
|
||||
string.extend_from_bytes(&[65, 66]).unwrap();
|
||||
|
||||
assert_eq!(string.deref(), c"AB");
|
||||
|
||||
let mut string = CString::<4>::new();
|
||||
string.extend_from_bytes(&[65, 66, 67]).unwrap();
|
||||
|
||||
assert_eq!(string.deref(), c"ABC");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn as_ref() {
|
||||
let mut string = CString::<4>::new();
|
||||
string.extend_from_bytes(b"foo").unwrap();
|
||||
assert_eq!(string.as_ref(), c"foo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn borrow() {
|
||||
let mut string = CString::<4>::new();
|
||||
string.extend_from_bytes(b"foo").unwrap();
|
||||
assert_eq!(Borrow::<CStr>::borrow(&string), c"foo");
|
||||
}
|
||||
|
||||
mod equality {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn c_string() {
|
||||
// Empty strings
|
||||
assert!(CString::<1>::new() == CString::<1>::new());
|
||||
assert!(CString::<1>::new() == CString::<2>::new());
|
||||
assert!(CString::<1>::from_bytes_with_nul(b"\0").unwrap() == CString::<3>::new());
|
||||
|
||||
// Single character
|
||||
assert!(
|
||||
CString::<2>::from_bytes_with_nul(b"a\0").unwrap()
|
||||
== CString::<2>::from_bytes_with_nul(b"a\0").unwrap()
|
||||
);
|
||||
assert!(
|
||||
CString::<2>::from_bytes_with_nul(b"a\0").unwrap()
|
||||
== CString::<3>::from_bytes_with_nul(b"a\0").unwrap()
|
||||
);
|
||||
assert!(
|
||||
CString::<2>::from_bytes_with_nul(b"a\0").unwrap()
|
||||
!= CString::<2>::from_bytes_with_nul(b"b\0").unwrap()
|
||||
);
|
||||
|
||||
// Multiple characters
|
||||
assert!(
|
||||
CString::<4>::from_bytes_with_nul(b"abc\0").unwrap()
|
||||
== CString::<4>::from_bytes_with_nul(b"abc\0").unwrap()
|
||||
);
|
||||
assert!(
|
||||
CString::<3>::from_bytes_with_nul(b"ab\0").unwrap()
|
||||
!= CString::<4>::from_bytes_with_nul(b"abc\0").unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod ordering {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn c_string() {
|
||||
assert_eq!(
|
||||
CString::<1>::new().partial_cmp(&CString::<1>::new()),
|
||||
Some(Ordering::Equal)
|
||||
);
|
||||
assert_eq!(
|
||||
CString::<2>::from_bytes_with_nul(b"a\0")
|
||||
.unwrap()
|
||||
.partial_cmp(&CString::<2>::from_bytes_with_nul(b"b\0").unwrap()),
|
||||
Some(Ordering::Less)
|
||||
);
|
||||
assert_eq!(
|
||||
CString::<2>::from_bytes_with_nul(b"b\0")
|
||||
.unwrap()
|
||||
.partial_cmp(&CString::<2>::from_bytes_with_nul(b"a\0").unwrap()),
|
||||
Some(Ordering::Greater)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn c_str() {
|
||||
assert_eq!(c"".partial_cmp(&CString::<1>::new()), Some(Ordering::Equal));
|
||||
assert_eq!(
|
||||
c"a".partial_cmp(&CString::<2>::from_bytes_with_nul(b"b\0").unwrap()),
|
||||
Some(Ordering::Less)
|
||||
);
|
||||
assert_eq!(
|
||||
c"b".partial_cmp(&CString::<2>::from_bytes_with_nul(b"a\0").unwrap()),
|
||||
Some(Ordering::Greater)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -157,6 +157,7 @@
|
||||
extern crate alloc;
|
||||
|
||||
pub use binary_heap::BinaryHeap;
|
||||
pub use c_string::CString;
|
||||
pub use deque::Deque;
|
||||
pub use history_buf::{HistoryBuf, OldestOrdered};
|
||||
pub use index_map::IndexMap;
|
||||
@ -171,6 +172,7 @@ pub use vec::{Vec, VecView};
|
||||
#[cfg(test)]
|
||||
mod test_helpers;
|
||||
|
||||
pub mod c_string;
|
||||
pub mod deque;
|
||||
pub mod history_buf;
|
||||
pub mod index_map;
|
||||
|
12
src/ufmt.rs
12
src/ufmt.rs
@ -1,4 +1,5 @@
|
||||
use crate::{
|
||||
c_string::{self, CString},
|
||||
len_type::LenType,
|
||||
string::{StringInner, StringStorage},
|
||||
vec::{VecInner, VecStorage},
|
||||
@ -19,6 +20,7 @@ impl<S: StringStorage + ?Sized> uDisplay for StringInner<S> {
|
||||
|
||||
impl<S: StringStorage + ?Sized> uWrite for StringInner<S> {
|
||||
type Error = CapacityError;
|
||||
|
||||
#[inline]
|
||||
fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
|
||||
self.push_str(s)
|
||||
@ -27,12 +29,22 @@ impl<S: StringStorage + ?Sized> uWrite for StringInner<S> {
|
||||
|
||||
impl<LenT: LenType, S: VecStorage<u8> + ?Sized> uWrite for VecInner<u8, LenT, S> {
|
||||
type Error = CapacityError;
|
||||
|
||||
#[inline]
|
||||
fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
|
||||
self.extend_from_slice(s.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize, LenT: LenType> uWrite for CString<N, LenT> {
|
||||
type Error = c_string::ExtendError;
|
||||
|
||||
#[inline]
|
||||
fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
|
||||
self.extend_from_bytes(s.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{String, Vec};
|
||||
|
Loading…
x
Reference in New Issue
Block a user