alphanumeric_stepper/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
42#![cfg_attr(docsrs, feature(doc_cfg))]
43
44mod errors;
45
46extern crate alloc;
47
48use alloc::{string::String, vec::Vec};
49
50pub use errors::*;
51
52#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct AlphanumericStepper<T = u32> {
55 width: usize,
56 max_numbers: Vec<T>,
57}
58
59impl<T> AlphanumericStepper<T> {
60 #[inline]
62 pub const fn width(&self) -> usize {
63 self.width
64 }
65}
66
67impl<T: Copy> AlphanumericStepper<T> {
68 #[inline]
70 pub fn max_number(&self) -> T {
71 self.max_numbers[self.width]
72 }
73}
74
75macro_rules! impl_alphanumeric_stepper_backend {
76 ($($integer:ty),+ $(,)?) => {
77 $(
78 impl AlphanumericStepper<$integer> {
79 #[inline]
80 fn pow(base: $integer, exp: usize) -> $integer {
81 base.pow(exp as u32)
82 }
83
84 #[inline]
85 fn checked_pow(base: $integer, exp: usize) -> Option<$integer> {
86 let exp_u32: u32 = exp.try_into().ok()?;
87
88 base.checked_pow(exp_u32)
89 }
90
91 fn push_padded_number_to_vec(
92 mut number: $integer,
93 width: usize,
94 v: &mut Vec<u8>,
95 ) {
96 debug_assert!(width > 0);
97
98 let mut divisor = Self::pow(10, width - 1);
99
100 for _ in 0..width {
101 let digit = number / divisor;
102
103 v.push(b'0' + digit as u8);
104
105 number -= digit * divisor;
106 divisor /= 10;
107 }
108 }
109
110 #[inline]
112 pub fn new(width: usize) -> Result<Self, AlphanumericStepperBuildError> {
113 if width == 0 {
114 return Err(AlphanumericStepperBuildError::InvalidWidth);
115 }
116
117 let mut sum: $integer = 0;
118 let mut term = Self::checked_pow(10, width)
119 .ok_or(AlphanumericStepperBuildError::InvalidWidth)?;
120 let mut max_numbers = Vec::with_capacity(width + 1);
121
122 for alphabet_count in 0..=width {
123 sum = sum
124 .checked_add(term)
125 .ok_or(AlphanumericStepperBuildError::InvalidWidth)?;
126
127 max_numbers.push(sum - 1);
128
129 if alphabet_count < width {
130 term = (term / 10)
131 .checked_mul(26)
132 .ok_or(AlphanumericStepperBuildError::InvalidWidth)?;
133 }
134 }
135
136 Ok(Self {
137 width,
138 max_numbers,
139 })
140 }
141
142 #[inline]
144 pub fn encode(&self, number: $integer) -> Result<String, AlphanumericStepperEncodeError> {
145 if number > self.max_number() {
146 return Err(AlphanumericStepperEncodeError::NumberOutOfRange);
147 }
148
149 let mut s = String::with_capacity(self.width);
150
151 self.encode_to_vec_inner(number, unsafe { s.as_mut_vec()} );
152
153 Ok(s)
154 }
155
156 #[inline]
158 pub fn encode_to_string(
159 &self,
160 number: $integer,
161 s: &mut String,
162 ) -> Result<(), AlphanumericStepperEncodeError> {
163 self.encode_to_vec(number, unsafe { s.as_mut_vec()} )
164 }
165
166 #[inline]
168 pub fn encode_to_vec(
169 &self,
170 number: $integer,
171 v: &mut Vec<u8>,
172 ) -> Result<(), AlphanumericStepperEncodeError> {
173 if number > self.max_number() {
174 return Err(AlphanumericStepperEncodeError::NumberOutOfRange);
175 }
176
177 self.encode_to_vec_inner(number, v);
178
179 Ok(())
180 }
181
182 fn encode_to_vec_inner(
184 &self,
185 number: $integer,
186 v: &mut Vec<u8>,
187 ) {
188 v.reserve(self.width);
189
190 if number <= self.max_numbers[0] {
191 Self::push_padded_number_to_vec(number, self.width, v);
192 } else {
193 for (alphabet_count, max_number) in
194 self.max_numbers.iter().copied().enumerate().skip(1)
195 {
196 if number > max_number {
197 continue;
198 }
199
200 let digit_count = self.width - alphabet_count;
201 let mut n = number - self.max_numbers[alphabet_count - 1] - 1;
202 let digit_base = Self::pow(10, digit_count);
203 let mut p = digit_base
204 .wrapping_mul(Self::pow(26, alphabet_count - 1));
205
206 for _ in 0..alphabet_count {
207 let d = n / p;
208
209 v.push(b'A' + d as u8);
210
211 n -= d * p;
212 p /= 26;
213 }
214
215 if digit_count > 0 {
216 Self::push_padded_number_to_vec(n, digit_count, v);
217 }
218
219 break;
220 }
221 }
222 }
223
224 #[cfg(feature = "std")]
226 pub fn encode_to_writer<W>(
227 &self,
228 number: $integer,
229 writer: &mut W,
230 ) -> Result<(), AlphanumericStepperEncodeWriteError>
231 where
232 W: std::io::Write + ?Sized,
233 {
234 if number > self.max_number() {
235 return Err(AlphanumericStepperEncodeWriteError::NumberOutOfRange);
236 }
237
238 if number <= self.max_numbers[0] {
239 std::io::Write::write_fmt(writer, format_args!("{:0>width$}", number, width = self.width))?;
240 } else {
241 for (alphabet_count, max_number) in
242 self.max_numbers.iter().copied().enumerate().skip(1)
243 {
244 if number > max_number {
245 continue;
246 }
247
248 let digit_count = self.width - alphabet_count;
249 let mut n = number - self.max_numbers[alphabet_count - 1] - 1;
250 let digit_base = Self::pow(10, digit_count);
251 let mut p = digit_base
252 .wrapping_mul(Self::pow(26, alphabet_count - 1));
253
254 for _ in 0..alphabet_count {
255 let d = n / p;
256
257 std::io::Write::write_all(writer, &[b'A' + d as u8])?;
258
259 n -= d * p;
260 p /= 26;
261 }
262
263 if digit_count > 0 {
264 std::io::Write::write_fmt(writer, format_args!("{:0>width$}", n, width = digit_count))?;
265 }
266
267 break;
268 }
269 }
270
271 Ok(())
272 }
273
274 pub fn decode(&self, s: impl AsRef<str>) -> Result<$integer, AlphanumericStepperDecodeError> {
276 let s = s.as_ref();
277
278 if s.len() != self.width {
279 return Err(AlphanumericStepperDecodeError::InvalidLength);
280 }
281
282 let bytes = s.as_bytes();
283 let mut alphabet_count = 0;
284
285 while alphabet_count < bytes.len() {
286 let b = bytes[alphabet_count];
287
288 if b.is_ascii_uppercase() {
289 alphabet_count += 1;
290 } else if b.is_ascii_digit() {
291 break;
292 } else {
293 return Err(AlphanumericStepperDecodeError::InvalidCharacter);
294 }
295 }
296
297 for &b in &bytes[alphabet_count..] {
298 if !b.is_ascii_digit() {
299 return Err(AlphanumericStepperDecodeError::InvalidCharacter);
300 }
301 }
302
303 let digit_count = self.width - alphabet_count;
304 let mut number = if alphabet_count == 0 {
305 0
306 } else {
307 self.max_numbers[alphabet_count - 1] + 1
308 };
309
310 let mut n: $integer = 0;
311
312 for &b in &bytes[..alphabet_count] {
313 n = n
314 .wrapping_mul(26)
315 .wrapping_add((b - b'A') as $integer);
316 }
317
318 number = number
319 .wrapping_add(n.wrapping_mul(Self::pow(10, digit_count)));
320
321 n = 0;
322
323 for &b in &bytes[alphabet_count..] {
324 n = n
325 .wrapping_mul(10)
326 .wrapping_add((b - b'0') as $integer);
327 }
328
329 number = number.wrapping_add(n);
330
331 Ok(number)
332 }
333 }
334 )+
335 };
336}
337
338impl_alphanumeric_stepper_backend!(u8, u16, u32, u64, u128, usize);