@@ -88,39 +88,71 @@ pub const Decoder = struct {
8888 // Once we know the number of pairs, we can look at each pair in turn to determine
8989 // the size of the key and the key name, as well as the value's type and payload.
9090 const map_len = field .size ;
91- var map_key : ? []const u8 = null ;
9291 var field_count : usize = 0 ;
93- inline for (std .meta .fields (T )) | f | next_field : {
94- // Skip struct fields whose name starts with an underscore, e.g., _allocator.
95- if (f .name [0 ] == '_' ) {
96- break :next_field ;
97- }
92+ while (field_count < map_len ) : (field_count += 1 ) {
93+ const map_key = try self .decodeValue (allocator , []const u8 );
94+
95+ var found = false ;
96+ inline for (std .meta .fields (T )) | f | {
97+ // Skip struct fields whose name starts with an underscore, e.g., _arena.
98+ if (f .name [0 ] == '_' ) {
99+ continue ;
100+ }
98101
99- // Don't decode more struct fields than the number of the map entries.
100- if (field_count >= map_len ) {
101- break ;
102+ if (std .mem .eql (u8 , map_key , f .name )) {
103+ const map_value = try self .decodeValue (allocator , f .type );
104+ @field (record , f .name ) = map_value ;
105+ found = true ;
106+ break ;
107+ }
102108 }
103109
104- // The map key is nulled to advance the map decoding to the next key.
105- // Otherwise the key is used to decode the next struct field.
106- if (map_key == null ) {
107- map_key = try self .decodeValue (allocator , []const u8 );
108- }
109- // Struct fields must match the layout (names and order) in the database.
110- // When the names don't match, the struct field is skipped.
111- // This is usefull for optional fields, e.g., some db records have city.is_in_european_union flag.
112- if (! std .mem .eql (u8 , map_key .? , f .name )) {
113- break :next_field ;
110+ // If the field wasn't found in the struct, skip the value in the database.
111+ if (! found ) {
112+ try self .skipValue ();
114113 }
114+ }
115+
116+ return record ;
117+ }
118+
119+ // Skips a value in the database without decoding it.
120+ // This is used when the database has fields that don't exist in the target struct.
121+ fn skipValue (self : * Decoder ) ! void {
122+ const field = self .decodeFieldSizeAndType ();
123+
124+ if (field .type == FieldType .Pointer ) {
125+ const next_offset = self .decodePointer (field .size );
126+ const prev_offset = self .offset ;
115127
116- const map_value = try self .decodeValue (allocator , f .type );
117- @field (record , f .name ) = map_value ;
128+ self .offset = next_offset ;
129+ try self .skipValue ();
130+ self .offset = prev_offset ;
118131
119- field_count += 1 ;
120- map_key = null ;
132+ return ;
121133 }
122134
123- return record ;
135+ switch (field .type ) {
136+ // Bool has no payload, size is encoded in the control byte.
137+ .Bool = > {},
138+ // Skip each array element.
139+ .Array = > {
140+ for (0.. field .size ) | _ | {
141+ try self .skipValue ();
142+ }
143+ },
144+ // Skip each map key-value pair.
145+ .Map = > {
146+ for (0.. field .size ) | _ | {
147+ try self .skipValue ();
148+ try self .skipValue ();
149+ }
150+ },
151+ // For other types, just advance the offset.
152+ else = > {
153+ self .offset += field .size ;
154+ },
155+ }
124156 }
125157
126158 // Decodes a struct's field value which can be a built-in data type or another struct.
0 commit comments