rudy_dwarf/parser/btreemap.rs
1//! BTreeMap parser implementation using combinators
2//!
3//! Inspired by the `BTreeMapProvider` in [gdb_provider.py](https://github.com/rust-lang/rust/blob/513999b936c37902120380f4171963d1f1d80347/src/etc/gdb_providers.py#L4)
4//!
5//! Overview: BTreeMap stores key-value pairs in a B-tree structure with internal nodes and leaf nodes. The tree has a
6//! height, and we traverse from the root down to the leaves.
7//!
8//! Algorithm:
9//! 1. Start with the BTreeMap's root node and height
10//! 2. If the map is empty (length = 0), return immediately
11//! 3. Extract the root node pointer from the Option wrapper
12//! 4. Recursively traverse the tree starting from the root with the given height
13//! 5. For each node:
14//! - If it's an internal node (height > 0):
15//! - Cast to InternalNode type to access edges
16//! - Recursively traverse each child edge before processing keys/values
17//! - Process the node's key-value pairs:
18//! - Read the len field to know how many pairs are in this node
19//! - Iterate through indices 0 to len-1
20//! - For each index, yield the key and value from the arrays
21//!
22//!
23//! Key details:
24//! - Internal nodes have edges (child pointers) in addition to keys/values
25//! - Leaf nodes only have keys/values
26//! - The tree is traversed in-order (left edge, key/value, right edge)
27//! - Zero-sized types need special handling
28
29use anyhow::Result;
30use rudy_types::{BTreeNodeLayout, BTreeRootLayout, MapLayout, MapVariant};
31
32use super::{primitives::entry_type, Parser};
33use crate::{
34 parser::{
35 children::parse_children,
36 option::parse_option_entry,
37 pointers::nonnull,
38 primitives::{data_offset, is_member, member, resolved_generic},
39 },
40 Die, DwarfDb,
41};
42
43/// Parser for btree BTreeMap layout
44pub fn btree_map() -> BTreeMapParser {
45 BTreeMapParser
46}
47
48pub struct BTreeMapParser;
49
50impl Parser<MapLayout<Die>> for BTreeMapParser {
51 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<MapLayout<Die>> {
52 tracing::debug!("resolving btree map type: {}", entry.print(db));
53
54 // Parse key type, value type, root field, and length field from BTreeMap
55 let (key_type, value_type, (root_offset, root_option_type), length_offset) =
56 parse_children((
57 resolved_generic("K"),
58 resolved_generic("V"),
59 is_member("root").then(data_offset().and(entry_type())),
60 is_member("length").then(data_offset()),
61 ))
62 .parse(db, entry)?;
63
64 tracing::debug!("resolving root field: {}", root_option_type.print(db));
65
66 // Parse the Option<NodeRef> to get the Some variant which contains the NodeRef
67 let (_, _, _, some_variant) = parse_option_entry().parse(db, root_option_type)?;
68 let node_ref_type = some_variant.layout;
69
70 tracing::debug!("resolving node ref field: {}", node_ref_type.print(db));
71
72 // Parse the NodeRef struct to get height and node pointer offsets
73 let (height_offset, (node_offset, node_ptr_type)) = parse_children((
74 is_member("height").then(data_offset()),
75 is_member("node").then(data_offset().and(entry_type())),
76 ))
77 .parse(db, node_ref_type)?;
78
79 // Create the root layout
80 let root_layout = BTreeRootLayout {
81 node_offset,
82 height_offset,
83 };
84
85 // The node pointer is a NonNull<LeafNode<K, V>>, resolve it to get the leaf node type
86 let leaf_node_type = nonnull().parse(db, node_ptr_type)?;
87
88 tracing::debug!("resolving leaf node type: {}", leaf_node_type.print(db));
89
90 // Parse the LeafNode structure to get offsets
91 let (len_offset, keys_offset, vals_offset) = parse_children((
92 is_member("len").then(data_offset()),
93 is_member("keys").then(data_offset()),
94 is_member("vals").then(data_offset()),
95 ))
96 .parse(db, leaf_node_type)?;
97
98 // To get the InternalNode type, we need to find the parent field which contains
99 // Option<NonNull<InternalNode<K, V>>>. We'll extract this type.
100 let parent_option_type = member("parent")
101 .then(entry_type())
102 .parse(db, leaf_node_type)?;
103
104 // Parse the Option to get the Some variant
105 let (_, _, _, parent_some_variant) = parse_option_entry().parse(db, parent_option_type)?;
106
107 // The Some variant contains NonNull<InternalNode<K, V>>, resolve it
108 let internal_node_type = nonnull().parse(db, parent_some_variant.layout)?;
109
110 // For InternalNode, we need the edges offset. The edges field is specific to InternalNode.
111 let edges_offset = member("edges")
112 .then(data_offset())
113 .parse(db, internal_node_type)?;
114
115 // Create the node layout
116 let node_layout = BTreeNodeLayout {
117 keys_offset,
118 vals_offset,
119 len_offset,
120 edges_offset,
121 };
122
123 Ok(MapLayout {
124 key_type,
125 value_type,
126 variant: MapVariant::BTreeMap {
127 length_offset,
128 root_offset,
129 root_layout,
130 node_layout,
131 },
132 })
133 }
134}