[Groonga-commit] groonga/grnxx at 8278355 [master] Implement grnxx::map::Patricia<Bytes>.

Back to archive index

susumu.yata null+****@clear*****
Tue Jun 18 17:00:55 JST 2013


susumu.yata	2013-06-18 17:00:55 +0900 (Tue, 18 Jun 2013)

  New Revision: 8278355bf2ccb757c06ecb457a21f32f16990216
  https://github.com/groonga/grnxx/commit/8278355bf2ccb757c06ecb457a21f32f16990216

  Message:
    Implement grnxx::map::Patricia<Bytes>.

  Added files:
    lib/grnxx/map/patricia/node.hpp
  Modified files:
    lib/grnxx/map/patricia.cpp
    lib/grnxx/map/patricia.hpp
    lib/grnxx/map/patricia/Makefile.am
    lib/grnxx/map/patricia/dummy.cpp
    lib/grnxx/map/patricia/header.cpp
    lib/grnxx/map/patricia/header.hpp

  Modified: lib/grnxx/map/patricia.cpp (+510 -5)
===================================================================
--- lib/grnxx/map/patricia.cpp    2013-06-17 17:42:28 +0900 (0267615)
+++ lib/grnxx/map/patricia.cpp    2013-06-18 17:00:55 +0900 (056e690)
@@ -28,12 +28,26 @@
 
 namespace grnxx {
 namespace map {
+namespace {
+
+constexpr uint64_t ROOT_NODE_ID = 0;
+
+using patricia::NODE_INVALID_OFFSET;
+
+using patricia::NODE_DEAD;
+using patricia::NODE_LEAF;
+using patricia::NODE_BRANCH;
+using patricia::NODE_TERMINAL;
+
+}  // namespace
 
 template <typename T>
 Patricia<T>::Patricia()
     : storage_(nullptr),
       storage_node_id_(STORAGE_INVALID_NODE_ID),
-      header_(nullptr) {}
+      header_(nullptr),
+      nodes_(),
+      keys_() {}
 
 template <typename T>
 Patricia<T>::~Patricia() {}
@@ -74,17 +88,17 @@ uint32_t Patricia<T>::storage_node_id() const {
 
 template <typename T>
 MapType Patricia<T>::type() const {
-  return MAP_DOUBLE_ARRAY;
+  return MAP_PATRICIA;
 }
 
 template <typename T>
 int64_t Patricia<T>::max_key_id() const {
-  return header_->max_key_id;
+  return keys_->max_key_id();
 }
 
 template <typename T>
 uint64_t Patricia<T>::num_keys() const {
-  return header_->num_keys;
+  return keys_->num_keys();
 }
 
 template <typename T>
@@ -131,7 +145,498 @@ template class Patricia<int64_t>;
 template class Patricia<uint64_t>;
 template class Patricia<double>;
 template class Patricia<GeoPoint>;
-template class Patricia<Bytes>;
+
+Patricia<Bytes>::Patricia()
+    : storage_(nullptr),
+      storage_node_id_(STORAGE_INVALID_NODE_ID),
+      header_(nullptr),
+      nodes_(),
+      keys_() {}
+
+Patricia<Bytes>::~Patricia() {}
+
+Patricia<Bytes> *Patricia<Bytes>::create(Storage *storage,
+                                         uint32_t storage_node_id,
+                                         const MapOptions &options) {
+  std::unique_ptr<Patricia> map(new (std::nothrow) Patricia);
+  if (!map) {
+    GRNXX_ERROR() << "new grnxx::map::Patricia failed";
+    return nullptr;
+  }
+  if (!map->create_map(storage, storage_node_id, options)) {
+    return nullptr;
+  }
+  return map.release();
+}
+
+Patricia<Bytes> *Patricia<Bytes>::open(Storage *storage,
+                                       uint32_t storage_node_id) {
+  std::unique_ptr<Patricia> map(new (std::nothrow) Patricia);
+  if (!map) {
+    GRNXX_ERROR() << "new grnxx::map::Patricia failed";
+    return nullptr;
+  }
+  if (!map->open_map(storage, storage_node_id)) {
+    return nullptr;
+  }
+  return map.release();
+}
+
+uint32_t Patricia<Bytes>::storage_node_id() const {
+  return storage_node_id_;
+}
+
+MapType Patricia<Bytes>::type() const {
+  return MAP_PATRICIA;
+}
+
+int64_t Patricia<Bytes>::max_key_id() const {
+  return keys_->max_key_id();
+}
+
+uint64_t Patricia<Bytes>::num_keys() const {
+  return keys_->num_keys();
+}
+
+bool Patricia<Bytes>::get(int64_t key_id, Key *key) {
+  if ((key_id < MAP_MIN_KEY_ID) || (key_id > keys_->max_key_id())) {
+    // Out of range.
+    return false;
+  }
+  bool bit;
+  if (!keys_->get_bit(key_id, &bit)) {
+    // Error.
+    return false;
+  }
+  if (!bit) {
+    // Not found.
+    return false;
+  }
+  if (!keys_->get_key(key_id, key)) {
+    // Error.
+    return false;
+  }
+  return true;
+}
+
+bool Patricia<Bytes>::unset(int64_t key_id) {
+  Key key;
+  if (!get(key_id, &key)) {
+    // Not found or error.
+    return false;
+  }
+  const uint64_t bit_size = key.size() * 8;
+  uint64_t node_id = ROOT_NODE_ID;
+  Node *prev_node = nullptr;
+  for ( ; ; ) {
+    Node * const node = nodes_->get_pointer(node_id);
+    if (!node) {
+      // Error.
+      return false;
+    }
+    switch (node->status()) {
+      case NODE_DEAD: {
+        // Not found.
+        return false;
+      }
+      case NODE_LEAF: {
+        if (node->key_id() != key_id) {
+          // Not found.
+          return false;
+        }
+        keys_->unset(key_id);
+        if (prev_node) {
+          Node * const sibling_node = node + (node_id ^ 1) - node_id;
+          *prev_node = *sibling_node;
+        } else {
+          *node = Node::dead_node();
+        }
+        return true;
+      }
+      case NODE_BRANCH: {
+        if (node->bit_pos() >= bit_size) {
+          // Not found.
+          return false;
+        }
+        node_id = node->offset() +
+                  ((key[node->bit_pos() / 8] >> (node->bit_pos() % 8)) & 1);
+        break;
+      }
+      case NODE_TERMINAL: {
+        if (node->bit_size() > bit_size) {
+          // Not found.
+          return false;
+        }
+        node_id = node->offset() + (node->bit_size() < bit_size);
+        break;
+      }
+    }
+    prev_node = node;
+  }
+}
+
+//bool Patricia<Bytes>::reset(int64_t key_id, KeyArg dest_key) {
+//  // TODO
+//  return false;
+//}
+
+bool Patricia<Bytes>::find(KeyArg key, int64_t *key_id) {
+  const uint64_t bit_size = key.size() * 8;
+  uint64_t node_id = ROOT_NODE_ID;
+  for ( ; ; ) {
+    Node node;
+    if (!nodes_->get(node_id, &node)) {
+      // Error.
+      return false;
+    }
+    switch (node.status()) {
+      case NODE_DEAD: {
+        // Not found.
+        return false;
+      }
+      case NODE_LEAF: {
+        Key stored_key;
+        if (!keys_->get_key(node.key_id(), &stored_key)) {
+          // Error.
+          return false;
+        }
+        if (key != stored_key) {
+          // Not found.
+          return false;
+        }
+        if (key_id) {
+          *key_id = node.key_id();
+        }
+        return true;
+      }
+      case NODE_BRANCH: {
+        if (node.bit_pos() >= bit_size) {
+          // Not found.
+          return false;
+        }
+        node_id = node.offset() +
+                  ((key[node.bit_pos() / 8] >> (node.bit_pos() % 8)) & 1);
+        break;
+      }
+      case NODE_TERMINAL: {
+        if (node.bit_size() > bit_size) {
+          // Not found.
+          return false;
+        }
+        node_id = node.offset() + (node.bit_size() < bit_size);
+        break;
+      }
+    }
+  }
+}
+
+bool Patricia<Bytes>::add(KeyArg key, int64_t *key_id) {
+  const uint64_t bit_size = key.size() * 8;
+  uint64_t node_id = ROOT_NODE_ID;
+  Node * node;
+  for (node = nodes_->get_pointer(node_id);
+       node && (node->status() != NODE_LEAF);
+       node = nodes_->get_pointer(node_id)) {
+    switch (node->status()) {
+      case NODE_DEAD: {
+        // The patricia is empty.
+        int64_t next_key_id;
+        if (!keys_->add(key, &next_key_id)) {
+          // Error.
+          return false;
+        }
+        *node = Node::leaf_node(next_key_id);
+        if (key_id) {
+          *key_id = next_key_id;
+        }
+        return true;
+      }
+      case NODE_BRANCH: {
+        node_id = node->offset();
+        if (node->bit_pos() < bit_size) {
+          node_id += (key[node->bit_pos() / 8] >> (node->bit_pos() % 8)) & 1;
+        }
+        break;
+      }
+      case NODE_TERMINAL: {
+        node_id = node->offset() + (node->bit_size() < bit_size);
+        break;
+      }
+    }
+  }
+  if (!node) {
+    // Error.
+    return false;
+  }
+  // "node" points to a leaf node.
+  Key stored_key;
+  if (!keys_->get_key(node->key_id(), &stored_key)) {
+    // Error.
+    return false;
+  }
+  const uint64_t min_size =
+      (key.size() < stored_key.size()) ? key.size() : stored_key.size();
+  uint64_t count;
+  for (count = 0; count < min_size; ++count) {
+    if (key[count] != stored_key[count]) {
+      break;
+    }
+  }
+  if (count < min_size) {
+    const uint8_t diff = key[count] ^ stored_key[count];
+    count *= 8;
+    count += bit_scan_forward(diff);
+  } else {
+    if (key.size() == stored_key.size()) {
+      // Found.
+      if (key_id) {
+        *key_id = node->key_id();
+      }
+      return false;
+    }
+    count *= 8;
+  }
+  Node * const next_nodes = nodes_->get_pointer(header_->next_node_id);
+  node_id = ROOT_NODE_ID;
+  for (node = nodes_->get_pointer(node_id);
+       node && (node->status() != NODE_LEAF);
+       node = nodes_->get_pointer(node_id)) {
+    switch (node->status()) {
+      case NODE_BRANCH: {
+        if (count <= node->bit_pos()) {
+          int64_t next_key_id;
+          if (!keys_->add(key, &next_key_id)) {
+            // Error.
+            return false;
+          }
+          if (count == bit_size) {
+            // Create a terminal node.
+            next_nodes[0] = Node::leaf_node(next_key_id);
+            next_nodes[1] = *node;
+            *node = Node::terminal_node(count, header_->next_node_id);
+          } else {
+            // Create a branch node.
+            if (key[count / 8] & (1U << (count % 8))) {
+              next_nodes[0] = *node;
+              next_nodes[1] = Node::leaf_node(next_key_id);
+            } else {
+              next_nodes[0] = Node::leaf_node(next_key_id);
+              next_nodes[1] = *node;
+            }
+            *node = Node::branch_node(count, header_->next_node_id);
+          }
+          header_->next_node_id += 2;
+          if (key_id) {
+            *key_id = next_key_id;
+          }
+          return true;
+        }
+        node_id = node->offset();
+        if (node->bit_pos() < count) {
+          node_id += (key[node->bit_pos() / 8] >> (node->bit_pos() % 8)) & 1;
+        }
+        break;
+      }
+      case NODE_TERMINAL: {
+        if (count < node->bit_size()) {
+          int64_t next_key_id;
+          if (!keys_->add(key, &next_key_id)) {
+            // Error.
+            return false;
+          }
+          if (count == bit_size) {
+            // Create a terminal node.
+            next_nodes[0] = Node::leaf_node(next_key_id);
+            next_nodes[1] = *node;
+            *node = Node::terminal_node(count, header_->next_node_id);
+          } else {
+            // Create a branch node.
+            if (key[count / 8] & (1U << (count % 8))) {
+              next_nodes[0] = *node;
+              next_nodes[1] = Node::leaf_node(next_key_id);
+            } else {
+              next_nodes[0] = Node::leaf_node(next_key_id);
+              next_nodes[1] = *node;
+            }
+            *node = Node::branch_node(count, header_->next_node_id);
+          }
+          header_->next_node_id += 2;
+          if (key_id) {
+            *key_id = next_key_id;
+          }
+          return true;
+        }
+        node_id = node->offset() + (node->bit_size() < bit_size);
+        break;
+      }
+    }
+  }
+  if (!node) {
+    // Error.
+    return false;
+  }
+  int64_t next_key_id;
+  if (!keys_->add(key, &next_key_id)) {
+    // Error.
+    return false;
+  }
+  if (count == bit_size) {
+    // Create a terminal node.
+    next_nodes[0] = Node::leaf_node(next_key_id);
+    next_nodes[1] = *node;
+    *node = Node::terminal_node(count, header_->next_node_id);
+  } else if (count == (stored_key.size() * 8)) {
+    // Create a terminal node.
+    next_nodes[0] = *node;
+    next_nodes[1] = Node::leaf_node(next_key_id);
+    *node = Node::terminal_node(count, header_->next_node_id);
+  } else {
+    // Create a branch node.
+    if (key[count / 8] & (1U << (count % 8))) {
+      next_nodes[0] = *node;
+      next_nodes[1] = Node::leaf_node(next_key_id);
+    } else {
+      next_nodes[0] = Node::leaf_node(next_key_id);
+      next_nodes[1] = *node;
+    }
+    *node = Node::branch_node(count, header_->next_node_id);
+  }
+  header_->next_node_id += 2;
+  if (key_id) {
+    *key_id = next_key_id;
+  }
+  return true;
+}
+
+bool Patricia<Bytes>::remove(KeyArg key) {
+  const uint64_t bit_size = key.size() * 8;
+  uint64_t node_id = ROOT_NODE_ID;
+  Node * prev_node = nullptr;
+  for ( ; ; ) {
+    Node * const node = nodes_->get_pointer(node_id);
+    if (!node) {
+      // Error.
+      return false;
+    }
+    switch (node->status()) {
+      case NODE_DEAD: {
+        // Not found.
+        return false;
+      }
+      case NODE_LEAF: {
+        Key stored_key;
+        if (!keys_->get_key(node->key_id(), &stored_key)) {
+          // Error.
+          return false;
+        }
+        if (stored_key != key) {
+          // Not found.
+          return false;
+        }
+        keys_->unset(node->key_id());
+        if (prev_node) {
+          Node * const sibling_node = node + (node_id ^ 1) - node_id;
+          *prev_node = *sibling_node;
+        } else {
+          *node = Node::dead_node();
+        }
+        return true;
+      }
+      case NODE_BRANCH: {
+        if (node->bit_pos() >= bit_size) {
+          // Not found.
+          return false;
+        }
+        node_id = node->offset() +
+                  ((key[node->bit_pos() / 8] >> (node->bit_pos() % 8)) & 1);
+        break;
+      }
+      case NODE_TERMINAL: {
+        if (node->bit_size() > bit_size) {
+          // Not found.
+          return false;
+        }
+        node_id = node->offset() + (node->bit_size() < bit_size);
+        break;
+      }
+    }
+    prev_node = node;
+  }
+}
+
+//bool Patricia<Bytes>::replace(KeyArg src_key, KeyArg dest_key,
+//                              int64_t *key_id) {
+//  // TODO
+//  return false;
+//}
+
+//bool Patricia<Bytes>::find_longest_prefix_match(KeyArg query, int64_t *key_id,
+//                                                Key *key) {
+//  // TODO
+//  return false;
+//}
+
+bool Patricia<Bytes>::truncate() {
+  Node * const root_node = nodes_->get_pointer(ROOT_NODE_ID);
+  if (!root_node) {
+    return false;
+  }
+  if (!keys_->truncate()) {
+    return false;
+  }
+  *root_node = Node::dead_node();
+  return true;
+}
+
+bool Patricia<Bytes>::create_map(Storage *storage, uint32_t storage_node_id,
+                                 const MapOptions &) {
+  storage_ = storage;
+  StorageNode storage_node =
+      storage->create_node(storage_node_id, sizeof(Header));
+  if (!storage_node) {
+    return false;
+  }
+  storage_node_id_ = storage_node.id();
+  header_ = static_cast<Header *>(storage_node.body());
+  *header_ = Header();
+  nodes_.reset(NodeArray::create(storage, storage_node_id_));
+  keys_.reset(KeyStore<Bytes>::create(storage, storage_node_id_));
+  if (!nodes_ || !keys_) {
+    storage->unlink_node(storage_node_id_);
+    return false;
+  }
+  header_->nodes_storage_node_id = nodes_->storage_node_id();
+  header_->keys_storage_node_id = keys_->storage_node_id();
+  Node * const root_node = nodes_->get_pointer(ROOT_NODE_ID);
+  if (!root_node) {
+    storage->unlink_node(storage_node_id_);
+    return false;
+  }
+  *root_node = Node::dead_node();
+  return true;
+}
+
+bool Patricia<Bytes>::open_map(Storage *storage, uint32_t storage_node_id) {
+  storage_ = storage;
+  StorageNode storage_node = storage->open_node(storage_node_id);
+  if (!storage_node) {
+    return false;
+  }
+  if (storage_node.size() < sizeof(Header)) {
+    GRNXX_ERROR() << "invalid format: size = " << storage_node.size()
+                  << ", header_size = " << sizeof(Header);
+    return false;
+  }
+  storage_node_id_ = storage_node_id;
+  header_ = static_cast<Header *>(storage_node.body());
+  // TODO: Check the format.
+  nodes_.reset(NodeArray::open(storage, header_->nodes_storage_node_id));
+  keys_.reset(KeyStore<Bytes>::open(storage, header_->keys_storage_node_id));
+  if (!nodes_ || !keys_) {
+    return false;
+  }
+  return true;
+}
 
 }  // namespace map
 }  // namespace grnxx

  Modified: lib/grnxx/map/patricia.hpp (+58 -0)
===================================================================
--- lib/grnxx/map/patricia.hpp    2013-06-17 17:42:28 +0900 (207da4a)
+++ lib/grnxx/map/patricia.hpp    2013-06-18 17:00:55 +0900 (bedac35)
@@ -22,7 +22,11 @@
 
 #include <memory>
 
+#include "grnxx/array.hpp"
+#include "grnxx/bytes.hpp"
 #include "grnxx/map.hpp"
+#include "grnxx/map/key_store.hpp"
+#include "grnxx/map/patricia/node.hpp"
 #include "grnxx/types.hpp"
 
 namespace grnxx {
@@ -38,6 +42,9 @@ struct Header;
 
 template <typename T>
 class Patricia : public Map<T> {
+  using Node = patricia::Node;
+  using NodeArray = Array<Node, 65536, 8192, 8192>;
+
  public:
   using Header = patricia::Header;
   using Key = typename Map<T>::Key;
@@ -73,6 +80,57 @@ class Patricia : public Map<T> {
   Storage *storage_;
   uint32_t storage_node_id_;
   Header *header_;
+  std::unique_ptr<NodeArray> nodes_;
+  std::unique_ptr<KeyStore<T>> keys_;
+
+  bool create_map(Storage *storage, uint32_t storage_node_id,
+                  const MapOptions &options);
+  bool open_map(Storage *storage, uint32_t storage_node_id);
+};
+
+template <>
+class Patricia<Bytes> : public Map<Bytes> {
+  using Node = patricia::Node;
+  using NodeArray = Array<Node>;
+
+ public:
+  using Header = patricia::Header;
+  using Key = typename Map<Bytes>::Key;
+  using KeyArg = typename Map<Bytes>::KeyArg;
+  using Cursor = typename Map<Bytes>::Cursor;
+
+  Patricia();
+  ~Patricia();
+
+  static Patricia *create(Storage *storage, uint32_t storage_node_id,
+                          const MapOptions &options = MapOptions());
+  static Patricia *open(Storage *storage, uint32_t storage_node_id);
+
+  uint32_t storage_node_id() const;
+  MapType type() const;
+
+  int64_t max_key_id() const;
+  uint64_t num_keys() const;
+
+  bool get(int64_t key_id, Key *key = nullptr);
+  bool unset(int64_t key_id);
+//  bool reset(int64_t key_id, KeyArg dest_key);
+
+  bool find(KeyArg key, int64_t *key_id = nullptr);
+  bool add(KeyArg key, int64_t *key_id = nullptr);
+  bool remove(KeyArg key);
+//  bool replace(KeyArg src_key, KeyArg dest_key, int64_t *key_id = nullptr);
+
+//  bool find_longest_prefix_match(KeyArg query, int64_t *key_id, Key *key);
+
+  bool truncate();
+
+ private:
+  Storage *storage_;
+  uint32_t storage_node_id_;
+  Header *header_;
+  std::unique_ptr<NodeArray> nodes_;
+  std::unique_ptr<KeyStore<Bytes>> keys_;
 
   bool create_map(Storage *storage, uint32_t storage_node_id,
                   const MapOptions &options);

  Modified: lib/grnxx/map/patricia/Makefile.am (+2 -1)
===================================================================
--- lib/grnxx/map/patricia/Makefile.am    2013-06-17 17:42:28 +0900 (232d00b)
+++ lib/grnxx/map/patricia/Makefile.am    2013-06-18 17:00:55 +0900 (17e7ebf)
@@ -9,4 +9,5 @@ libgrnxx_map_patricia_la_SOURCES =	\
 libgrnxx_map_patricia_includedir = ${includedir}/grnxx/map/patricia
 libgrnxx_map_patricia_include_HEADERS =	\
 	dummy.hpp			\
-	header.hpp
+	header.hpp			\
+	node.hpp

  Modified: lib/grnxx/map/patricia/dummy.cpp (+1 -0)
===================================================================
--- lib/grnxx/map/patricia/dummy.cpp    2013-06-17 17:42:28 +0900 (c54fb55)
+++ lib/grnxx/map/patricia/dummy.cpp    2013-06-18 17:00:55 +0900 (28aeb81)
@@ -18,6 +18,7 @@
 #include "grnxx/map/patricia/dummy.hpp"
 
 #include "grnxx/map/patricia/header.hpp"
+#include "grnxx/map/patricia/node.hpp"
 
 namespace grnxx {
 namespace map {

  Modified: lib/grnxx/map/patricia/header.cpp (+4 -3)
===================================================================
--- lib/grnxx/map/patricia/header.cpp    2013-06-17 17:42:28 +0900 (5991821)
+++ lib/grnxx/map/patricia/header.cpp    2013-06-18 17:00:55 +0900 (abe4727)
@@ -24,9 +24,10 @@ namespace map {
 namespace patricia {
 
 Header::Header()
-    : map_type(MAP_ARRAY),
-      max_key_id(MAP_MIN_KEY_ID - 1),
-      num_keys(0) {}
+    : map_type(MAP_PATRICIA),
+      next_node_id(2),
+      nodes_storage_node_id(STORAGE_INVALID_NODE_ID),
+      keys_storage_node_id(STORAGE_INVALID_NODE_ID) {}
 
 }  // namespace patricia
 }  // namespace map

  Modified: lib/grnxx/map/patricia/header.hpp (+3 -2)
===================================================================
--- lib/grnxx/map/patricia/header.hpp    2013-06-17 17:42:28 +0900 (ae0e474)
+++ lib/grnxx/map/patricia/header.hpp    2013-06-18 17:00:55 +0900 (05fa44c)
@@ -29,8 +29,9 @@ namespace patricia {
 
 struct Header {
   MapType map_type;
-  int64_t max_key_id;
-  uint64_t num_keys;
+  uint64_t next_node_id;
+  uint32_t nodes_storage_node_id;
+  uint32_t keys_storage_node_id;
 
   Header();
 };

  Added: lib/grnxx/map/patricia/node.hpp (+140 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/map/patricia/node.hpp    2013-06-18 17:00:55 +0900 (5c14d98)
@@ -0,0 +1,140 @@
+/*
+  Copyright (C) 2012-2013  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#ifndef GRNXX_MAP_PATRICIA_NODE_HPP
+#define GRNXX_MAP_PATRICIA_NODE_HPP
+
+#include "grnxx/features.hpp"
+
+#include "grnxx/bytes.hpp"
+#include "grnxx/types.hpp"
+
+namespace grnxx {
+namespace map {
+namespace patricia {
+
+constexpr uint64_t NODE_INVALID_OFFSET = 0;
+
+enum NodeStatus : uint64_t {
+  NODE_DEAD     = 0,
+  NODE_LEAF     = 1,
+  NODE_BRANCH   = 2,
+  NODE_TERMINAL = 3
+};
+
+//constexpr uint64_t NODE_DEAD = 0;
+//constexpr uint64_t NODE_LEAF = 1;
+//constexpr uint64_t NODE_BRANCH = 2;
+//constexpr uint64_t NODE_TERMINAL = 3;
+
+// The internal structure is as follows:
+// - Common
+//   62-63 ( 2): status (DEAD, LEAF, BRANCH, TERMINAL)
+// - Leaf: LEAF
+//    0-39 (40): key_id
+//   40-61 (22): reserved
+// - Branch or Terminal: BRANCH || TERMINAL
+//   16-57 (42): offset
+//   58-61 ( 4): reserved
+// - Branch: BRANCH
+//    0-15 (16): bit_pos
+// - Terminal: TERMINAL
+//    0-15 (16): bit_size
+// where 0 is the LSB and 63 is the MSB.
+
+class Node {
+  static constexpr uint64_t STATUS_MASK    = (1ULL << 2) - 1;
+  static constexpr uint8_t  STATUS_SHIFT   = 62;
+
+  static constexpr uint64_t KEY_ID_MASK    = (1ULL << 40) - 1;
+  static constexpr uint8_t  KEY_ID_SHIFT   = 0;
+
+  static constexpr uint64_t OFFSET_MASK    = (1ULL << 42) - 1;
+  static constexpr uint8_t  OFFSET_SHIFT   = 16;
+
+  static constexpr uint64_t BIT_POS_MASK   = (1ULL << 16) - 1;
+  static constexpr uint8_t  BIT_POS_SHIFT  = 0;
+
+  static constexpr uint64_t BIT_SIZE_MASK  = (1ULL << 16) - 1;
+  static constexpr uint8_t  BIT_SIZE_SHIFT = 0;
+
+ public:
+
+  Node() = default;
+
+  // Create a node that has neither descendants nor an associated key.
+  static Node dead_node() {
+    return Node((NODE_DEAD << STATUS_SHIFT));
+  }
+  // Create a node that has an associated key that is identified by "key_id".
+  static Node leaf_node(int64_t key_id) {
+    return Node((NODE_LEAF << STATUS_SHIFT) |
+                ((key_id & KEY_ID_MASK) << KEY_ID_SHIFT));
+  }
+  // Create a node that works as a 0/1 branch.
+  // If key["bit_pos"] == 0, the next node ID is "offset".
+  // Otherwise, the next node ID is "offset" + 1.
+  static Node branch_node(uint64_t bit_pos, uint64_t offset) {
+    return Node((NODE_BRANCH << STATUS_SHIFT) |
+                ((bit_pos & BIT_POS_MASK) << BIT_POS_SHIFT) |
+                ((offset & OFFSET_MASK) << OFFSET_SHIFT));
+  }
+  // Create a node that works as a short/long branch.
+  // If key_size <= "bit_size", the next node ID is "offset".
+  // Otherwise, the next node ID is "offset" + 1.
+  static Node terminal_node(uint64_t bit_size, uint64_t offset) {
+    return Node((NODE_TERMINAL << STATUS_SHIFT) |
+                ((bit_size & BIT_SIZE_MASK) << BIT_SIZE_SHIFT) |
+                ((offset & OFFSET_MASK) << OFFSET_SHIFT));
+  }
+
+  // Return the node status.
+  uint64_t status() const {
+    return (value_ >> STATUS_SHIFT) & STATUS_MASK;
+  }
+
+  // Return the associated key ID.
+  int64_t key_id() const {
+    return (value_ >> KEY_ID_SHIFT) & KEY_ID_MASK;
+  }
+
+  // Return the offset to the next nodes.
+  uint64_t offset() const {
+    return (value_ >> OFFSET_SHIFT) & OFFSET_MASK;
+  }
+
+  // Return the position of the branch.
+  uint64_t bit_pos() const {
+    return (value_ >> BIT_POS_SHIFT) & BIT_POS_MASK;
+  }
+
+  // Return the branch condition.
+  uint64_t bit_size() const {
+    return (value_ >> BIT_SIZE_SHIFT) & BIT_SIZE_MASK;
+  }
+
+ private:
+  uint64_t value_;
+
+  explicit Node(uint64_t value) : value_(value) {}
+};
+
+}  // namespace patricia
+}  // namespace map
+}  // namespace grnxx
+
+#endif  // GRNXX_MAP_PATRICIA_NODE_HPP
-------------- next part --------------
HTML����������������������������...
下载 



More information about the Groonga-commit mailing list
Back to archive index