Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove leaf bias from BTreeMap's BoxedNode and NodeRef #67980

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 27 additions & 20 deletions src/etc/gdb_rust_pretty_printing.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,27 +333,34 @@ def children(self):
# Yield each key (and optionally value) from a BoxedNode.
def children_of_node(boxed_node, height, want_values):
node_ptr = boxed_node['ptr']['pointer']
if height > 0:
type_name = str(node_ptr.type.target()).replace('LeafNode', 'InternalNode')
node_type = gdb.lookup_type(type_name)
node_ptr = node_ptr.cast(node_type.pointer())
leaf = node_ptr['data']
else:
leaf = node_ptr.dereference()
keys = leaf['keys']
if want_values:
values = leaf['vals']
length = int(leaf['len'])
for i in xrange(0, length + 1):
head = node_ptr.dereference()
length = int(head['len'])
if length > 0:
if height > 0:
child_ptr = node_ptr['edges'][i]['value']['value']
for child in children_of_node(child_ptr, height - 1, want_values):
yield child
if i < length:
if want_values:
yield (keys[i]['value']['value'], values[i]['value']['value'])
else:
yield keys[i]['value']['value']
type_name = str(node_ptr.type.target()).replace('NodeHeader', 'InternalNode', 1)
node_type = gdb.lookup_type(type_name)
node_ptr = node_ptr.cast(node_type.pointer())
leaf = node_ptr['data']
edges = node_ptr['edges']
else:
type_name = str(node_ptr.type.target()).replace('NodeHeader', 'LeafNode', 1)
node_type = gdb.lookup_type(type_name)
node_ptr = node_ptr.cast(node_type.pointer())
leaf = node_ptr.dereference()
edges = None
keys = leaf['keys']
if want_values:
values = leaf['vals']
for i in xrange(0, length + 1):
if edges:
child_ptr = edges[i]['value']['value']
for child in children_of_node(child_ptr, height - 1, want_values):
yield child
if i < length:
if want_values:
yield (keys[i]['value']['value'], values[i]['value']['value'])
else:
yield keys[i]['value']['value']

class RustStdBTreeSetPrinter(object):
def __init__(self, val):
Expand Down
40 changes: 20 additions & 20 deletions src/liballoc/collections/btree/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,30 +152,34 @@ impl<K, V> InternalNode<K, V> {
}
}

/// An owned pointer to a node. This basically is either `Box<LeafNode<K, V>>` or
/// `Box<InternalNode<K, V>>`. However, it contains no information as to which of the two types
/// of nodes is actually behind the box, and, partially due to this lack of information, has no
/// destructor.
/// A managed, non-null pointer to a node. This is either an owned pointer to
/// `LeafNode<K, V>`, an owned pointer to `InternalNode<K, V>`, or a (not owned)
/// pointer to `NodeHeader<(), ()` (more specifically, the pointer to EMPTY_ROOT_NODE).
/// All of these types have a `NodeHeader<K, V>` prefix, meaning that they have at
/// least the same size as `NodeHeader<K, V>` and store the same kinds of data at the same
/// offsets; and they have a pointer alignment at least as large as `NodeHeader<K, V>`'s.
/// So that's the pointee type we store, and `as_header()` is unconditionally safe.
/// However, `BoxedNode` contains no information as to which of the three types
/// of nodes it actually contains, and, partially due to this lack of information,
/// has no destructor.
struct BoxedNode<K, V> {
ptr: Unique<LeafNode<K, V>>,
ptr: Unique<NodeHeader<K, V>>,
}

impl<K, V> BoxedNode<K, V> {
fn from_leaf(node: Box<LeafNode<K, V>>) -> Self {
BoxedNode { ptr: Box::into_unique(node) }
BoxedNode { ptr: Box::into_unique(node).cast() }
}

fn from_internal(node: Box<InternalNode<K, V>>) -> Self {
unsafe {
BoxedNode { ptr: Unique::new_unchecked(Box::into_raw(node) as *mut LeafNode<K, V>) }
}
BoxedNode { ptr: Box::into_unique(node).cast() }
}

unsafe fn from_ptr(ptr: NonNull<LeafNode<K, V>>) -> Self {
unsafe fn from_ptr(ptr: NonNull<NodeHeader<K, V>>) -> Self {
BoxedNode { ptr: Unique::from(ptr) }
}

fn as_ptr(&self) -> NonNull<LeafNode<K, V>> {
fn as_ptr(&self) -> NonNull<NodeHeader<K, V>> {
NonNull::from(self.ptr)
}
}
Expand All @@ -197,11 +201,7 @@ impl<K, V> Root<K, V> {

pub fn shared_empty_root() -> Self {
Root {
node: unsafe {
BoxedNode::from_ptr(NonNull::new_unchecked(
&EMPTY_ROOT_NODE as *const _ as *const LeafNode<K, V> as *mut _,
))
},
node: unsafe { BoxedNode::from_ptr(NonNull::from(&EMPTY_ROOT_NODE).cast()) },
height: 0,
}
}
Expand Down Expand Up @@ -310,7 +310,7 @@ impl<K, V> Root<K, V> {
/// Turning this into a `NodeHeader` reference is always safe.
pub struct NodeRef<BorrowType, K, V, Type> {
height: usize,
node: NonNull<LeafNode<K, V>>,
node: NonNull<NodeHeader<K, V>>,
// `root` is null unless the borrow type is `Mut`
root: *const Root<K, V>,
_marker: PhantomData<(BorrowType, Type)>,
Expand Down Expand Up @@ -372,11 +372,11 @@ impl<BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> {
/// See `NodeRef` on why the node may not be a shared root.
unsafe fn as_leaf(&self) -> &LeafNode<K, V> {
debug_assert!(!self.is_shared_root());
self.node.as_ref()
&*(self.node.as_ptr() as *const LeafNode<K, V>)
}

fn as_header(&self) -> &NodeHeader<K, V> {
unsafe { &*(self.node.as_ptr() as *const NodeHeader<K, V>) }
unsafe { self.node.as_ref() }
}

/// Returns whether the node is the shared, empty root.
Expand Down Expand Up @@ -505,7 +505,7 @@ impl<'a, K, V, Type> NodeRef<marker::Mut<'a>, K, V, Type> {
/// This also implies you can invoke this member on the shared root, but the resulting pointer
/// might not be properly aligned and definitely would not allow accessing keys and values.
fn as_leaf_mut(&mut self) -> *mut LeafNode<K, V> {
self.node.as_ptr()
self.node.as_ptr() as *mut LeafNode<K, V>
}

/// The caller must ensure that the node is not the shared root.
Expand Down
21 changes: 16 additions & 5 deletions src/test/debuginfo/pretty-std-collections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,26 @@
// gdb-command: print btree_map
// gdb-check:$2 = BTreeMap<i32, i32>(len: 15) = {[0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 5, [6] = 6, [7] = 7, [8] = 8, [9] = 9, [10] = 10, [11] = 11, [12] = 12, [13] = 13, [14] = 14}

// gdb-command: print empty_btree_map
// gdb-check:$3 = BTreeMap<(), ()>(len: 0)

// gdb-command: print nasty_btree_map
// gdb-check:$4 = BTreeMap<i32, pretty_std_collections::MyNodeHeader>(len: 1) = {[1] = pretty_std_collections::MyNodeHeader (11)}

// gdb-command: print vec_deque
// gdb-check:$3 = VecDeque<i32>(len: 3, cap: 8) = {5, 3, 7}
// gdb-check:$5 = VecDeque<i32>(len: 3, cap: 8) = {5, 3, 7}

// gdb-command: print vec_deque2
// gdb-check:$4 = VecDeque<i32>(len: 7, cap: 8) = {2, 3, 4, 5, 6, 7, 8}
// gdb-check:$6 = VecDeque<i32>(len: 7, cap: 8) = {2, 3, 4, 5, 6, 7, 8}

#![allow(unused_variables)]
use std::collections::BTreeSet;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::collections::VecDeque;

struct MyNodeHeader(i32); // helps to ensure we don't blindly replace substring "NodeHeader"

fn main() {

// BTreeSet
let mut btree_set = BTreeSet::new();
for i in 0..15 {
Expand All @@ -45,6 +51,9 @@ fn main() {
for i in 0..15 {
btree_map.insert(i, i);
}
let empty_btree_map: BTreeMap<(), ()> = BTreeMap::new();
let mut nasty_btree_map: BTreeMap<i32, MyNodeHeader> = BTreeMap::new();
nasty_btree_map.insert(1, MyNodeHeader(11));

// VecDeque
let mut vec_deque = VecDeque::new();
Expand All @@ -63,4 +72,6 @@ fn main() {
zzz(); // #break
}

fn zzz() { () }
fn zzz() {
()
}