]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
crimson/onode-staged-tree: remove compile-time node SIZE
authorYingxin Cheng <yingxin.cheng@intel.com>
Mon, 7 Jun 2021 02:45:18 +0000 (10:45 +0800)
committerYingxin Cheng <yingxin.cheng@intel.com>
Fri, 11 Jun 2021 14:59:06 +0000 (22:59 +0800)
Switch to run-time node_extent_t::node_size instead.

Signed-off-by: Yingxin Cheng <yingxin.cheng@intel.com>
src/crimson/os/seastore/onode_manager/staged-fltree/stages/node_stage.cc
src/crimson/os/seastore/onode_manager/staged-fltree/stages/node_stage.h
src/crimson/os/seastore/onode_manager/staged-fltree/stages/node_stage_layout.cc
src/crimson/os/seastore/onode_manager/staged-fltree/stages/node_stage_layout.h

index 20d5ac893955c26dcbbb52ba21e0caa4dffca5ff..246e5eb4640f0673b5a2bc2dcd2984860f110be0 100644 (file)
@@ -18,7 +18,8 @@ const char* NODE_T::p_left_bound() const
     // N3 internal node doesn't have the right part
     return nullptr;
   } else {
-    auto ret = p_start() + fields().get_item_end_offset(keys());
+    auto ret = p_start() +
+               fields().get_item_end_offset(keys(), node_size);
     if constexpr (NODE_TYPE == node_type_t::INTERNAL) {
       if (is_level_tail()) {
         ret -= sizeof(laddr_t);
@@ -36,7 +37,8 @@ node_offset_t NODE_T::size_to_nxt_at(index_t index) const
                 FIELD_TYPE == field_type_t::N1) {
     return FieldType::estimate_insert_one();
   } else if constexpr (FIELD_TYPE == field_type_t::N2) {
-    auto p_end = p_start() + p_fields->get_item_end_offset(index);
+    auto p_end = p_start() +
+                 p_fields->get_item_end_offset(index, node_size);
     return FieldType::estimate_insert_one() + ns_oid_view_t(p_end).size();
   } else {
     ceph_abort("N3 node is not nested");
@@ -49,8 +51,10 @@ memory_range_t NODE_T::get_nxt_container(index_t index) const
   if constexpr (std::is_same_v<FieldType, internal_fields_3_t>) {
     ceph_abort("N3 internal node doesn't have the right part");
   } else {
-    node_offset_t item_start_offset = p_fields->get_item_start_offset(index);
-    node_offset_t item_end_offset = p_fields->get_item_end_offset(index);
+    node_offset_t item_start_offset = p_fields->get_item_start_offset(
+        index, node_size);
+    node_offset_t item_end_offset = p_fields->get_item_end_offset(
+        index, node_size);
     assert(item_start_offset < item_end_offset);
     auto item_p_start = p_start() + item_start_offset;
     auto item_p_end = p_start() + item_end_offset;
@@ -81,6 +85,8 @@ template <typename FieldType, node_type_t NODE_TYPE>
 void NODE_T::update_is_level_tail(
     NodeExtentMutable& mut, const node_extent_t& extent, bool value)
 {
+  assert(mut.get_length() == extent.node_size);
+  assert(mut.get_read() == extent.p_start());
   node_header_t::update_is_level_tail(mut, extent.p_fields->header, value);
 }
 
@@ -90,13 +96,16 @@ memory_range_t NODE_T::insert_prefix_at(
     NodeExtentMutable& mut, const node_extent_t& node, const full_key_t<KT>& key,
     index_t index, node_offset_t size, const char* p_left_bound)
 {
+  assert(mut.get_length() == node.node_size);
+  assert(mut.get_read() == node.p_start());
   if constexpr (FIELD_TYPE == field_type_t::N0 ||
                 FIELD_TYPE == field_type_t::N1) {
     assert(index <= node.keys());
     assert(p_left_bound == node.p_left_bound());
     assert(size > FieldType::estimate_insert_one());
     auto size_right = size - FieldType::estimate_insert_one();
-    const char* p_insert = node.p_start() + node.fields().get_item_end_offset(index);
+    const char* p_insert = node.p_start() +
+                           node.fields().get_item_end_offset(index, mut.get_length());
     const char* p_insert_front = p_insert - size_right;
     FieldType::template insert_at<KT>(mut, key, node.fields(), index, size_right);
     mut.shift_absolute(p_left_bound,
@@ -130,6 +139,8 @@ template <typename FieldType, node_type_t NODE_TYPE>
 void NODE_T::update_size_at(
     NodeExtentMutable& mut, const node_extent_t& node, index_t index, int change)
 {
+  assert(mut.get_length() == node.node_size);
+  assert(mut.get_read() == node.p_start());
   assert(index < node.keys());
   FieldType::update_size_at(mut, node.fields(), index, change);
 }
@@ -138,6 +149,8 @@ template <typename FieldType, node_type_t NODE_TYPE>
 node_offset_t NODE_T::trim_until(
     NodeExtentMutable& mut, const node_extent_t& node, index_t index)
 {
+  assert(mut.get_length() == node.node_size);
+  assert(mut.get_read() == node.p_start());
   assert(!node.is_level_tail());
   auto keys = node.keys();
   assert(index <= keys);
@@ -159,14 +172,18 @@ node_offset_t NODE_T::trim_at(
     NodeExtentMutable& mut, const node_extent_t& node,
     index_t index, node_offset_t trimmed)
 {
+  assert(mut.get_length() == node.node_size);
+  assert(mut.get_read() == node.p_start());
   assert(!node.is_level_tail());
   assert(index < node.keys());
   if constexpr (std::is_same_v<FieldType, internal_fields_3_t>) {
     ceph_abort("not implemented");
   } else {
-    node_offset_t offset = node.p_fields->get_item_start_offset(index);
+    extent_len_t node_size = mut.get_length();
+    node_offset_t offset = node.p_fields->get_item_start_offset(
+        index, node_size);
     size_t new_offset = offset + trimmed;
-    assert(new_offset < node.p_fields->get_item_end_offset(index));
+    assert(new_offset < node.p_fields->get_item_end_offset(index, node_size));
     mut.copy_in_absolute(const_cast<void*>(node.p_fields->p_offset(index)),
                          node_offset_t(new_offset));
     mut.copy_in_absolute(
@@ -181,6 +198,8 @@ node_offset_t NODE_T::erase_at(
     NodeExtentMutable& mut, const node_extent_t& node,
     index_t index, const char* p_left_bound)
 {
+  assert(mut.get_length() == node.node_size);
+  assert(mut.get_read() == node.p_start());
   if constexpr (FIELD_TYPE == field_type_t::N0 ||
                 FIELD_TYPE == field_type_t::N1) {
     assert(node.keys() > 0);
@@ -211,13 +230,18 @@ APPEND_T::Appender(NodeExtentMutable* p_mut, const node_extent_t& node, bool ope
 {
   assert(p_start == node.p_start());
   assert(node.keys());
+  assert(node.node_size == p_mut->get_length());
+  extent_len_t node_size = node.node_size;
   if (open) {
     // seek as open_nxt()
     if constexpr (FIELD_TYPE == field_type_t::N0 ||
                   FIELD_TYPE == field_type_t::N1) {
-      p_append_left = p_start + node.fields().get_key_start_offset(node.keys() - 1);
+      p_append_left = p_start + node.fields().get_key_start_offset(
+          node.keys() - 1, node_size);
       p_append_left += sizeof(typename FieldType::key_t);
-      p_append_right = p_start + node.fields().get_item_end_offset(node.keys() - 1);
+      p_append_right = p_start +
+                       node.fields().get_item_end_offset(node.keys() - 1,
+                                                         node_size);
     } else if constexpr (FIELD_TYPE == field_type_t::N2) {
       ceph_abort("not implemented");
     } else {
@@ -226,10 +250,14 @@ APPEND_T::Appender(NodeExtentMutable* p_mut, const node_extent_t& node, bool ope
     num_keys = node.keys() - 1;
   } else {
     if constexpr (std::is_same_v<FieldType, internal_fields_3_t>) {
+      std::ignore = node_size;
       ceph_abort("not implemented");
     } else {
-      p_append_left = p_start + node.fields().get_key_start_offset(node.keys());
-      p_append_right = p_start + node.fields().get_item_end_offset(node.keys());
+      p_append_left = p_start + node.fields().get_key_start_offset(
+          node.keys(), node_size);
+      p_append_right = p_start +
+                       node.fields().get_item_end_offset(node.keys(),
+                                                         node_size);
     }
     num_keys = node.keys();
   }
@@ -245,6 +273,8 @@ void APPEND_T::append(const node_extent_t& src, index_t from, index_t items)
   } else {
     assert(p_src == &src);
   }
+  assert(p_src->node_size == p_mut->get_length());
+  extent_len_t node_size = src.node_size;
   if (items == 0) {
     return;
   }
@@ -252,11 +282,14 @@ void APPEND_T::append(const node_extent_t& src, index_t from, index_t items)
   assert(from + items <= src.keys());
   num_keys += items;
   if constexpr (std::is_same_v<FieldType, internal_fields_3_t>) {
+    std::ignore = node_size;
     ceph_abort("not implemented");
   } else {
     // append left part forwards
-    node_offset_t offset_left_start = src.fields().get_key_start_offset(from);
-    node_offset_t offset_left_end = src.fields().get_key_start_offset(from + items);
+    node_offset_t offset_left_start = src.fields().get_key_start_offset(
+        from, node_size);
+    node_offset_t offset_left_end = src.fields().get_key_start_offset(
+        from + items, node_size);
     node_offset_t left_size = offset_left_end - offset_left_start;
     if (num_keys == 0) {
       // no need to adjust offset
@@ -266,7 +299,8 @@ void APPEND_T::append(const node_extent_t& src, index_t from, index_t items)
           src.p_start() + offset_left_start, left_size);
     } else {
       node_offset_t step_size = FieldType::estimate_insert_one();
-      node_offset_t offset_base = src.fields().get_item_end_offset(from);
+      node_offset_t offset_base = src.fields().get_item_end_offset(
+          from, node_size);
       int offset_change = p_append_right - p_start - offset_base;
       auto p_offset_dst = p_append_left;
       if constexpr (FIELD_TYPE != field_type_t::N2) {
@@ -277,8 +311,10 @@ void APPEND_T::append(const node_extent_t& src, index_t from, index_t items)
         p_offset_dst += sizeof(typename FieldType::key_t);
       }
       for (auto i = from; i < from + items; ++i) {
-        p_mut->copy_in_absolute(p_offset_dst,
-            node_offset_t(src.fields().get_item_start_offset(i) + offset_change));
+        p_mut->copy_in_absolute(
+            p_offset_dst,
+            node_offset_t(src.fields().get_item_start_offset(i, node_size) +
+                          offset_change));
         p_offset_dst += step_size;
       }
       assert(p_append_left + left_size + sizeof(typename FieldType::key_t) ==
@@ -287,8 +323,10 @@ void APPEND_T::append(const node_extent_t& src, index_t from, index_t items)
     p_append_left += left_size;
 
     // append right part backwards
-    node_offset_t offset_right_start = src.fields().get_item_end_offset(from + items);
-    node_offset_t offset_right_end = src.fields().get_item_end_offset(from);
+    node_offset_t offset_right_start = src.fields().get_item_end_offset(
+        from + items, node_size);
+    node_offset_t offset_right_end = src.fields().get_item_end_offset(
+        from, node_size);
     node_offset_t right_size = offset_right_end - offset_right_start;
     p_append_right -= right_size;
     p_mut->copy_in_absolute(p_append_right,
index e726e0ed978cf49f2140f3939261aa6d6f059905..35e5598f0b50f5c53258f54454b28b8a0605672f 100644 (file)
@@ -44,7 +44,7 @@ class node_extent_t {
   const char* p_start() const { return fields_start(*p_fields); }
 
   const char* off_to_ptr(node_offset_t off) const {
-    assert(off <= FieldType::SIZE);
+    assert(off <= node_size);
     return p_start() + off;
   }
 
@@ -52,26 +52,30 @@ class node_extent_t {
     auto _ptr = static_cast<const char*>(ptr);
     assert(_ptr >= p_start());
     auto off = _ptr - p_start();
-    assert(off <= FieldType::SIZE);
+    assert(off <= node_size);
     return off;
   }
 
   bool is_level_tail() const { return p_fields->is_level_tail(); }
   level_t level() const { return p_fields->header.level; }
   node_offset_t free_size() const {
-    return p_fields->template free_size_before<NODE_TYPE>(keys());
+    return p_fields->template free_size_before<NODE_TYPE>(
+        keys(), node_size);
+  }
+  node_offset_t total_size() const {
+    return p_fields->total_size(node_size);
   }
-  node_offset_t total_size() const { return p_fields->total_size(); }
   const char* p_left_bound() const;
   template <node_type_t T = NODE_TYPE>
   std::enable_if_t<T == node_type_t::INTERNAL, const laddr_packed_t*>
   get_end_p_laddr() const {
     assert(is_level_tail());
     if constexpr (FIELD_TYPE == field_type_t::N3) {
-      return p_fields->get_p_child_addr(keys());
+      return p_fields->get_p_child_addr(keys(), node_size);
     } else {
-      auto offset_start = p_fields->get_item_end_offset(keys());
-      assert(offset_start <= FieldType::SIZE);
+      auto offset_start = p_fields->get_item_end_offset(
+          keys(), node_size);
+      assert(offset_start <= node_size);
       offset_start -= sizeof(laddr_packed_t);
       auto p_addr = p_start() + offset_start;
       return reinterpret_cast<const laddr_packed_t*>(p_addr);
@@ -82,9 +86,12 @@ class node_extent_t {
   using key_get_type = typename FieldType::key_get_type;
   static constexpr auto CONTAINER_TYPE = ContainerType::INDEXABLE;
   index_t keys() const { return p_fields->num_keys; }
-  key_get_type operator[] (index_t index) const { return p_fields->get_key(index); }
+  key_get_type operator[] (index_t index) const {
+    return p_fields->get_key(index, node_size);
+  }
   node_offset_t size_before(index_t index) const {
-    auto free_size = p_fields->template free_size_before<NODE_TYPE>(index);
+    auto free_size = p_fields->template free_size_before<NODE_TYPE>(
+        index, node_size);
     assert(total_size() >= free_size);
     return total_size() - free_size;
   }
@@ -98,7 +105,7 @@ class node_extent_t {
   get_p_value(index_t index) const {
     assert(index < keys());
     if constexpr (NODE_TYPE == node_type_t::INTERNAL) {
-      return p_fields->get_p_child_addr(index);
+      return p_fields->get_p_child_addr(index, node_size);
     } else {
       auto range = get_nxt_container(index);
       auto ret = reinterpret_cast<const value_header_t*>(range.p_start);
@@ -204,7 +211,7 @@ class node_extent_t<FieldType, NODE_TYPE>::Appender {
     assert(p_fields->num_keys == 0);
 #endif
     p_append_left = p_start + FieldType::HEADER_SIZE;
-    p_append_right = p_start + FieldType::SIZE;
+    p_append_right = p_start + p_mut->get_length();
   }
   Appender(NodeExtentMutable*, const node_extent_t&, bool open = false);
   void append(const node_extent_t& src, index_t from, index_t items);
index b2c4ef71a477e90b6170a179b904b9039fd5dca8..83b70f7ffa73249024ac4b5d8a09fd3ec24ac5f3 100644 (file)
@@ -36,11 +36,12 @@ void F013_T::update_size_at(
     NodeExtentMutable& mut, const me_t& node, index_t index, int change)
 {
   assert(index <= node.num_keys);
+  extent_len_t node_size = mut.get_length();
 #ifndef NDEBUG
   // check underflow
   if (change < 0 && index != node.num_keys) {
-    assert(node.get_item_start_offset(index) <
-           node.get_item_end_offset(index));
+    assert(node.get_item_start_offset(index, node_size) <
+           node.get_item_end_offset(index, node_size));
   }
 #endif
   for (const auto* p_slot = &node.slots[index];
@@ -55,7 +56,7 @@ void F013_T::update_size_at(
   // check overflow
   if (change > 0 && index != node.num_keys) {
     assert(node.num_keys > 0);
-    assert(node.get_key_start_offset(node.num_keys) <=
+    assert(node.get_key_start_offset(node.num_keys, node_size) <=
            node.slots[node.num_keys - 1].right_offset);
   }
 #endif
@@ -84,14 +85,18 @@ void F013_T::insert_at(
     const me_t& node, index_t index, node_offset_t size_right)
 {
   assert(index <= node.num_keys);
+  extent_len_t node_size = mut.get_length();
   update_size_at(mut, node, index, size_right);
   auto p_insert = const_cast<char*>(fields_start(node)) +
-                  node.get_key_start_offset(index);
-  auto p_shift_end = fields_start(node) + node.get_key_start_offset(node.num_keys);
+                  node.get_key_start_offset(index, node_size);
+  auto p_shift_end = fields_start(node) +
+                     node.get_key_start_offset(node.num_keys, node_size);
   mut.shift_absolute(p_insert, p_shift_end - p_insert, estimate_insert_one());
   mut.copy_in_absolute((void*)&node.num_keys, num_keys_t(node.num_keys + 1));
   append_key(mut, key_t::template from_key<KT>(key), p_insert);
-  append_offset(mut, node.get_item_end_offset(index) - size_right, p_insert);
+  append_offset(mut,
+                node.get_item_end_offset(index, node_size) - size_right,
+                p_insert);
 }
 #define IA_TEMPLATE(ST, KT) template void F013_INST(ST)::      \
     insert_at<KT>(NodeExtentMutable&, const full_key_t<KT>&, \
@@ -107,13 +112,15 @@ template <typename SlotType>
 node_offset_t F013_T::erase_at(
     NodeExtentMutable& mut, const me_t& node, index_t index, const char* p_left_bound)
 {
-  auto offset_item_start = node.get_item_start_offset(index);
-  auto offset_item_end = node.get_item_end_offset(index);
+  extent_len_t node_size = mut.get_length();
+  auto offset_item_start = node.get_item_start_offset(index, node_size);
+  auto offset_item_end = node.get_item_end_offset(index, node_size);
   assert(offset_item_start < offset_item_end);
   auto erase_size = offset_item_end - offset_item_start;
   // fix and shift the left part
   update_size_at(mut, node, index + 1, -erase_size);
-  const char* p_shift_start = fields_start(node) + node.get_key_start_offset(index + 1);
+  const char* p_shift_start = fields_start(node) +
+                              node.get_key_start_offset(index + 1, node_size);
   extent_len_t shift_len = sizeof(SlotType) * (node.num_keys - index - 1);
   int shift_off = -(int)sizeof(SlotType);
   mut.shift_absolute(p_shift_start, shift_len, shift_off);
index 24e37a638a1f07d50153756d915c8ad29ce1c3c6..cf2bf199aece2fd46041986df94d6a1b4f74e477 100644 (file)
@@ -82,19 +82,17 @@ const char* fields_start(const FieldType& node) {
 
 template <node_type_t NODE_TYPE, typename FieldType>
 node_range_t fields_free_range_before(
-    const FieldType& node, index_t index) {
+    const FieldType& node, index_t index, extent_len_t node_size) {
   assert(index <= node.num_keys);
-  node_offset_t offset_start = node.get_key_start_offset(index);
-  node_offset_t offset_end =
-    (index == 0 ? FieldType::SIZE
-                   : node.get_item_start_offset(index - 1));
+  node_offset_t offset_start = node.get_key_start_offset(index, node_size);
+  node_offset_t offset_end = node.get_item_end_offset(index, node_size);
   if constexpr (NODE_TYPE == node_type_t::INTERNAL) {
     if (node.is_level_tail() && index == node.num_keys) {
       offset_end -= sizeof(laddr_t);
     }
   }
   assert(offset_start <= offset_end);
-  assert(offset_end - offset_start < FieldType::SIZE);
+  assert(offset_end - offset_start < (int)node_size);
   return {offset_start, offset_end};
 }
 
@@ -129,39 +127,46 @@ struct _node_fields_013_t {
   using key_get_type = const key_t&;
   using me_t = _node_fields_013_t<SlotType>;
   static constexpr field_type_t FIELD_TYPE = SlotType::FIELD_TYPE;
-  static constexpr node_offset_t SIZE = NODE_BLOCK_SIZE;
   static constexpr node_offset_t HEADER_SIZE =
     sizeof(node_header_t) + sizeof(num_keys_t);
   static constexpr node_offset_t ITEM_OVERHEAD = SlotType::OVERHEAD;
 
   bool is_level_tail() const { return header.get_is_level_tail(); }
-  node_offset_t total_size() const { return SIZE; }
-  key_get_type get_key(index_t index) const {
+  node_offset_t total_size(extent_len_t node_size) const {
+    return node_size;
+  }
+  key_get_type get_key(
+      index_t index, extent_len_t node_size) const {
     assert(index < num_keys);
     return slots[index].key;
   }
-  node_offset_t get_key_start_offset(index_t index) const {
+  node_offset_t get_key_start_offset(
+      index_t index, extent_len_t node_size) const {
     assert(index <= num_keys);
     auto offset = HEADER_SIZE + sizeof(SlotType) * index;
-    assert(offset < SIZE);
+    assert(offset < node_size);
     return offset;
   }
-  node_offset_t get_item_start_offset(index_t index) const {
+  node_offset_t get_item_start_offset(
+      index_t index, extent_len_t node_size) const {
     assert(index < num_keys);
     auto offset = slots[index].right_offset;
-    assert(offset <= SIZE);
+    assert(offset < node_size);
     return offset;
   }
   const void* p_offset(index_t index) const {
     assert(index < num_keys);
     return &slots[index].right_offset;
   }
-  node_offset_t get_item_end_offset(index_t index) const {
-    return index == 0 ? SIZE : get_item_start_offset(index - 1);
+  node_offset_t get_item_end_offset(
+      index_t index, extent_len_t node_size) const {
+    return index == 0 ? node_size
+                      : get_item_start_offset(index - 1, node_size);
   }
   template <node_type_t NODE_TYPE>
-  node_offset_t free_size_before(index_t index) const {
-    auto range = fields_free_range_before<NODE_TYPE>(*this, index);
+  node_offset_t free_size_before(
+      index_t index, extent_len_t node_size) const {
+    auto range = fields_free_range_before<NODE_TYPE>(*this, index, node_size);
     return range.end - range.start;
   }
 
@@ -216,43 +221,48 @@ struct node_fields_2_t {
   using key_t = ns_oid_view_t;
   using key_get_type = key_t;
   static constexpr field_type_t FIELD_TYPE = field_type_t::N2;
-  static constexpr node_offset_t SIZE = NODE_BLOCK_SIZE;
   static constexpr node_offset_t HEADER_SIZE =
     sizeof(node_header_t) + sizeof(num_keys_t);
   static constexpr node_offset_t ITEM_OVERHEAD = sizeof(node_offset_t);
 
   bool is_level_tail() const { return header.get_is_level_tail(); }
-  node_offset_t total_size() const { return SIZE; }
-  key_get_type get_key(index_t index) const {
+  node_offset_t total_size(extent_len_t node_size) const {
+    return node_size;
+  }
+  key_get_type get_key(
+      index_t index, extent_len_t node_size) const {
     assert(index < num_keys);
-    node_offset_t item_end_offset =
-      (index == 0 ? SIZE : offsets[index - 1]);
-    assert(item_end_offset <= SIZE);
+    node_offset_t item_end_offset = get_item_end_offset(index, node_size);
     const char* p_start = fields_start(*this);
     return key_t(p_start + item_end_offset);
   }
-  node_offset_t get_key_start_offset(index_t index) const {
+  node_offset_t get_key_start_offset(
+      index_t index, extent_len_t node_size) const {
     assert(index <= num_keys);
     auto offset = HEADER_SIZE + sizeof(node_offset_t) * num_keys;
-    assert(offset <= SIZE);
+    assert(offset <= node_size);
     return offset;
   }
-  node_offset_t get_item_start_offset(index_t index) const {
+  node_offset_t get_item_start_offset(
+      index_t index, extent_len_t node_size) const {
     assert(index < num_keys);
     auto offset = offsets[index];
-    assert(offset <= SIZE);
+    assert(offset < node_size);
     return offset;
   }
   const void* p_offset(index_t index) const {
     assert(index < num_keys);
     return &offsets[index];
   }
-  node_offset_t get_item_end_offset(index_t index) const {
-    return index == 0 ? SIZE : get_item_start_offset(index - 1);
+  node_offset_t get_item_end_offset(
+      index_t index, extent_len_t node_size) const {
+    return index == 0 ? node_size
+                      : get_item_start_offset(index - 1, node_size);
   }
   template <node_type_t NODE_TYPE>
-  node_offset_t free_size_before(index_t index) const {
-    auto range = fields_free_range_before<NODE_TYPE>(*this, index);
+  node_offset_t free_size_before(
+      index_t index, extent_len_t node_size) const {
+    auto range = fields_free_range_before<NODE_TYPE>(*this, index, node_size);
     return range.end - range.start;
   }
 
@@ -303,7 +313,6 @@ struct internal_fields_3_t {
   // should be enough to index all keys under 64 KiB node
   using num_keys_t = uint16_t;
   static constexpr field_type_t FIELD_TYPE = field_type_t::N3;
-  static constexpr node_offset_t SIZE = NODE_BLOCK_SIZE;
   static constexpr node_offset_t HEADER_SIZE =
     sizeof(node_header_t) + sizeof(num_keys_t);
   static constexpr node_offset_t ITEM_SIZE =
@@ -311,23 +320,24 @@ struct internal_fields_3_t {
   static constexpr node_offset_t ITEM_OVERHEAD = 0u;
 
   bool is_level_tail() const { return header.get_is_level_tail(); }
-  node_offset_t total_size() const {
+  node_offset_t total_size(extent_len_t node_size) const {
     if (is_level_tail()) {
-      return SIZE - sizeof(snap_gen_t);
+      return node_size - sizeof(snap_gen_t);
     } else {
-      return SIZE;
+      return node_size;
     }
   }
-  key_get_type get_key(index_t index) const {
+  key_get_type get_key(
+      index_t index, extent_len_t node_size) const {
     assert(index < num_keys);
     return keys[index];
   }
   template <node_type_t NODE_TYPE>
   std::enable_if_t<NODE_TYPE == node_type_t::INTERNAL, node_offset_t>
-  free_size_before(index_t index) const {
+  free_size_before(index_t index, extent_len_t node_size) const {
     assert(index <= num_keys);
-    assert(num_keys <= get_max_num_keys());
-    auto free = total_size() - HEADER_SIZE -
+    assert(num_keys <= get_max_num_keys(node_size));
+    auto free = total_size(node_size) - HEADER_SIZE -
                 index * ITEM_SIZE;
     if (is_level_tail() && index == num_keys) {
       free -= sizeof(laddr_t);
@@ -335,6 +345,22 @@ struct internal_fields_3_t {
     return free;
   }
 
+  const laddr_packed_t* get_p_child_addr(
+      index_t index, extent_len_t node_size) const {
+#ifndef NDEBUG
+    if (is_level_tail()) {
+      assert(index <= num_keys);
+    } else {
+      assert(index < num_keys);
+    }
+#endif
+    auto p_addrs = reinterpret_cast<const laddr_packed_t*>(
+        &keys[get_num_keys_limit(node_size)]);
+    auto ret = p_addrs + index;
+    assert((const char*)ret < fields_start(*this) + node_size);
+    return ret;
+  }
+
   static node_offset_t estimate_insert_one() { return ITEM_SIZE; }
 
   template <KeyT KT>
@@ -355,12 +381,12 @@ struct internal_fields_3_t {
   snap_gen_t keys[];
 
  private:
-  num_keys_t get_max_num_keys() const {
-    auto num_limit = get_num_keys_limit();
+  num_keys_t get_max_num_keys(extent_len_t node_size) const {
+    auto num_limit = get_num_keys_limit(node_size);
     return (is_level_tail() ? num_limit - 1 : num_limit);
   }
-  static num_keys_t get_num_keys_limit() {
-    return (SIZE - HEADER_SIZE) / ITEM_SIZE;
+  static num_keys_t get_num_keys_limit(extent_len_t node_size) {
+    return (node_size - HEADER_SIZE) / ITEM_SIZE;
   }
 } __attribute__((packed));