├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.2.3 2 | 3 | - Fix the `&str` example in the readme. 4 | 5 | # 0.2.2 6 | 7 | - Update the readme to reflect the API change in 0.2.1 8 | 9 | # 0.2.1 10 | 11 | - `str` is in `core`, so the `std` feature can be removed. 12 | 13 | # 0.2.0 14 | 15 | - Increased ergonomics of the `numtoa` method to return a `&[u8]` instead of a `usize`. 16 | - Removed the restriction on only accepting `&[u8; 20]` for `numtoa_str`. 17 | 18 | # 0.1.0 19 | 20 | - Added the `std` feature to add the `numtoa_str` method. 21 | - `numtoa_str` returns a `&str` rather than a `usize`. 22 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "numtoa" 3 | version = "0.2.4" 4 | authors = ["Michael Aaron Murphy "] 5 | license = "MIT OR Apache-2.0" 6 | documentation = "https://docs.rs/numtoa" 7 | description = "Convert numbers into stack-allocated byte arrays" 8 | repository = "https://gitlab.com/mmstick/numtoa" 9 | keywords = ["numbers", "convert", "numtoa", "itoa", "no_std"] 10 | categories = ["value-formatting"] 11 | readme = "README.md" 12 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Michael Aaron Murphy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NumToA 2 | 3 | ## `#![no_std]` Compatible with Zero Heap Allocations 4 | 5 | The standard library provides a convenient method of converting numbers into strings, but these strings are 6 | heap-allocated. If you have an application which needs to convert large volumes of numbers into strings, but don't 7 | want to pay the price of heap allocation, this crate provides an efficient `no_std`-compatible method of heaplessly converting numbers 8 | into their string representations, storing the representation within a reusable byte array. 9 | 10 | ## Supports Multiple Bases 11 | 12 | In addition to supporting the standard base 10 conversion, this implementation allows you to select the base of 13 | your choice. Therefore, if you want a binary representation, set the base to 2. If you want hexadecimal, set the 14 | base to 16. 15 | 16 | ## Supports Const Contexts 17 | 18 | This library's API includes `const` functions that can be used to convert numbers into their string representation at compile time, allowing developers to build smaller & faster executables. 19 | 20 | ## `&str` Example 21 | 22 | ```rust 23 | use numtoa::NumToA; 24 | 25 | let mut buffer = [0u8; 20]; 26 | println!("{}", 12345.numtoa_str(10, &mut buffer)); 27 | println!("{}", 256652.numtoa_str(10, &mut buffer)); 28 | ``` 29 | 30 | ## `&[u8]` Example 31 | 32 | ```rust 33 | use numtoa::NumToA; 34 | use std::io::{self, Write}; 35 | 36 | let stdout = io::stdout(); 37 | let mut stdout = stdout.lock(); 38 | let mut buffer = [0u8; 20]; 39 | 40 | let number: u32 = 162392; 41 | let _ = stdout.write(number.numtoa(10, &mut buffer)); 42 | let _ = stdout.write(b"\n"); 43 | assert_eq!(number.numtoa(10, &mut buffer), b"162392"); 44 | 45 | let number: i32 = -6235; 46 | let _ = stdout.write(number.numtoa(10, &mut buffer)); 47 | let _ = stdout.write(b"\n"); 48 | 49 | let number: i8 = -128; 50 | let _ = stdout.write(number.numtoa(10, &mut buffer)); 51 | let _ = stdout.write(b"\n"); 52 | 53 | let number: i8 = 53; 54 | let _ = stdout.write(number.numtoa(10, &mut buffer)); 55 | let _ = stdout.write(b"\n"); 56 | 57 | let number: i16 = -256; 58 | let _ = stdout.write(number.numtoa(10, &mut buffer)); 59 | let _ = stdout.write(b"\n"); 60 | 61 | let number: i16 = -32768; 62 | let _ = stdout.write(number.numtoa(10, &mut buffer)); 63 | let _ = stdout.write(b"\n"); 64 | 65 | let number: u64 = 35320842; 66 | let _ = stdout.write(number.numtoa(10, &mut buffer)); 67 | let _ = stdout.write(b"\n"); 68 | 69 | let number: u64 = 18446744073709551615; 70 | let _ = stdout.write(number.numtoa(10, &mut buffer)); 71 | let _ = stdout.write(b"\n"); 72 | ``` 73 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! The standard library provides a convenient method of converting numbers into strings, but these strings are 2 | //! heap-allocated. If you have an application which needs to convert large volumes of numbers into strings, but don't 3 | //! want to pay the price of heap allocation, this crate provides an efficient `no_std`-compatible method of heaplessly converting numbers 4 | //! into their string representations, storing the representation within a reusable byte array. 5 | //! 6 | //! In addition to supporting the standard base 10 conversion, this implementation allows you to select the base of 7 | //! your choice. Therefore, if you want a binary representation, set the base to 2. If you want hexadecimal, set the 8 | //! base to 16. 9 | //! 10 | //! # Convenience Example 11 | //! 12 | //! ``` 13 | //! use numtoa::NumToA; 14 | //! 15 | //! let mut buf = [0u8; 20]; 16 | //! let mut string = String::new(); 17 | //! 18 | //! for number in (1..10) { 19 | //! string.push_str(number.numtoa_str(10, &mut buf)); 20 | //! string.push('\n'); 21 | //! } 22 | //! 23 | //! println!("{}", string); 24 | //! ``` 25 | //! 26 | //! ## Base 10 Example 27 | //! ``` 28 | //! use numtoa::NumToA; 29 | //! use std::io::{self, Write}; 30 | //! 31 | //! let stdout = io::stdout(); 32 | //! let mut stdout = stdout.lock(); 33 | //! let mut buffer = [0u8; 20]; 34 | //! 35 | //! let number: u32 = 162392; 36 | //! let _ = stdout.write(number.numtoa(10, &mut buffer)); 37 | //! let _ = stdout.write(b"\n"); 38 | //! assert_eq!(number.numtoa(10, &mut buffer), b"162392"); 39 | //! 40 | //! let number: i32 = -6235; 41 | //! let _ = stdout.write(number.numtoa(10, &mut buffer)); 42 | //! let _ = stdout.write(b"\n"); 43 | //! assert_eq!(number.numtoa(10, &mut buffer), b"-6235"); 44 | //! 45 | //! let number: i8 = -128; 46 | //! let _ = stdout.write(number.numtoa(10, &mut buffer)); 47 | //! let _ = stdout.write(b"\n"); 48 | //! assert_eq!(number.numtoa(10, &mut buffer), b"-128"); 49 | //! 50 | //! let number: i8 = 53; 51 | //! let _ = stdout.write(number.numtoa(10, &mut buffer)); 52 | //! let _ = stdout.write(b"\n"); 53 | //! assert_eq!(number.numtoa(10, &mut buffer), b"53"); 54 | //! 55 | //! let number: i16 = -256; 56 | //! let _ = stdout.write(number.numtoa(10, &mut buffer)); 57 | //! let _ = stdout.write(b"\n"); 58 | //! assert_eq!(number.numtoa(10, &mut buffer), b"-256"); 59 | //! 60 | //! let number: i16 = -32768; 61 | //! let _ = stdout.write(number.numtoa(10, &mut buffer)); 62 | //! let _ = stdout.write(b"\n"); 63 | //! assert_eq!(number.numtoa(10, &mut buffer), b"-32768"); 64 | //! 65 | //! let number: u64 = 35320842; 66 | //! let _ = stdout.write(number.numtoa(10, &mut buffer)); 67 | //! let _ = stdout.write(b"\n"); 68 | //! assert_eq!(number.numtoa(10, &mut buffer), b"35320842"); 69 | //! 70 | //! let number: u64 = 18446744073709551615; 71 | //! let _ = stdout.write(number.numtoa(10, &mut buffer)); 72 | //! let _ = stdout.write(b"\n"); 73 | //! assert_eq!(number.numtoa(10, &mut buffer), b"18446744073709551615"); 74 | //! ``` 75 | 76 | #![no_std] 77 | use core::fmt::{Debug, Display, Formatter}; 78 | use core::mem::size_of; 79 | use core::ops::Deref; 80 | use core::str; 81 | 82 | /// Converts a number into a string representation, storing the conversion into a mutable byte slice. 83 | pub trait NumToA { 84 | /// Given a base for encoding and a mutable byte slice, write the number into the byte slice and return the 85 | /// indice where the inner string begins. The inner string can be extracted by slicing the byte slice from 86 | /// that indice. 87 | /// 88 | /// # Panics 89 | /// If the supplied buffer is smaller than the number of bytes needed to write the integer, this will panic. 90 | /// On debug builds, this function will perform a check on base 10 conversions to ensure that the input array 91 | /// is large enough to hold the largest possible value in digits. 92 | /// 93 | /// # Example 94 | /// ``` 95 | /// use numtoa::NumToA; 96 | /// use std::io::{self, Write}; 97 | /// 98 | /// let stdout = io::stdout(); 99 | /// let stdout = &mut io::stdout(); 100 | /// 101 | /// // Allocate a buffer that will be reused in each iteration. 102 | /// let mut buffer = [0u8; 20]; 103 | /// 104 | /// let number = 15325; 105 | /// let _ = stdout.write(number.numtoa(10, &mut buffer)); 106 | /// 107 | /// let number = 1241; 108 | /// let _ = stdout.write(number.numtoa(10, &mut buffer)); 109 | /// 110 | /// assert_eq!(12345.numtoa(10, &mut buffer), b"12345"); 111 | /// ``` 112 | fn numtoa(self, base: Self, string: &mut [u8]) -> &[u8]; 113 | 114 | /// Convenience method for quickly getting a string from the input's array buffer. 115 | fn numtoa_str(self, base: Self, buf: &mut [u8]) -> &str; 116 | } 117 | 118 | // A lookup table to prevent the need for conditional branching 119 | // The value of the remainder of each step will be used as the index 120 | const LOOKUP: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 121 | 122 | // A lookup table optimized for decimal lookups. Each two indices represents one possible number. 123 | const DEC_LOOKUP: &[u8; 200] = b"0001020304050607080910111213141516171819\ 124 | 2021222324252627282930313233343536373839\ 125 | 4041424344454647484950515253545556575859\ 126 | 6061626364656667686970717273747576777879\ 127 | 8081828384858687888990919293949596979899"; 128 | 129 | /// The result of a number conversion to ascii containing a string with at most length `N` bytes/characters 130 | pub struct AsciiNumber { 131 | string: [u8; N], 132 | start: usize, 133 | } 134 | 135 | impl AsciiNumber { 136 | /// Get the ascii representation of the number as a byte slice 137 | pub const fn as_slice(&self) -> &[u8] { 138 | self.string.split_at(self.start).1 139 | } 140 | /// Get the ascii representation of the number as a string slice 141 | pub const fn as_str(&self) -> &str { 142 | unsafe { core::str::from_utf8_unchecked(Self::as_slice(self)) } 143 | } 144 | } 145 | 146 | impl Deref for AsciiNumber { 147 | type Target = str; 148 | fn deref(&self) -> &::Target { 149 | self.as_str() 150 | } 151 | } 152 | 153 | impl Display for AsciiNumber { 154 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> { 155 | Display::fmt(self.as_str(), f) 156 | } 157 | } 158 | 159 | impl Debug for AsciiNumber { 160 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> { 161 | Debug::fmt(self.as_str(), f) 162 | } 163 | } 164 | 165 | macro_rules! copy_2_dec_lut_bytes { 166 | ($to:ident,$to_index:expr,$lut_index:expr) => { 167 | $to[$to_index as usize] = DEC_LOOKUP[$lut_index as usize]; 168 | $to[$to_index as usize+1] = DEC_LOOKUP[$lut_index as usize+1]; 169 | }; 170 | } 171 | 172 | macro_rules! base_10 { 173 | ($number:ident, $index:ident, $string:ident) => { 174 | // Decode four characters at the same time 175 | while $number > 9999 { 176 | let rem = ($number % 10000) as u16; 177 | let (frst, scnd) = ((rem / 100) * 2, (rem % 100) * 2); 178 | copy_2_dec_lut_bytes!($string, $index-3, frst); 179 | copy_2_dec_lut_bytes!($string, $index-1, scnd); 180 | $index = $index.wrapping_sub(4); 181 | $number /= 10000; 182 | } 183 | if $number > 999 { 184 | let (frst, scnd) = (($number / 100) * 2, ($number % 100) * 2); 185 | copy_2_dec_lut_bytes!($string, $index-3, frst); 186 | copy_2_dec_lut_bytes!($string, $index-1, scnd); 187 | $index = $index.wrapping_sub(4); 188 | } else if $number > 99 { 189 | let section = ($number as u16 / 10) * 2; 190 | copy_2_dec_lut_bytes!($string, $index-2, section); 191 | $string[$index] = LOOKUP[($number % 10) as usize]; 192 | $index = $index.wrapping_sub(3); 193 | } else if $number > 9 { 194 | $number *= 2; 195 | copy_2_dec_lut_bytes!($string, $index-1, $number); 196 | $index = $index.wrapping_sub(2); 197 | } else { 198 | $string[$index] = LOOKUP[$number as usize]; 199 | $index = $index.wrapping_sub(1); 200 | } 201 | } 202 | } 203 | 204 | macro_rules! impl_unsigned_numtoa_for { 205 | ( 206 | $type_name:ty, 207 | $core_function_name:ident, 208 | $str_function_name:ident 209 | ) => { 210 | 211 | pub const fn $core_function_name(mut num: $type_name, base: $type_name, string: &mut [u8]) -> &[u8] { 212 | // Check if the buffer is large enough and panic on debug builds if it isn't 213 | if cfg!(debug_assertions) { 214 | if base == 10 { 215 | match size_of::<$type_name>() { 216 | 2 => debug_assert!(string.len() >= 5, "u16 base 10 conversions require at least 5 bytes"), 217 | 4 => debug_assert!(string.len() >= 10, "u32 base 10 conversions require at least 10 bytes"), 218 | 8 => debug_assert!(string.len() >= 20, "u64 base 10 conversions require at least 20 bytes"), 219 | 16 => debug_assert!(string.len() >= 39, "u128 base 10 conversions require at least 39 bytes"), 220 | _ => unreachable!() 221 | } 222 | } 223 | } 224 | 225 | let mut index = string.len() - 1; 226 | if num == 0 { 227 | string[index] = b'0'; 228 | return string.split_at(index).1; 229 | } 230 | 231 | if base == 10 { 232 | // Convert using optimized base 10 algorithm 233 | base_10!(num, index, string); 234 | } else { 235 | while num != 0 { 236 | let rem = num % base; 237 | string[index] = LOOKUP[rem as usize]; 238 | index = index.wrapping_sub(1); 239 | num /= base; 240 | } 241 | } 242 | 243 | string.split_at(index.wrapping_add(1)).1 244 | } 245 | 246 | pub const fn $str_function_name(num: $type_name, base: $type_name, string: &mut [u8]) -> &str { 247 | unsafe { core::str::from_utf8_unchecked($core_function_name(num, base, string)) } 248 | } 249 | 250 | impl NumToA for $type_name { 251 | fn numtoa(self, base: $type_name, string: &mut [u8]) -> &[u8] { 252 | $core_function_name(self, base, string) 253 | } 254 | fn numtoa_str(self, base: $type_name, buf: &mut [u8]) -> &str { 255 | $str_function_name(self, base, buf) 256 | } 257 | } 258 | 259 | } 260 | } 261 | 262 | macro_rules! impl_signed_numtoa_for { 263 | ( 264 | $type_name:ty, 265 | $core_function_name:ident, 266 | $str_function_name:ident 267 | ) => { 268 | 269 | pub const fn $core_function_name(mut num: $type_name, base: $type_name, string: &mut [u8]) -> &[u8] { 270 | if cfg!(debug_assertions) { 271 | if base == 10 { 272 | match size_of::<$type_name>() { 273 | 2 => debug_assert!(string.len() >= 6, "i16 base 10 conversions require at least 6 bytes"), 274 | 4 => debug_assert!(string.len() >= 11, "i32 base 10 conversions require at least 11 bytes"), 275 | 8 => debug_assert!(string.len() >= 19, "i64 base 10 conversions require at least 19 bytes"), 276 | 16 => debug_assert!(string.len() >= 39, "i128 base 10 conversions require at least 39 bytes"), 277 | _ => unreachable!() 278 | } 279 | } 280 | } 281 | 282 | let mut index = string.len() - 1; 283 | let mut is_negative = false; 284 | 285 | if num < 0 { 286 | is_negative = true; 287 | num = match num.checked_abs() { 288 | Some(value) => value, 289 | None => { 290 | let value = <$type_name>::max_value(); 291 | string[index] = LOOKUP[((value % base + 1) % base) as usize]; 292 | index -= 1; 293 | value / base + ((value % base == base - 1) as $type_name) 294 | } 295 | }; 296 | } else if num == 0 { 297 | string[index] = b'0'; 298 | return string.split_at(index).1; 299 | } 300 | 301 | if base == 10 { 302 | // Convert using optimized base 10 algorithm 303 | base_10!(num, index, string); 304 | } else { 305 | while num != 0 { 306 | let rem = num % base; 307 | string[index] = LOOKUP[rem as usize]; 308 | index = index.wrapping_sub(1); 309 | num /= base; 310 | } 311 | } 312 | 313 | if is_negative { 314 | string[index] = b'-'; 315 | index = index.wrapping_sub(1); 316 | } 317 | 318 | string.split_at(index.wrapping_add(1)).1 319 | } 320 | 321 | pub const fn $str_function_name(num: $type_name, base: $type_name, string: &mut [u8]) -> &str { 322 | unsafe { core::str::from_utf8_unchecked($core_function_name(num, base, string)) } 323 | } 324 | 325 | impl NumToA for $type_name { 326 | fn numtoa(self, base: $type_name, string: &mut [u8]) -> &[u8] { 327 | $core_function_name(self, base, string) 328 | } 329 | 330 | fn numtoa_str(self, base: $type_name, buf: &mut [u8]) -> &str { 331 | $str_function_name(self, base, buf) 332 | } 333 | } 334 | } 335 | } 336 | 337 | impl_signed_numtoa_for!(i16,numtoa_i16,numtoa_i16_str); 338 | impl_signed_numtoa_for!(i32,numtoa_i32,numtoa_i32_str); 339 | impl_signed_numtoa_for!(i64,numtoa_i64,numtoa_i64_str); 340 | impl_signed_numtoa_for!(i128,numtoa_i128,numtoa_i128_str); 341 | impl_signed_numtoa_for!(isize,numtoa_isize,numtoa_isize_str); 342 | impl_unsigned_numtoa_for!(u16,numtoa_u16,numtoa_u16_str); 343 | impl_unsigned_numtoa_for!(u32,numtoa_u32,numtoa_u32_str); 344 | impl_unsigned_numtoa_for!(u64,numtoa_u64,numtoa_u64_str); 345 | impl_unsigned_numtoa_for!(u128,numtoa_u128,numtoa_u128_str); 346 | impl_unsigned_numtoa_for!(usize,numtoa_usize,numtoa_usize_str); 347 | 348 | pub const fn numtoa_i8(mut num: i8, base: i8, string: &mut [u8]) -> &[u8] { 349 | if cfg!(debug_assertions) { 350 | if base == 10 { 351 | debug_assert!(string.len() >= 4, "i8 conversions need at least 4 bytes"); 352 | } 353 | } 354 | 355 | let mut index = string.len() - 1; 356 | let mut is_negative = false; 357 | 358 | if num < 0 { 359 | is_negative = true; 360 | num = match num.checked_abs() { 361 | Some(value) => value, 362 | None => { 363 | let value = ::max_value(); 364 | string[index] = LOOKUP[((value % base + 1) % base) as usize]; 365 | index -= 1; 366 | value / base + ((value % base == base - 1) as i8) 367 | } 368 | }; 369 | } else if num == 0 { 370 | string[index] = b'0'; 371 | return string.split_at(index).1; 372 | } 373 | 374 | if base == 10 { 375 | if num > 99 { 376 | let section = (num / 10) * 2; 377 | copy_2_dec_lut_bytes!(string, index-2, section); 378 | string[index] = LOOKUP[(num % 10) as usize]; 379 | index = index.wrapping_sub(3); 380 | } else if num > 9 { 381 | let idx = num as usize * 2; 382 | copy_2_dec_lut_bytes!(string, index-1, idx); 383 | index = index.wrapping_sub(2); 384 | } else { 385 | string[index] = LOOKUP[num as usize]; 386 | index = index.wrapping_sub(1); 387 | } 388 | } else { 389 | while num != 0 { 390 | let rem = num % base; 391 | string[index] = LOOKUP[rem as usize]; 392 | index = index.wrapping_sub(1); 393 | num /= base; 394 | } 395 | } 396 | 397 | if is_negative { 398 | string[index] = b'-'; 399 | index = index.wrapping_sub(1); 400 | } 401 | 402 | string.split_at(index.wrapping_add(1)).1 403 | } 404 | 405 | pub const fn numtoa_i8_str(num: i8, base: i8, string: &mut [u8]) -> &str { 406 | unsafe { str::from_utf8_unchecked(numtoa_i8(num, base, string)) } 407 | } 408 | 409 | impl NumToA for i8 { 410 | fn numtoa(self, base: i8, string: &mut [u8]) -> &[u8] { 411 | numtoa_i8(self, base, string) 412 | } 413 | 414 | fn numtoa_str(self, base: Self, buf: &mut [u8]) -> &str { 415 | numtoa_i8_str(self, base, buf) 416 | } 417 | } 418 | 419 | pub const fn numtoa_u8(mut num: u8, base: u8, string: &mut [u8]) -> &[u8] { 420 | if cfg!(debug_assertions) { 421 | if base == 10 { 422 | debug_assert!(string.len() >= 3, "u8 conversions need at least 3 bytes"); 423 | } 424 | } 425 | 426 | let mut index = string.len() - 1; 427 | if num == 0 { 428 | string[index] = b'0'; 429 | return string.split_at(index).1; 430 | } 431 | 432 | if base == 10 { 433 | if num > 99 { 434 | let section = (num / 10) * 2; 435 | copy_2_dec_lut_bytes!(string, index-2, section); 436 | string[index] = LOOKUP[(num % 10) as usize]; 437 | index = index.wrapping_sub(3); 438 | } else if num > 9 { 439 | num *= 2; 440 | copy_2_dec_lut_bytes!(string, index-1, num); 441 | index = index.wrapping_sub(2); 442 | } else { 443 | string[index] = LOOKUP[num as usize]; 444 | index = index.wrapping_sub(1); 445 | } 446 | } else { 447 | while num != 0 { 448 | let rem = num % base; 449 | string[index] = LOOKUP[rem as usize]; 450 | index = index.wrapping_sub(1); 451 | num /= base; 452 | } 453 | } 454 | 455 | string.split_at(1).1 456 | } 457 | 458 | pub const fn numtoa_u8_str(num: u8, base: u8, string: &mut [u8]) -> &str { 459 | unsafe { str::from_utf8_unchecked(numtoa_u8(num, base, string)) } 460 | } 461 | 462 | impl NumToA for u8 { 463 | fn numtoa(self, base: u8, string: &mut [u8]) -> &[u8] { 464 | numtoa_u8(self, base, string) 465 | } 466 | 467 | fn numtoa_str(self, base: Self, buf: &mut [u8]) -> &str { 468 | numtoa_u8_str(self, base, buf) 469 | } 470 | } 471 | 472 | macro_rules! impl_numtoa_base_n_init_for { 473 | ( 474 | $type_name:ty, 475 | $base:expr, 476 | $core_function_name:ident, 477 | $base_n_function_name:ident, 478 | $needed_buffer_size:expr) => { 479 | 480 | pub const fn $base_n_function_name(num: $type_name) -> AsciiNumber<$needed_buffer_size> { 481 | let mut string = [0_u8; $needed_buffer_size]; 482 | let len = $core_function_name(num, $base, &mut string).len(); 483 | return AsciiNumber { string, start:$needed_buffer_size-len} 484 | } 485 | 486 | }; 487 | } 488 | 489 | pub mod base10 { 490 | 491 | use AsciiNumber; 492 | use numtoa_u8; 493 | use numtoa_u16; 494 | use numtoa_u32; 495 | use numtoa_u64; 496 | use numtoa_u128; 497 | use numtoa_i8; 498 | use numtoa_i16; 499 | use numtoa_i32; 500 | use numtoa_i64; 501 | use numtoa_i128; 502 | 503 | impl_numtoa_base_n_init_for!(u8,10,numtoa_u8,u8,3); // 255 504 | impl_numtoa_base_n_init_for!(u16,10,numtoa_u16,u16,5); // 65535 505 | impl_numtoa_base_n_init_for!(u32,10,numtoa_u32,u32,10); // 4294967295 506 | impl_numtoa_base_n_init_for!(u64,10,numtoa_u64,u64,20); // 18446744073709551615 507 | impl_numtoa_base_n_init_for!(u128,10,numtoa_u128,u128,39); // 340282366920938463463374607431768211455 508 | impl_numtoa_base_n_init_for!(i8,10,numtoa_i8,i8,4); // -128 509 | impl_numtoa_base_n_init_for!(i16,10,numtoa_i16,i16,6); // -32768 510 | impl_numtoa_base_n_init_for!(i32,10,numtoa_i32,i32,11); // -2147483648 511 | impl_numtoa_base_n_init_for!(i64,10,numtoa_i64,i64,20); // -9223372036854775808 512 | impl_numtoa_base_n_init_for!(i128,10,numtoa_i128,i128,40); // -170141183460469231731687303715884105728 513 | 514 | } 515 | 516 | #[test] 517 | fn str_convenience_core() { 518 | assert_eq!("256123", numtoa_i32_str(256123_i32, 10, &mut [0u8; 20])); 519 | } 520 | 521 | #[test] 522 | fn str_convenience_trait() { 523 | assert_eq!("256123", 256123.numtoa_str(10, &mut [0u8; 20])); 524 | } 525 | 526 | #[test] 527 | fn str_convenience_base10() { 528 | assert_eq!("256123", base10::i32(256123).as_str()); 529 | } 530 | 531 | #[test] 532 | #[should_panic] 533 | fn base10_u8_array_too_small_core() { 534 | let _ = numtoa_u8(0_u8, 10, &mut [0u8; 2]); 535 | } 536 | 537 | #[test] 538 | #[should_panic] 539 | fn base10_u8_array_too_small_trait() { 540 | let _ = 0u8.numtoa(10, &mut [0u8; 2]); 541 | } 542 | 543 | #[test] 544 | fn base10_u8_array_just_right_core() { 545 | let _ = numtoa_u8(0, 10, &mut [0u8; 3]); 546 | } 547 | 548 | #[test] 549 | fn base10_u8_array_just_right_trait() { 550 | let _ = 0u8.numtoa(10, &mut [0u8; 3]); 551 | } 552 | 553 | #[test] 554 | #[should_panic] 555 | fn base10_i8_array_too_small() { 556 | let mut buffer = [0u8; 3]; 557 | let _ = 0i8.numtoa(10, &mut buffer); 558 | } 559 | 560 | #[test] 561 | fn base10_i8_array_just_right() { 562 | let mut buffer = [0u8; 4]; 563 | assert_eq!((-127i8).numtoa(10, &mut buffer), b"-127"); 564 | } 565 | 566 | #[test] 567 | #[should_panic] 568 | fn base10_i16_array_too_small() { 569 | let mut buffer = [0u8; 5]; 570 | let _ = 0i16.numtoa(10, &mut buffer); 571 | } 572 | 573 | #[test] 574 | fn base10_i16_array_just_right() { 575 | let mut buffer = [0u8; 6]; 576 | assert_eq!((-12768i16).numtoa(10, &mut buffer), b"-12768"); 577 | } 578 | 579 | #[test] 580 | #[should_panic] 581 | fn base10_u16_array_too_small() { 582 | let mut buffer = [0u8; 4]; 583 | let _ = 0u16.numtoa(10, &mut buffer); 584 | } 585 | 586 | #[test] 587 | fn base10_u16_array_just_right() { 588 | let mut buffer = [0u8; 5]; 589 | let _ = 0u16.numtoa(10, &mut buffer); 590 | } 591 | 592 | #[test] 593 | #[should_panic] 594 | fn base10_i32_array_too_small() { 595 | let mut buffer = [0u8; 10]; 596 | let _ = 0i32.numtoa(10, &mut buffer); 597 | } 598 | 599 | #[test] 600 | fn base10_i32_array_just_right() { 601 | let mut buffer = [0u8; 11]; 602 | let _ = 0i32.numtoa(10, &mut buffer); 603 | } 604 | 605 | #[test] 606 | #[should_panic] 607 | fn base10_u32_array_too_small() { 608 | let mut buffer = [0u8; 9]; 609 | let _ = 0u32.numtoa(10, &mut buffer); 610 | } 611 | 612 | #[test] 613 | fn base10_u32_array_just_right() { 614 | let mut buffer = [0u8; 10]; 615 | let _ = 0u32.numtoa(10, &mut buffer); 616 | } 617 | 618 | #[test] 619 | #[should_panic] 620 | fn base10_i64_array_too_small() { 621 | let mut buffer = [0u8; 18]; 622 | let _ = 0i64.numtoa(10, &mut buffer); 623 | } 624 | 625 | #[test] 626 | fn base10_i64_array_just_right() { 627 | let mut buffer = [0u8; 19]; 628 | let _ = 0i64.numtoa(10, &mut buffer); 629 | } 630 | 631 | #[test] 632 | #[should_panic] 633 | fn base10_u64_array_too_small() { 634 | let mut buffer = [0u8; 19]; 635 | let _ = 0u64.numtoa(10, &mut buffer); 636 | } 637 | 638 | #[test] 639 | fn base10_u64_array_just_right() { 640 | let mut buffer = [0u8; 20]; 641 | let _ = 0u64.numtoa(10, &mut buffer); 642 | } 643 | 644 | #[test] 645 | fn base10_i8_all_core() { 646 | for i in i8::MIN..i8::MAX { 647 | let _ = numtoa_i8(i, 10, &mut [0u8; 4]); 648 | } 649 | } 650 | 651 | #[test] 652 | fn base10_i8_all_trait() { 653 | for i in i8::MIN..i8::MAX { 654 | let _ = i.numtoa(10, &mut [0u8; 4]); 655 | } 656 | } 657 | 658 | #[test] 659 | fn base10_i8_all_base10() { 660 | for i in i8::MIN..i8::MAX { 661 | let _ = base10::i8(i); 662 | } 663 | } 664 | 665 | #[test] 666 | fn base10_u8_all() { 667 | let mut buffer = [0u8; 3]; 668 | for i in u8::MIN..u8::MAX { 669 | let _ = i.numtoa(10, &mut buffer); 670 | } 671 | } 672 | 673 | #[test] 674 | #[should_panic] 675 | fn base10_i128_array_too_small() { 676 | let mut buffer = [0u8; 38]; 677 | let _ = 0i128.numtoa(10, &mut buffer); 678 | } 679 | 680 | #[test] 681 | fn base10_i128_array_just_right() { 682 | let mut buffer = [0u8; 39]; 683 | let _ = 0i128.numtoa(10, &mut buffer); 684 | } 685 | 686 | #[test] 687 | #[should_panic] 688 | fn base10_u128_array_too_small() { 689 | let mut buffer = [0u8; 38]; 690 | let _ = 0u128.numtoa(10, &mut buffer); 691 | } 692 | 693 | #[test] 694 | fn base10_u128_array_just_right() { 695 | let mut buffer = [0u8; 39]; 696 | let _ = 0u128.numtoa(10, &mut buffer); 697 | } 698 | 699 | #[test] 700 | fn base8_min_signed_number() { 701 | let mut buffer = [0u8; 50]; 702 | assert_eq!((-128i8).numtoa(8, &mut buffer), b"-200"); 703 | assert_eq!((-32768i16).numtoa(8, &mut buffer), b"-100000"); 704 | assert_eq!((-2147483648i32).numtoa(8, &mut buffer), b"-20000000000"); 705 | assert_eq!((-9223372036854775808i64).numtoa(8, &mut buffer), b"-1000000000000000000000"); 706 | assert_eq!((i128::MIN).numtoa(8, &mut buffer), b"-2000000000000000000000000000000000000000000"); 707 | } 708 | 709 | #[test] 710 | fn base16_min_signed_number() { 711 | let mut buffer = [0u8; 40]; 712 | assert_eq!((-128i8).numtoa(16, &mut buffer), b"-80"); 713 | assert_eq!((-32768i16).numtoa(16, &mut buffer), b"-8000"); 714 | assert_eq!((-2147483648i32).numtoa(16, &mut buffer), b"-80000000"); 715 | assert_eq!((-9223372036854775808i64).numtoa(16, &mut buffer), b"-8000000000000000"); 716 | assert_eq!((i128::MIN).numtoa(16, &mut buffer), b"-80000000000000000000000000000000"); 717 | } 718 | --------------------------------------------------------------------------------