Skip to content

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

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

Closed
wants to merge 3 commits into from
Closed
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
@@ -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):
40 changes: 20 additions & 20 deletions src/liballoc/collections/btree/node.rs
Original file line number Diff line number Diff line change
@@ -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)
}
}
@@ -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,
}
}
@@ -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)>,
@@ -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.
@@ -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.
21 changes: 16 additions & 5 deletions src/test/debuginfo/pretty-std-collections.rs
Original file line number Diff line number Diff line change
@@ -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 {
@@ -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();
@@ -63,4 +72,6 @@ fn main() {
zzz(); // #break
}

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