1 //! This is a base16 (e.g. hexadecimal) encoding and decoding library with an
2 //! emphasis on performance. The API is very similar and inspired by the base64
3 //! crate's API, however it's less complex (base16 is much more simple than
4 //! base64).
5 //!
6 //! # Encoding
7 //!
8 //! The config options at the moment are limited to the output case (upper vs
9 //! lower).
10 //!
11 //! | Function                           | Output                       | Allocates               | Requires `alloc` feature |
12 //! | ---------------------------------- | ---------------------------- | ----------------------- | ------------------------ |
13 //! | [`encode_upper`], [`encode_lower`] | Returns a new `String`       | Always                  | Yes                      |
14 //! | [`encode_config`]                  | Returns a new `String`       | Always                  | Yes                      |
15 //! | [`encode_config_buf`]              | Appends to provided `String` | If buffer needs to grow | Yes                      |
16 //! | [`encode_config_slice`]            | Writes to provided `&[u8]`   | Never                   | No                       |
17 //!
18 //! # Decoding
19 //!
20 //! Note that there are no config options (In the future one might be added to
21 //! restrict the input character set, but it's not clear to me that this is
22 //! useful).
23 //!
24 //! | Function          | Output                        | Allocates               | Requires `alloc` feature |
25 //! | ----------------- | ----------------------------- | ----------------------- | ------------------------ |
26 //! | [`decode`]        | Returns a new `Vec<u8>`       | Always                  | Yes                      |
27 //! | [`decode_slice`]  | Writes to provided `&[u8]`    | Never                   | No                       |
28 //! | [`decode_buf`]    | Appends to provided `Vec<u8>` | If buffer needs to grow | Yes                      |
29 //!
30 //! # Features
31 //!
32 //! This crate has two features, both are enabled by default and exist to allow
33 //! users in `no_std` environments to disable various portions of .
34 //!
35 //! - The `"alloc"` feature, which is on by default, adds a number of helpful
36 //!   functions that require use of the [`alloc`][alloc_crate] crate, but not the
37 //!   rest of `std`.
38 //!     - This is `no_std` compatible.
39 //!     - Each function should list whether or not it requires this feature
40 //!       under the `Availability` of its documentation.
41 //!
42 //! - The `"std"` feature, which is on by default, enables the `"alloc"`
43 //!   feature, and additionally makes [`DecodeError`] implement the
44 //!   `std::error::Error` trait.
45 //!
46 //!     - Frustratingly, this trait is in `std` (and not in `core` or `alloc`),
47 //!       but not implementing it would be quite annoying for some users, so
48 //!       it's kept, even though it's what prevents us from being `no_std`
49 //!       compatible in all configurations.
50 //!
51 //! [alloc_crate]: https://doc.rust-lang.org/alloc/index.html
52 
53 #![cfg_attr(not(feature = "std"), no_std)]
54 #![deny(missing_docs)]
55 
56 #[cfg(feature = "alloc")]
57 extern crate alloc;
58 
59 #[cfg(feature = "alloc")]
60 use alloc::{vec::Vec, string::String};
61 
62 /// Configuration options for encoding. Just specifies whether or not output
63 /// should be uppercase or lowercase.
64 #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
65 pub enum EncConfig {
66     /// Encode using lower case characters for hex values >= 10
67     EncodeLower,
68     /// Encode using upper case characters for hex values >= 10
69     EncodeUpper,
70 }
71 
72 pub use EncConfig::*;
73 
74 #[inline]
encoded_size(source_len: usize) -> usize75 fn encoded_size(source_len: usize) -> usize {
76     const USIZE_TOP_BIT: usize = 1usize << (core::mem::size_of::<usize>() * 8 - 1);
77     if (source_len & USIZE_TOP_BIT) != 0 {
78         usize_overflow(source_len)
79     }
80     source_len << 1
81 }
82 
83 #[inline]
encode_slice_raw(src: &[u8], cfg: EncConfig, dst: &mut [u8])84 fn encode_slice_raw(src: &[u8], cfg: EncConfig, dst: &mut [u8]) {
85     let lut = if cfg == EncodeLower { HEX_LOWER } else { HEX_UPPER };
86     debug_assert!(dst.len() == encoded_size(src.len()));
87     dst.chunks_exact_mut(2).zip(src.iter().copied()).for_each(|(d, sb)| {
88         d[0] = lut[(sb >> 4) as usize];
89         d[1] = lut[(sb & 0xf) as usize];
90     })
91 }
92 
93 #[cfg(feature = "alloc")]
94 #[inline]
encode_to_string(bytes: &[u8], cfg: EncConfig) -> String95 fn encode_to_string(bytes: &[u8], cfg: EncConfig) -> String {
96     let size = encoded_size(bytes.len());
97     let mut buf: Vec<u8> = Vec::with_capacity(size);
98     unsafe { buf.set_len(size); }
99     encode_slice_raw(bytes, cfg, &mut buf);
100     debug_assert!(core::str::from_utf8(&buf).is_ok());
101     unsafe { String::from_utf8_unchecked(buf) }
102 }
103 
104 #[cfg(feature = "alloc")]
105 #[inline]
grow_vec_uninitialized(v: &mut Vec<u8>, grow_by: usize) -> usize106 unsafe fn grow_vec_uninitialized(v: &mut Vec<u8>, grow_by: usize) -> usize {
107     v.reserve(grow_by);
108     let initial_len = v.len();
109     let new_len = initial_len + grow_by;
110     debug_assert!(new_len <= v.capacity());
111     v.set_len(new_len);
112     initial_len
113 }
114 
115 /// Encode bytes as base16, using lower case characters for nibbles between 10
116 /// and 15 (`a` through `f`).
117 ///
118 /// This is equivalent to `base16::encode_config(bytes, base16::EncodeUpper)`.
119 ///
120 /// # Example
121 ///
122 /// ```
123 /// assert_eq!(base16::encode_lower(b"Hello World"), "48656c6c6f20576f726c64");
124 /// assert_eq!(base16::encode_lower(&[0xff, 0xcc, 0xaa]), "ffccaa");
125 /// ```
126 ///
127 /// # Availability
128 ///
129 /// This function is only available when the `alloc` feature is enabled, as it
130 /// needs to produce a String.
131 #[cfg(feature = "alloc")]
132 #[inline]
encode_lower<T: ?Sized + AsRef<[u8]>>(input: &T) -> String133 pub fn encode_lower<T: ?Sized + AsRef<[u8]>>(input: &T) -> String {
134     encode_to_string(input.as_ref(), EncodeLower)
135 }
136 
137 /// Encode bytes as base16, using upper case characters for nibbles between
138 /// 10 and 15 (`A` through `F`).
139 ///
140 /// This is equivalent to `base16::encode_config(bytes, base16::EncodeUpper)`.
141 ///
142 /// # Example
143 ///
144 /// ```
145 /// assert_eq!(base16::encode_upper(b"Hello World"), "48656C6C6F20576F726C64");
146 /// assert_eq!(base16::encode_upper(&[0xff, 0xcc, 0xaa]), "FFCCAA");
147 /// ```
148 ///
149 /// # Availability
150 ///
151 /// This function is only available when the `alloc` feature is enabled, as it
152 /// needs to produce a `String`.
153 #[cfg(feature = "alloc")]
154 #[inline]
encode_upper<T: ?Sized + AsRef<[u8]>>(input: &T) -> String155 pub fn encode_upper<T: ?Sized + AsRef<[u8]>>(input: &T) -> String {
156     encode_to_string(input.as_ref(), EncodeUpper)
157 }
158 
159 
160 /// Encode `input` into a string using the listed config. The resulting string
161 /// contains `input.len() * 2` bytes.
162 ///
163 /// # Example
164 ///
165 /// ```
166 /// let data = [1, 2, 3, 0xaa, 0xbb, 0xcc];
167 /// assert_eq!(base16::encode_config(&data, base16::EncodeLower), "010203aabbcc");
168 /// assert_eq!(base16::encode_config(&data, base16::EncodeUpper), "010203AABBCC");
169 /// ```
170 ///
171 /// # Availability
172 ///
173 /// This function is only available when the `alloc` feature is enabled, as it
174 /// needs to produce a `String`.
175 #[cfg(feature = "alloc")]
176 #[inline]
encode_config<T: ?Sized + AsRef<[u8]>>(input: &T, cfg: EncConfig) -> String177 pub fn encode_config<T: ?Sized + AsRef<[u8]>>(input: &T, cfg: EncConfig) -> String {
178     encode_to_string(input.as_ref(), cfg)
179 }
180 
181 /// Encode `input` into the end of the provided buffer. Returns the number of
182 /// bytes that were written.
183 ///
184 /// Only allocates when `dst.size() + (input.len() * 2) >= dst.capacity()`.
185 ///
186 /// # Example
187 ///
188 /// ```
189 /// let messages = &["Taako, ", "Merle, ", "Magnus"];
190 /// let mut buffer = String::new();
191 /// for msg in messages {
192 ///     let bytes_written = base16::encode_config_buf(msg.as_bytes(),
193 ///                                                   base16::EncodeUpper,
194 ///                                                   &mut buffer);
195 ///     assert_eq!(bytes_written, msg.len() * 2);
196 /// }
197 /// assert_eq!(buffer, "5461616B6F2C204D65726C652C204D61676E7573");
198 /// ```
199 /// # Availability
200 ///
201 /// This function is only available when the `alloc` feature is enabled, as it
202 /// needs write to a `String`.
203 #[cfg(feature = "alloc")]
204 #[inline]
encode_config_buf<T: ?Sized + AsRef<[u8]>>(input: &T, cfg: EncConfig, dst: &mut String) -> usize205 pub fn encode_config_buf<T: ?Sized + AsRef<[u8]>>(input: &T,
206                                                   cfg: EncConfig,
207                                                   dst: &mut String) -> usize {
208     let src = input.as_ref();
209     let bytes_to_write = encoded_size(src.len());
210     // Swap the string out while we work on it, so that if we panic, we don't
211     // leave behind garbage (we do clear the string if we panic, but that's
212     // better than UB)
213     let mut buf = core::mem::replace(dst, String::new()).into_bytes();
214     let cur_size = unsafe { grow_vec_uninitialized(&mut buf, bytes_to_write) };
215 
216     encode_slice_raw(src, cfg, &mut buf[cur_size..]);
217 
218     debug_assert!(core::str::from_utf8(&buf).is_ok());
219     // Put `buf` back into `dst`.
220     *dst = unsafe { String::from_utf8_unchecked(buf) };
221 
222     bytes_to_write
223 }
224 
225 /// Write bytes as base16 into the provided output buffer. Never allocates.
226 ///
227 /// This is useful if you wish to avoid allocation entirely (e.g. your
228 /// destination buffer is on the stack), or control it precisely.
229 ///
230 /// # Panics
231 ///
232 /// Panics if the desination buffer is insufficiently large.
233 ///
234 /// # Example
235 ///
236 /// ```
237 /// # extern crate core as std;
238 /// // Writing to a statically sized buffer on the stack.
239 /// let message = b"Wu-Tang Killa Bees";
240 /// let mut buffer = [0u8; 1024];
241 ///
242 /// let wrote = base16::encode_config_slice(message,
243 ///                                         base16::EncodeLower,
244 ///                                         &mut buffer);
245 ///
246 /// assert_eq!(message.len() * 2, wrote);
247 /// assert_eq!(std::str::from_utf8(&buffer[..wrote]).unwrap(),
248 ///            "57752d54616e67204b696c6c612042656573");
249 ///
250 /// // Appending to an existing buffer is possible too.
251 /// let wrote2 = base16::encode_config_slice(b": The Swarm",
252 ///                                          base16::EncodeLower,
253 ///                                          &mut buffer[wrote..]);
254 /// let write_end = wrote + wrote2;
255 /// assert_eq!(std::str::from_utf8(&buffer[..write_end]).unwrap(),
256 ///            "57752d54616e67204b696c6c6120426565733a2054686520537761726d");
257 /// ```
258 /// # Availability
259 ///
260 /// This function is available whether or not the `alloc` feature is enabled.
261 #[inline]
encode_config_slice<T: ?Sized + AsRef<[u8]>>(input: &T, cfg: EncConfig, dst: &mut [u8]) -> usize262 pub fn encode_config_slice<T: ?Sized + AsRef<[u8]>>(input: &T,
263                                                     cfg: EncConfig,
264                                                     dst: &mut [u8]) -> usize {
265     let src = input.as_ref();
266     let need_size = encoded_size(src.len());
267     if dst.len() < need_size {
268         dest_too_small_enc(dst.len(), need_size);
269     }
270     encode_slice_raw(src, cfg, &mut dst[..need_size]);
271     need_size
272 }
273 
274 /// Encode a single character as hex, returning a tuple containing the two
275 /// encoded bytes in big-endian order -- the order the characters would be in
276 /// when written out (e.g. the top nibble is the first item in the tuple)
277 ///
278 /// # Example
279 /// ```
280 /// assert_eq!(base16::encode_byte(0xff, base16::EncodeLower), [b'f', b'f']);
281 /// assert_eq!(base16::encode_byte(0xa0, base16::EncodeUpper), [b'A', b'0']);
282 /// assert_eq!(base16::encode_byte(3, base16::EncodeUpper), [b'0', b'3']);
283 /// ```
284 /// # Availability
285 ///
286 /// This function is available whether or not the `alloc` feature is enabled.
287 #[inline]
encode_byte(byte: u8, cfg: EncConfig) -> [u8; 2]288 pub fn encode_byte(byte: u8, cfg: EncConfig) -> [u8; 2] {
289     let lut = if cfg == EncodeLower { HEX_LOWER } else { HEX_UPPER };
290     let lo = lut[(byte & 15) as usize];
291     let hi = lut[(byte >> 4) as usize];
292     [hi, lo]
293 }
294 
295 /// Convenience wrapper for `base16::encode_byte(byte, base16::EncodeLower)`
296 ///
297 /// See also `base16::encode_byte_u`.
298 ///
299 /// # Example
300 /// ```
301 /// assert_eq!(base16::encode_byte_l(0xff), [b'f', b'f']);
302 /// assert_eq!(base16::encode_byte_l(30), [b'1', b'e']);
303 /// assert_eq!(base16::encode_byte_l(0x2d), [b'2', b'd']);
304 /// ```
305 /// # Availability
306 ///
307 /// This function is available whether or not the `alloc` feature is enabled.
308 #[inline]
encode_byte_l(byte: u8) -> [u8; 2]309 pub fn encode_byte_l(byte: u8) -> [u8; 2] {
310     encode_byte(byte, EncodeLower)
311 }
312 
313 /// Convenience wrapper for `base16::encode_byte(byte, base16::EncodeUpper)`
314 ///
315 /// See also `base16::encode_byte_l`.
316 ///
317 /// # Example
318 /// ```
319 /// assert_eq!(base16::encode_byte_u(0xff), [b'F', b'F']);
320 /// assert_eq!(base16::encode_byte_u(30), [b'1', b'E']);
321 /// assert_eq!(base16::encode_byte_u(0x2d), [b'2', b'D']);
322 /// ```
323 /// # Availability
324 ///
325 /// This function is available whether or not the `alloc` feature is enabled.
326 #[inline]
encode_byte_u(byte: u8) -> [u8; 2]327 pub fn encode_byte_u(byte: u8) -> [u8; 2] {
328     encode_byte(byte, EncodeUpper)
329 }
330 
331 /// Represents a problem with the data we want to decode.
332 ///
333 /// This implements `std::error::Error` and `Display` if the `std`
334 /// feature is enabled, but only `Display` if it is not.
335 #[derive(Debug, PartialEq, Eq, Clone)]
336 pub enum DecodeError {
337     /// An invalid byte was found in the input (bytes must be `[0-9a-fA-F]`)
338     InvalidByte {
339         /// The index at which the problematic byte was found.
340         index: usize,
341         /// The byte that we cannot decode.
342         byte: u8
343     },
344     /// The length of the input not a multiple of two
345     InvalidLength {
346         /// The input length.
347         length: usize
348     },
349 }
350 
351 #[cold]
invalid_length(length: usize) -> DecodeError352 fn invalid_length(length: usize) -> DecodeError {
353     DecodeError::InvalidLength { length }
354 }
355 
356 #[cold]
invalid_byte(index: usize, src: &[u8]) -> DecodeError357 fn invalid_byte(index: usize, src: &[u8]) -> DecodeError {
358     DecodeError::InvalidByte { index, byte: src[index] }
359 }
360 
361 impl core::fmt::Display for DecodeError {
fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result362     fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
363         match *self {
364             DecodeError::InvalidByte { index, byte } => {
365                 write!(f, "Invalid byte `b{:?}`, at index {}.",
366                        byte as char, index)
367             }
368             DecodeError::InvalidLength { length } =>
369                 write!(f, "Base16 data cannot have length {} (must be even)",
370                        length),
371         }
372     }
373 }
374 
375 #[cfg(feature = "std")]
376 impl std::error::Error for DecodeError {
description(&self) -> &str377     fn description(&self) -> &str {
378         match *self {
379             DecodeError::InvalidByte { .. } => "Illegal byte in base16 data",
380             DecodeError::InvalidLength { .. } => "Illegal length for base16 data",
381         }
382     }
383 
cause(&self) -> Option<&dyn std::error::Error>384     fn cause(&self) -> Option<&dyn std::error::Error> {
385         None
386     }
387 }
388 
389 #[inline]
decode_slice_raw(src: &[u8], dst: &mut[u8]) -> Result<(), usize>390 fn decode_slice_raw(src: &[u8], dst: &mut[u8]) -> Result<(), usize> {
391     // checked in caller.
392     debug_assert!(src.len() / 2 == dst.len());
393     debug_assert!((src.len() & 1) == 0);
394     src.chunks_exact(2).enumerate().zip(dst.iter_mut()).try_for_each(|((si, s), d)| {
395         let r0 = DECODE_LUT[s[0] as usize];
396         let r1 = DECODE_LUT[s[1] as usize];
397         if (r0 | r1) >= 0 {
398             *d = ((r0 << 4) | r1) as u8;
399             Ok(())
400         } else {
401             Err(si * 2)
402         }
403     }).map_err(|bad_idx| raw_decode_err(bad_idx, src))
404 }
405 
406 #[cold]
407 #[inline(never)]
raw_decode_err(idx: usize, src: &[u8]) -> usize408 fn raw_decode_err(idx: usize, src: &[u8]) -> usize {
409     let b0 = src[idx];
410     if decode_byte(b0).is_none() {
411         idx
412     } else {
413         idx + 1
414     }
415 }
416 
417 /// Decode bytes from base16, and return a new `Vec<u8>` containing the results.
418 ///
419 /// # Example
420 ///
421 /// ```
422 /// assert_eq!(&base16::decode("48656c6c6f20576f726c64".as_bytes()).unwrap(),
423 ///            b"Hello World");
424 /// assert_eq!(&base16::decode(b"deadBEEF").unwrap(),
425 ///            &[0xde, 0xad, 0xbe, 0xef]);
426 /// // Error cases:
427 /// assert_eq!(base16::decode(b"Not Hexadecimal!"),
428 ///            Err(base16::DecodeError::InvalidByte { byte: b'N', index: 0 }));
429 /// assert_eq!(base16::decode(b"a"),
430 ///            Err(base16::DecodeError::InvalidLength { length: 1 }));
431 /// ```
432 /// # Availability
433 ///
434 /// This function is only available when the `alloc` feature is enabled, as it
435 /// needs to produce a Vec.
436 #[cfg(feature = "alloc")]
437 #[inline]
decode<T: ?Sized + AsRef<[u8]>>(input: &T) -> Result<Vec<u8>, DecodeError>438 pub fn decode<T: ?Sized + AsRef<[u8]>>(input: &T) -> Result<Vec<u8>, DecodeError> {
439     let src = input.as_ref();
440     if (src.len() & 1) != 0 {
441         return Err(invalid_length(src.len()));
442     }
443     let need_size = src.len() >> 1;
444     let mut dst = Vec::with_capacity(need_size);
445     unsafe { dst.set_len(need_size); }
446     match decode_slice_raw(src, &mut dst) {
447         Ok(()) => Ok(dst),
448         Err(index) => Err(invalid_byte(index, src))
449     }
450 }
451 
452 
453 /// Decode bytes from base16, and appends into the provided buffer. Only
454 /// allocates if the buffer could not fit the data. Returns the number of bytes
455 /// written.
456 ///
457 /// In the case of an error, the buffer should remain the same size.
458 ///
459 /// # Example
460 ///
461 /// ```
462 /// # extern crate core as std;
463 /// # extern crate alloc;
464 /// # use alloc::vec::Vec;
465 /// let mut result = Vec::new();
466 /// assert_eq!(base16::decode_buf(b"4d61646f6b61", &mut result).unwrap(), 6);
467 /// assert_eq!(base16::decode_buf(b"486F6D757261", &mut result).unwrap(), 6);
468 /// assert_eq!(std::str::from_utf8(&result).unwrap(), "MadokaHomura");
469 /// ```
470 /// # Availability
471 ///
472 /// This function is only available when the `alloc` feature is enabled, as it
473 /// needs to write to a Vec.
474 #[cfg(feature = "alloc")]
475 #[inline]
decode_buf<T: ?Sized + AsRef<[u8]>>(input: &T, v: &mut Vec<u8>) -> Result<usize, DecodeError>476 pub fn decode_buf<T: ?Sized + AsRef<[u8]>>(input: &T, v: &mut Vec<u8>) -> Result<usize, DecodeError> {
477     let src = input.as_ref();
478     if (src.len() & 1) != 0 {
479         return Err(invalid_length(src.len()));
480     }
481     // Swap the vec out while we work on it, so that if we panic, we don't leave
482     // behind garbage (this will end up cleared if we panic, but that's better
483     // than UB)
484     let mut work = core::mem::replace(v, Vec::default());
485     let need_size = src.len() >> 1;
486     let current_size = unsafe {
487         grow_vec_uninitialized(&mut work, need_size)
488     };
489     match decode_slice_raw(src, &mut work[current_size..]) {
490         Ok(()) => {
491             // Swap back
492             core::mem::swap(v, &mut work);
493             Ok(need_size)
494         }
495         Err(index) => {
496             work.truncate(current_size);
497             // Swap back
498             core::mem::swap(v, &mut work);
499             Err(invalid_byte(index, src))
500         }
501     }
502 }
503 
504 /// Decode bytes from base16, and write into the provided buffer. Never
505 /// allocates.
506 ///
507 /// In the case of a decoder error, the output is not specified, but in practice
508 /// will remain untouched for an `InvalidLength` error, and will contain the
509 /// decoded input up to the problem byte in the case of an InvalidByte error.
510 ///
511 /// # Panics
512 ///
513 /// Panics if the provided buffer is not large enough for the input.
514 ///
515 /// # Example
516 /// ```
517 /// let msg = "476f6f642072757374206c6962726172696573207573652073696c6c79206578616d706c6573";
518 /// let mut buf = [0u8; 1024];
519 /// assert_eq!(base16::decode_slice(&msg[..], &mut buf).unwrap(), 38);
520 /// assert_eq!(&buf[..38], b"Good rust libraries use silly examples".as_ref());
521 ///
522 /// let msg2 = b"2E20416C736F2C20616E696D65207265666572656e636573";
523 /// assert_eq!(base16::decode_slice(&msg2[..], &mut buf[38..]).unwrap(), 24);
524 /// assert_eq!(&buf[38..62], b". Also, anime references".as_ref());
525 /// ```
526 /// # Availability
527 ///
528 /// This function is available whether or not the `alloc` feature is enabled.
529 #[inline]
decode_slice<T: ?Sized + AsRef<[u8]>>(input: &T, out: &mut [u8]) -> Result<usize, DecodeError>530 pub fn decode_slice<T: ?Sized + AsRef<[u8]>>(input: &T, out: &mut [u8]) -> Result<usize, DecodeError> {
531     let src = input.as_ref();
532     if (src.len() & 1) != 0 {
533         return Err(invalid_length(src.len()));
534     }
535     let need_size = src.len() >> 1;
536     if out.len() < need_size {
537         dest_too_small_dec(out.len(), need_size);
538     }
539     match decode_slice_raw(src, &mut out[..need_size]) {
540         Ok(()) => Ok(need_size),
541         Err(index) => Err(invalid_byte(index, src))
542     }
543 }
544 
545 /// Decode a single character as hex.
546 ///
547 /// Returns `None` for values outside the ASCII hex range.
548 ///
549 /// # Example
550 /// ```
551 /// assert_eq!(base16::decode_byte(b'a'), Some(10));
552 /// assert_eq!(base16::decode_byte(b'B'), Some(11));
553 /// assert_eq!(base16::decode_byte(b'0'), Some(0));
554 /// assert_eq!(base16::decode_byte(b'q'), None);
555 /// assert_eq!(base16::decode_byte(b'x'), None);
556 /// ```
557 /// # Availability
558 ///
559 /// This function is available whether or not the `alloc` feature is enabled.
560 #[inline]
decode_byte(c: u8) -> Option<u8>561 pub fn decode_byte(c: u8) -> Option<u8> {
562     if c.wrapping_sub(b'0') <= 9 {
563         Some(c.wrapping_sub(b'0'))
564     } else if c.wrapping_sub(b'a') < 6 {
565         Some(c.wrapping_sub(b'a') + 10)
566     } else if c.wrapping_sub(b'A') < 6 {
567         Some(c.wrapping_sub(b'A') + 10)
568     } else {
569         None
570     }
571 }
572 static HEX_UPPER: [u8; 16] = *b"0123456789ABCDEF";
573 static HEX_LOWER: [u8; 16] = *b"0123456789abcdef";
574 static DECODE_LUT: [i8; 256] = [
575     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
576     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
577     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  0,  1,  2,  3,  4,  5,
578         6,  7,  8,  9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1,
579     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
580     -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1,
581     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
582     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
583     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
584     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
585     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
586     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
587     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
588     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
589     -1, -1, -1, -1
590 ];
591 // Outlined assertions.
592 #[inline(never)]
593 #[cold]
usize_overflow(len: usize) -> !594 fn usize_overflow(len: usize) -> ! {
595     panic!("usize overflow when computing size of destination: {}", len);
596 }
597 
598 #[cold]
599 #[inline(never)]
dest_too_small_enc(dst_len: usize, need_size: usize) -> !600 fn dest_too_small_enc(dst_len: usize, need_size: usize) -> ! {
601     panic!("Destination is not large enough to encode input: {} < {}", dst_len, need_size);
602 }
603 
604 #[cold]
605 #[inline(never)]
dest_too_small_dec(dst_len: usize, need_size: usize) -> !606 fn dest_too_small_dec(dst_len: usize, need_size: usize) -> ! {
607     panic!("Destination buffer not large enough for decoded input {} < {}", dst_len, need_size);
608 }
609 
610 // encoded_size smoke tests
611 #[cfg(test)]
612 mod tests {
613     use super::*;
614     #[test]
615     #[should_panic]
616     #[cfg(pointer_size )]
test_encoded_size_panic_top_bit()617     fn test_encoded_size_panic_top_bit() {
618         #[cfg(target_pointer_width = "64")]
619         let usz = 0x8000_0000_0000_0000usize;
620         #[cfg(target_pointer_width = "32")]
621         let usz = 0x8000_0000usize;
622         let _ = encoded_size(usz);
623     }
624 
625     #[test]
626     #[should_panic]
test_encoded_size_panic_max()627     fn test_encoded_size_panic_max() {
628         let _ = encoded_size(usize::max_value());
629     }
630 
631     #[test]
test_encoded_size_allows_almost_max()632     fn test_encoded_size_allows_almost_max() {
633         #[cfg(target_pointer_width = "64")]
634         let usz = 0x7fff_ffff_ffff_ffffusize;
635         #[cfg(target_pointer_width = "32")]
636         let usz = 0x7fff_ffffusize;
637         assert_eq!(encoded_size(usz), usz * 2);
638     }
639 }
640