[Groonga-commit] groonga/grnxx at cf70a4c [master] Add an alpha version of Array implementations.

Back to archive index

susumu.yata null+****@clear*****
Tue Jul 9 15:59:47 JST 2013


susumu.yata	2013-07-09 15:59:47 +0900 (Tue, 09 Jul 2013)

  New Revision: cf70a4c95a6cdf5159210846afe3377a6342c45e
  https://github.com/groonga/grnxx/commit/cf70a4c95a6cdf5159210846afe3377a6342c45e

  Message:
    Add an alpha version of Array implementations.

  Added files:
    lib/grnxx/array2.cpp
    lib/grnxx/array2.hpp
    lib/grnxx/array2_impl.cpp
    lib/grnxx/array2_impl.hpp
  Modified files:
    lib/grnxx/Makefile.am

  Modified: lib/grnxx/Makefile.am (+4 -0)
===================================================================
--- lib/grnxx/Makefile.am    2013-07-09 15:57:26 +0900 (0909220)
+++ lib/grnxx/Makefile.am    2013-07-09 15:59:47 +0900 (3a4356b)
@@ -15,6 +15,8 @@ libgrnxx_la_LDFLAGS = @AM_LTLDFLAGS@
 libgrnxx_la_SOURCES =			\
 	array.cpp			\
 	array_impl.cpp			\
+	array2.cpp			\
+	array2_impl.cpp			\
 	backtrace.cpp			\
 	broken_down_time.cpp		\
 	charset.cpp			\
@@ -41,6 +43,8 @@ libgrnxx_includedir = ${includedir}/grnxx
 libgrnxx_include_HEADERS =		\
 	array.hpp			\
 	array_impl.hpp			\
+	array2.hpp			\
+	array2_impl.hpp			\
 	backtrace.hpp			\
 	broken_down_time.hpp		\
 	bytes.hpp			\

  Added: lib/grnxx/array2.cpp (+32 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/array2.cpp    2013-07-09 15:59:47 +0900 (e6412a3)
@@ -0,0 +1,32 @@
+/*
+  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
+*/
+#include "grnxx/array2.hpp"
+
+#include "grnxx/exception.hpp"
+#include "grnxx/logger.hpp"
+
+namespace grnxx {
+namespace alpha {
+
+void ArrayErrorHandler::throw_memory_error() {
+  GRNXX_ERROR() << "new grnxx::Array failed";
+  throw MemoryError();
+}
+
+}  // namespace alpha
+}  // namespace grnxx

  Added: lib/grnxx/array2.hpp (+124 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/array2.hpp    2013-07-09 15:59:47 +0900 (02bf59c)
@@ -0,0 +1,124 @@
+/*
+  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_ARRAY2_HPP
+#define GRNXX_ARRAY2_HPP
+
+#include "grnxx/features.hpp"
+
+#include <memory>
+#include <new>
+
+#include "grnxx/array2_impl.hpp"
+#include "grnxx/types.hpp"
+
+namespace grnxx {
+
+class Storage;
+
+namespace alpha {
+
+struct ArrayErrorHandler {
+  static void throw_memory_error();
+};
+
+template <typename T, uint64_t PAGE_SIZE = 0, uint64_t TABLE_SIZE = 0>
+class Array {
+  // Test template parameters.
+  static_assert((PAGE_SIZE != 0) || (TABLE_SIZE == 0),
+                "TABLE_SIZE must be zero if PAGE_SIZE is zero");
+  static_assert((PAGE_SIZE & (PAGE_SIZE - 1)) == 0,
+                "PAGE_SIZE must be zero or a power of two");
+  static_assert((TABLE_SIZE & (TABLE_SIZE - 1)) == 0,
+                "TABLE_SIZE must be zero or a power of two");
+
+  using Impl = ArrayImpl<T, PAGE_SIZE, TABLE_SIZE>;
+
+ public:
+  using Value = typename Impl::Value;
+  using ValueArg = typename Impl::ValueArg;
+
+  ~Array() {}
+
+  // Create an array.
+  static Array *create(Storage *storage, uint32_t storage_node_id,
+                       uint64_t size) {
+    std::unique_ptr<Array> array(create_instance());
+    array->impl_.create(storage, storage_node_id, size);
+    return array.release();
+  }
+  // Create an array with default value.
+  static Array *create(Storage *storage, uint32_t storage_node_id,
+                       uint64_t size, ValueArg default_value) {
+    std::unique_ptr<Array> array(create_instance());
+    array->impl_.create(storage, storage_node_id, size, default_value);
+    return array.release();
+  }
+
+  // Open an array.
+  static Array *open(Storage *storage, uint32_t storage_node_id) {
+    std::unique_ptr<Array> array(create_instance());
+    array->impl_.open(storage, storage_node_id);
+    return array.release();
+  }
+
+  // Unlink an array.
+  static bool unlink(Storage *storage, uint32_t storage_node_id) {
+    return Impl::unlink(storage, storage_node_id);
+  }
+
+  // Return the storage node ID.
+  uint32_t storage_node_id() const {
+    return impl_.storage_node_id();
+  }
+  // Return the number of values.
+  uint64_t size() const {
+    return impl_.size();
+  }
+
+  // Get a reference to a value.
+  Value &operator[](uint64_t value_id) {
+    return *impl_.get_value(value_id);
+  }
+  // Get a value.
+  Value get(uint64_t value_id) {
+    return *impl_.get_value(value_id);
+  }
+  // Set a value.
+  void set(uint64_t value_id, ValueArg value) {
+    *impl_.get_value(value_id) = value;
+  }
+
+ private:
+  Impl impl_;
+
+  Array() : impl_() {}
+
+  // Create an instance or throw an exception on failure.
+  static Array *create_instance() {
+    Array * const array = new (std::nothrow) Array;
+    if (!array) {
+      ArrayErrorHandler::throw_memory_error();
+    }
+    return array;
+  }
+};
+
+}  // namespace alpha
+}  // namespace grnxx
+
+#endif  // GRNXX_ARRAY2_HPP

  Added: lib/grnxx/array2_impl.cpp (+507 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/array2_impl.cpp    2013-07-09 15:59:47 +0900 (8246f94)
@@ -0,0 +1,507 @@
+/*
+  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
+*/
+#include "grnxx/array2_impl.hpp"
+
+#include <new>
+
+#include "grnxx/bytes.hpp"
+#include "grnxx/common_header.hpp"
+#include "grnxx/exception.hpp"
+#include "grnxx/lock.hpp"
+#include "grnxx/logger.hpp"
+#include "grnxx/storage.hpp"
+
+namespace grnxx {
+namespace alpha {
+namespace {
+
+constexpr char FORMAT_STRING[] = "grnxx::Array";
+
+}  // namespace
+
+struct ArrayHeader {
+  CommonHeader common_header;
+  uint64_t value_size;
+  uint64_t page_size;
+  uint64_t table_size;
+  uint64_t secondary_table_size;
+  uint64_t size;
+  uint32_t has_default_value;
+  union {
+    uint32_t page_storage_node_id;
+    uint32_t table_storage_node_id;
+    uint32_t secondary_table_storage_node_id;
+  };
+  Mutex page_mutex;
+  Mutex table_mutex;
+
+  // Initialize the members except "common_header".
+  ArrayHeader();
+
+  // Return true iff the header seems to be correct.
+  explicit operator bool() const;
+};
+
+ArrayHeader::ArrayHeader()
+    : common_header(FORMAT_STRING),
+      value_size(0),
+      page_size(0),
+      table_size(0),
+      secondary_table_size(0),
+      size(0),
+      has_default_value(0),
+      page_storage_node_id(STORAGE_INVALID_NODE_ID),
+      page_mutex(),
+      table_mutex() {}
+
+ArrayHeader::operator bool() const {
+  return common_header.format() == FORMAT_STRING;
+}
+
+Array1D::Array1D()
+    : page_(nullptr),
+      size_(0),
+      storage_node_id_(STORAGE_INVALID_NODE_ID) {}
+
+Array1D::~Array1D() {}
+
+void Array1D::create(Storage *storage, uint32_t storage_node_id,
+                   uint64_t value_size, uint64_t,
+                   uint64_t, uint64_t size,
+                   const void *default_value, ArrayFillPage fill_page) {
+  if (!storage) {
+    GRNXX_ERROR() << "invalid argument: storage = nullptr";
+    throw LogicError();
+  }
+  StorageNode storage_node =
+      storage->create_node(storage_node_id, sizeof(ArrayHeader));
+  storage_node_id_ = storage_node.id();
+  try {
+    ArrayHeader * const header =
+        static_cast<ArrayHeader *>(storage_node.body());
+    *header = ArrayHeader();
+    header->value_size = value_size;
+    header->page_size = size;
+    header->size = size;
+    // Create a page.
+    StorageNode page_node =
+        storage->create_node(storage_node_id_, value_size * size);
+    header->page_storage_node_id = page_node.id();
+    page_ = page_node.body();
+    if (default_value) {
+      header->has_default_value = 1;
+      fill_page(page_, size, default_value);
+    }
+    size_ = size;
+  } catch (...) {
+    storage->unlink_node(storage_node_id_);
+    throw;
+  }
+}
+
+void Array1D::open(Storage *storage, uint32_t storage_node_id,
+                   uint64_t value_size, uint64_t,
+                   uint64_t, ArrayFillPage) {
+  if (!storage) {
+    GRNXX_ERROR() << "invalid argument: storage = nullptr";
+    throw LogicError();
+  }
+  StorageNode storage_node = storage->open_node(storage_node_id);
+  if (storage_node.size() < sizeof(ArrayHeader)) {
+    GRNXX_ERROR() << "too small header: size = " << storage_node.size();
+    throw LogicError();
+  }
+  storage_node_id_ = storage_node.id();
+  const ArrayHeader * const header =
+      static_cast<ArrayHeader *>(storage_node.body());
+  if (!*header) {
+    GRNXX_ERROR() << "wrong format: expected = " << FORMAT_STRING
+                  << ", actual = " << header->common_header.format();
+    throw LogicError();
+  }
+  if (header->value_size != value_size) {
+    GRNXX_ERROR() << "wrong value_size: expected = " << value_size
+                  << ", actual = " << header->value_size;
+    throw LogicError();
+  }
+  StorageNode page_node = storage->open_node(header->page_storage_node_id);
+  page_ = page_node.body();
+  size_ = header->size;
+}
+
+bool Array1D::unlink(Storage *storage, uint32_t storage_node_id,
+                     uint64_t value_size, uint64_t page_size,
+                     uint64_t table_size) {
+  Array1D array;
+  array.open(storage, storage_node_id,
+             value_size, page_size, table_size, nullptr);
+  return storage->unlink_node(storage_node_id);
+}
+
+Array2D::Array2D()
+    : pages_(),
+      size_(0),
+      storage_(nullptr),
+      storage_node_id_(STORAGE_INVALID_NODE_ID),
+      header_(nullptr),
+      fill_page_(nullptr),
+      table_(nullptr),
+      mutex_() {}
+
+Array2D::~Array2D() {}
+
+void Array2D::create(Storage *storage, uint32_t storage_node_id,
+                   uint64_t value_size, uint64_t page_size,
+                   uint64_t, uint64_t size,
+                   const void *default_value, ArrayFillPage fill_page) {
+  if (!storage) {
+    GRNXX_ERROR() << "invalid argument: storage = nullptr";
+    throw LogicError();
+  }
+  if ((size % page_size) != 0) {
+    const uint64_t adjusted_size = size + page_size - (size % page_size);
+    GRNXX_WARNING() << "size adjustment: before = " << size
+                    << ", after = " << adjusted_size
+                    << ", page_size = " << page_size;
+    size = adjusted_size;
+  }
+  storage_ = storage;
+  uint64_t storage_node_size = sizeof(ArrayHeader);
+  if (default_value) {
+    storage_node_size += value_size;
+  }
+  StorageNode storage_node =
+      storage->create_node(storage_node_id, storage_node_size);
+  storage_node_id_ = storage_node.id();
+  try {
+    header_ = static_cast<ArrayHeader *>(storage_node.body());
+    *header_ = ArrayHeader();
+    header_->value_size = value_size;
+    header_->page_size = page_size;
+    header_->table_size = size / page_size;
+    header_->size = size;
+    if (default_value) {
+      header_->has_default_value = 1;
+      std::memcpy(header_ + 1, default_value, value_size);
+      fill_page_ = fill_page;
+    }
+    // Create a table.
+    StorageNode table_node = storage->create_node(
+        storage_node_id_, sizeof(uint32_t) * header_->table_size);
+    header_->table_storage_node_id = table_node.id();
+    table_ = static_cast<uint32_t *>(table_node.body());
+    for (uint64_t i = 0; i < header_->table_size; ++i) {
+      table_[i] = STORAGE_INVALID_NODE_ID;
+    }
+    reserve_pages();
+    size_ = size;
+  } catch (...) {
+    storage->unlink_node(storage_node_id_);
+    throw;
+  }
+}
+
+void Array2D::open(Storage *storage, uint32_t storage_node_id,
+                   uint64_t value_size, uint64_t page_size,
+                   uint64_t , ArrayFillPage fill_page) {
+  if (!storage) {
+    GRNXX_ERROR() << "invalid argument: storage = nullptr";
+    throw LogicError();
+  }
+  storage_ = storage;
+  StorageNode storage_node = storage->open_node(storage_node_id);
+  if (storage_node.size() < sizeof(ArrayHeader)) {
+    GRNXX_ERROR() << "too small header: size = " << storage_node.size();
+    throw LogicError();
+  }
+  storage_node_id_ = storage_node.id();
+  header_ = static_cast<ArrayHeader *>(storage_node.body());
+  if (!*header_) {
+    GRNXX_ERROR() << "wrong format: expected = " << FORMAT_STRING
+                  << ", actual = " << header_->common_header.format();
+    throw LogicError();
+  }
+  if (header_->value_size != value_size) {
+    GRNXX_ERROR() << "wrong value_size: expected = " << value_size
+                  << ", actual = " << header_->value_size;
+    throw LogicError();
+  }
+  if (header_->page_size != page_size) {
+    GRNXX_ERROR() << "wrong page_size: expected = " << page_size
+                  << ", actual = " << header_->page_size;
+    throw LogicError();
+  }
+  if (header_->has_default_value) {
+    fill_page_ = fill_page;
+  }
+  StorageNode table_node = storage->open_node(header_->table_storage_node_id);
+  table_ = static_cast<uint32_t *>(table_node.body());
+  reserve_pages();
+  size_ = header_->size;
+}
+
+bool Array2D::unlink(Storage *storage, uint32_t storage_node_id,
+                     uint64_t value_size, uint64_t page_size,
+                     uint64_t table_size) {
+  Array2D array;
+  array.open(storage, storage_node_id,
+             value_size, page_size, table_size, nullptr);
+  return storage->unlink_node(storage_node_id);
+}
+
+void Array2D::reserve_pages() {
+  pages_.reset(new (std::nothrow) void *[header_->table_size]);
+  if (!pages_) {
+    GRNXX_ERROR() << "new void *[] failed: size = " << header_->table_size;
+    throw MemoryError();
+  }
+  for (uint64_t i = 0; i < header_->table_size; ++i) {
+    pages_[i] = invalid_page_address();
+  }
+}
+
+void Array2D::reserve_page(uint64_t page_id) {
+  Lock inter_thread_lock(&mutex_);
+  if (pages_[page_id] == invalid_page_address()) {
+    StorageNode page_node;
+    if (table_[page_id] == STORAGE_INVALID_NODE_ID) {
+      Lock inter_process_lock(&header_->table_mutex);
+      if (table_[page_id] == STORAGE_INVALID_NODE_ID) {
+        // Create a page.
+        page_node =
+            storage_->create_node(header_->table_storage_node_id,
+                                  header_->value_size * header_->page_size);
+        if (header_->has_default_value) {
+          fill_page_(page_node.body(), header_->page_size, header_ + 1);
+        }
+        table_[page_id] = page_node.id();
+      }
+    }
+    if (!page_node) {
+      page_node = storage_->open_node(table_[page_id]);
+    }
+    pages_[page_id] = static_cast<char *>(page_node.body())
+        - (header_->value_size * header_->page_size * page_id);
+  }
+}
+
+Array3D::Array3D()
+    : tables_(),
+      size_(0),
+      storage_(nullptr),
+      storage_node_id_(STORAGE_INVALID_NODE_ID),
+      header_(nullptr),
+      fill_page_(nullptr),
+      secondary_table_(nullptr),
+      page_mutex_(),
+      table_mutex_() {}
+
+Array3D::~Array3D() {
+  if (tables_) {
+    for (uint64_t i = 0; i < header_->secondary_table_size; ++i) {
+      if (tables_[i] != invalid_table_address()) {
+        delete [] (tables_[i] + (header_->table_size * i));
+      }
+    }
+  }
+}
+
+void Array3D::create(Storage *storage, uint32_t storage_node_id,
+                     uint64_t value_size, uint64_t page_size,
+                     uint64_t table_size, uint64_t size,
+                     const void *default_value, ArrayFillPage fill_page) {
+  if (!storage) {
+    GRNXX_ERROR() << "invalid argument: storage = nullptr";
+    throw LogicError();
+  }
+  if ((size % (page_size * table_size)) != 0) {
+    const uint64_t adjusted_size =
+        size + (page_size * table_size) - (size % (page_size * table_size));
+    GRNXX_WARNING() << "size adjustment: before = " << size
+                    << ", after = " << adjusted_size
+                    << ", page_size = " << page_size
+                    << ", table_size = " << table_size;
+    size = adjusted_size;
+  }
+  storage_ = storage;
+  uint64_t storage_node_size = sizeof(ArrayHeader);
+  if (default_value) {
+    storage_node_size += value_size;
+  }
+  StorageNode storage_node =
+      storage->create_node(storage_node_id, storage_node_size);
+  storage_node_id_ = storage_node.id();
+  try {
+    header_ = static_cast<ArrayHeader *>(storage_node.body());
+    *header_ = ArrayHeader();
+    header_->value_size = value_size;
+    header_->page_size = page_size;
+    header_->table_size = table_size;
+    header_->secondary_table_size = size / (page_size * table_size);
+    header_->size = size;
+    if (default_value) {
+      header_->has_default_value = 1;
+      std::memcpy(header_ + 1, default_value, value_size);
+      fill_page_ = fill_page;
+    }
+    // Create a secondary table.
+    StorageNode secondary_table_node = storage->create_node(
+        storage_node_id_, sizeof(uint32_t) * header_->secondary_table_size);
+    header_->secondary_table_storage_node_id = secondary_table_node.id();
+    secondary_table_ = static_cast<uint32_t *>(secondary_table_node.body());
+    for (uint64_t i = 0; i < header_->secondary_table_size; ++i) {
+      secondary_table_[i] = STORAGE_INVALID_NODE_ID;
+    }
+    reserve_tables();
+    size_ = size;
+  } catch (...) {
+    storage->unlink_node(storage_node_id_);
+    throw;
+  }
+}
+
+void Array3D::open(Storage *storage, uint32_t storage_node_id,
+                   uint64_t value_size, uint64_t page_size,
+                   uint64_t table_size, ArrayFillPage fill_page) {
+  if (!storage) {
+    GRNXX_ERROR() << "invalid argument: storage = nullptr";
+    throw LogicError();
+  }
+  storage_ = storage;
+  StorageNode storage_node = storage->open_node(storage_node_id);
+  if (storage_node.size() < sizeof(ArrayHeader)) {
+    GRNXX_ERROR() << "too small header: size = " << storage_node.size();
+    throw LogicError();
+  }
+  storage_node_id_ = storage_node.id();
+  header_ = static_cast<ArrayHeader *>(storage_node.body());
+  if (!*header_) {
+    GRNXX_ERROR() << "wrong format: expected = " << FORMAT_STRING
+                  << ", actual = " << header_->common_header.format();
+    throw LogicError();
+  }
+  if (header_->value_size != value_size) {
+    GRNXX_ERROR() << "wrong value_size: expected = " << value_size
+                  << ", actual = " << header_->value_size;
+    throw LogicError();
+  }
+  if (header_->page_size != page_size) {
+    GRNXX_ERROR() << "wrong page_size: expected = " << page_size
+                  << ", actual = " << header_->page_size;
+    throw LogicError();
+  }
+  if (header_->table_size != table_size) {
+    GRNXX_ERROR() << "wrong table_size: expected = " << table_size
+                  << ", actual = " << header_->table_size;
+    throw LogicError();
+  }
+  if (header_->has_default_value) {
+    fill_page_ = fill_page;
+  }
+  StorageNode secondary_table_node =
+      storage->open_node(header_->secondary_table_storage_node_id);
+  secondary_table_ = static_cast<uint32_t *>(secondary_table_node.body());
+  reserve_tables();
+  size_ = header_->size;
+}
+
+bool Array3D::unlink(Storage *storage, uint32_t storage_node_id,
+                     uint64_t value_size, uint64_t page_size,
+                     uint64_t table_size) {
+  Array3D array;
+  array.open(storage, storage_node_id,
+             value_size, page_size, table_size, nullptr);
+  return storage->unlink_node(storage_node_id);
+}
+
+void Array3D::reserve_tables() {
+  tables_.reset(new (std::nothrow) void **[header_->secondary_table_size]);
+  if (!tables_) {
+    GRNXX_ERROR() << "new void **[] failed: size = "
+                  << header_->secondary_table_size;
+    throw MemoryError();
+  }
+  for (uint64_t i = 0; i < header_->secondary_table_size; ++i) {
+    tables_[i] = invalid_table_address();
+  }
+}
+
+void Array3D::reserve_page(uint64_t page_id) {
+  const uint64_t table_id = page_id / header_->table_size;
+  if (tables_[table_id] == invalid_table_address()) {
+    reserve_table(table_id);
+  }
+  Lock inter_thread_lock(&page_mutex_);
+  if (tables_[table_id][page_id] == invalid_page_address()) {
+    StorageNode page_node;
+    StorageNode table_node = storage_->open_node(secondary_table_[table_id]);
+    uint32_t * const table = static_cast<uint32_t *>(table_node.body())
+        - (header_->table_size * table_id);
+    if (table[page_id] == STORAGE_INVALID_NODE_ID) {
+      Lock inter_process_lock(&header_->page_mutex);
+      if (table[page_id] == STORAGE_INVALID_NODE_ID) {
+        // Create a page.
+        page_node = storage_->create_node(
+            secondary_table_[table_id],
+            header_->value_size * header_->page_size);
+        if (header_->has_default_value) {
+          fill_page_(page_node.body(), header_->page_size, header_ + 1);
+        }
+        table[page_id] = page_node.id();
+      }
+    }
+    if (!page_node) {
+      page_node = storage_->open_node(table[page_id]);
+    }
+    tables_[table_id][page_id] = static_cast<char *>(page_node.body())
+        - (header_->value_size * header_->page_size * page_id);
+  }
+}
+
+void Array3D::reserve_table(uint64_t table_id) {
+  Lock inter_thread_lock(&table_mutex_);
+  if (tables_[table_id] == invalid_table_address()) {
+    if (secondary_table_[table_id] == STORAGE_INVALID_NODE_ID) {
+      Lock inter_process_lock(&header_->table_mutex);
+      if (secondary_table_[table_id] == STORAGE_INVALID_NODE_ID) {
+        // Create a table.
+        StorageNode table_node =
+            storage_->create_node(header_->secondary_table_storage_node_id,
+                                  sizeof(uint32_t) * header_->table_size);
+        uint32_t * const table = static_cast<uint32_t *>(table_node.body());
+        for (uint64_t i = 0; i < header_->table_size; ++i) {
+          table[i] = STORAGE_INVALID_NODE_ID;
+        }
+        secondary_table_[table_id] = table_node.id();
+      }
+    }
+    // Create a table cache.
+    void ** const pages = new (std::nothrow) void *[header_->table_size];
+    if (!pages) {
+      GRNXX_ERROR() << "new void *[] failed: size = " << header_->table_size;
+      throw MemoryError();
+    }
+    for (uint64_t i = 0; i < header_->table_size; ++i) {
+      pages[i] = invalid_page_address();
+    }
+    tables_[table_id] = pages - (header_->table_size * table_id);
+  }
+}
+
+}  // namespace alpha
+}  // namespace grnxx

  Added: lib/grnxx/array2_impl.hpp (+295 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/array2_impl.hpp    2013-07-09 15:59:47 +0900 (9d11ff2)
@@ -0,0 +1,295 @@
+/*
+  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_ARRAY2_IMPL_HPP
+#define GRNXX_ARRAY2_IMPL_HPP
+
+#include "grnxx/features.hpp"
+
+#include <memory>
+
+#include "grnxx/mutex.hpp"
+#include "grnxx/traits.hpp"
+#include "grnxx/types.hpp"
+
+namespace grnxx {
+
+class Storage;
+
+namespace alpha {
+
+struct ArrayHeader;
+
+// Fill "page" with "value".
+using ArrayFillPage = void (*)(void *page, uint64_t page_size,
+                               const void *value);
+
+class Array1D {
+ public:
+  Array1D();
+  ~Array1D();
+
+  // Create an array.
+  void create(Storage *storage, uint32_t storage_node_id,
+              uint64_t value_size, uint64_t page_size,
+              uint64_t table_size, uint64_t size,
+              const void *default_value, ArrayFillPage fill_page);
+  // Open an array.
+  void open(Storage *storage, uint32_t storage_node_id,
+            uint64_t value_size, uint64_t page_size,
+            uint64_t table_size, ArrayFillPage fill_page);
+
+  // Unlink an array.
+  static bool unlink(Storage *storage, uint32_t storage_node_id,
+                     uint64_t value_size, uint64_t page_size,
+                     uint64_t table_size);
+
+  // Return the storage node ID.
+  uint32_t storage_node_id() const {
+    return storage_node_id_;
+  }
+  // Return the number of values.
+  uint64_t size() const {
+    return size_;
+  }
+
+  // Return a pointer to a value.
+  template <typename T, uint64_t, uint64_t>
+  T *get_value(uint64_t value_id) {
+    return &static_cast<T *>(page_)[value_id];
+  }
+
+ private:
+  void *page_;
+  uint64_t size_;
+  uint32_t storage_node_id_;
+};
+
+class Array2D {
+ public:
+  Array2D();
+  ~Array2D();
+
+  // Create an array.
+  void create(Storage *storage, uint32_t storage_node_id,
+              uint64_t value_size, uint64_t page_size,
+              uint64_t table_size, uint64_t size,
+              const void *default_value, ArrayFillPage fill_page);
+  // Open an array.
+  void open(Storage *storage, uint32_t storage_node_id,
+            uint64_t value_size, uint64_t page_size,
+            uint64_t table_size, ArrayFillPage fill_page);
+
+  // Unlink an array.
+  static bool unlink(Storage *storage, uint32_t storage_node_id,
+                     uint64_t value_size, uint64_t page_size,
+                     uint64_t table_size);
+
+  // Return the storage node ID.
+  uint32_t storage_node_id() const {
+    return storage_node_id_;
+  }
+  // Return the number of values.
+  uint64_t size() const {
+    return size_;
+  }
+
+  // Return a pointer to a value.
+  template <typename T, uint64_t PAGE_SIZE, uint64_t TABLE_SIZE>
+  T *get_value(uint64_t value_id) {
+    const uint64_t page_id = value_id / PAGE_SIZE;
+    if (pages_[page_id] == invalid_page_address()) {
+      reserve_page(page_id);
+    }
+    return &static_cast<T *>(pages_[page_id])[value_id];
+  }
+
+ private:
+  std::unique_ptr<void *[]> pages_;
+  uint64_t size_;
+  Storage *storage_;
+  uint32_t storage_node_id_;
+  ArrayHeader *header_;
+  ArrayFillPage fill_page_;
+  uint32_t *table_;
+  Mutex mutex_;
+
+  void reserve_pages();
+  void reserve_page(uint64_t page_id);
+
+  static void *invalid_page_address() {
+    return static_cast<char *>(nullptr) - 1;
+  }
+};
+
+class Array3D {
+  using ArrayPageFiller = void (*)(void *page, const void *value);
+
+ public:
+  Array3D();
+  ~Array3D();
+
+  // Create an array.
+  void create(Storage *storage, uint32_t storage_node_id,
+              uint64_t value_size, uint64_t page_size,
+              uint64_t table_size, uint64_t size,
+              const void *default_value, ArrayFillPage fill_page);
+  // Open an array.
+  void open(Storage *storage, uint32_t storage_node_id,
+            uint64_t value_size, uint64_t page_size,
+            uint64_t table_size, ArrayFillPage fill_page);
+
+  // Unlink an array.
+  static bool unlink(Storage *storage, uint32_t storage_node_id,
+                     uint64_t value_size, uint64_t page_size,
+                     uint64_t table_size);
+
+  // Return the storage node ID.
+  uint32_t storage_node_id() const {
+    return storage_node_id_;
+  }
+  // Return the number of values.
+  uint64_t size() const {
+    return size_;
+  }
+
+  // Return a pointer to a value.
+  template <typename T, uint64_t PAGE_SIZE, uint64_t TABLE_SIZE>
+  T *get_value(uint64_t value_id) {
+    const uint64_t table_id = value_id / (PAGE_SIZE * TABLE_SIZE);
+    const uint64_t page_id = value_id / PAGE_SIZE;
+    if ((tables_[table_id] == invalid_page_address()) ||
+        (tables_[table_id][page_id] == invalid_page_address())) {
+      reserve_page(page_id);
+    }
+    return &static_cast<T *>(tables_[table_id][page_id])[value_id];
+  }
+
+ private:
+  std::unique_ptr<void **[]> tables_;
+  uint64_t size_;
+  Storage *storage_;
+  uint32_t storage_node_id_;
+  ArrayHeader *header_;
+  ArrayFillPage fill_page_;
+  uint32_t *secondary_table_;
+  Mutex page_mutex_;
+  Mutex table_mutex_;
+
+  void reserve_tables();
+  void reserve_page(uint64_t page_id);
+  void reserve_table(uint64_t table_id);
+
+  static void **invalid_table_address() {
+    return reinterpret_cast<void **>(static_cast<char *>(nullptr) + 1);
+  }
+  static void *invalid_page_address() {
+    return static_cast<char *>(nullptr) + 1;
+  }
+};
+
+template <uint64_t PAGE_SIZE, uint64_t TABLE_SIZE>
+struct ArrayImplSelector;
+
+// Use Array1D.
+template <>
+struct ArrayImplSelector<0, 0> {
+  using Type = Array1D;
+};
+
+// Use Array2D.
+template <uint64_t PAGE_SIZE>
+struct ArrayImplSelector<PAGE_SIZE, 0> {
+  using Type = Array2D;
+};
+
+// Use Array3D.
+template <uint64_t PAGE_SIZE, uint64_t TABLE_SIZE>
+struct ArrayImplSelector {
+  using Type = Array3D;
+};
+
+template <typename T>
+struct ArrayPageFiller {
+  // Fill "page" with "value".
+  // This function is used to initialize a page.
+  static void fill_page(void *page, uint64_t page_size, const void *value) {
+    for (uint64_t i = 0; i < page_size; ++i) {
+      static_cast<T *>(page)[i] = *static_cast<const T *>(value);
+    }
+  }
+};
+
+template <typename T, uint64_t PAGE_SIZE, uint64_t TABLE_SIZE>
+class ArrayImpl {
+  using Impl = typename ArrayImplSelector<PAGE_SIZE, TABLE_SIZE>::Type;
+
+ public:
+  using Value = typename Traits<T>::Type;
+  using ValueArg = typename Traits<T>::ArgumentType;
+
+  ArrayImpl() : impl_() {}
+  ~ArrayImpl() {}
+
+  // Create an array.
+  void create(Storage *storage, uint32_t storage_node_id, uint64_t size) {
+    impl_.create(storage, storage_node_id,
+                 sizeof(Value), PAGE_SIZE, TABLE_SIZE, size,
+                 nullptr, ArrayPageFiller<Value>::fill_page);
+  }
+  // Create an array with the default value.
+  void create(Storage *storage, uint32_t storage_node_id, uint64_t size,
+              ValueArg default_value) {
+    impl_.create(storage, storage_node_id,
+                 sizeof(Value), PAGE_SIZE, TABLE_SIZE, size,
+                 &default_value, ArrayPageFiller<Value>::fill_page);
+  }
+  // Open an array.
+  void open(Storage *storage, uint32_t storage_node_id) {
+    impl_.open(storage, storage_node_id,
+               sizeof(Value), PAGE_SIZE, TABLE_SIZE,
+               ArrayPageFiller<Value>::fill_page);
+  }
+
+  // Unlink an array.
+  static bool unlink(Storage *storage, uint32_t storage_node_id) {
+    return Impl::unlink(storage, storage_node_id,
+                        sizeof(Value), PAGE_SIZE, TABLE_SIZE);
+  }
+
+  // Return the storage node ID.
+  uint32_t storage_node_id() const {
+    return impl_.storage_node_id();
+  }
+  // Return the number of values.
+  uint64_t size() const {
+    return impl_.size();
+  }
+
+  // Return a pointer to a value.
+  Value *get_value(uint64_t value_id) {
+    return impl_. template get_value<Value, PAGE_SIZE, TABLE_SIZE>(value_id);
+  }
+
+ private:
+  Impl impl_;
+};
+
+}  // namespace alpha
+}  // namespace grnxx
+
+#endif  // GRNXX_ARRAY2_IMPL_HPP
-------------- next part --------------
HTML����������������������������...
Download 



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