1- use std:: { fmt, str:: FromStr } ;
1+ use std:: {
2+ fmt:: { self , Debug } ,
3+ mem,
4+ str:: FromStr ,
5+ sync:: atomic:: Ordering ,
6+ } ;
27
38use crate :: Error ;
49
@@ -186,7 +191,7 @@ impl FromStr for Level {
186191///
187192/// Use [`LevelFilter::test`] method to check if a [`Level`] satisfies the
188193/// filter condition.
189- #[ repr( align( 4 ) ) ]
194+ #[ repr( u16 , align( 4 ) ) ]
190195#[ derive( Copy , Clone , Eq , PartialEq , Hash , Debug ) ]
191196pub enum LevelFilter {
192197 /// Disables all levels.
@@ -269,6 +274,29 @@ impl LevelFilter {
269274 None
270275 }
271276 }
277+
278+ #[ must_use]
279+ fn discriminant ( & self ) -> u16 {
280+ // SAFETY:
281+ // Because `LevelFilter` is marked `repr(u16)`, its layout is a `repr(C)`
282+ // `union` between `repr(C)` structs, each of which has the `u16` discriminant
283+ // as its first field, so we can read the discriminant without offsetting the
284+ // pointer.
285+ unsafe { * <* const _ >:: from ( self ) . cast :: < u16 > ( ) }
286+ }
287+
288+ #[ must_use]
289+ fn level ( & self ) -> Option < Level > {
290+ match * self {
291+ Self :: Equal ( level)
292+ | Self :: NotEqual ( level)
293+ | Self :: MoreSevere ( level)
294+ | Self :: MoreSevereEqual ( level)
295+ | Self :: MoreVerbose ( level)
296+ | Self :: MoreVerboseEqual ( level) => Some ( level) ,
297+ Self :: Off | Self :: All => None ,
298+ }
299+ }
272300}
273301
274302#[ cfg( feature = "log" ) ]
@@ -281,6 +309,98 @@ impl From<log::LevelFilter> for LevelFilter {
281309 }
282310}
283311
312+ // Atomic
313+
314+ // This struct must have the same memory layout as `LevelFilter`.
315+ #[ repr( C , align( 4 ) ) ]
316+ #[ derive( Copy , Clone , Eq , PartialEq , Hash , Debug ) ]
317+ struct LevelFilterLayout {
318+ discriminant : u16 , // Keep the type in sync with the repr of `LevelFilter`
319+ level : Level ,
320+ }
321+
322+ impl LevelFilterLayout {
323+ const UNDEFINED_FALLBACK : Level = Level :: Critical ;
324+ }
325+
326+ impl From < LevelFilter > for LevelFilterLayout {
327+ fn from ( value : LevelFilter ) -> Self {
328+ // Use `mem::transmute` here is undefined behavior, because `LevelFilter`
329+ // contains uninitialized bytes.
330+ Self {
331+ discriminant : value. discriminant ( ) ,
332+ level : value. level ( ) . unwrap_or ( Self :: UNDEFINED_FALLBACK ) ,
333+ }
334+ }
335+ }
336+
337+ impl From < LevelFilterLayout > for LevelFilter {
338+ fn from ( inner : LevelFilterLayout ) -> Self {
339+ // SAFETY:
340+ // - Repr of `LevelFilter` is C union.
341+ // - Repr of `LevelFilterLayout` is C struct, it has the same memory layout as
342+ // `LevelFilter`.
343+ // - `LevelFilterLayout` doesn't contain uninitialized bytes.
344+ unsafe { mem:: transmute :: < _ , LevelFilter > ( inner) }
345+ }
346+ }
347+
348+ /// Atomic version of [`LevelFilter`] which can be safely shared between
349+ /// threads.
350+ pub struct AtomicLevelFilter {
351+ inner : atomic:: Atomic < LevelFilterLayout > ,
352+ }
353+
354+ impl AtomicLevelFilter {
355+ const ORDERING : Ordering = Ordering :: Relaxed ;
356+
357+ /// Creates a new `AtomicLevelFilter`.
358+ pub fn new ( init : LevelFilter ) -> Self {
359+ Self {
360+ inner : atomic:: Atomic :: new ( init. into ( ) ) ,
361+ }
362+ }
363+
364+ /// Loads the level filter with `Relaxed` ordering.
365+ pub fn get ( & self ) -> LevelFilter {
366+ self . load ( Self :: ORDERING )
367+ }
368+
369+ /// Stores a level filter with `Relaxed` ordering.
370+ pub fn set ( & self , new : LevelFilter ) {
371+ self . store ( new, Self :: ORDERING ) ;
372+ }
373+
374+ /// Loads the level filter.
375+ ///
376+ /// # Panics
377+ ///
378+ /// Panics if the ordering is `Release` or `AcqRel`.
379+ pub fn load ( & self , ordering : Ordering ) -> LevelFilter {
380+ self . inner . load ( ordering) . into ( )
381+ }
382+
383+ /// Stores a level filter.
384+ ///
385+ /// # Panics
386+ ///
387+ /// Panics if the ordering is `Acquire` or `AcqRel`.
388+ pub fn store ( & self , value : LevelFilter , ordering : Ordering ) {
389+ self . inner . store ( value. into ( ) , ordering) ;
390+ }
391+
392+ /// Stores a level filter, returning the old level filter.
393+ pub fn swap ( & self , new : LevelFilter , ordering : Ordering ) -> LevelFilter {
394+ self . inner . swap ( new. into ( ) , ordering) . into ( )
395+ }
396+ }
397+
398+ impl Debug for AtomicLevelFilter {
399+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
400+ self . get ( ) . fmt ( f)
401+ }
402+ }
403+
284404#[ cfg( test) ]
285405mod tests {
286406 use std:: mem:: { align_of, size_of} ;
@@ -290,8 +410,10 @@ mod tests {
290410
291411 const_assert ! ( atomic:: Atomic :: <Level >:: is_lock_free( ) ) ;
292412 const_assert ! ( atomic:: Atomic :: <LevelFilter >:: is_lock_free( ) ) ;
413+ const_assert ! ( atomic:: Atomic :: <LevelFilterLayout >:: is_lock_free( ) ) ;
293414 const_assert ! ( size_of:: <Level >( ) * 2 == size_of:: <LevelFilter >( ) ) ;
294- const_assert ! ( align_of:: <Level >( ) * 2 == align_of:: <LevelFilter >( ) ) ;
415+ const_assert ! ( align_of:: <LevelFilterLayout >( ) == align_of:: <LevelFilter >( ) ) ;
416+ const_assert ! ( size_of:: <LevelFilterLayout >( ) == size_of:: <LevelFilter >( ) ) ;
295417
296418 #[ test]
297419 fn from_usize ( ) {
@@ -446,4 +568,28 @@ mod tests {
446568 LevelFilter :: MoreSevereEqual ( Level :: Trace )
447569 ) ;
448570 }
571+
572+ #[ test]
573+ fn atomic_level_filter ( ) {
574+ let atomic_level_filter = AtomicLevelFilter :: new ( LevelFilter :: All ) ;
575+
576+ let assert_this = |new : LevelFilter | {
577+ assert_ne ! ( atomic_level_filter. get( ) , new) ;
578+ atomic_level_filter. set ( new) ;
579+ assert_eq ! ( atomic_level_filter. get( ) , new) ;
580+ } ;
581+
582+ fn produce_all ( cond : impl Fn ( Level ) -> LevelFilter ) -> impl Iterator < Item = LevelFilter > {
583+ Level :: iter ( ) . map ( cond)
584+ }
585+
586+ assert_this ( LevelFilter :: Off ) ;
587+ produce_all ( LevelFilter :: Equal ) . for_each ( assert_this) ;
588+ produce_all ( LevelFilter :: NotEqual ) . for_each ( assert_this) ;
589+ produce_all ( LevelFilter :: MoreSevere ) . for_each ( assert_this) ;
590+ produce_all ( LevelFilter :: MoreSevereEqual ) . for_each ( assert_this) ;
591+ produce_all ( LevelFilter :: MoreVerbose ) . for_each ( assert_this) ;
592+ produce_all ( LevelFilter :: MoreVerboseEqual ) . for_each ( assert_this) ;
593+ assert_this ( LevelFilter :: All ) ;
594+ }
449595}
0 commit comments