to_str/
lib.rs

1//! `no_std` friendly interface for conversion to str
2//!
3//! ```
4//! type Buffer = to_str::Buffer64;
5//!
6//! let mut buf = String::new();
7//! let _ = buf.push_str(Buffer::fmt(5usize).as_str());
8//! assert_eq!(buf, "5");
9//!
10//! buf.push_str(Buffer::fmt(0usize).as_str());
11//! assert_eq!(buf, "50");
12//! buf.push_str(Buffer::fmt(&5usize).as_str());
13//! assert_eq!(buf, "505");
14//! buf.push_str(Buffer::fmt(&mut 0usize).as_str());
15//! assert_eq!(buf, "5050");
16//! ```
17
18#![warn(missing_docs)]
19#![cfg_attr(feature = "cargo-clippy", allow(clippy::style))]
20#![no_std]
21
22mod buffer;
23mod numeric;
24
25pub use buffer::Buffer;
26
27///Alias to buffer that can be used to write `128` bit integers
28pub type Buffer128 = Buffer<[u8; i128::TEXT_SIZE]>;
29///Alias to buffer that can be used to write `64` bit integers
30pub type Buffer64 = Buffer<[u8; i64::TEXT_SIZE]>;
31///Alias to buffer that can be used to write `32` bit integers
32pub type Buffer32 = Buffer<[u8; i32::TEXT_SIZE]>;
33///Alias to buffer that can be used to write `isize` bit integers
34pub type BufferSized = Buffer<[u8; isize::TEXT_SIZE]>;
35
36///Describes conversion to string
37///
38///This trait is unsafe due to following requirements:
39///
40///- Implementation must never read buffer, unless it was already written by it;
41///- It writes from the end of buffer (necessary only when you use `Buffer`).
42pub unsafe trait ToStr {
43    ///Max size in bytes to hold the string
44    ///
45    ///Implementation MUST guarantee that this size of buffer is enough to fit any possible textual
46    ///representation
47    const TEXT_SIZE: usize;
48
49    ///Writes textual representation to the buffer
50    ///
51    ///Returns `str` stored in the provided `buffer`
52    ///
53    ///Can panic, if buffer is not sufficient.
54    ///Or write only partially
55    ///
56    ///Implementation is allowed to write any part of the buffer.
57    ///It is not allowed to read it, unless it was written already.
58    ///
59    ///# Safety:
60    ///
61    ///Debug builds must never invoke UB when calling this function.
62    ///
63    ///UB in release mode is fine if one wants to write efficient code.
64    fn to_str<'a>(&self, buffer: &'a mut [u8]) -> &'a str;
65
66    #[inline]
67    ///Performs textual conversion by writing to the buffer, if possible.
68    ///
69    ///If not possible MUST return `None`
70    ///
71    ///By default returns `None` if buffer size is below `TEXT_SIZE`
72    ///Otherwise calls `to_str()` while passing buffer as it is
73    fn to_str_if<'a>(&self, buffer: &'a mut [u8]) -> Option<&'a str> {
74        if buffer.len() < Self::TEXT_SIZE {
75            None
76        } else {
77            Some(self.to_str(buffer))
78        }
79    }
80}
81
82unsafe impl<'a, T: ?Sized + ToStr> ToStr for &'a T {
83    const TEXT_SIZE: usize = T::TEXT_SIZE;
84
85    #[inline(always)]
86    fn to_str<'b>(&self, buffer: &'b mut [u8]) -> &'b str {
87        (&**self).to_str(buffer)
88    }
89}
90
91unsafe impl<'a, T: ?Sized + ToStr> ToStr for &'a mut T {
92    const TEXT_SIZE: usize = T::TEXT_SIZE;
93
94    #[inline(always)]
95    fn to_str<'b>(&self, buffer: &'b mut [u8]) -> &'b str {
96        (&**self).to_str(buffer)
97    }
98}