diff options
author | Jonas Schievink <[email protected]> | 2020-06-23 18:42:19 +0100 |
---|---|---|
committer | Jonas Schievink <[email protected]> | 2020-06-24 15:53:56 +0100 |
commit | c019002d17185f4e8be54a978ab5d67bc632f518 (patch) | |
tree | dcc7bbb30e35b179f53c8198835999d9166882b9 | |
parent | f9a1a9cd3c7757ca3f8ba59287b5d36645008b9b (diff) |
Slightly reduce ItemTree memory footprint
-rw-r--r-- | crates/ra_hir_def/src/attr.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_def/src/item_tree.rs | 73 | ||||
-rw-r--r-- | crates/ra_hir_def/src/item_tree/lower.rs | 46 |
3 files changed, 68 insertions, 53 deletions
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs index e0f468bf3..197737ffc 100644 --- a/crates/ra_hir_def/src/attr.rs +++ b/crates/ra_hir_def/src/attr.rs | |||
@@ -38,6 +38,8 @@ impl ops::Deref for Attrs { | |||
38 | } | 38 | } |
39 | 39 | ||
40 | impl Attrs { | 40 | impl Attrs { |
41 | pub const EMPTY: Attrs = Attrs { entries: None }; | ||
42 | |||
41 | pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs { | 43 | pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs { |
42 | match def { | 44 | match def { |
43 | AttrDefId::ModuleId(module) => { | 45 | AttrDefId::ModuleId(module) => { |
diff --git a/crates/ra_hir_def/src/item_tree.rs b/crates/ra_hir_def/src/item_tree.rs index a75271703..d55b3f777 100644 --- a/crates/ra_hir_def/src/item_tree.rs +++ b/crates/ra_hir_def/src/item_tree.rs | |||
@@ -35,16 +35,8 @@ use crate::{ | |||
35 | }; | 35 | }; |
36 | use smallvec::SmallVec; | 36 | use smallvec::SmallVec; |
37 | 37 | ||
38 | /// The item tree of a source file. | 38 | #[derive(Default, Debug, Eq, PartialEq)] |
39 | #[derive(Debug, Eq, PartialEq)] | 39 | struct ItemTreeData { |
40 | pub struct ItemTree { | ||
41 | file_id: HirFileId, | ||
42 | top_level: Vec<ModItem>, | ||
43 | top_attrs: Attrs, | ||
44 | attrs: FxHashMap<ModItem, Attrs>, | ||
45 | empty_attrs: Attrs, | ||
46 | inner_items: FxHashMap<FileAstId<ast::ModuleItem>, SmallVec<[ModItem; 1]>>, | ||
47 | |||
48 | imports: Arena<Import>, | 40 | imports: Arena<Import>, |
49 | extern_crates: Arena<ExternCrate>, | 41 | extern_crates: Arena<ExternCrate>, |
50 | functions: Arena<Function>, | 42 | functions: Arena<Function>, |
@@ -63,6 +55,26 @@ pub struct ItemTree { | |||
63 | exprs: Arena<Expr>, | 55 | exprs: Arena<Expr>, |
64 | } | 56 | } |
65 | 57 | ||
58 | #[derive(Debug, Eq, PartialEq, Hash)] | ||
59 | enum AttrOwner { | ||
60 | /// Attributes on an item. | ||
61 | ModItem(ModItem), | ||
62 | /// Inner attributes of the source file. | ||
63 | TopLevel, | ||
64 | // FIXME: Store variant and field attrs, and stop reparsing them in `attrs_query`. | ||
65 | } | ||
66 | |||
67 | /// The item tree of a source file. | ||
68 | #[derive(Debug, Eq, PartialEq)] | ||
69 | pub struct ItemTree { | ||
70 | file_id: HirFileId, | ||
71 | top_level: SmallVec<[ModItem; 1]>, | ||
72 | attrs: FxHashMap<AttrOwner, Attrs>, | ||
73 | inner_items: FxHashMap<FileAstId<ast::ModuleItem>, SmallVec<[ModItem; 1]>>, | ||
74 | |||
75 | data: Option<Box<ItemTreeData>>, | ||
76 | } | ||
77 | |||
66 | impl ItemTree { | 78 | impl ItemTree { |
67 | pub fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> { | 79 | pub fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> { |
68 | let _p = ra_prof::profile("item_tree_query").detail(|| format!("{:?}", file_id)); | 80 | let _p = ra_prof::profile("item_tree_query").detail(|| format!("{:?}", file_id)); |
@@ -95,7 +107,9 @@ impl ItemTree { | |||
95 | } | 107 | } |
96 | }; | 108 | }; |
97 | 109 | ||
98 | item_tree.top_attrs = top_attrs.unwrap_or_default(); | 110 | if let Some(attrs) = top_attrs { |
111 | item_tree.attrs.insert(AttrOwner::TopLevel, attrs); | ||
112 | } | ||
99 | Arc::new(item_tree) | 113 | Arc::new(item_tree) |
100 | } | 114 | } |
101 | 115 | ||
@@ -103,26 +117,9 @@ impl ItemTree { | |||
103 | Self { | 117 | Self { |
104 | file_id, | 118 | file_id, |
105 | top_level: Default::default(), | 119 | top_level: Default::default(), |
106 | top_attrs: Default::default(), | ||
107 | attrs: Default::default(), | 120 | attrs: Default::default(), |
108 | empty_attrs: Default::default(), | ||
109 | inner_items: Default::default(), | 121 | inner_items: Default::default(), |
110 | imports: Default::default(), | 122 | data: Default::default(), |
111 | extern_crates: Default::default(), | ||
112 | functions: Default::default(), | ||
113 | structs: Default::default(), | ||
114 | fields: Default::default(), | ||
115 | unions: Default::default(), | ||
116 | enums: Default::default(), | ||
117 | variants: Default::default(), | ||
118 | consts: Default::default(), | ||
119 | statics: Default::default(), | ||
120 | traits: Default::default(), | ||
121 | impls: Default::default(), | ||
122 | type_aliases: Default::default(), | ||
123 | mods: Default::default(), | ||
124 | macro_calls: Default::default(), | ||
125 | exprs: Default::default(), | ||
126 | } | 123 | } |
127 | } | 124 | } |
128 | 125 | ||
@@ -134,11 +131,11 @@ impl ItemTree { | |||
134 | 131 | ||
135 | /// Returns the inner attributes of the source file. | 132 | /// Returns the inner attributes of the source file. |
136 | pub fn top_level_attrs(&self) -> &Attrs { | 133 | pub fn top_level_attrs(&self) -> &Attrs { |
137 | &self.top_attrs | 134 | self.attrs.get(&AttrOwner::TopLevel).unwrap_or(&Attrs::EMPTY) |
138 | } | 135 | } |
139 | 136 | ||
140 | pub fn attrs(&self, of: ModItem) -> &Attrs { | 137 | pub fn attrs(&self, of: ModItem) -> &Attrs { |
141 | self.attrs.get(&of).unwrap_or(&self.empty_attrs) | 138 | self.attrs.get(&AttrOwner::ModItem(of)).unwrap_or(&Attrs::EMPTY) |
142 | } | 139 | } |
143 | 140 | ||
144 | /// Returns the lowered inner items that `ast` corresponds to. | 141 | /// Returns the lowered inner items that `ast` corresponds to. |
@@ -169,6 +166,14 @@ impl ItemTree { | |||
169 | let ptr = map.get(id); | 166 | let ptr = map.get(id); |
170 | ptr.to_node(&root) | 167 | ptr.to_node(&root) |
171 | } | 168 | } |
169 | |||
170 | fn data(&self) -> &ItemTreeData { | ||
171 | self.data.as_ref().expect("attempted to access data of empty ItemTree") | ||
172 | } | ||
173 | |||
174 | fn data_mut(&mut self) -> &mut ItemTreeData { | ||
175 | self.data.get_or_insert_with(Box::default) | ||
176 | } | ||
172 | } | 177 | } |
173 | 178 | ||
174 | /// Trait implemented by all nodes in the item tree. | 179 | /// Trait implemented by all nodes in the item tree. |
@@ -246,7 +251,7 @@ macro_rules! mod_items { | |||
246 | } | 251 | } |
247 | 252 | ||
248 | fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self { | 253 | fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self { |
249 | &tree.$fld[index] | 254 | &tree.data().$fld[index] |
250 | } | 255 | } |
251 | 256 | ||
252 | fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>> { | 257 | fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>> { |
@@ -266,7 +271,7 @@ macro_rules! mod_items { | |||
266 | type Output = $typ; | 271 | type Output = $typ; |
267 | 272 | ||
268 | fn index(&self, index: Idx<$typ>) -> &Self::Output { | 273 | fn index(&self, index: Idx<$typ>) -> &Self::Output { |
269 | &self.$fld[index] | 274 | &self.data().$fld[index] |
270 | } | 275 | } |
271 | } | 276 | } |
272 | )+ | 277 | )+ |
@@ -296,7 +301,7 @@ macro_rules! impl_index { | |||
296 | type Output = $t; | 301 | type Output = $t; |
297 | 302 | ||
298 | fn index(&self, index: Idx<$t>) -> &Self::Output { | 303 | fn index(&self, index: Idx<$t>) -> &Self::Output { |
299 | &self.$fld[index] | 304 | &self.data().$fld[index] |
300 | } | 305 | } |
301 | } | 306 | } |
302 | )+ | 307 | )+ |
diff --git a/crates/ra_hir_def/src/item_tree/lower.rs b/crates/ra_hir_def/src/item_tree/lower.rs index 733fcac7a..7d28fe7c6 100644 --- a/crates/ra_hir_def/src/item_tree/lower.rs +++ b/crates/ra_hir_def/src/item_tree/lower.rs | |||
@@ -66,6 +66,10 @@ impl Ctx { | |||
66 | self.tree | 66 | self.tree |
67 | } | 67 | } |
68 | 68 | ||
69 | fn data(&mut self) -> &mut ItemTreeData { | ||
70 | self.tree.data_mut() | ||
71 | } | ||
72 | |||
69 | fn lower_mod_item(&mut self, item: &ast::ModuleItem, inner: bool) -> Option<ModItems> { | 73 | fn lower_mod_item(&mut self, item: &ast::ModuleItem, inner: bool) -> Option<ModItems> { |
70 | assert!(inner || self.inner_items.is_empty()); | 74 | assert!(inner || self.inner_items.is_empty()); |
71 | 75 | ||
@@ -124,7 +128,7 @@ impl Ctx { | |||
124 | } | 128 | } |
125 | 129 | ||
126 | fn add_attrs(&mut self, item: ModItem, attrs: Attrs) { | 130 | fn add_attrs(&mut self, item: ModItem, attrs: Attrs) { |
127 | match self.tree.attrs.entry(item) { | 131 | match self.tree.attrs.entry(AttrOwner::ModItem(item)) { |
128 | Entry::Occupied(mut entry) => { | 132 | Entry::Occupied(mut entry) => { |
129 | *entry.get_mut() = entry.get().merge(attrs); | 133 | *entry.get_mut() = entry.get().merge(attrs); |
130 | } | 134 | } |
@@ -169,7 +173,7 @@ impl Ctx { | |||
169 | ast::StructKind::Unit => StructDefKind::Unit, | 173 | ast::StructKind::Unit => StructDefKind::Unit, |
170 | }; | 174 | }; |
171 | let res = Struct { name, visibility, generic_params, fields, ast_id, kind }; | 175 | let res = Struct { name, visibility, generic_params, fields, ast_id, kind }; |
172 | Some(id(self.tree.structs.alloc(res))) | 176 | Some(id(self.data().structs.alloc(res))) |
173 | } | 177 | } |
174 | 178 | ||
175 | fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields { | 179 | fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields { |
@@ -190,7 +194,7 @@ impl Ctx { | |||
190 | let start = self.next_field_idx(); | 194 | let start = self.next_field_idx(); |
191 | for field in fields.fields() { | 195 | for field in fields.fields() { |
192 | if let Some(data) = self.lower_record_field(&field) { | 196 | if let Some(data) = self.lower_record_field(&field) { |
193 | self.tree.fields.alloc(data); | 197 | self.data().fields.alloc(data); |
194 | } | 198 | } |
195 | } | 199 | } |
196 | let end = self.next_field_idx(); | 200 | let end = self.next_field_idx(); |
@@ -209,7 +213,7 @@ impl Ctx { | |||
209 | let start = self.next_field_idx(); | 213 | let start = self.next_field_idx(); |
210 | for (i, field) in fields.fields().enumerate() { | 214 | for (i, field) in fields.fields().enumerate() { |
211 | if let Some(data) = self.lower_tuple_field(i, &field) { | 215 | if let Some(data) = self.lower_tuple_field(i, &field) { |
212 | self.tree.fields.alloc(data); | 216 | self.data().fields.alloc(data); |
213 | } | 217 | } |
214 | } | 218 | } |
215 | let end = self.next_field_idx(); | 219 | let end = self.next_field_idx(); |
@@ -236,7 +240,7 @@ impl Ctx { | |||
236 | }; | 240 | }; |
237 | let ast_id = self.source_ast_id_map.ast_id(union); | 241 | let ast_id = self.source_ast_id_map.ast_id(union); |
238 | let res = Union { name, visibility, generic_params, fields, ast_id }; | 242 | let res = Union { name, visibility, generic_params, fields, ast_id }; |
239 | Some(id(self.tree.unions.alloc(res))) | 243 | Some(id(self.data().unions.alloc(res))) |
240 | } | 244 | } |
241 | 245 | ||
242 | fn lower_enum(&mut self, enum_: &ast::EnumDef) -> Option<FileItemTreeId<Enum>> { | 246 | fn lower_enum(&mut self, enum_: &ast::EnumDef) -> Option<FileItemTreeId<Enum>> { |
@@ -249,14 +253,14 @@ impl Ctx { | |||
249 | }; | 253 | }; |
250 | let ast_id = self.source_ast_id_map.ast_id(enum_); | 254 | let ast_id = self.source_ast_id_map.ast_id(enum_); |
251 | let res = Enum { name, visibility, generic_params, variants, ast_id }; | 255 | let res = Enum { name, visibility, generic_params, variants, ast_id }; |
252 | Some(id(self.tree.enums.alloc(res))) | 256 | Some(id(self.data().enums.alloc(res))) |
253 | } | 257 | } |
254 | 258 | ||
255 | fn lower_variants(&mut self, variants: &ast::EnumVariantList) -> Range<Idx<Variant>> { | 259 | fn lower_variants(&mut self, variants: &ast::EnumVariantList) -> Range<Idx<Variant>> { |
256 | let start = self.next_variant_idx(); | 260 | let start = self.next_variant_idx(); |
257 | for variant in variants.variants() { | 261 | for variant in variants.variants() { |
258 | if let Some(data) = self.lower_variant(&variant) { | 262 | if let Some(data) = self.lower_variant(&variant) { |
259 | self.tree.variants.alloc(data); | 263 | self.data().variants.alloc(data); |
260 | } | 264 | } |
261 | } | 265 | } |
262 | let end = self.next_variant_idx(); | 266 | let end = self.next_variant_idx(); |
@@ -327,7 +331,7 @@ impl Ctx { | |||
327 | }; | 331 | }; |
328 | res.generic_params = self.lower_generic_params(GenericsOwner::Function(&res), func); | 332 | res.generic_params = self.lower_generic_params(GenericsOwner::Function(&res), func); |
329 | 333 | ||
330 | Some(id(self.tree.functions.alloc(res))) | 334 | Some(id(self.data().functions.alloc(res))) |
331 | } | 335 | } |
332 | 336 | ||
333 | fn lower_type_alias( | 337 | fn lower_type_alias( |
@@ -341,7 +345,7 @@ impl Ctx { | |||
341 | let generic_params = self.lower_generic_params(GenericsOwner::TypeAlias, type_alias); | 345 | let generic_params = self.lower_generic_params(GenericsOwner::TypeAlias, type_alias); |
342 | let ast_id = self.source_ast_id_map.ast_id(type_alias); | 346 | let ast_id = self.source_ast_id_map.ast_id(type_alias); |
343 | let res = TypeAlias { name, visibility, bounds, generic_params, type_ref, ast_id }; | 347 | let res = TypeAlias { name, visibility, bounds, generic_params, type_ref, ast_id }; |
344 | Some(id(self.tree.type_aliases.alloc(res))) | 348 | Some(id(self.data().type_aliases.alloc(res))) |
345 | } | 349 | } |
346 | 350 | ||
347 | fn lower_static(&mut self, static_: &ast::StaticDef) -> Option<FileItemTreeId<Static>> { | 351 | fn lower_static(&mut self, static_: &ast::StaticDef) -> Option<FileItemTreeId<Static>> { |
@@ -351,7 +355,7 @@ impl Ctx { | |||
351 | let mutable = static_.mut_token().is_some(); | 355 | let mutable = static_.mut_token().is_some(); |
352 | let ast_id = self.source_ast_id_map.ast_id(static_); | 356 | let ast_id = self.source_ast_id_map.ast_id(static_); |
353 | let res = Static { name, visibility, mutable, type_ref, ast_id }; | 357 | let res = Static { name, visibility, mutable, type_ref, ast_id }; |
354 | Some(id(self.tree.statics.alloc(res))) | 358 | Some(id(self.data().statics.alloc(res))) |
355 | } | 359 | } |
356 | 360 | ||
357 | fn lower_const(&mut self, konst: &ast::ConstDef) -> FileItemTreeId<Const> { | 361 | fn lower_const(&mut self, konst: &ast::ConstDef) -> FileItemTreeId<Const> { |
@@ -360,7 +364,7 @@ impl Ctx { | |||
360 | let visibility = self.lower_visibility(konst); | 364 | let visibility = self.lower_visibility(konst); |
361 | let ast_id = self.source_ast_id_map.ast_id(konst); | 365 | let ast_id = self.source_ast_id_map.ast_id(konst); |
362 | let res = Const { name, visibility, type_ref, ast_id }; | 366 | let res = Const { name, visibility, type_ref, ast_id }; |
363 | id(self.tree.consts.alloc(res)) | 367 | id(self.data().consts.alloc(res)) |
364 | } | 368 | } |
365 | 369 | ||
366 | fn lower_module(&mut self, module: &ast::Module) -> Option<FileItemTreeId<Mod>> { | 370 | fn lower_module(&mut self, module: &ast::Module) -> Option<FileItemTreeId<Mod>> { |
@@ -386,7 +390,7 @@ impl Ctx { | |||
386 | }; | 390 | }; |
387 | let ast_id = self.source_ast_id_map.ast_id(module); | 391 | let ast_id = self.source_ast_id_map.ast_id(module); |
388 | let res = Mod { name, visibility, kind, ast_id }; | 392 | let res = Mod { name, visibility, kind, ast_id }; |
389 | Some(id(self.tree.mods.alloc(res))) | 393 | Some(id(self.data().mods.alloc(res))) |
390 | } | 394 | } |
391 | 395 | ||
392 | fn lower_trait(&mut self, trait_def: &ast::TraitDef) -> Option<FileItemTreeId<Trait>> { | 396 | fn lower_trait(&mut self, trait_def: &ast::TraitDef) -> Option<FileItemTreeId<Trait>> { |
@@ -417,7 +421,7 @@ impl Ctx { | |||
417 | items: items.unwrap_or_default(), | 421 | items: items.unwrap_or_default(), |
418 | ast_id, | 422 | ast_id, |
419 | }; | 423 | }; |
420 | Some(id(self.tree.traits.alloc(res))) | 424 | Some(id(self.data().traits.alloc(res))) |
421 | } | 425 | } |
422 | 426 | ||
423 | fn lower_impl(&mut self, impl_def: &ast::ImplDef) -> Option<FileItemTreeId<Impl>> { | 427 | fn lower_impl(&mut self, impl_def: &ast::ImplDef) -> Option<FileItemTreeId<Impl>> { |
@@ -440,7 +444,7 @@ impl Ctx { | |||
440 | .collect(); | 444 | .collect(); |
441 | let ast_id = self.source_ast_id_map.ast_id(impl_def); | 445 | let ast_id = self.source_ast_id_map.ast_id(impl_def); |
442 | let res = Impl { generic_params, target_trait, target_type, is_negative, items, ast_id }; | 446 | let res = Impl { generic_params, target_trait, target_type, is_negative, items, ast_id }; |
443 | Some(id(self.tree.impls.alloc(res))) | 447 | Some(id(self.data().impls.alloc(res))) |
444 | } | 448 | } |
445 | 449 | ||
446 | fn lower_use(&mut self, use_item: &ast::UseItem) -> Vec<FileItemTreeId<Import>> { | 450 | fn lower_use(&mut self, use_item: &ast::UseItem) -> Vec<FileItemTreeId<Import>> { |
@@ -451,7 +455,7 @@ impl Ctx { | |||
451 | 455 | ||
452 | // Every use item can expand to many `Import`s. | 456 | // Every use item can expand to many `Import`s. |
453 | let mut imports = Vec::new(); | 457 | let mut imports = Vec::new(); |
454 | let tree = &mut self.tree; | 458 | let tree = self.tree.data_mut(); |
455 | ModPath::expand_use_item( | 459 | ModPath::expand_use_item( |
456 | InFile::new(self.file, use_item.clone()), | 460 | InFile::new(self.file, use_item.clone()), |
457 | &self.hygiene, | 461 | &self.hygiene, |
@@ -484,7 +488,7 @@ impl Ctx { | |||
484 | let is_macro_use = extern_crate.has_atom_attr("macro_use"); | 488 | let is_macro_use = extern_crate.has_atom_attr("macro_use"); |
485 | 489 | ||
486 | let res = ExternCrate { path, alias, visibility, is_macro_use, ast_id }; | 490 | let res = ExternCrate { path, alias, visibility, is_macro_use, ast_id }; |
487 | Some(id(self.tree.extern_crates.alloc(res))) | 491 | Some(id(self.data().extern_crates.alloc(res))) |
488 | } | 492 | } |
489 | 493 | ||
490 | fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> { | 494 | fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> { |
@@ -511,7 +515,7 @@ impl Ctx { | |||
511 | 515 | ||
512 | let is_builtin = attrs.by_key("rustc_builtin_macro").exists(); | 516 | let is_builtin = attrs.by_key("rustc_builtin_macro").exists(); |
513 | let res = MacroCall { name, path, is_export, is_builtin, is_local_inner, ast_id }; | 517 | let res = MacroCall { name, path, is_export, is_builtin, is_local_inner, ast_id }; |
514 | Some(id(self.tree.macro_calls.alloc(res))) | 518 | Some(id(self.data().macro_calls.alloc(res))) |
515 | } | 519 | } |
516 | 520 | ||
517 | fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec<ModItem> { | 521 | fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec<ModItem> { |
@@ -619,10 +623,14 @@ impl Ctx { | |||
619 | } | 623 | } |
620 | 624 | ||
621 | fn next_field_idx(&self) -> Idx<Field> { | 625 | fn next_field_idx(&self) -> Idx<Field> { |
622 | Idx::from_raw(RawId::from(self.tree.fields.len() as u32)) | 626 | Idx::from_raw(RawId::from( |
627 | self.tree.data.as_ref().map_or(0, |data| data.fields.len() as u32), | ||
628 | )) | ||
623 | } | 629 | } |
624 | fn next_variant_idx(&self) -> Idx<Variant> { | 630 | fn next_variant_idx(&self) -> Idx<Variant> { |
625 | Idx::from_raw(RawId::from(self.tree.variants.len() as u32)) | 631 | Idx::from_raw(RawId::from( |
632 | self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32), | ||
633 | )) | ||
626 | } | 634 | } |
627 | } | 635 | } |
628 | 636 | ||