88
99use crate :: backend:: fd:: { AsFd , AsRawFd } ;
1010use crate :: ffi:: CStr ;
11+ use core:: hint:: unreachable_unchecked;
1112use core:: mem:: { self , MaybeUninit } ;
12- use itoa :: { Buffer , Integer } ;
13+ use core :: num :: NonZeroU8 ;
1314#[ cfg( all( feature = "std" , unix) ) ]
1415use std:: os:: unix:: ffi:: OsStrExt ;
1516#[ cfg( all( feature = "std" , target_os = "wasi" ) ) ]
@@ -36,35 +37,155 @@ use {core::fmt, std::ffi::OsStr, std::path::Path};
3637/// ```
3738#[ derive( Clone ) ]
3839pub struct DecInt {
39- // Enough to hold an {u,i}64 and NUL terminator.
40- buf : [ MaybeUninit < u8 > ; u64:: MAX_STR_LEN + 1 ] ,
41- len : usize ,
40+ buf : [ MaybeUninit < u8 > ; BUF_LEN ] ,
41+ len : NonZeroU8 ,
4242}
43- const _: ( ) = assert ! ( u64 :: MAX_STR_LEN == i64 :: MAX_STR_LEN ) ;
43+
44+ /// Enough to hold an {u,i}64 and NUL terminator.
45+ const BUF_LEN : usize = U64_MAX_STR_LEN + 1 ;
46+
47+ /// Maximum length of a formatted [`u64`].
48+ const U64_MAX_STR_LEN : usize = "18446744073709551615" . len ( ) ;
49+
50+ /// Maximum length of a formatted [`i64`].
51+ #[ allow( dead_code) ]
52+ const I64_MAX_STR_LEN : usize = "-9223372036854775808" . len ( ) ;
53+
54+ const _: ( ) = assert ! ( U64_MAX_STR_LEN == I64_MAX_STR_LEN ) ;
55+
56+ mod private {
57+ pub trait Sealed : Copy {
58+ type Unsigned : super :: Integer ;
59+
60+ fn as_unsigned ( self ) -> ( bool , Self :: Unsigned ) ;
61+ fn eq_zero ( self ) -> bool ;
62+ fn div_mod_10 ( & mut self ) -> u8 ;
63+ }
64+
65+ macro_rules! impl_unsigned {
66+ ( $( $ty: ty) +) => { $(
67+ impl Sealed for $ty {
68+ type Unsigned = $ty;
69+
70+ #[ inline]
71+ fn as_unsigned( self ) -> ( bool , $ty) {
72+ ( false , self )
73+ }
74+
75+ #[ inline]
76+ fn eq_zero( self ) -> bool {
77+ self == 0
78+ }
79+
80+ #[ inline]
81+ fn div_mod_10( & mut self ) -> u8 {
82+ let result = ( * self % 10 ) as u8 ;
83+ * self /= 10 ;
84+ result
85+ }
86+ }
87+ ) + }
88+ }
89+
90+ macro_rules! impl_signed {
91+ ( $( $signed: ty : $unsigned: ty) +) => { $(
92+ impl Sealed for $signed {
93+ type Unsigned = $unsigned;
94+
95+ #[ inline]
96+ fn as_unsigned( self ) -> ( bool , $unsigned) {
97+ if self >= 0 {
98+ ( false , self as $unsigned)
99+ } else {
100+ ( true , !( self as $unsigned) + 1 )
101+ }
102+ }
103+
104+ #[ inline]
105+ fn eq_zero( self ) -> bool {
106+ unimplemented!( )
107+ }
108+
109+ #[ inline]
110+ fn div_mod_10( & mut self ) -> u8 {
111+ unimplemented!( )
112+ }
113+ }
114+ ) + }
115+ }
116+
117+ impl_unsigned ! ( u8 u16 u32 u64 ) ;
118+ impl_signed ! ( i8 : u8 i16 : u16 i32 : u32 i64 : u64 ) ;
119+
120+ #[ cfg( any(
121+ target_pointer_width = "16" ,
122+ target_pointer_width = "32" ,
123+ target_pointer_width = "64"
124+ ) ) ]
125+ const _: ( ) = {
126+ impl_unsigned ! ( usize ) ;
127+ impl_signed ! ( isize : usize ) ;
128+ } ;
129+ }
130+
131+ /// An integer that can be used by [`DecInt::new`].
132+ pub trait Integer : private:: Sealed { }
133+
134+ impl Integer for i8 { }
135+ impl Integer for i16 { }
136+ impl Integer for i32 { }
137+ impl Integer for i64 { }
138+ impl Integer for u8 { }
139+ impl Integer for u16 { }
140+ impl Integer for u32 { }
141+ impl Integer for u64 { }
142+
143+ #[ cfg( any(
144+ target_pointer_width = "16" ,
145+ target_pointer_width = "32" ,
146+ target_pointer_width = "64"
147+ ) ) ]
148+ const _: ( ) = {
149+ impl Integer for isize { }
150+ impl Integer for usize { }
151+ } ;
44152
45153impl DecInt {
46154 /// Construct a new path component from an integer.
47- #[ inline]
48155 pub fn new < Int : Integer > ( i : Int ) -> Self {
49- let mut buf = [ MaybeUninit :: uninit ( ) ; 21 ] ;
50-
51- let mut str_buf = Buffer :: new ( ) ;
52- let str_buf = str_buf. format ( i) ;
53- assert ! (
54- str_buf. len( ) < buf. len( ) ,
55- "{str_buf}{} unsupported." ,
56- core:: any:: type_name:: <Int >( )
57- ) ;
58-
59- buf[ ..str_buf. len ( ) ] . copy_from_slice ( unsafe {
60- // SAFETY: you can always go from init to uninit
61- mem:: transmute :: < & [ u8 ] , & [ MaybeUninit < u8 > ] > ( str_buf. as_bytes ( ) )
62- } ) ;
63- buf[ str_buf. len ( ) ] = MaybeUninit :: new ( 0 ) ;
64-
65- Self {
156+ use private:: Sealed ;
157+
158+ let ( is_neg, mut i) = i. as_unsigned ( ) ;
159+ let mut len = 1 ;
160+ let mut buf = [ MaybeUninit :: uninit ( ) ; BUF_LEN ] ;
161+ buf[ BUF_LEN - 1 ] = MaybeUninit :: new ( b'\0' ) ;
162+
163+ // We use `loop { …; if cond { break } }` instead of `while !cond { … }` so the loop is
164+ // entered at least once. This way `0` does not need a special handling.
165+ loop {
166+ len += 1 ;
167+ if len > BUF_LEN {
168+ // SAFETY: a stringified i64/u64 cannot be longer than `U64_MAX_STR_LEN` bytes
169+ unsafe { unreachable_unchecked ( ) } ;
170+ }
171+ buf[ BUF_LEN - len] = MaybeUninit :: new ( b'0' + i. div_mod_10 ( ) ) ;
172+ if i. eq_zero ( ) {
173+ break ;
174+ }
175+ }
176+
177+ if is_neg {
178+ len += 1 ;
179+ if len > BUF_LEN {
180+ // SAFETY: a stringified i64/u64 cannot be longer than `U64_MAX_STR_LEN` bytes
181+ unsafe { unreachable_unchecked ( ) } ;
182+ }
183+ buf[ BUF_LEN - len] = MaybeUninit :: new ( b'-' ) ;
184+ }
185+
186+ DecInt {
66187 buf,
67- len : str_buf . len ( ) ,
188+ len : NonZeroU8 :: new ( len as u8 ) . unwrap ( ) ,
68189 }
69190 }
70191
@@ -96,7 +217,12 @@ impl DecInt {
96217 /// Return the raw byte buffer including the NUL byte.
97218 #[ inline]
98219 pub fn as_bytes_with_nul ( & self ) -> & [ u8 ] {
99- let init = & self . buf [ ..=self . len ] ;
220+ let len = usize:: from ( self . len . get ( ) ) ;
221+ if len > BUF_LEN {
222+ // SAFETY: a stringified i64/u64 cannot be longer than `U64_MAX_STR_LEN` bytes
223+ unsafe { unreachable_unchecked ( ) } ;
224+ }
225+ let init = & self . buf [ ( self . buf . len ( ) - len) ..] ;
100226 // SAFETY: we're guaranteed to have initialized len+1 bytes.
101227 unsafe { mem:: transmute :: < & [ MaybeUninit < u8 > ] , & [ u8 ] > ( init) }
102228 }
0 commit comments