@@ -54,10 +54,8 @@ pub const CAPACITY: usize = 2 * B - 1;
5454/// `NodeHeader` because we do not want unnecessary padding between `len` and the keys.
5555/// Crucially, `NodeHeader` can be safely transmuted to different K and V. (This is exploited
5656/// by `as_header`.)
57- /// See `into_key_slice` for an explanation of K2. K2 cannot be safely transmuted around
58- /// because the size of `NodeHeader` depends on its alignment!
5957#[ repr( C ) ]
60- struct NodeHeader < K , V , K2 = ( ) > {
58+ struct NodeHeader < K , V > {
6159 /// We use `*const` as opposed to `*mut` so as to be covariant in `K` and `V`.
6260 /// This either points to an actual node or is null.
6361 parent : * const InternalNode < K , V > ,
@@ -72,9 +70,6 @@ struct NodeHeader<K, V, K2 = ()> {
7270 /// This next to `parent_idx` to encourage the compiler to join `len` and
7371 /// `parent_idx` into the same 32-bit word, reducing space overhead.
7472 len : u16 ,
75-
76- /// See `into_key_slice`.
77- keys_start : [ K2 ; 0 ] ,
7873}
7974#[ repr( C ) ]
8075struct LeafNode < K , V > {
@@ -128,7 +123,7 @@ unsafe impl Sync for NodeHeader<(), ()> {}
128123// We use just a header in order to save space, since no operation on an empty tree will
129124// ever take a pointer past the first key.
130125static EMPTY_ROOT_NODE : NodeHeader < ( ) , ( ) > =
131- NodeHeader { parent : ptr:: null ( ) , parent_idx : MaybeUninit :: uninit ( ) , len : 0 , keys_start : [ ] } ;
126+ NodeHeader { parent : ptr:: null ( ) , parent_idx : MaybeUninit :: uninit ( ) , len : 0 } ;
132127
133128/// The underlying representation of internal nodes. As with `LeafNode`s, these should be hidden
134129/// behind `BoxedNode`s to prevent dropping uninitialized keys and values. Any pointer to an
@@ -390,7 +385,7 @@ impl<BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> {
390385 }
391386
392387 /// Borrows a view into the keys stored in the node.
393- /// Works on all possible nodes, including the shared root.
388+ /// The caller must ensure that the node is not the shared root.
394389 pub fn keys ( & self ) -> & [ K ] {
395390 self . reborrow ( ) . into_key_slice ( )
396391 }
@@ -528,47 +523,9 @@ impl<'a, K, V, Type> NodeRef<marker::Mut<'a>, K, V, Type> {
528523
529524impl < ' a , K : ' a , V : ' a , Type > NodeRef < marker:: Immut < ' a > , K , V , Type > {
530525 fn into_key_slice ( self ) -> & ' a [ K ] {
531- // We have to be careful here because we might be pointing to the shared root.
532- // In that case, we must not create an `&LeafNode`. We could just return
533- // an empty slice whenever the length is 0 (this includes the shared root),
534- // but we want to avoid that run-time check.
535- // Instead, we create a slice pointing into the node whenever possible.
536- // We can sometimes do this even for the shared root, as the slice will be
537- // empty and `NodeHeader` contains an empty `keys_start` array.
538- // We cannot *always* do this because:
539- // - `keys_start` is not correctly typed because we want `NodeHeader`'s size to
540- // not depend on the alignment of `K` (needed because `as_header` should be safe).
541- // For this reason, `NodeHeader` has this `K2` parameter (that's usually `()`
542- // and hence just adds a size-0-align-1 field, not affecting layout).
543- // If the correctly typed header is more highly aligned than the allocated header,
544- // we cannot transmute safely.
545- // - Even if we can transmute, the offset of a correctly typed `keys_start` might
546- // be different and outside the bounds of the allocated header!
547- // So we do an alignment check and a size check first, that will be evaluated
548- // at compile-time, and only do any run-time check in the rare case that
549- // the compile-time checks signal danger.
550- if ( mem:: align_of :: < NodeHeader < K , V , K > > ( ) > mem:: align_of :: < NodeHeader < K , V > > ( )
551- || mem:: size_of :: < NodeHeader < K , V , K > > ( ) != mem:: size_of :: < NodeHeader < K , V > > ( ) )
552- && self . is_shared_root ( )
553- {
554- & [ ]
555- } else {
556- // If we are a `LeafNode<K, V>`, we can always transmute to
557- // `NodeHeader<K, V, K>` and `keys_start` always has the same offset
558- // as the actual `keys`.
559- // Thanks to the checks above, we know that we can transmute to
560- // `NodeHeader<K, V, K>` and that `keys_start` will be
561- // in-bounds of some allocation even if this is the shared root!
562- // (We might be one-past-the-end, but that is allowed by LLVM.)
563- // Thus we can use `NodeHeader<K, V, K>`
564- // to compute the pointer where the keys start.
565- // This entire hack will become unnecessary once
566- // <https://github.com/rust-lang/rfcs/pull/2582> lands, then we can just take a raw
567- // pointer to the `keys` field of `*const InternalNode<K, V>`.
568- let header = self . as_header ( ) as * const _ as * const NodeHeader < K , V , K > ;
569- let keys = unsafe { & ( * header) . keys_start as * const _ as * const K } ;
570- unsafe { slice:: from_raw_parts ( keys, self . len ( ) ) }
571- }
526+ debug_assert ! ( !self . is_shared_root( ) ) ;
527+ // We cannot be the shared root, so `as_leaf` is okay.
528+ unsafe { slice:: from_raw_parts ( MaybeUninit :: first_ptr ( & self . as_leaf ( ) . keys ) , self . len ( ) ) }
572529 }
573530
574531 /// The caller must ensure that the node is not the shared root.
0 commit comments