@@ -644,8 +644,35 @@ impl GroupedFields {
644644 }
645645
646646 /// Returns an iterator over all lazy fields (both data and meta categories).
647+ ///
648+ /// The order is **sorted by category** — transient variants first, then
649+ /// meta, then data — with schema declaration order preserved within each
650+ /// category. This grouping is load-bearing for codegen: contiguous
651+ /// categories in the generated `LazyField` enum let LLVM lower
652+ /// `is_persistent()` / `is_meta()` to a single integer range check on the
653+ /// discriminant tag instead of a per-variant jump table.
654+ ///
655+ /// Every downstream generator (enum declaration, `index_and_persistence`,
656+ /// restore merge arms, `build_lazy_index`) iterates `all_lazy()` and uses
657+ /// `enumerate()` positions as the variant index, so they all pick up the
658+ /// same sorted order consistently. `persistent_lazy(category)` does not
659+ /// need to match this order because its consumers (bincode encode/decode,
660+ /// clone arms) use per-category enumeration and Rust match arms are
661+ /// order-independent.
647662 fn all_lazy ( & self ) -> impl Iterator < Item = & FieldInfo > {
648- self . fields . iter ( ) . filter ( |f| !f. is_flag ( ) && f. lazy )
663+ let mut lazy: Vec < & FieldInfo > = self
664+ . fields
665+ . iter ( )
666+ . filter ( |f| !f. is_flag ( ) && f. lazy )
667+ . collect ( ) ;
668+ // Stable sort by category rank; within a category, preserve schema
669+ // declaration order.
670+ lazy. sort_by_key ( |f| match f. category {
671+ Category :: Transient => 0u8 ,
672+ Category :: Meta => 1 ,
673+ Category :: Data => 2 ,
674+ } ) ;
675+ lazy. into_iter ( )
649676 }
650677
651678 /// Returns true if there are any lazy fields.
@@ -1009,41 +1036,66 @@ fn generate_lazy_field_enum(grouped_fields: &GroupedFields) -> TokenStream {
10091036 } )
10101037 . collect ( ) ;
10111038
1012- // Generate is_persistent (transient check) method arms
1013- let is_persistent_arms: Vec < _ > = all_lazy_fields
1039+ // Or-pattern lists for `is_persistent` / `is_meta`. Because `all_lazy()`
1040+ // returns variants grouped by category (transient, then meta, then data),
1041+ // these lists cover contiguous runs of the enum, giving LLVM the clearest
1042+ // shape to lower each predicate to a single integer range check on the
1043+ // discriminant tag.
1044+ let persistent_patterns: Vec < _ > = all_lazy_fields
10141045 . iter ( )
1015- . map ( |field| {
1016- let variant_name = & field. variant_name ;
1017- let is_persistent = !field. is_transient ( ) ;
1018- quote ! {
1019- LazyField :: #variant_name( _) => #is_persistent
1020- }
1046+ . filter ( |f| !f. is_transient ( ) )
1047+ . map ( |f| {
1048+ let variant_name = & f. variant_name ;
1049+ quote ! { LazyField :: #variant_name( _) }
10211050 } )
10221051 . collect ( ) ;
1023-
1024- // Generate is_meta/is_data method arms
1025- let is_meta_arms: Vec < _ > = all_lazy_fields
1052+ let meta_patterns: Vec < _ > = all_lazy_fields
10261053 . iter ( )
1027- . map ( |field| {
1028- let variant_name = & field. variant_name ;
1029- let is_meta = field. category == Category :: Meta ;
1030- quote ! {
1031- LazyField :: #variant_name( _) => #is_meta
1032- }
1054+ . filter ( |f| f. category == Category :: Meta )
1055+ . map ( |f| {
1056+ let variant_name = & f. variant_name ;
1057+ quote ! { LazyField :: #variant_name( _) }
1058+ } )
1059+ . collect ( ) ;
1060+ let data_patterns: Vec < _ > = all_lazy_fields
1061+ . iter ( )
1062+ . filter ( |f| f. category == Category :: Data )
1063+ . map ( |f| {
1064+ let variant_name = & f. variant_name ;
1065+ quote ! { LazyField :: #variant_name( _) }
10331066 } )
10341067 . collect ( ) ;
10351068
1036- // Generate discriminant_index method arms: each variant maps to its
1037- // position in the enum definition. Used to index into a fixed-size array
1038- // for O(1) variant lookup (e.g. when merging transient residue on restore).
1039- let discriminant_arms: Vec < _ > = all_lazy_fields
1069+ // `matches!(self, ... | ... )` requires at least one pattern. Fall back to
1070+ // `false` if the schema has no variants in a given category.
1071+ let is_persistent_body = if persistent_patterns. is_empty ( ) {
1072+ quote ! { false }
1073+ } else {
1074+ quote ! { matches!( self , #( #persistent_patterns) |* ) }
1075+ } ;
1076+ let is_meta_body = if meta_patterns. is_empty ( ) {
1077+ quote ! { false }
1078+ } else {
1079+ quote ! { matches!( self , #( #meta_patterns) |* ) }
1080+ } ;
1081+ let is_data_body = if data_patterns. is_empty ( ) {
1082+ quote ! { false }
1083+ } else {
1084+ quote ! { matches!( self , #( #data_patterns) |* ) }
1085+ } ;
1086+
1087+ // (discriminant, is_persistent) arms for the restore prescan — each
1088+ // variant maps to its position in the enum definition (used as a
1089+ // fixed-size array offset) paired with its persistence bit.
1090+ let index_and_persistence_arms: Vec < _ > = all_lazy_fields
10401091 . iter ( )
10411092 . enumerate ( )
10421093 . map ( |( idx, field) | {
10431094 let variant_name = & field. variant_name ;
10441095 let idx = idx as u8 ;
1096+ let is_persistent = !field. is_transient ( ) ;
10451097 quote ! {
1046- LazyField :: #variant_name( _) => #idx
1098+ LazyField :: #variant_name( _) => ( #idx, #is_persistent )
10471099 }
10481100 } )
10491101 . collect ( ) ;
@@ -1072,32 +1124,43 @@ fn generate_lazy_field_enum(grouped_fields: &GroupedFields) -> TokenStream {
10721124 }
10731125 }
10741126
1075- #[ doc = "Returns true if this field should be persisted (not transient)" ]
1127+ #[ doc = "Returns true if this field should be persisted (not transient)." ]
1128+ #[ doc = "" ]
1129+ #[ doc = "Variants are sorted so persistent variants form a contiguous" ]
1130+ #[ doc = "range; LLVM can lower this `matches!` to a single integer" ]
1131+ #[ doc = "compare on the discriminant tag." ]
1132+ #[ inline]
10761133 pub fn is_persistent( & self ) -> bool {
1077- match self {
1078- #( #is_persistent_arms) , *
1079- }
1134+ #is_persistent_body
10801135 }
10811136
1082- #[ doc = "Returns true if this field belongs to the meta category" ]
1137+ #[ doc = "Returns true if this field belongs to the meta category." ]
1138+ #[ doc = "" ]
1139+ #[ doc = "Meta variants form a contiguous range between the transient" ]
1140+ #[ doc = "prefix and the data suffix; expect a range-check lowering." ]
1141+ #[ inline]
10831142 pub fn is_meta( & self ) -> bool {
1084- match self {
1085- #( #is_meta_arms) , *
1086- }
1143+ #is_meta_body
10871144 }
10881145
1089- #[ doc = "Returns true if this field belongs to the data category" ]
1146+ #[ doc = "Returns true if this field belongs to the data category." ]
1147+ #[ doc = "" ]
1148+ #[ doc = "Data variants form the trailing contiguous range of the" ]
1149+ #[ doc = "enum; expect a range-check lowering." ]
1150+ #[ inline]
10901151 pub fn is_data( & self ) -> bool {
1091- ! self . is_meta ( )
1152+ #is_data_body
10921153 }
10931154
1094- #[ doc = "Variant index (position in the LazyField enum definition) ." ]
1155+ #[ doc = "Variant index paired with whether this variant is persistent ." ]
10951156 #[ doc = "" ]
1096- #[ doc = "Stable per-variant index usable as an array offset; see" ]
1097- #[ doc = "`NUM_VARIANTS` for the array size." ]
1098- pub const fn discriminant_index( & self ) -> u8 {
1157+ #[ doc = "Index is the variant's position in the LazyField enum" ]
1158+ #[ doc = "definition, usable as an array offset of size `NUM_VARIANTS`." ]
1159+ #[ doc = "Used by the restore prescan to answer both \" where does this" ]
1160+ #[ doc = "variant live?\" and \" do we care about it?\" in a single match." ]
1161+ const fn index_and_persistence( & self ) -> ( u8 , bool ) {
10991162 match self {
1100- #( #discriminant_arms ) , *
1163+ #( #index_and_persistence_arms ) , *
11011164 }
11021165 }
11031166 }
@@ -3571,13 +3634,17 @@ fn generate_snapshot_restore_methods(grouped_fields: &GroupedFields) -> TokenStr
35713634
35723635 #restore_meta_flags
35733636
3574- // Merge persistent meta lazy fields. Build a discriminant → index
3575- // map over `self.lazy` once, then merge each source variant in
3576- // O(1) by discriminant.
3577- let index = Self :: build_lazy_index( & self . lazy) ;
3578- for field in source. lazy {
3579- debug_assert!( field. is_persistent( ) ) ;
3580- self . restore_lazy_field( field, & index) ;
3637+ // Cold path when `self.lazy` has no persistent residue: bulk
3638+ // extend. Otherwise build a discriminant → position index and
3639+ // merge each source variant in O(1).
3640+ match Self :: build_lazy_index( & self . lazy) {
3641+ None => self . lazy. extend( source. lazy) ,
3642+ Some ( index) => {
3643+ for field in source. lazy {
3644+ debug_assert!( field. is_persistent( ) ) ;
3645+ self . restore_lazy_field( field, & index) ;
3646+ }
3647+ }
35813648 }
35823649 }
35833650
@@ -3591,10 +3658,14 @@ fn generate_snapshot_restore_methods(grouped_fields: &GroupedFields) -> TokenStr
35913658
35923659 #restore_data_flags
35933660
3594- let index = Self :: build_lazy_index( & self . lazy) ;
3595- for field in source. lazy {
3596- debug_assert!( field. is_persistent( ) ) ;
3597- self . restore_lazy_field( field, & index) ;
3661+ match Self :: build_lazy_index( & self . lazy) {
3662+ None => self . lazy. extend( source. lazy) ,
3663+ Some ( index) => {
3664+ for field in source. lazy {
3665+ debug_assert!( field. is_persistent( ) ) ;
3666+ self . restore_lazy_field( field, & index) ;
3667+ }
3668+ }
35983669 }
35993670 }
36003671
@@ -3611,25 +3682,43 @@ fn generate_snapshot_restore_methods(grouped_fields: &GroupedFields) -> TokenStr
36113682
36123683 #restore_all_flags
36133684
3614- let index = Self :: build_lazy_index( & self . lazy) ;
3615- for field in source. lazy {
3616- debug_assert!( field. is_persistent( ) ) ;
3617- self . restore_lazy_field( field, & index) ;
3685+ match Self :: build_lazy_index( & self . lazy) {
3686+ None => self . lazy. extend( source. lazy) ,
3687+ Some ( index) => {
3688+ for field in source. lazy {
3689+ debug_assert!( field. is_persistent( ) ) ;
3690+ self . restore_lazy_field( field, & index) ;
3691+ }
3692+ }
36183693 }
36193694 }
36203695
36213696 /// Build a discriminant → position lookup table over `lazy`.
36223697 ///
3623- /// `u8::MAX` marks "variant not present". Relies on `lazy.len() < 255`
3624- /// which is trivially true (at most `LazyField::NUM_VARIANTS` entries,
3625- /// which is well under 255).
3626- fn build_lazy_index( lazy: & [ LazyField ] ) -> [ u8 ; LazyField :: NUM_VARIANTS ] {
3698+ /// Returns `None` if `lazy` contains no persistent entries — the hot
3699+ /// path for cold restores, where the caller can skip per-field
3700+ /// dispatch and bulk-extend directly. `Some(index)` is returned
3701+ /// only when persistent residue is present; `u8::MAX` marks
3702+ /// "variant not present".
3703+ ///
3704+ /// Relies on `lazy.len() < 255`, which is trivially true (at most
3705+ /// `LazyField::NUM_VARIANTS` entries, well under 255).
3706+ fn build_lazy_index(
3707+ lazy: & [ LazyField ] ,
3708+ ) -> Option <[ u8 ; LazyField :: NUM_VARIANTS ] > {
36273709 debug_assert!( lazy. len( ) < u8 :: MAX as usize ) ;
3710+ // Fast path: fresh task with no residue at all.
3711+ if lazy. is_empty( ) {
3712+ return None ;
3713+ }
36283714 let mut index = [ u8 :: MAX ; LazyField :: NUM_VARIANTS ] ;
3715+ let mut any_persistent = false ;
36293716 for ( i, f) in lazy. iter( ) . enumerate( ) {
3630- index[ f. discriminant_index( ) as usize ] = i as u8 ;
3717+ let ( d, is_persistent) = f. index_and_persistence( ) ;
3718+ index[ d as usize ] = i as u8 ;
3719+ any_persistent |= is_persistent;
36313720 }
3632- index
3721+ any_persistent . then_some ( index)
36333722 }
36343723
36353724 /// Merge a single persistent `LazyField` from a decoded snapshot into
@@ -3656,9 +3745,8 @@ fn generate_snapshot_restore_methods(grouped_fields: &GroupedFields) -> TokenStr
36563745
36573746/// Generate a match arm for `restore_lazy_field` that merges an incoming
36583747/// persistent `filter_transient` variant into `self.lazy` using the precomputed
3659- /// discriminant → position `index`. `discriminant` must equal
3660- /// `all_lazy().position(...)` for this variant — i.e. its
3661- /// `discriminant_index()` value.
3748+ /// discriminant → position `index`. `discriminant` must equal the variant's
3749+ /// position in `all_lazy()` (and in the `LazyField` enum definition).
36623750///
36633751/// On residue hit: merge the incoming collection into the existing one.
36643752/// On miss: push the variant. `source.lazy` never contains duplicate variants,
0 commit comments