@@ -21,6 +21,7 @@ use parking_lot::RwLock;
2121use vortex_error:: VortexExpect ;
2222use vortex_utils:: aliases:: DefaultHashBuilder ;
2323use vortex_utils:: aliases:: dash_map:: DashMap ;
24+ use vortex_utils:: aliases:: hash_set:: HashSet ;
2425
2526/// Global string interner for [`Id`] values.
2627static INTERNER : LazyLock < ThreadedRodeo < Spur , DefaultHashBuilder > > =
@@ -227,32 +228,32 @@ impl ReadContext {
227228/// 1. This object holds an Arc of RwLock internally because we need concurrent access from the
228229/// layout writer code path. We should update SegmentSink to take an Array rather than
229230/// ByteBuffer such that serializing arrays is done sequentially.
230- /// 2. The name is terrible. `Interner<T> ` is better, but I want to minimize breakage for now.
231+ /// 2. The name is terrible. `Interner` is better, but I want to minimize breakage for now.
231232#[ derive( Clone , Debug ) ]
232- pub struct Context < T > {
233+ pub struct Context {
233234 // TODO(ngates): it's a long story, but if we make SegmentSink and SegmentSource take an
234235 // enum of Segment { Array, DType, Buffer } then we don't actually need a mutable context
235236 // in the LayoutWriter, therefore we don't need a RwLock here and everyone is happier.
236237 ids : Arc < RwLock < Vec < Id > > > ,
237- // Optional registry used to filter the permissible interned items .
238- registry : Option < Registry < T > > ,
238+ // Optional set used to filter the permissible interned IDs .
239+ valid_ids : Option < Arc < HashSet < Id > > > ,
239240}
240241
241- impl < T > Default for Context < T > {
242+ impl Default for Context {
242243 fn default ( ) -> Self {
243244 Self {
244245 ids : Arc :: new ( RwLock :: new ( Vec :: new ( ) ) ) ,
245- registry : None ,
246+ valid_ids : None ,
246247 }
247248 }
248249}
249250
250- impl < T : Clone > Context < T > {
251+ impl Context {
251252 /// Create a context with the given initial IDs.
252253 pub fn new ( ids : Vec < Id > ) -> Self {
253254 Self {
254255 ids : Arc :: new ( RwLock :: new ( ids) ) ,
255- registry : None ,
256+ valid_ids : None ,
256257 }
257258 }
258259
@@ -261,18 +262,18 @@ impl<T: Clone> Context<T> {
261262 Self :: default ( )
262263 }
263264
264- /// Configure a registry to restrict the permissible set of interned items .
265- pub fn with_registry ( mut self , registry : Registry < T > ) -> Self {
266- self . registry = Some ( registry ) ;
265+ /// Restrict the permissible set of interned IDs .
266+ pub fn with_valid_ids ( mut self , ids : impl IntoIterator < Item = Id > ) -> Self {
267+ self . valid_ids = Some ( Arc :: new ( ids . into_iter ( ) . collect ( ) ) ) ;
267268 self
268269 }
269270
270271 /// Intern an ID, returning its index.
271272 pub fn intern ( & self , id : & Id ) -> Option < u16 > {
272- if let Some ( registry ) = & self . registry
273- && registry . find ( id) . is_none ( )
273+ if let Some ( valid_ids ) = & self . valid_ids
274+ && !valid_ids . contains ( id)
274275 {
275- // ID not in registry , cannot intern.
276+ // ID is not valid , cannot intern.
276277 return None ;
277278 }
278279
@@ -295,3 +296,24 @@ impl<T: Clone> Context<T> {
295296 self . ids . read ( ) . clone ( )
296297 }
297298}
299+
300+ #[ cfg( test) ]
301+ mod tests {
302+ use super :: CachedId ;
303+ use super :: Context ;
304+
305+ static VALID : CachedId = CachedId :: new ( "vortex.test.valid" ) ;
306+ static INVALID : CachedId = CachedId :: new ( "vortex.test.invalid" ) ;
307+
308+ #[ test]
309+ fn context_filters_interned_ids ( ) {
310+ let valid = * VALID ;
311+ let invalid = * INVALID ;
312+ let context = Context :: empty ( ) . with_valid_ids ( [ valid] ) ;
313+
314+ assert_eq ! ( context. intern( & valid) , Some ( 0 ) ) ;
315+ assert_eq ! ( context. intern( & valid) , Some ( 0 ) ) ;
316+ assert_eq ! ( context. intern( & invalid) , None ) ;
317+ assert_eq ! ( context. to_ids( ) , [ valid] ) ;
318+ }
319+ }
0 commit comments