@@ -52,55 +52,121 @@ use std::iter::zip;
52
52
/// This optimizes for the common cases where `buf.len()` is a small power of 2,
53
53
/// where the array write is optimized as few and large stores as possible.
54
54
#[ inline]
55
- pub fn small_memset < T : Clone + Copy , const UP_TO : usize , const WITH_DEFAULT : bool > (
55
+ pub fn small_memset < T : Clone + Copy , const N : usize , const WITH_DEFAULT : bool > (
56
56
buf : & mut [ T ] ,
57
57
val : T ,
58
58
) {
59
59
fn as_array < T : Clone + Copy , const N : usize > ( buf : & mut [ T ] ) -> & mut [ T ; N ] {
60
60
buf. try_into ( ) . unwrap ( )
61
61
}
62
- match buf. len ( ) {
63
- 01 if UP_TO >= 01 => * as_array ( buf) = [ val; 01 ] ,
64
- 02 if UP_TO >= 02 => * as_array ( buf) = [ val; 02 ] ,
65
- 04 if UP_TO >= 04 => * as_array ( buf) = [ val; 04 ] ,
66
- 08 if UP_TO >= 08 => * as_array ( buf) = [ val; 08 ] ,
67
- 16 if UP_TO >= 16 => * as_array ( buf) = [ val; 16 ] ,
68
- 32 if UP_TO >= 32 => * as_array ( buf) = [ val; 32 ] ,
69
- 64 if UP_TO >= 64 => * as_array ( buf) = [ val; 64 ] ,
70
- _ => {
71
- if WITH_DEFAULT {
72
- buf. fill ( val)
73
- }
62
+ if N == 0 {
63
+ if WITH_DEFAULT {
64
+ buf. fill ( val)
74
65
}
66
+ } else {
67
+ assert ! ( buf. len( ) == N ) ; // Meant to be optimized out.
68
+ * as_array ( buf) = [ val; N ] ;
75
69
}
76
70
}
77
71
78
- pub struct CaseSetter < const UP_TO : usize , const WITH_DEFAULT : bool > {
72
+ pub trait CaseSetter {
73
+ fn set < T : Clone + Copy > ( & self , buf : & mut [ T ] , val : T ) ;
74
+
75
+ /// # Safety
76
+ ///
77
+ /// Caller must ensure that no elements of the written range are concurrently
78
+ /// borrowed (immutably or mutably) at all during the call to `set_disjoint`.
79
+ fn set_disjoint < T , V > ( & self , buf : & DisjointMut < T > , val : V )
80
+ where
81
+ T : AsMutPtr < Target = V > ,
82
+ V : Clone + Copy ;
83
+ }
84
+
85
+ pub struct CaseSetterN < const N : usize , const WITH_DEFAULT : bool > {
79
86
offset : usize ,
80
87
len : usize ,
81
88
}
82
89
83
- impl < const UP_TO : usize , const WITH_DEFAULT : bool > CaseSetter < UP_TO , WITH_DEFAULT > {
90
+ impl < const N : usize , const WITH_DEFAULT : bool > CaseSetterN < N , WITH_DEFAULT > {
91
+ const fn len ( & self ) -> usize {
92
+ if N == 0 {
93
+ self . len
94
+ } else {
95
+ N
96
+ }
97
+ }
98
+ }
99
+
100
+ impl < const N : usize , const WITH_DEFAULT : bool > CaseSetter for CaseSetterN < N , WITH_DEFAULT > {
84
101
#[ inline]
85
- pub fn set < T : Clone + Copy > ( & self , buf : & mut [ T ] , val : T ) {
86
- small_memset :: < T , UP_TO , WITH_DEFAULT > ( & mut buf[ self . offset ..] [ ..self . len ] , val) ;
102
+ fn set < T : Clone + Copy > ( & self , buf : & mut [ T ] , val : T ) {
103
+ small_memset :: < _ , N , WITH_DEFAULT > ( & mut buf[ self . offset ..] [ ..self . len ( ) ] , val) ;
87
104
}
88
105
89
106
/// # Safety
90
107
///
91
108
/// Caller must ensure that no elements of the written range are concurrently
92
109
/// borrowed (immutably or mutably) at all during the call to `set_disjoint`.
93
110
#[ inline]
94
- pub fn set_disjoint < T , V > ( & self , buf : & DisjointMut < T > , val : V )
111
+ fn set_disjoint < T , V > ( & self , buf : & DisjointMut < T > , val : V )
95
112
where
96
113
T : AsMutPtr < Target = V > ,
97
114
V : Clone + Copy ,
98
115
{
99
- let mut buf = buf. index_mut ( self . offset ..self . offset + self . len ) ;
100
- small_memset :: < V , UP_TO , WITH_DEFAULT > ( & mut * buf, val) ;
116
+ let mut buf = buf. index_mut ( ( self . offset .., .. self . len ( ) ) ) ;
117
+ small_memset :: < _ , N , WITH_DEFAULT > ( & mut * buf, val) ;
101
118
}
102
119
}
103
120
121
+ /// Rank-2 polymorphic closures aren't a thing in Rust yet,
122
+ /// so we need to emulate this through a generic trait with a generic method.
123
+ /// Unforunately, this means we have to write the closure sugar manually.
124
+ pub trait SetCtx < T > {
125
+ fn call < S : CaseSetter > ( self , case : & S , ctx : T ) -> Self ;
126
+ }
127
+
128
+ /// Emulate a closure for a [`SetCtx`] `impl`.
129
+ macro_rules! set_ctx {
130
+ (
131
+ // `||` is used instead of just `|` due to this bug: <https://github.com/rust-lang/rustfmt/issues/6228>.
132
+ ||
133
+ $( $lifetime: lifetime, ) ?
134
+ $case: ident,
135
+ $ctx: ident: $T: ty,
136
+ // Note that the required trailing `,` is so `:expr` can precede `|`.
137
+ $( $up_var: ident: $up_var_ty: ty$( = $up_var_val: expr) ?, ) *
138
+ || $body: block
139
+ ) => { {
140
+ use $crate:: src:: ctx:: SetCtx ;
141
+ use $crate:: src:: ctx:: CaseSetter ;
142
+
143
+ struct F $( <$lifetime>) ? {
144
+ $( $up_var: $up_var_ty, ) *
145
+ }
146
+
147
+ impl $( <$lifetime>) ? SetCtx <$T> for F $( <$lifetime>) ? {
148
+ fn call<S : CaseSetter >( self , $case: & S , $ctx: $T) -> Self {
149
+ let Self {
150
+ $( $up_var, ) *
151
+ } = self ;
152
+ $body
153
+ // We destructure and re-structure `Self` so that we
154
+ // can move out of refs without using `ref`/`ref mut`,
155
+ // which I don't know how to match on in a macro.
156
+ Self {
157
+ $( $up_var, ) *
158
+ }
159
+ }
160
+ }
161
+
162
+ F {
163
+ $( $up_var$( : $up_var_val) ?, ) *
164
+ }
165
+ } } ;
166
+ }
167
+
168
+ pub ( crate ) use set_ctx;
169
+
104
170
/// The entrypoint to the [`CaseSet`] API.
105
171
///
106
172
/// `UP_TO` and `WITH_DEFAULT` are made const generic parameters rather than have multiple `case_set*` `fn`s,
@@ -117,11 +183,25 @@ impl<const UP_TO: usize, const WITH_DEFAULT: bool> CaseSet<UP_TO, WITH_DEFAULT>
117
183
/// The `len` and `offset` are supplied here and
118
184
/// applied to each `buf` passed to [`CaseSetter::set`] in `set_ctx`.
119
185
#[ inline]
120
- pub fn one < T , F > ( ctx : T , len : usize , offset : usize , mut set_ctx : F )
186
+ pub fn one < T , F > ( ctx : T , len : usize , offset : usize , set_ctx : F ) -> F
121
187
where
122
- F : FnMut ( & CaseSetter < UP_TO , WITH_DEFAULT > , T ) ,
188
+ F : SetCtx < T > ,
123
189
{
124
- set_ctx ( & CaseSetter { offset, len } , ctx) ;
190
+ macro_rules! set_ctx {
191
+ ( $N: literal) => {
192
+ set_ctx. call( & CaseSetterN :: <$N, WITH_DEFAULT > { offset, len } , ctx)
193
+ } ;
194
+ }
195
+ match len {
196
+ 01 if UP_TO >= 01 => set_ctx ! ( 01 ) ,
197
+ 02 if UP_TO >= 02 => set_ctx ! ( 02 ) ,
198
+ 04 if UP_TO >= 04 => set_ctx ! ( 04 ) ,
199
+ 08 if UP_TO >= 08 => set_ctx ! ( 08 ) ,
200
+ 16 if UP_TO >= 16 => set_ctx ! ( 16 ) ,
201
+ 32 if UP_TO >= 32 => set_ctx ! ( 32 ) ,
202
+ 64 if UP_TO >= 64 => set_ctx ! ( 64 ) ,
203
+ _ => set_ctx ! ( 0 ) ,
204
+ }
125
205
}
126
206
127
207
/// Perform many case sets in one call.
@@ -138,10 +218,10 @@ impl<const UP_TO: usize, const WITH_DEFAULT: bool> CaseSet<UP_TO, WITH_DEFAULT>
138
218
offsets : [ usize ; N ] ,
139
219
mut set_ctx : F ,
140
220
) where
141
- F : FnMut ( & CaseSetter < UP_TO , WITH_DEFAULT > , T ) ,
221
+ F : SetCtx < T > ,
142
222
{
143
223
for ( dir, ( len, offset) ) in zip ( dirs, zip ( lens, offsets) ) {
144
- Self :: one ( dir, len, offset, & mut set_ctx) ;
224
+ set_ctx = Self :: one ( dir, len, offset, set_ctx) ;
145
225
}
146
226
}
147
227
}
0 commit comments