33
44use core:: any:: Any ;
55use core:: cell:: Cell ;
6- use core:: ops :: Deref ;
6+ use core:: marker :: PhantomData ;
77
88/// A zero-cost wrapper guaranteed not to share its memory location with any other valid (in-scope)
99/// variable (even `const` equal to the inner value). Use for `static` variables that have their
1010/// addresses compared with [core::ptr::eq].
1111///
12- /// It has same size, layout and alignment as type parameter `T `.
12+ /// It has same size, layout and alignment as type parameter `OWN `.
1313///
1414/// `T` must implement [Any]. That is automatic for any types that don't have any lifetimes (other
15- /// than `'static`). This requirement givesn an earlier error when `NonDeDuplicated` is used other
15+ /// than `'static`). This requirement gives an earlier error when `NonDeDuplicated` is used other
1616/// than intended.
1717///
1818/// We don't just limit to be `:'static`, because then `NonDeDuplicated` could still be somewhat
@@ -37,17 +37,23 @@ use core::ops::Deref;
3737/// But, by requiring `NonDeDuplicated`'s generic parameter `T` to implement [Any] the first
3838/// example above fails, too. That prevents mistakes earlier.
3939#[ repr( transparent) ]
40- pub struct NonDeDuplicated < T : Any > {
41- cell : Cell < T > ,
40+ pub struct NonDeDuplicatedFlexible < FROM , OWN : Any + Send + Sync , TO : Any + ?Sized > {
41+ cell : Cell < OWN > ,
42+ _f : PhantomData < FROM > ,
43+ _t : PhantomData < TO > ,
4244}
4345
44- impl < T : Any > NonDeDuplicated < T > {
46+ pub type NonDeDuplicated < T > = NonDeDuplicatedFlexible < T , T , T > ;
47+
48+ impl < T : Any + Send + Sync > NonDeDuplicated < T > {
4549 /// Construct a new instance.
4650 pub const fn new ( value : T ) -> Self {
4751 Self {
4852 //Using core::hint::black_box() seems unnecessary.
49- //cell: Cell::new(core::hint::black_box(data )),
53+ //cell: Cell::new(core::hint::black_box(value )),
5054 cell : Cell :: new ( value) ,
55+ _f : PhantomData ,
56+ _t : PhantomData ,
5157 }
5258 }
5359
@@ -58,37 +64,83 @@ impl<T: Any> NonDeDuplicated<T> {
5864 }
5965}
6066
61- impl < T : Any > Deref for NonDeDuplicated < T > {
62- type Target = T ;
63-
64- fn deref ( & self ) -> & Self :: Target {
65- self . get ( )
67+ pub type NonDeDuplicatedStr < const N : usize > = NonDeDuplicatedFlexible < & ' static str , [ u8 ; N ] , str > ;
68+ impl < const N : usize > NonDeDuplicatedStr < N > {
69+ /// Construct a new instance.
70+ pub const fn new ( s : & str ) -> Self {
71+ if s. len ( ) > N {
72+ let msg = match s. len ( ) - N {
73+ 1 => "N is 1 byte too small." ,
74+ 2 => "N is 2 bytes too small." ,
75+ 3 => "N is 3 bytes too small." ,
76+ 4 => "N is 4 bytes too small." ,
77+ _ => "N is more than 4 bytes too small." ,
78+ } ;
79+ panic ! ( "{}" , msg)
80+ }
81+ if s. len ( ) < N {
82+ let msg = match N - s. len ( ) {
83+ 1 => "N is 1 byte too large." ,
84+ 2 => "N is 2 bytes too large." ,
85+ 3 => "N is 3 bytes too large." ,
86+ 4 => "N is 4 bytes too large." ,
87+ _ => "N is more than 4 bytes too large." ,
88+ } ;
89+ panic ! ( "{}" , msg)
90+ }
91+ let bytes: & [ u8 ] = s. as_bytes ( ) ;
92+ let mut arr = [ 0u8 ; N ] ;
93+ let mut i = 0 ;
94+ while i < bytes. len ( ) {
95+ arr[ i] = bytes[ i] ;
96+ i += 1 ;
97+ }
98+ Self {
99+ cell : Cell :: new ( arr) ,
100+ _f : PhantomData ,
101+ _t : PhantomData ,
102+ }
66103 }
67- }
68104
69- impl < T : Any > From < T > for NonDeDuplicated < T > {
70- fn from ( value : T ) -> Self {
71- Self :: new ( value)
105+ /// Get a reference.
106+ ///
107+ /// Implementation details: Since this type, and this function, is intended to be used for
108+ /// `static` or `const` variables, speed doesn't matter. So, we use [core::str::from_utf8]
109+ /// (instead of [core::str::from_utf8_unchecked]).
110+ pub const fn get ( & self ) -> & str {
111+ let ptr = self . cell . as_ptr ( ) ;
112+ let bytes = unsafe { & * ptr } ;
113+ match core:: str:: from_utf8 ( bytes) {
114+ Ok ( s) => s,
115+ Err ( _) => unreachable ! ( ) ,
116+ }
72117 }
73118}
74119
75- /// For now, [Sync] requires that `T ` is both [Sync] AND [Send], following
120+ /// For now, [Sync] requires that `OWN ` is both [Sync] AND [Send], following
76121/// [std::sync::Mutex](https://doc.rust-lang.org/nightly/std/sync/struct.Mutex.html#impl-Sync-for-Mutex%3CT%3E).
77122/// However, from <https://doc.rust-lang.org/nightly/core/marker/trait.Sync.html> it seems that `T:
78123/// Send` may be unnecessary? Please advise.
79124///
80125/// Either way, [NonDeDuplicated] exists specifically for static variables. Those get never moved
81126/// out. So, unlike [std::sync::Mutex], [NonDeDuplicated] itself doesn't need to implement [Send].
82- unsafe impl < T : Any + Send + Sync > Sync for NonDeDuplicated < T > { }
127+ ///
128+ /// Also, unclear if `TO` needs to be [Send] and [Sync].
129+ unsafe impl < FROM , OWN : Any + Send + Sync , TO : Any + ?Sized > Sync
130+ for NonDeDuplicatedFlexible < FROM , OWN , TO >
131+ {
132+ }
83133
84134/// [NonDeDuplicated] is intended for `static` (immutable) variables only. So [Drop::drop] panics in
85- /// debug builds.
86- impl < T : Any > Drop for NonDeDuplicated < T > {
135+ /// debug/miri builds.
136+ impl < FROM , OWN : Any + Send + Sync , TO : Any + ?Sized > Drop
137+ for NonDeDuplicatedFlexible < FROM , OWN , TO >
138+ {
87139 fn drop ( & mut self ) {
88140 // If the client uses Box::leak() or friends, then drop() will NOT happen. That is OK: A
89141 // leaked reference will have static lifetime.
90142 #[ cfg( any( debug_assertions, miri) ) ]
91- panic ! ( "Do not use for local variables or on heap. Use for static variables only." )
143+ panic ! ( "Do not use for local variables, const, or on heap. Use for static variables only." )
92144 }
93145}
94146
@@ -103,16 +155,16 @@ mod tests {
103155 #[ test]
104156 #[ cfg( any( debug_assertions, miri) ) ]
105157 #[ should_panic(
106- expected = "Do not use for local variables or on heap. Use for static variables only."
158+ expected = "Do not use for local variables, const, or on heap. Use for static variables only."
107159 ) ]
108160 fn drop_panics_in_debug_and_miri ( ) {
109- let _: NonDeDuplicated < ( ) > = ( ) . into ( ) ;
161+ let _: NonDeDuplicated < ( ) > = NonDeDuplicated :: new ( ( ) ) ;
110162 }
111163
112164 #[ cfg( not( any( debug_assertions, miri) ) ) ]
113165 #[ test]
114166 fn drop_silent_in_release ( ) {
115- let _: NonDeDuplicated < ( ) > = ( ) . into ( ) ;
167+ let _: NonDeDuplicated < ( ) > = NonDeDuplicated :: new ( ( ) ) ;
116168 }
117169
118170 const U8_CONST : u8 = b'A' ;
@@ -124,20 +176,6 @@ mod tests {
124176 assert ! ( !ptr:: eq( & U8_STATIC_1 , & U8_STATIC_2 ) ) ;
125177 }
126178
127- fn _deref ( ) -> & ' static u8 {
128- static N : NonDeDuplicated < u8 > = NonDeDuplicated :: < u8 > :: new ( 0 ) ;
129- & N
130- }
131-
132- #[ test]
133- fn deref_of_copy_type ( ) {
134- static N : NonDeDuplicated < u8 > = NonDeDuplicated :: < u8 > :: new ( 0 ) ;
135-
136- let deref = & * N ;
137- let get = N . get ( ) ;
138- assert ! ( ptr:: eq( deref, get) ) ;
139- }
140-
141179 #[ cfg( not( any( debug_assertions, miri) ) ) ]
142180 /// In release, [U8_CONST] gets optimized away and points to the same address as
143181 /// [U8_STATIC_1]!
@@ -162,37 +200,44 @@ mod tests {
162200 assert ! ( !ptr:: eq( U8_NDD_REF , & U8_STATIC_2 ) ) ;
163201 }
164202
165- const STR_CONST_FROM_BYTE_ARRAY : & str = {
166- if let Ok ( s) = str:: from_utf8 ( & [ b'H' , b'i' ] ) {
167- s
168- } else {
169- panic ! ( )
170- }
171- } ;
172- const STR_CONST_FROM_BYTE_STRING : & str = {
173- if let Ok ( s) = str:: from_utf8 ( b"Hello" ) {
174- s
175- } else {
176- panic ! ( )
203+ const STR_CONST_FROM_BYTE_ARRAY_HI : & str = {
204+ match str:: from_utf8 ( & [ b'H' , b'i' ] ) {
205+ Ok ( s) => s,
206+ Err ( _) => unreachable ! ( ) ,
177207 }
178208 } ;
179209
180210 #[ cfg( not( miri) ) ]
181211 #[ test]
182- #[ should_panic( expected = "assertion failed: !ptr::eq(STR_CONST_FROM_BYTE_ARRAY , \" Hi\" )" ) ]
212+ #[ should_panic( expected = "assertion failed: !ptr::eq(STR_CONST_FROM_BYTE_ARRAY_HI , \" Hi\" )" ) ]
183213 fn str_global_byte_slice_const_and_local_str_release_and_debug ( ) {
184- assert ! ( !ptr:: eq( STR_CONST_FROM_BYTE_ARRAY , "Hi" ) ) ;
214+ assert ! ( !ptr:: eq( STR_CONST_FROM_BYTE_ARRAY_HI , "Hi" ) ) ;
185215 }
186216 #[ cfg( miri) ]
187217 #[ test]
188218 fn str_global_byte_slice_const_and_local_str_miri ( ) {
189- assert ! ( !ptr:: eq( STR_CONST_FROM_BYTE_ARRAY , "Hi" ) ) ;
219+ assert ! ( !ptr:: eq( STR_CONST_FROM_BYTE_ARRAY_HI , "Hi" ) ) ;
190220 }
191221
222+ const STR_CONST_FROM_BYTE_STRING_HELLO : & str = {
223+ match str:: from_utf8 ( b"Hello" ) {
224+ Ok ( s) => s,
225+ Err ( _) => unreachable ! ( ) ,
226+ }
227+ } ;
228+
192229 /// This is the same for all three: release, debug AND miri!
193230 #[ test]
194- fn str_global_byte_by_byte_const_and_local_static_miri ( ) {
195- assert ! ( ptr:: eq( STR_CONST_FROM_BYTE_STRING , "Hello" ) ) ;
231+ fn str_global_byte_by_byte_const_and_local_static ( ) {
232+ assert ! ( ptr:: eq( STR_CONST_FROM_BYTE_STRING_HELLO , "Hello" ) ) ;
233+ }
234+
235+ static STR_NDD_HI : NonDeDuplicatedStr < 5 > = NonDeDuplicatedStr :: new ( "Hello" ) ;
236+ #[ test]
237+ fn str_ndd_hi ( ) {
238+ assert ! ( !ptr:: eq( STR_NDD_HI . get( ) , "Hi" ) ) ;
239+ assert ! ( !ptr:: eq( STR_NDD_HI . get( ) , STR_CONST_FROM_BYTE_ARRAY_HI ) ) ;
240+ assert ! ( !ptr:: eq( STR_NDD_HI . get( ) , STR_CONST_FROM_BYTE_STRING_HELLO ) ) ;
196241 }
197242
198243 static STR_STATIC : & str = "Ciao" ;
@@ -214,6 +259,14 @@ mod tests {
214259 assert ! ( !ptr:: eq( local_const_based_slice, STR_STATIC ) ) ;
215260 }
216261
262+ static STR_NDD_CIAO : NonDeDuplicatedStr < 4 > = NonDeDuplicatedStr :: new ( "Ciao" ) ;
263+ #[ test]
264+ fn str_local_const_based_and_str_ndd ( ) {
265+ const LOCAL_CONST_ARR : [ u8 ; 4 ] = [ b'C' , b'i' , b'a' , b'o' ] ;
266+ let local_const_based_slice: & str = str:: from_utf8 ( & LOCAL_CONST_ARR ) . unwrap ( ) ;
267+ assert ! ( !ptr:: eq( local_const_based_slice, STR_NDD_CIAO . get( ) ) ) ;
268+ }
269+
217270 mod cross_module_static {
218271 pub static STATIC_OPT_U8_A : Option < u8 > = Some ( b'A' ) ;
219272 }
0 commit comments