awint_ext/awi_struct/strings.rs
1use alloc::{string::String, vec::Vec};
2use core::num::NonZeroUsize;
3
4use awint_core::{Bits, SerdeError};
5
6use crate::{
7 string_internals::{
8 bits_to_string_radix, bits_to_vec_radix, internal_from_bytes_general,
9 internal_from_bytes_radix, internal_from_str,
10 },
11 Awi,
12};
13
14/// # non-`const` string representation conversion
15impl Awi {
16 /// Creates a `Vec<u8>` representing `bits` (sign indicators, prefixes, and
17 /// postfixes not included). This function performs allocation. This is
18 /// a wrapper around [awint_core::Bits::to_bytes_radix] that truncates
19 /// leading zeros. An additional `min_chars` specifies the minimum
20 /// number of characters that should exist. `min_chars` specifies the
21 /// minimum number of chars in the integer part, inserting leading '0's if
22 /// there are not enough chars, just like Rust's built in `{:0d}`
23 /// formatting. Note that an empty vector will be returned if
24 /// `min_chars == 0 && bits.is_zero()`.
25 ///
26 /// # Errors
27 ///
28 /// This can only return an error if `radix` is not in the range 2..=36 or
29 /// if resource exhaustion occurs.
30 pub fn bits_to_vec_radix(
31 bits: &Bits,
32 signed: bool,
33 radix: u8,
34 upper: bool,
35 min_chars: usize,
36 ) -> Result<Vec<u8>, SerdeError> {
37 bits_to_vec_radix(bits, signed, radix, upper, min_chars)
38 }
39
40 /// Creates a string representing `bits`. This function performs allocation.
41 /// This does the same thing as [Awi::bits_to_vec_radix] but with a
42 /// `String`.
43 pub fn bits_to_string_radix(
44 bits: &Bits,
45 signed: bool,
46 radix: u8,
47 upper: bool,
48 min_chars: usize,
49 ) -> Result<String, SerdeError> {
50 bits_to_string_radix(bits, signed, radix, upper, min_chars)
51 }
52
53 /// Creates an `Awi` representing the given arguments. This function
54 /// performs allocation. This is a wrapper around
55 /// [awint_core::Bits::bytes_radix_] that zero or sign resizes the
56 /// result to match `bw`.
57 ///
58 /// # Errors
59 ///
60 /// See the error conditions of [Bits::bytes_radix_]. Note that `-` is
61 /// an invalid character even though `to_vec_radix` can return `-`. This
62 /// is because we need to handle both unsigned and signed integer
63 /// inputs, specified only by `sign`. If the input is a negative signed
64 /// integer representation with `-` appended to the front, the subslice
65 /// `src[1..]` can be taken and `sign` can be set to `Some(true)`.
66 pub fn from_bytes_radix(
67 sign: Option<bool>,
68 src: &[u8],
69 radix: u8,
70 bw: NonZeroUsize,
71 ) -> Result<Awi, SerdeError> {
72 let mut res = Awi::zero(bw);
73 internal_from_bytes_radix(&mut res, sign, src, radix)?;
74 Ok(res)
75 }
76
77 /// Creates an `Awi` representing the given arguments. This does the same
78 /// thing as [Awi::from_bytes_radix] but with an `&str`.
79 pub fn from_str_radix(
80 sign: Option<bool>,
81 str: &str,
82 radix: u8,
83 bw: NonZeroUsize,
84 ) -> Result<Awi, SerdeError> {
85 let mut res = Awi::zero(bw);
86 internal_from_bytes_radix(&mut res, sign, str.as_bytes(), radix)?;
87 Ok(res)
88 }
89
90 /// Creates an `Awi` representing the given arguments. This function
91 /// performs allocation. In addition to the arguments and semantics from
92 /// [Awi::from_bytes_radix], this function includes the ability to deal
93 /// with general fixed point integer deserialization. `src` is now split
94 /// into separate `integer` and `fraction` parts. An exponent `exp` further
95 /// multiplies the numerical value by `radix^exp`. `fp` is the location
96 /// of the fixed point in the output representation of the numerical
97 /// value (e.x. for a plain integer `fp == 0`). `fp` can be negative or
98 /// greater than the bitwidth.
99 ///
100 /// This function uses a single rigorous round-to-even that occurs after
101 /// the exponent and fixed point multiplier are applied and before any
102 /// numerical information is lost.
103 ///
104 /// See [crate::FP::to_vec_general] for the inverse of this function.
105 ///
106 /// # Errors
107 ///
108 /// See the error conditions of [Awi::from_bytes_radix]. The precision
109 /// can now be arbitrarily large (any overflow in the low numerical
110 /// significance direction will be rounded), but overflow can still happen
111 /// in the more significant direction. Empty strings are interpreted as a
112 /// zero value.
113 pub fn from_bytes_general(
114 sign: Option<bool>,
115 integer: &[u8],
116 fraction: &[u8],
117 exp: isize,
118 radix: u8,
119 bw: NonZeroUsize,
120 fp: isize,
121 ) -> Result<Awi, SerdeError> {
122 let mut res = Awi::zero(bw);
123 internal_from_bytes_general(&mut res, sign, integer, fraction, exp, radix, fp)?;
124 Ok(res)
125 }
126
127 /// Creates an `Awi` representing the given arguments. This does the same
128 /// thing as [Awi::from_bytes_general] but with `&str`s.
129 pub fn from_str_general(
130 sign: Option<bool>,
131 integer: &str,
132 fraction: &str,
133 exp: isize,
134 radix: u8,
135 bw: NonZeroUsize,
136 fp: isize,
137 ) -> Result<Awi, SerdeError> {
138 let mut res = Awi::zero(bw);
139 internal_from_bytes_general(
140 &mut res,
141 sign,
142 integer.as_bytes(),
143 fraction.as_bytes(),
144 exp,
145 radix,
146 fp,
147 )?;
148 Ok(res)
149 }
150}
151
152impl core::str::FromStr for Awi {
153 type Err = SerdeError;
154
155 /// Creates an `Awi` described by `s`. There are three modes of operation
156 /// which invoke [Awi::from_str_radix] or [Awi::from_str_general]
157 /// differently.
158 ///
159 /// Note: there is currently a
160 /// [bug](https://github.com/rust-lang/rust/issues/108385) in Rust that
161 /// causes certain fixed point literals to fail to parse when attempting
162 /// to use them in the concatenation macros. In case of getting
163 /// "literal is not supported" errors, use `Awi::from_str` directly.
164 ///
165 /// Additionally, note that it is easy to cause resource exhaustion with
166 /// large bitwidths, exponents, or fixed points that can approach
167 /// `usize::MAX`. In a future version of `awint` we should have a guarded
168 /// function for helping with entering literals through things like UIs.
169 ///
170 /// All valid inputs must begin with '0'-'9' or a '-' followed by '0'-'9'.
171 ///
172 /// If only ' _ ', '0', and '1' chars are present, this function uses binary
173 /// mode. It will interpret the input as a binary string, the number of '0's
174 /// and '1's of which is the bitwidth (including leading '0's and excluding
175 /// ' _ 's). For example: 42 in binary is 101010. If "101010" is entered
176 /// into this function, it will return an `ExtAwi` with bitwidth 6 and
177 /// unsigned value 42. "0000101010" results in bitwidth 10 and unsigned
178 /// value 42. "1111_1111" results in bitwidth 8 and signed value -128 or
179 /// equivalently unsigned value 255.
180 ///
181 /// In integer mode, a decimal bitwidth must be specified after a 'u'
182 /// (unsigned) or 'i' (signed) suffix. A prefix of "0b" specifies a binary
183 /// radix, "0o" specifies an octal radix, "0x" specifies hexadecimal,
184 /// otherwise a decimal radix is used. For example: "42u10" entered into
185 /// this function creates an `ExtAwi` with bitwidth 10 and unsigned
186 /// value 42. "-42i10" results in bitwidth 10 and signed value of -42.
187 /// "0xffff_ffffu32" results in bitwidth 32 and an unsigned value of
188 /// 0xffffffff (also 4294967295 in decimal and u32::MAX).
189 /// "0x1_0000_0000u32" results in an error with `SerdeError::Overflow`,
190 /// because it exceeds the maximum unsigned value for a 32 bit integer.
191 /// "123" results in `SerdeError::EmptyBitwidth`, because it is not in
192 /// binary mode and no bitwidth suffix has been supplied.
193 ///
194 /// If, after the bitwidth, an 'f' char is present, fixed point mode is
195 /// activated. A decimal fixed point position must be specified after the
196 /// 'f' that tells where the fixed point will be in the resulting bits (see
197 /// [crate::FP] for more). If the most significant numerical bit would be
198 /// cut off, `SerdeError::Overflow` is returned.
199 ///
200 /// Additionally, an exponent char 'e' (for non-hexadecimal radixes only) or
201 /// 'p' can be included after the integer or fraction parts but before the
202 /// bitwidth suffix. The exponent as typed uses the radix of the integer
203 /// part, and it is raised to the same radix when modifying the numerical
204 /// value. The exponent can only be negative for fixed point mode. For
205 /// example: "123e5u32" has numerical value 12300000. "123e-5u32" returns an
206 /// error since it is trying to use a negative exponent in integer mode.
207 /// "-0x1234.5678p-3i32f16" has a numerical value of -0x1234.5678 *
208 /// 0x10^-0x3 and uses [Awi::from_bytes_general] to round-to-even to a 32
209 /// bit fixed point number with fixed point position 16. You probably want
210 /// to use underscores to make it clearer where different parts are, e.x.
211 /// "-0x1234.5678_p-3_i32_f16".
212 ///
213 /// For all parts including the integer, fraction, exponent, bitwidth, and
214 /// fixed point parts, if their prefix char exists but there is not at least
215 /// one '0' for them, some kind of empty error is returned. For example:
216 /// "0xu8" should be "0x0u8". ".i8f0" should be "0.0i8f0". "1u32f" should be
217 /// "1u32f0".
218 fn from_str(s: &str) -> Result<Self, Self::Err> {
219 internal_from_str(s, Awi::zero)
220 }
221}