You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

btree_impl.tpp 20 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. /* Copyright 2019 Huawei Technologies Co., Ltd.All Rights Reserved.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. #ifndef DATASET_UTIL_BTREE_H_
  16. #define DATASET_UTIL_BTREE_H_
  17. #include "btree.h"
  18. namespace mindspore {
  19. namespace dataset {
  20. template<typename K, typename V, typename C, typename T>
  21. typename BPlusTree<K, V, C, T>::IndexRc BPlusTree<K, V, C, T>::InnerNode::Sort() {
  22. // Build an inverse map. Basically it means keys[i] should be relocated to keys[inverse[i]];
  23. Allocator<slot_type> alloc(this->alloc_);
  24. slot_type *inverse = nullptr;
  25. try {
  26. inverse = alloc.allocate(traits::kInnerSlots);
  27. } catch (std::bad_alloc &e) {
  28. return IndexRc::kOutOfMemory;
  29. } catch (std::exception &e) {
  30. return IndexRc::kUnexpectedError;
  31. }
  32. for (slot_type i = 0; i < slotuse_; i++) {
  33. inverse[slot_dir_[i]] = i;
  34. }
  35. for (slot_type i = 0; i < slotuse_; i++) {
  36. while (inverse[i] != i) {
  37. slot_type j = inverse[i];
  38. slot_type k = inverse[j];
  39. // Swap the key
  40. std::swap(keys_[j], keys_[i]);
  41. // Swap the pointers.
  42. std::swap(data_[j + 1], data_[i + 1]);
  43. // one key in order.
  44. inverse[j] = j;
  45. // continue to move
  46. inverse[i] = k;
  47. }
  48. slot_dir_[i] = i;
  49. }
  50. if (inverse != nullptr) {
  51. alloc.deallocate(inverse);
  52. inverse = nullptr;
  53. }
  54. return IndexRc::kOk;
  55. }
  56. template<typename K, typename V, typename C, typename T>
  57. typename BPlusTree<K, V, C, T>::IndexRc BPlusTree<K, V, C, T>::InnerNode::Split(BPlusTree<K, V, C, T>::InnerNode *to,
  58. key_type *split_key) {
  59. DS_ASSERT(to);
  60. DS_ASSERT(to->slotuse_ == 0);
  61. // It is simpler to sort first, then split. Other alternative is to move key by key to the
  62. // new node. Also we need to deal with the 'holes' after a key is moved.
  63. RETURN_IF_BAD_RC(this->Sort());
  64. slot_type mid = slotuse_ >> 1;
  65. slot_type num_keys_to_move = slotuse_ - (mid + 1);
  66. *split_key = keys_[mid];
  67. errno_t err = memmove_s(to->keys_, sizeof(to->keys_), keys_ + mid + 1, num_keys_to_move * sizeof(key_type));
  68. if (err != EOK) {
  69. return IndexRc::kUnexpectedError;
  70. }
  71. err = memcpy_s(to->data_, sizeof(to->data_), data_ + mid + 1, (num_keys_to_move + 1) * sizeof(BaseNode * ));
  72. if (err != EOK) {
  73. return IndexRc::kUnexpectedError;
  74. }
  75. for (slot_type i = 0; i < num_keys_to_move; i++) {
  76. to->slot_dir_[i] = i;
  77. }
  78. slotuse_ -= (num_keys_to_move + 1); // the split key is moved up. So one less
  79. to->slotuse_ += num_keys_to_move;
  80. return IndexRc::kOk;
  81. }
  82. template<typename K, typename V, typename C, typename T>
  83. typename BPlusTree<K, V, C, T>::IndexRc
  84. BPlusTree<K, V, C, T>::InnerNode::InsertIntoSlot(slot_type slot, const key_type &key,
  85. BPlusTree<K, V, C, T>::BaseNode *ptr) {
  86. if (is_full()) {
  87. return IndexRc::kSlotFull;
  88. }
  89. // Shift the slot entries to the right and make room for the new comer.
  90. // We don't sort the key and/or the data array until node split
  91. auto num_keys_to_move = slotuse_ - slot;
  92. if (num_keys_to_move > 0) {
  93. auto *src = &slot_dir_[slot];
  94. auto *dest = &slot_dir_[slot + 1];
  95. auto destMax = sizeof(slot_dir_) - sizeof(slot_type) * (slot + 1);
  96. auto amt = sizeof(slot_type) * num_keys_to_move;
  97. errno_t err = memmove_s(dest, destMax, src, amt);
  98. if (err) {
  99. return IndexRc::kUnexpectedError;
  100. }
  101. }
  102. slot_dir_[slot] = slotuse_;
  103. keys_[slotuse_] = key;
  104. data_[slotuse_ + 1] = ptr;
  105. ++slotuse_;
  106. return IndexRc::kOk;
  107. }
  108. template<typename K, typename V, typename C, typename T>
  109. typename BPlusTree<K, V, C, T>::IndexRc BPlusTree<K, V, C, T>::LeafNode::Sort() {
  110. // Build an inverse map. Basically it means keys[i] should be relocated to keys[inverse[i]];
  111. Allocator<slot_type> alloc(this->alloc_);
  112. slot_type *inverse = nullptr;
  113. try {
  114. inverse = alloc.allocate(traits::kLeafSlots);
  115. } catch (std::bad_alloc &e) {
  116. return IndexRc::kOutOfMemory;
  117. } catch (std::exception &e) {
  118. return IndexRc::kUnexpectedError;
  119. }
  120. for (slot_type i = 0; i < slotuse_; i++) {
  121. inverse[slot_dir_[i]] = i;
  122. }
  123. for (slot_type i = 0; i < slotuse_; i++) {
  124. while (inverse[i] != i) {
  125. slot_type j = inverse[i];
  126. slot_type k = inverse[j];
  127. // Swap the key
  128. std::swap(keys_[j], keys_[i]);
  129. // Swap the shared pointers
  130. std::swap(data_[j], data_[i]);
  131. // one key in order.
  132. inverse[j] = j;
  133. // continue to move
  134. inverse[i] = k;
  135. }
  136. slot_dir_[i] = i;
  137. }
  138. if (inverse != nullptr) {
  139. alloc.deallocate(inverse);
  140. inverse = nullptr;
  141. }
  142. return IndexRc::kOk;
  143. }
  144. template<typename K, typename V, typename C, typename T>
  145. typename BPlusTree<K, V, C, T>::IndexRc BPlusTree<K, V, C, T>::LeafNode::Split(BPlusTree<K, V, C, T>::LeafNode *to) {
  146. DS_ASSERT(to);
  147. DS_ASSERT(to->slotuse_ == 0);
  148. // It is simpler to sort first, then split. Other alternative is to move key by key to the
  149. // new node. Also we need to deal with the 'holes' after a key is moved.
  150. RETURN_IF_BAD_RC(this->Sort());
  151. slot_type mid = slotuse_ >> 1;
  152. slot_type num_keys_to_move = slotuse_ - mid;
  153. errno_t err = memmove_s(to->keys_, sizeof(to->keys_), keys_ + mid, num_keys_to_move * sizeof(key_type));
  154. if (err) {
  155. return IndexRc::kUnexpectedError;
  156. }
  157. for (slot_type i = 0; i < num_keys_to_move; i++) {
  158. to->data_[i] = std::move(data_[i + mid]);
  159. to->slot_dir_[i] = i;
  160. }
  161. slotuse_ -= num_keys_to_move;
  162. to->slotuse_ += num_keys_to_move;
  163. return IndexRc::kOk;
  164. }
  165. template<typename K, typename V, typename C, typename T>
  166. typename BPlusTree<K, V, C, T>::IndexRc
  167. BPlusTree<K, V, C, T>::LeafNode::InsertIntoSlot(BPlusTree<K, V, C, T>::LockPathCB *insCB, slot_type slot,
  168. const key_type &key,
  169. std::shared_ptr<value_type> value) {
  170. if (is_full()) {
  171. // If we need to do node split, we need to ensure all the intermediate nodes are locked exclusive.
  172. // Otherwise we need to do a retry.
  173. if (insCB == nullptr || !insCB->latch_shared_) {
  174. return IndexRc::kSlotFull;
  175. } else {
  176. return IndexRc::kRetry;
  177. }
  178. }
  179. // We can now let go all the locks of the parent. Nothing we do from now on will change the
  180. // structure of the tree.
  181. if (insCB) {
  182. insCB->UnlockMyParents(this);
  183. }
  184. // Shift the slot entries to the right and make room for the new comer.
  185. // We don't sort the key and/or the data array until node split
  186. auto num_keys_to_move = slotuse_ - slot;
  187. if (num_keys_to_move > 0) {
  188. auto *src = &slot_dir_[slot];
  189. auto *dest = &slot_dir_[slot + 1];
  190. auto destMax = sizeof(slot_dir_) - sizeof(slot_type) * (slot + 1);
  191. auto amt = sizeof(slot_type) * num_keys_to_move;
  192. errno_t err = memmove_s(dest, destMax, src, amt);
  193. if (err) {
  194. return IndexRc::kUnexpectedError;
  195. }
  196. }
  197. slot_dir_[slot] = slotuse_;
  198. keys_[slotuse_] = key;
  199. data_[slotuse_] = std::move(value);
  200. ++slotuse_;
  201. return IndexRc::kOk;
  202. }
  203. template<typename K, typename V, typename C, typename T>
  204. typename BPlusTree<K, V, C, T>::IndexRc BPlusTree<K, V, C, T>::AllocateInner(BPlusTree<K, V, C, T>::InnerNode **p) {
  205. if (p == nullptr) {
  206. return IndexRc::kNullPointer;
  207. }
  208. typename InnerNode::alloc_type alloc(alloc_);
  209. InnerNode *ptr = nullptr;
  210. try {
  211. ptr = alloc.allocate(1);
  212. } catch (std::bad_alloc &e) {
  213. return IndexRc::kOutOfMemory;
  214. } catch (std::exception &e) {
  215. return IndexRc::kUnexpectedError;
  216. }
  217. *p = new(ptr) InnerNode(alloc_);
  218. all_.Prepend(ptr);
  219. stats_.inner_nodes_++;
  220. return IndexRc::kOk;
  221. }
  222. template<typename K, typename V, typename C, typename T>
  223. typename BPlusTree<K, V, C, T>::IndexRc BPlusTree<K, V, C, T>::AllocateLeaf(BPlusTree<K, V, C, T>::LeafNode **p) {
  224. if (p == nullptr) {
  225. return IndexRc::kNullPointer;
  226. }
  227. typename LeafNode::alloc_type alloc(this->alloc_);
  228. LeafNode *ptr = nullptr;
  229. try {
  230. ptr = alloc.allocate(1);
  231. } catch (std::bad_alloc &e) {
  232. return IndexRc::kOutOfMemory;
  233. } catch (std::exception &e) {
  234. return IndexRc::kUnexpectedError;
  235. }
  236. *p = new(ptr) LeafNode(alloc_);
  237. all_.Prepend(ptr);
  238. stats_.leaves_++;
  239. return IndexRc::kOk;
  240. }
  241. template<typename K, typename V, typename C, typename T>
  242. typename BPlusTree<K, V, C, T>::IndexRc
  243. BPlusTree<K, V, C, T>::LeafInsertKeyValue(BPlusTree<K, V, C, T>::LockPathCB *ins_cb,
  244. BPlusTree<K, V, C, T>::LeafNode *node, const key_type &key,
  245. std::shared_ptr<value_type> value, key_type *split_key,
  246. BPlusTree<K, V, C, T>::LeafNode **split_node) {
  247. bool duplicate;
  248. slot_type slot = FindSlot(node, key, &duplicate);
  249. if (duplicate) {
  250. return IndexRc::kDuplicateKey;
  251. }
  252. IndexRc rc = node->InsertIntoSlot(ins_cb, slot, key, value);
  253. if (rc == IndexRc::kSlotFull) {
  254. LeafNode *new_leaf = nullptr;
  255. rc = AllocateLeaf(&new_leaf);
  256. RETURN_IF_BAD_RC(rc);
  257. leaf_nodes_.InsertAfter(node, new_leaf);
  258. *split_node = new_leaf;
  259. if (slot == node->slotuse_ && traits::kAppendMode) {
  260. // Split high. Good for bulk load and keys are in asending order on insert
  261. *split_key = key;
  262. // Just insert the new key to the new leaf. No further need to move the keys
  263. // from one leaf to the other.
  264. rc = new_leaf->InsertIntoSlot(nullptr, 0, key, value);
  265. RETURN_IF_BAD_RC(rc);
  266. } else {
  267. // 50/50 split
  268. rc = node->Split(new_leaf);
  269. RETURN_IF_BAD_RC(rc);
  270. *split_key = new_leaf->keys_[0];
  271. if (LessThan(key, *split_key)) {
  272. rc = node->InsertIntoSlot(nullptr, slot, key, value);
  273. RETURN_IF_BAD_RC(rc);
  274. } else {
  275. slot -= node->slotuse_;
  276. rc = new_leaf->InsertIntoSlot(nullptr, slot, key, value);
  277. RETURN_IF_BAD_RC(rc);
  278. }
  279. }
  280. }
  281. return rc;
  282. }
  283. template<typename K, typename V, typename C, typename T>
  284. typename BPlusTree<K, V, C, T>::IndexRc
  285. BPlusTree<K, V, C, T>::InnerInsertKeyChild(BPlusTree<K, V, C, T>::InnerNode *node, const key_type &key,
  286. BPlusTree<K, V, C, T>::BaseNode *ptr,
  287. key_type *split_key, BPlusTree<K, V, C, T>::InnerNode **split_node) {
  288. bool duplicate;
  289. slot_type slot = FindSlot(node, key, &duplicate);
  290. if (duplicate) {
  291. return IndexRc::kDuplicateKey;
  292. }
  293. IndexRc rc = node->InsertIntoSlot(slot, key, ptr);
  294. if (rc == IndexRc::kSlotFull) {
  295. InnerNode *new_inner = nullptr;
  296. rc = AllocateInner(&new_inner);
  297. RETURN_IF_BAD_RC(rc);
  298. *split_node = new_inner;
  299. if (slot == node->slotuse_ && traits::kAppendMode) {
  300. *split_key = key;
  301. new_inner->data_[0] = node->data_[node->slotuse_];
  302. rc = new_inner->InsertIntoSlot(0, key, ptr);
  303. RETURN_IF_BAD_RC(rc);
  304. } else {
  305. rc = node->Split(new_inner, split_key);
  306. RETURN_IF_BAD_RC(rc);
  307. if (LessThan(key, *split_key)) {
  308. // Need to readjust the slot position since the split key is no longer in the two children.
  309. slot = FindSlot(node, key);
  310. rc = node->InsertIntoSlot(slot, key, ptr);
  311. RETURN_IF_BAD_RC(rc);
  312. } else {
  313. // Same reasoning as above
  314. slot = FindSlot(new_inner, key);
  315. rc = new_inner->InsertIntoSlot(slot, key, ptr);
  316. RETURN_IF_BAD_RC(rc);
  317. }
  318. }
  319. }
  320. return rc;
  321. }
  322. template<typename K, typename V, typename C, typename T>
  323. typename BPlusTree<K, V, C, T>::IndexRc
  324. BPlusTree<K, V, C, T>::InsertKeyValue(BPlusTree<K, V, C, T>::LockPathCB *ins_cb, BPlusTree<K, V, C, T>::BaseNode *n,
  325. const key_type &key,
  326. std::shared_ptr<value_type> value, key_type *split_key,
  327. BPlusTree<K, V, C, T>::BaseNode **split_node) {
  328. if (split_key == nullptr || split_node == nullptr) {
  329. return IndexRc::kUnexpectedError;
  330. }
  331. if (n->is_leafnode()) {
  332. if (ins_cb) {
  333. // Always lock the leaf in X.
  334. ins_cb->LockNode(n, LockPathCB::LockMode::kExclusive);
  335. }
  336. auto *leaf = static_cast<LeafNode *>(n);
  337. LeafNode *new_leaf = nullptr;
  338. RETURN_IF_BAD_RC(LeafInsertKeyValue(ins_cb, leaf, key, std::move(value), split_key, &new_leaf));
  339. if (new_leaf) {
  340. *split_node = new_leaf;
  341. }
  342. } else {
  343. if (ins_cb) {
  344. // For internal node, lock in S unless we are doing retry.
  345. if (ins_cb->latch_shared_) {
  346. ins_cb->LockNode(n, LockPathCB::LockMode::kShared);
  347. } else {
  348. ins_cb->LockNode(n, LockPathCB::LockMode::kExclusive);
  349. }
  350. }
  351. auto *inner = static_cast<InnerNode *>(n);
  352. slot_type slot = FindSlot(inner, key);
  353. BaseNode *new_child = nullptr;
  354. key_type new_key = key_type();
  355. RETURN_IF_BAD_RC(InsertKeyValue(ins_cb, FindBranch(inner, slot), key, std::move(value), &new_key, &new_child));
  356. if (new_child) {
  357. InnerNode *new_inner = nullptr;
  358. RETURN_IF_BAD_RC(InnerInsertKeyChild(inner, new_key, new_child, split_key, &new_inner));
  359. if (new_inner) {
  360. *split_node = new_inner;
  361. }
  362. }
  363. }
  364. return IndexRc::kOk;
  365. }
  366. template<typename K, typename V, typename C, typename T>
  367. typename BPlusTree<K, V, C, T>::IndexRc
  368. BPlusTree<K, V, C, T>::Locate(BPlusTree<K, V, C, T>::BaseNode *top, const key_type &key,
  369. BPlusTree<K, V, C, T>::LeafNode **ln,
  370. slot_type *s) const {
  371. if (ln == nullptr || s == nullptr) {
  372. return IndexRc::kNullPointer;
  373. }
  374. if (top == nullptr) {
  375. return IndexRc::kKeyNotFound;
  376. }
  377. if (top->is_leafnode()) {
  378. bool duplicate;
  379. auto *leaf = static_cast<LeafNode *>(top);
  380. slot_type slot = FindSlot(leaf, key, &duplicate);
  381. // Need exact match.
  382. if (duplicate) {
  383. *ln = leaf;
  384. *s = slot;
  385. } else {
  386. return IndexRc::kKeyNotFound;
  387. }
  388. } else {
  389. auto *inner = static_cast<InnerNode *>(top);
  390. slot_type slot = FindSlot(inner, key);
  391. return Locate(FindBranch(inner, slot), key, ln, s);
  392. }
  393. return IndexRc::kOk;
  394. }
  395. template <typename K, typename V, typename C, typename T>
  396. BPlusTree<K, V, C, T>::BPlusTree(const value_allocator &alloc)
  397. : alloc_(alloc), leaf_nodes_(&LeafNode::link_), all_(&BaseNode::lru_), root_(nullptr) {}
  398. template<typename K, typename V, typename C, typename T>
  399. BPlusTree<K, V, C, T>::~BPlusTree() noexcept {
  400. // We have a list of all the nodes allocated. Traverse them and free all the memory
  401. BaseNode *n = all_.head;
  402. BaseNode *t = nullptr;
  403. while (n) {
  404. t = n->lru_.next;
  405. all_.Remove(n);
  406. if (n->is_leafnode()) {
  407. auto *leaf = static_cast<LeafNode *>(n);
  408. typename LeafNode::alloc_type alloc(alloc_);
  409. leaf->~LeafNode();
  410. alloc.deallocate(leaf, 1);
  411. } else {
  412. auto *in = static_cast<InnerNode *>(n);
  413. typename InnerNode::alloc_type alloc(alloc_);
  414. in->~InnerNode();
  415. alloc.deallocate(in, 1);
  416. }
  417. n = t;
  418. }
  419. root_ = nullptr;
  420. }
  421. template<typename K, typename V, typename C, typename T>
  422. Status BPlusTree<K, V, C, T>::DoInsert(const key_type &key, const value_type &value) {
  423. IndexRc rc;
  424. if (root_ == nullptr) {
  425. UniqueLock lck(&rw_lock_);
  426. // Check again after we get the lock. Other thread may have created the root node already.
  427. if (root_ == nullptr) {
  428. LeafNode *leaf = nullptr;
  429. rc = AllocateLeaf(&leaf);
  430. if (rc != IndexRc::kOk) {
  431. return IndexRc2Status(rc);
  432. }
  433. leaf_nodes_.Append(leaf);
  434. root_ = leaf;
  435. }
  436. // lock will be unlocked when it goes out of scope.
  437. }
  438. bool retry = false;
  439. do {
  440. // Track all the paths to the target and lock each internal node in S.
  441. LockPathCB InsCB(this, retry);
  442. // Mark the numKeysArray invalid. We may latch the tree in S and multiple guys are doing insert.
  443. // But it is okay as we all set the same value.
  444. stats_.num_keys_array_valid_ = false;
  445. // Initially we lock path in S unless we need to do node split.
  446. retry = false;
  447. BaseNode *new_child = nullptr;
  448. key_type new_key = key_type();
  449. // We don't store the value directly into the leaf node as it is expensive to move it during node split.
  450. // Rather we store a pointer instead. The value_type must support the copy constructor.
  451. std::shared_ptr<value_type> ptr_value = std::make_shared<value_type>(value);
  452. rc = InsertKeyValue(&InsCB, root_, key, std::move(ptr_value), &new_key, &new_child);
  453. if (rc == IndexRc::kRetry) {
  454. retry = true;
  455. } else if (rc != IndexRc::kOk) {
  456. return IndexRc2Status(rc);
  457. } else if (new_child != nullptr) {
  458. // root is full
  459. InnerNode *new_root = nullptr;
  460. rc = AllocateInner(&new_root);
  461. if (rc == IndexRc::kOk) {
  462. rc = new_root->InsertIntoSlot(0, new_key, new_child);
  463. if (rc != IndexRc::kOk) {
  464. return IndexRc2Status(rc);
  465. }
  466. new_root->data_[0] = root_;
  467. root_ = new_root;
  468. stats_.level_++;
  469. } else {
  470. return IndexRc2Status(rc);
  471. }
  472. }
  473. } while (retry);
  474. (void) stats_.size_++;
  475. return Status::OK();
  476. }
  477. template <typename K, typename V, typename C, typename T>
  478. void BPlusTree<K, V, C, T>::PopulateNumKeys() {
  479. // Start from the root and we calculate how many leaf nodes as pointed to by each inner node.
  480. // The results are stored in the numKeys array in each inner node.
  481. (void)PopulateNumKeys(root_);
  482. // Indicate the result is accurate since we have the tree locked exclusive.
  483. stats_.num_keys_array_valid_ = true;
  484. }
  485. template <typename K, typename V, typename C, typename T>
  486. uint64_t BPlusTree<K, V, C, T>::PopulateNumKeys(BPlusTree<K, V, C, T>::BaseNode *n) {
  487. if (n->is_leafnode()) {
  488. auto *leaf = static_cast<LeafNode *>(n);
  489. return leaf->slotuse_;
  490. } else {
  491. auto *inner = static_cast<InnerNode *>(n);
  492. uint64_t num_keys = 0;
  493. for (auto i = 0; i < inner->slotuse_ + 1; i++) {
  494. inner->num_keys_[i] = PopulateNumKeys(inner->data_[i]);
  495. num_keys += inner->num_keys_[i];
  496. }
  497. return num_keys;
  498. }
  499. }
  500. template <typename K, typename V, typename C, typename T>
  501. typename BPlusTree<K, V, C, T>::key_type BPlusTree<K, V, C, T>::KeyAtPos(uint64_t inx) {
  502. if (stats_.num_keys_array_valid_ == false) {
  503. // We need exclusive access to the tree. If concurrent insert is going on, it is hard to get accurate numbers
  504. UniqueLock lck(&rw_lock_);
  505. // Check again.
  506. if (stats_.num_keys_array_valid_ == false) {
  507. PopulateNumKeys();
  508. }
  509. }
  510. // Now we know how many keys each inner branch contains, we can now traverse the correct node in log n time.
  511. return KeyAtPos(root_, inx);
  512. }
  513. template <typename K, typename V, typename C, typename T>
  514. typename BPlusTree<K, V, C, T>::key_type BPlusTree<K, V, C, T>::KeyAtPos(BPlusTree<K, V, C, T>::BaseNode *n, uint64_t inx) {
  515. if (n->is_leafnode()) {
  516. auto *leaf = static_cast<LeafNode *>(n);
  517. return leaf->keys_[leaf->slot_dir_[inx]];
  518. } else {
  519. auto *inner = static_cast<InnerNode *>(n);
  520. if ((inx + 1) > inner->num_keys_[0]) {
  521. inx -= inner->num_keys_[0];
  522. } else {
  523. return KeyAtPos(inner->data_[0], inx);
  524. }
  525. for (auto i = 0; i < inner->slotuse_; i++) {
  526. if ((inx + 1) > inner->num_keys_[inner->slot_dir_[i] + 1]) {
  527. inx -= inner->num_keys_[inner->slot_dir_[i]+1];
  528. } else {
  529. return KeyAtPos(inner->data_[inner->slot_dir_[i] + 1], inx);
  530. }
  531. }
  532. }
  533. // If we get here, inx is way too big. Instead of throwing exception, we will just return the default value
  534. // of key_type whatever it is.
  535. return key_type();
  536. }
  537. } // namespace dataset
  538. } // namespace mindspore
  539. #endif