[Groonga-commit] groonga/grnxx [master] Rename grnxx::alpha::BlobVector to grnxx::db::BlobVector.

Back to archive index

susumu.yata null+****@clear*****
Thu Dec 13 18:21:10 JST 2012


susumu.yata	2012-12-13 18:21:10 +0900 (Thu, 13 Dec 2012)

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

  Log:
    Rename grnxx::alpha::BlobVector to grnxx::db::BlobVector.

  Removed files:
    lib/alpha/blob_vector.cpp
    lib/alpha/blob_vector.hpp
    test/test_alpha_blob_vector.cpp
  Modified files:
    lib/alpha/Makefile.am
    lib/db/blob_vector.cpp
    lib/db/blob_vector.hpp
    test/Makefile.am
    test/test_db_blob_vector.cpp

  Modified: lib/alpha/Makefile.am (+0 -2)
===================================================================
--- lib/alpha/Makefile.am    2012-12-12 14:29:12 +0900 (fefe083)
+++ lib/alpha/Makefile.am    2012-12-13 18:21:10 +0900 (8044c8d)
@@ -1,10 +1,8 @@
 noinst_LTLIBRARIES = libgrnxx_alpha.la
 
 libgrnxx_alpha_la_SOURCES =		\
-	blob_vector.cpp			\
 	sample.cpp
 
 libgrnxx_alpha_includedir = ${includedir}/grnxx/alpha
 libgrnxx_alpha_include_HEADERS =	\
-	blob_vector.hpp			\
 	sample.hpp

  Deleted: lib/alpha/blob_vector.cpp (+0 -469) 100644
===================================================================
--- lib/alpha/blob_vector.cpp    2012-12-12 14:29:12 +0900 (2cd56ce)
+++ /dev/null
@@ -1,469 +0,0 @@
-/*
-  Copyright (C) 2012  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 "blob_vector.hpp"
-
-#include "../exception.hpp"
-#include "../lock.hpp"
-#include "../logger.hpp"
-
-namespace grnxx {
-namespace alpha {
-
-BlobVectorCreate BLOB_VECTOR_CREATE;
-BlobVectorOpen BLOB_VECTOR_OPEN;
-
-BlobVectorHeader::BlobVectorHeader(uint32_t table_block_id)
-  : table_block_id_(table_block_id),
-    value_store_block_id_(io::BLOCK_INVALID_ID),
-    index_store_block_id_(io::BLOCK_INVALID_ID),
-    next_page_id_(0),
-    next_value_offset_(0),
-    latest_frozen_page_id_(BLOB_VECTOR_INVALID_PAGE_ID),
-    latest_large_value_block_id_(io::BLOCK_INVALID_ID),
-    inter_process_mutex_(MUTEX_UNLOCKED) {}
-
-StringBuilder &BlobVectorHeader::write_to(StringBuilder &builder) const {
-  if (!builder) {
-    return builder;
-  }
-
-  builder << "{ table_block_id = " << table_block_id_
-          << ", value_store_block_id = " << value_store_block_id_
-          << ", index_store_block_id = " << index_store_block_id_
-          << ", next_page_id = " << next_page_id_
-          << ", next_value_offset = " << next_value_offset_
-          << ", latest_large_value_block_id = " << latest_large_value_block_id_
-          << ", inter_process_mutex = " << inter_process_mutex_;
-  return builder << " }";
-}
-
-std::unique_ptr<BlobVectorImpl> BlobVectorImpl::create(io::Pool pool) {
-  std::unique_ptr<BlobVectorImpl> vector(new (std::nothrow) BlobVectorImpl);
-  if (!vector) {
-    GRNXX_ERROR() << "new grnxx::io::VectorImpl failed";
-    GRNXX_THROW();
-  }
-  vector->create_vector(pool);
-  return vector;
-}
-
-std::unique_ptr<BlobVectorImpl> BlobVectorImpl::open(io::Pool pool,
-                                                     uint32_t block_id) {
-  std::unique_ptr<BlobVectorImpl> vector(new (std::nothrow) BlobVectorImpl);
-  if (!vector) {
-    GRNXX_ERROR() << "new grnxx::io::VectorImpl failed";
-    GRNXX_THROW();
-  }
-  vector->open_vector(pool, block_id);
-  return vector;
-}
-
-void BlobVectorImpl::set_value(uint64_t id, const Blob &value) {
-  const BlobVectorCell new_cell = create_value(value);
-  BlobVectorCell old_cell;
-  try {
-    do {
-      old_cell = table_[id];
-    } while (!atomic_compare_and_swap(old_cell, new_cell, &table_[id]));
-  } catch (...) {
-    // The new value is freed on failure.
-    free_value(new_cell);
-    throw;
-  }
-  // The old value is freed on success.
-  free_value(old_cell);
-}
-
-void BlobVectorImpl::append(uint64_t id, const Blob &value) {
-  if (!value || (value.length() == 0)) {
-    return;
-  }
-
-  for ( ; ; ) {
-    const BlobVectorCell old_cell = table_[id];
-    const Blob old_value = get_value(old_cell);
-    const BlobVectorCell new_cell = join_values(old_value, value);
-    if (atomic_compare_and_swap(old_cell, new_cell, &table_[id])) {
-      // The old value is freed on success.
-      free_value(old_cell);
-      break;
-    } else {
-      // The new value is freed on failure.
-      free_value(new_cell);
-    }
-  }
-}
-
-void BlobVectorImpl::prepend(uint64_t id, const Blob &value) {
-  if (!value || (value.length() == 0)) {
-    return;
-  }
-
-  for ( ; ; ) {
-    const BlobVectorCell old_cell = table_[id];
-    const Blob old_value = get_value(old_cell);
-    const BlobVectorCell new_cell = join_values(value, old_value);
-    if (atomic_compare_and_swap(old_cell, new_cell, &table_[id])) {
-      // The old value is freed on success.
-      free_value(old_cell);
-      break;
-    } else {
-      // The new value is freed on failure.
-      free_value(new_cell);
-    }
-  }
-}
-
-StringBuilder &BlobVectorImpl::write_to(StringBuilder &builder) const {
-  if (!builder) {
-    return builder;
-  }
-
-  builder << "{ pool = " << pool_.path()
-          << ", block_info = " << *block_info_
-          << ", header = " << *header_
-          << ", inter_thread_mutex = " << inter_thread_mutex_;
-  return builder << " }";
-}
-
-void BlobVectorImpl::unlink(io::Pool pool, uint32_t block_id) {
-  std::unique_ptr<BlobVectorImpl> vector =
-      BlobVectorImpl::open(pool, block_id);
-
-  if (vector->header_->latest_large_value_block_id() != io::BLOCK_INVALID_ID) {
-    uint32_t block_id = vector->header_->latest_large_value_block_id();
-    do {
-      auto value_header = static_cast<const BlobVectorValueHeader *>(
-          pool.get_block_address(block_id));
-      const uint32_t prev_block_id = value_header->prev_value_block_id();
-      pool.free_block(block_id);
-      block_id = prev_block_id;
-    } while (block_id != vector->header_->latest_large_value_block_id());
-  }
-  BlobVectorTable::unlink(pool, vector->header_->table_block_id());
-  pool.free_block(vector->block_info_->id());
-}
-
-BlobVectorImpl::BlobVectorImpl()
-  : pool_(),
-    block_info_(nullptr),
-    header_(nullptr),
-    recycler_(nullptr),
-    table_(),
-    value_store_(),
-    index_store_(),
-    inter_thread_mutex_(MUTEX_UNLOCKED) {}
-
-void BlobVectorImpl::create_vector(io::Pool pool) {
-  pool_ = pool;
-  block_info_ = pool.create_block(sizeof(BlobVectorHeader));
-
-  try {
-    table_.create(pool_, BlobVectorCell::null_value());
-  } catch (...) {
-    pool_.free_block(*block_info_);
-    throw;
-  }
-
-  void * const block_address = pool_.get_block_address(*block_info_);
-  header_ = static_cast<BlobVectorHeader *>(block_address);
-  *header_ = BlobVectorHeader(table_.block_id());
-
-  recycler_ = pool.mutable_recycler();
-}
-
-void BlobVectorImpl::open_vector(io::Pool pool, uint32_t block_id) {
-  pool_ = pool;
-  block_info_ = pool.get_block_info(block_id);
-  if (block_info_->size() < sizeof(BlobVectorHeader)) {
-    GRNXX_ERROR() << "invalid argument: block_info = " << *block_info_
-                  << ", header_size = " << sizeof(BlobVectorHeader);
-    GRNXX_THROW();
-  }
-
-  void * const block_address = pool_.get_block_address(*block_info_);
-  header_ = static_cast<BlobVectorHeader *>(block_address);
-
-  // TODO: Check the format!
-
-  recycler_ = pool.mutable_recycler();
-
-  // Open the core table.
-  table_.open(pool, header_->table_block_id());
-}
-
-Blob BlobVectorImpl::get_value(BlobVectorCell cell) {
-  switch (cell.type()) {
-    case BLOB_VECTOR_NULL: {
-      return Blob(nullptr);
-    }
-    case BLOB_VECTOR_SMALL: {
-      return Blob(cell);
-    }
-    case BLOB_VECTOR_MEDIUM: {
-      if (!value_store_) {
-        Lock lock(mutable_inter_thread_mutex());
-        if (!value_store_) {
-          value_store_.open(pool_, header_->value_store_block_id());
-        }
-      }
-      return Blob(&value_store_[cell.offset()], cell.medium_length());
-    }
-    case BLOB_VECTOR_LARGE: {
-      const auto value_header = static_cast<const BlobVectorValueHeader *>(
-          pool_.get_block_address(cell.block_id()));
-      return Blob(value_header + 1, value_header->length());
-    }
-    default: {
-      GRNXX_ERROR() << "invalid value type";
-      GRNXX_THROW();
-    }
-  }
-}
-
-BlobVectorCell BlobVectorImpl::create_value(const Blob &value) {
-  if (!value) {
-    return BlobVectorCell::null_value();
-  }
-
-  BlobVectorCell cell;
-  void *address;
-  if (value.length() < BLOB_VECTOR_MEDIUM_VALUE_MIN_LENGTH) {
-    address = create_small_value(value.length(), &cell);
-  } else if (value.length() < BLOB_VECTOR_LARGE_VALUE_MIN_LENGTH) {
-    address = create_medium_value(value.length(), &cell);
-  } else {
-    address = create_large_value(value.length(), &cell);
-  }
-  std::memcpy(address, value.address(), value.length());
-  return cell;
-}
-
-BlobVectorCell BlobVectorImpl::join_values(const Blob &lhs, const Blob &rhs) {
-  const uint64_t length = lhs.length() + rhs.length();
-  BlobVectorCell cell;
-  void *address;
-  if (length < BLOB_VECTOR_MEDIUM_VALUE_MIN_LENGTH) {
-    address = create_small_value(length, &cell);
-  } else if (length < BLOB_VECTOR_LARGE_VALUE_MIN_LENGTH) {
-    address = create_medium_value(length, &cell);
-  } else {
-    address = create_large_value(length, &cell);
-  }
-  std::memcpy(address, lhs.address(), lhs.length());
-  std::memcpy(static_cast<char *>(address) + lhs.length(),
-              rhs.address(), rhs.length());
-  return cell;
-}
-
-void *BlobVectorImpl::create_small_value(uint64_t length,
-                                         BlobVectorCell *cell) {
-  *cell = BlobVectorCell::small_value(length);
-  return cell->value();
-}
-
-void *BlobVectorImpl::create_medium_value(uint64_t length,
-                                          BlobVectorCell *cell) {
-  Lock lock(mutable_inter_thread_mutex());
-
-  if (!value_store_) {
-    if (header_->value_store_block_id() == io::BLOCK_INVALID_ID) {
-      Lock lock(mutable_inter_process_mutex());
-      if (header_->value_store_block_id() == io::BLOCK_INVALID_ID) {
-        value_store_.create(pool_);
-        header_->set_value_store_block_id(value_store_.block_id());
-      }
-    }
-    if (!value_store_) {
-      value_store_.open(pool_, header_->value_store_block_id());
-    }
-  }
-
-  if (!index_store_) {
-    if (header_->index_store_block_id() == io::BLOCK_INVALID_ID) {
-      Lock lock(mutable_inter_process_mutex());
-      if (header_->index_store_block_id() == io::BLOCK_INVALID_ID) {
-        index_store_.create(pool_, BlobVectorPageInfo());
-        header_->set_index_store_block_id(index_store_.block_id());
-      }
-    }
-    if (!index_store_) {
-      index_store_.open(pool_, header_->index_store_block_id());
-    }
-  }
-
-  // Unfreeze the oldest frozen page for reuse.
-  unfreeze_oldest_frozen_page();
-
-  uint64_t offset = header_->next_value_offset();
-  const uint64_t offset_in_page =
-      ((offset - 1) & (BLOB_VECTOR_VALUE_STORE_PAGE_SIZE - 1)) + 1;
-  const uint64_t size_left_in_page =
-      BLOB_VECTOR_VALUE_STORE_PAGE_SIZE - offset_in_page;
-
-  // Reserve a new page if there is not enough space in the current page.
-  if (length > size_left_in_page) {
-    if (offset != 0) {
-      // Freeze the current page if it is empty.
-      const uint32_t page_id = static_cast<uint32_t>(
-          (offset - 1) >> BLOB_VECTOR_VALUE_STORE_PAGE_SIZE_BITS);
-      if (index_store_[page_id].num_values() == 0) {
-        freeze_page(page_id);
-      }
-    }
-
-    const uint32_t page_id = header_->next_page_id();
-    offset = static_cast<uint64_t>(
-        page_id << BLOB_VECTOR_VALUE_STORE_PAGE_SIZE_BITS);
-    if (index_store_[page_id].next_page_id() != BLOB_VECTOR_INVALID_PAGE_ID) {
-      header_->set_next_page_id(index_store_[page_id].next_page_id());
-    } else {
-      header_->set_next_page_id(page_id + 1);
-    }
-    index_store_[page_id].set_num_values(0);
-  }
-  header_->set_next_value_offset(offset + length);
-
-  const uint32_t page_id = static_cast<uint32_t>(
-      offset >> BLOB_VECTOR_VALUE_STORE_PAGE_SIZE_BITS);
-  index_store_[page_id].set_num_values(index_store_[page_id].num_values() + 1);
-
-  *cell = BlobVectorCell::medium_value(offset, length);
-  return &value_store_[offset];
-}
-
-void *BlobVectorImpl::create_large_value(uint64_t length,
-                                         BlobVectorCell *cell) {
-  const io::BlockInfo *block_info =
-      pool_.create_block(sizeof(BlobVectorValueHeader) + length);
-  auto value_header = static_cast<BlobVectorValueHeader *>(
-      pool_.get_block_address(*block_info));
-  value_header->set_length(length);
-  register_large_value(block_info->id(), value_header);
-  *cell = BlobVectorCell::large_value(block_info->id());
-  return value_header + 1;
-}
-
-void BlobVectorImpl::free_value(BlobVectorCell cell) {
-  switch (cell.type()) {
-    case BLOB_VECTOR_NULL:
-    case BLOB_VECTOR_SMALL: {
-      break;
-    }
-    case BLOB_VECTOR_MEDIUM: {
-      Lock lock(mutable_inter_thread_mutex());
-
-      const uint32_t page_id = static_cast<uint32_t>(
-          cell.offset() >> BLOB_VECTOR_VALUE_STORE_PAGE_SIZE_BITS);
-      index_store_[page_id].set_num_values(
-          index_store_[page_id].num_values() - 1);
-      if (index_store_[page_id].num_values() == 0) {
-        const uint32_t current_page_id =
-            static_cast<uint32_t>(header_->next_value_offset()
-                                  >> BLOB_VECTOR_VALUE_STORE_PAGE_SIZE_BITS);
-        if (page_id != current_page_id) {
-          freeze_page(page_id);
-        }
-      }
-      break;
-    }
-    case BLOB_VECTOR_LARGE: {
-      const io::BlockInfo * const block_info =
-          pool_.get_block_info(cell.block_id());
-      unregister_large_value(block_info->id(),
-          static_cast<BlobVectorValueHeader *>(
-              pool_.get_block_address(*block_info)));
-      pool_.free_block(*block_info);
-      break;
-    }
-  }
-}
-
-void BlobVectorImpl::register_large_value(uint32_t block_id,
-    BlobVectorValueHeader *value_header) {
-  Lock lock(mutable_inter_process_mutex());
-  if (header_->latest_large_value_block_id() == io::BLOCK_INVALID_ID) {
-    value_header->set_next_value_block_id(block_id);
-    value_header->set_prev_value_block_id(block_id);
-  } else {
-    const uint32_t prev_id = header_->latest_large_value_block_id();
-    auto prev_header = static_cast<BlobVectorValueHeader *>(
-        pool_.get_block_address(prev_id));
-    const uint32_t next_id = prev_header->next_value_block_id();
-    auto next_header = static_cast<BlobVectorValueHeader *>(
-        pool_.get_block_address(next_id));
-    value_header->set_next_value_block_id(next_id);
-    value_header->set_prev_value_block_id(prev_id);
-    prev_header->set_next_value_block_id(block_id);
-    next_header->set_prev_value_block_id(block_id);
-  }
-  header_->set_latest_large_value_block_id(block_id);
-}
-
-void BlobVectorImpl::unregister_large_value(uint32_t block_id,
-    BlobVectorValueHeader *value_header) {
-  Lock lock(mutable_inter_process_mutex());
-  const uint32_t next_id = value_header->next_value_block_id();
-  const uint32_t prev_id = value_header->prev_value_block_id();
-  auto next_header = static_cast<BlobVectorValueHeader *>(
-      pool_.get_block_address(next_id));
-  auto prev_header = static_cast<BlobVectorValueHeader *>(
-      pool_.get_block_address(prev_id));
-  next_header->set_prev_value_block_id(prev_id);
-  prev_header->set_next_value_block_id(next_id);
-  if (block_id == header_->latest_large_value_block_id()) {
-    header_->set_latest_large_value_block_id(prev_id);
-  }
-}
-
-void BlobVectorImpl::freeze_page(uint32_t page_id) {
-  BlobVectorPageInfo &page_info = index_store_[page_id];
-  if (header_->latest_frozen_page_id() != BLOB_VECTOR_INVALID_PAGE_ID) {
-    BlobVectorPageInfo &latest_frozen_page_info =
-        index_store_[header_->latest_frozen_page_id()];
-    page_info.set_next_page_id(latest_frozen_page_info.next_page_id());
-    latest_frozen_page_info.set_next_page_id(page_id);
-  } else {
-    page_info.set_next_page_id(page_id);
-  }
-  page_info.set_stamp(recycler_->stamp());
-  header_->set_latest_frozen_page_id(page_id);
-}
-
-void BlobVectorImpl::unfreeze_oldest_frozen_page() {
-  if (header_->latest_frozen_page_id() != BLOB_VECTOR_INVALID_PAGE_ID) {
-    BlobVectorPageInfo &latest_frozen_page_info =
-        index_store_[header_->latest_frozen_page_id()];
-    const uint32_t oldest_frozen_page_id =
-        latest_frozen_page_info.next_page_id();
-    BlobVectorPageInfo &oldest_frozen_page_info =
-        index_store_[oldest_frozen_page_id];
-    if (recycler_->check(oldest_frozen_page_info.stamp())) {
-      latest_frozen_page_info.set_next_page_id(
-          oldest_frozen_page_info.next_page_id());
-      oldest_frozen_page_info.set_next_page_id(header_->next_page_id());
-      header_->set_next_page_id(oldest_frozen_page_id);
-      if (oldest_frozen_page_id == header_->latest_frozen_page_id()) {
-        header_->set_latest_frozen_page_id(BLOB_VECTOR_INVALID_PAGE_ID);
-      }
-    }
-  }
-}
-
-}  // namespace alpha
-}  // namespace grnxx

  Deleted: lib/alpha/blob_vector.hpp (+0 -551) 100644
===================================================================
--- lib/alpha/blob_vector.hpp    2012-12-12 14:29:12 +0900 (c4fe8f2)
+++ /dev/null
@@ -1,551 +0,0 @@
-/*
-  Copyright (C) 2012  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_ALPHA_BLOB_VECTOR_HPP
-#define GRNXX_ALPHA_BLOB_VECTOR_HPP
-
-#include "../db/vector.hpp"
-
-namespace grnxx {
-namespace alpha {
-
-using namespace grnxx::db;
-
-constexpr uint64_t BLOB_VECTOR_MAX_ID = uint64_t(1) << 40;
-
-constexpr uint32_t BLOB_VECTOR_INVALID_PAGE_ID = 0xFFFFFFFFU;
-
-constexpr uint64_t BLOB_VECTOR_SMALL_VALUE_MAX_LENGTH  = 7;
-
-constexpr uint64_t BLOB_VECTOR_MEDIUM_VALUE_MIN_LENGTH =
-    BLOB_VECTOR_SMALL_VALUE_MAX_LENGTH + 1;
-constexpr uint64_t BLOB_VECTOR_MEDIUM_VALUE_MAX_LENGTH = 65535;
-
-constexpr uint64_t BLOB_VECTOR_LARGE_VALUE_MIN_LENGTH  =
-    BLOB_VECTOR_MEDIUM_VALUE_MAX_LENGTH + 1;
-
-constexpr uint8_t  BLOB_VECTOR_UNIT_SIZE_BITS  = 3;
-constexpr uint64_t BLOB_VECTOR_UNIT_SIZE       =
-    uint64_t(1) << BLOB_VECTOR_UNIT_SIZE_BITS;
-
-constexpr uint8_t  BLOB_VECTOR_VALUE_STORE_PAGE_SIZE_BITS            = 19;
-constexpr uint8_t  BLOB_VECTOR_VALUE_STORE_TABLE_SIZE_BITS           = 12;
-constexpr uint8_t  BLOB_VECTOR_VALUE_STORE_SECONDARY_TABLE_SIZE_BITS = 16;
-
-constexpr uint64_t BLOB_VECTOR_VALUE_STORE_PAGE_SIZE            =
-    uint64_t(1) << BLOB_VECTOR_VALUE_STORE_PAGE_SIZE_BITS;
-constexpr uint64_t BLOB_VECTOR_VALUE_STORE_TABLE_SIZE           =
-    uint64_t(1) << BLOB_VECTOR_VALUE_STORE_TABLE_SIZE_BITS;
-constexpr uint64_t BLOB_VECTOR_VALUE_STORE_SECONDARY_TABLE_SIZE =
-    uint64_t(1) << BLOB_VECTOR_VALUE_STORE_SECONDARY_TABLE_SIZE_BITS;
-
-extern class BlobVectorCreate {} BLOB_VECTOR_CREATE;
-extern class BlobVectorOpen {} BLOB_VECTOR_OPEN;
-
-class BlobVectorHeader {
- public:
-  explicit BlobVectorHeader(uint32_t table_block_id);
-
-  uint32_t table_block_id() const {
-    return table_block_id_;
-  }
-  uint32_t value_store_block_id() const {
-    return value_store_block_id_;
-  }
-  uint32_t index_store_block_id() const {
-    return index_store_block_id_;
-  }
-  uint32_t next_page_id() const {
-    return next_page_id_;
-  }
-  uint64_t next_value_offset() const {
-    return next_value_offset_;
-  }
-  uint32_t latest_frozen_page_id() const {
-    return latest_frozen_page_id_;
-  }
-  uint32_t latest_large_value_block_id() const {
-    return latest_large_value_block_id_;
-  }
-
-  void set_value_store_block_id(uint32_t value) {
-    value_store_block_id_ = value;
-  }
-  void set_index_store_block_id(uint32_t value) {
-    index_store_block_id_ = value;
-  }
-  void set_next_page_id(uint32_t value) {
-    next_page_id_ = value;
-  }
-  void set_next_value_offset(uint64_t value) {
-    next_value_offset_ = value;
-  }
-  void set_latest_frozen_page_id(uint32_t value) {
-    latest_frozen_page_id_ = value;
-  }
-  void set_latest_large_value_block_id(uint32_t value) {
-    latest_large_value_block_id_ = value;
-  }
-
-  Mutex *mutable_inter_process_mutex() {
-    return &inter_process_mutex_;
-  }
-
-  StringBuilder &write_to(StringBuilder &builder) const;
-
- private:
-  uint32_t table_block_id_;
-  uint32_t value_store_block_id_;
-  uint32_t index_store_block_id_;
-  uint32_t next_page_id_;
-  uint64_t next_value_offset_;
-  uint32_t latest_frozen_page_id_;
-  uint32_t latest_large_value_block_id_;
-  Mutex inter_process_mutex_;
-};
-
-inline StringBuilder &operator<<(StringBuilder &builder,
-                                 const BlobVectorHeader &header) {
-  return header.write_to(builder);
-}
-
-enum BlobVectorValueType : uint8_t {
-  BLOB_VECTOR_NULL   = 0x00,
-  BLOB_VECTOR_SMALL  = 0x10,
-  BLOB_VECTOR_MEDIUM = 0x20,
-  BLOB_VECTOR_LARGE  = 0x30
-};
-
-constexpr uint8_t BLOB_VECTOR_TYPE_MASK = 0x30;
-
-class BlobVectorPageInfo {
- public:
-  BlobVectorPageInfo()
-    : next_page_id_(BLOB_VECTOR_INVALID_PAGE_ID), stamp_(0), reserved_(0) {}
-
-  uint32_t next_page_id() const {
-    return next_page_id_;
-  }
-  uint32_t num_values() const {
-    return num_values_;
-  }
-  uint16_t stamp() const {
-    return reserved_;
-  }
-
-  void set_next_page_id(uint32_t value) {
-    next_page_id_ = value;
-  }
-  void set_num_values(uint32_t value) {
-    num_values_ = value;
-  }
-  void set_stamp(uint16_t value) {
-    stamp_ = value;
-  }
-
- private:
-  union {
-    uint32_t next_page_id_;
-    uint32_t num_values_;
-  };
-  uint16_t stamp_;
-  uint16_t reserved_;
-};
-
-class BlobVectorValueHeader {
- public:
-  uint64_t length() const {
-    return length_;
-  }
-  uint32_t next_value_block_id() const {
-    return next_value_block_id_;
-  }
-  uint32_t prev_value_block_id() const {
-    return prev_value_block_id_;
-  }
-
-  void set_length(uint64_t value) {
-    length_ = value;
-  }
-  void set_next_value_block_id(uint32_t value) {
-    next_value_block_id_ = value;
-  }
-  void set_prev_value_block_id(uint32_t value) {
-    prev_value_block_id_ = value;
-  }
-
- private:
-  uint64_t length_;
-  uint32_t next_value_block_id_;
-  uint32_t prev_value_block_id_;
-};
-
-const uint8_t BLOB_VECTOR_CELL_FLAGS_MASK = 0xF0;
-
-class BlobVectorCell {
- public:
-  BlobVectorCell() = default;
-  explicit BlobVectorCell(std::nullptr_t) : qword_(0) {}
-
-  static BlobVectorCell null_value() {
-    return BlobVectorCell(nullptr);
-  }
-  static BlobVectorCell small_value(uint64_t length) {
-    BlobVectorCell cell(nullptr);
-    cell.bytes_[0] = BLOB_VECTOR_SMALL | static_cast<uint8_t>(length);
-    return cell;
-  }
-  static BlobVectorCell medium_value(uint64_t offset, uint64_t length) {
-    BlobVectorCell cell;
-    cell.bytes_[0] = BLOB_VECTOR_MEDIUM | static_cast<uint8_t>(offset >> 40);
-    cell.bytes_[1] = static_cast<uint8_t>(offset >> 32);
-    cell.words_[1] = static_cast<uint16_t>(length);
-    cell.dwords_[1] = static_cast<uint32_t>(offset);
-    return cell;
-  }
-  static BlobVectorCell large_value(uint32_t block_id) {
-    BlobVectorCell cell(nullptr);
-    cell.bytes_[0] = BLOB_VECTOR_LARGE;
-    cell.dwords_[1] = block_id;
-    return cell;
-  }
-
-  BlobVectorValueType type() const {
-    Flags flags;
-    flags.byte = flags_.byte & BLOB_VECTOR_TYPE_MASK;
-    return flags.type;
-  }
-
-  // Accessors to small values.
-  uint64_t small_length() const {
-    return bytes_[0] & ~BLOB_VECTOR_CELL_FLAGS_MASK;
-  }
-  const void *value() const {
-    return &bytes_[1];
-  }
-  void *value() {
-    return &bytes_[1];
-  }
-
-  // Accessors to medium values.
-  uint64_t medium_length() const {
-    return words_[1];
-  }
-  uint64_t offset() const {
-    return (static_cast<uint64_t>(bytes_[0] &
-                                  ~BLOB_VECTOR_CELL_FLAGS_MASK) << 40) |
-           (static_cast<uint64_t>(bytes_[1]) << 32) | dwords_[1];
-  }
-
-  // Accessors to large values.
-  uint32_t block_id() const {
-    return dwords_[1];
-  }
-
- private:
-  union Flags {
-    uint8_t byte;
-    BlobVectorValueType type;
-  };
-
-  union {
-    Flags flags_;
-    uint8_t bytes_[8];
-    uint16_t words_[4];
-    uint32_t dwords_[2];
-    uint64_t qword_;
-  };
-};
-
-static_assert(sizeof(BlobVectorCell) == sizeof(uint64_t),
-              "sizeof(BlobVectorCell) != sizeof(uint64_t)");
-
-class Blob {
- public:
-  Blob() : address_(nullptr), length_(0), cell_() {}
-  explicit Blob(std::nullptr_t) : address_(nullptr), length_(0), cell_() {}
-  Blob(const void *address, uint64_t length)
-    : address_(address), length_(length), cell_() {}
-  explicit Blob(BlobVectorCell small_value_cell)
-    : address_(), length_(), cell_(small_value_cell) {
-    address_ = cell_.value();
-    length_ = cell_.small_length();
-  }
-
-  // Note: address_ refers to the own value if it is small.
-  Blob(const Blob &rhs) : address_(), length_(rhs.length_), cell_(rhs.cell_) {
-    if (rhs.address() == rhs.cell_.value()) {
-      address_ = cell_.value();
-    } else {
-      address_ = rhs.address_;
-    }
-  }
-  Blob &operator=(const Blob &rhs) {
-    length_ = rhs.length_;
-    cell_ = rhs.cell_;
-    if (rhs.address() == rhs.cell_.value()) {
-      address_ = cell_.value();
-    } else {
-      address_ = rhs.address_;
-    }
-    return *this;
-  }
-
-  Blob &operator=(std::nullptr_t) {
-    return *this = Blob(nullptr);
-  }
-
-  explicit operator bool() const {
-    return static_cast<bool>(address_);
-  }
-
-  const void *address() const {
-    return address_;
-  }
-  uint64_t length() const {
-    return length_;
-  }
-
-  void set_address(const void *value) {
-    address_ = value;
-  }
-  void set_length(uint64_t value) {
-    length_ = value;
-  }
-
- private:
-  const void *address_;
-  uint64_t length_;
-  BlobVectorCell cell_;
-};
-
-class BlobVector;
-
-class BlobRef {
- public:
-  BlobRef(BlobVector *vector, uint64_t id) : vector_(*vector), id_(id) {}
-
-  operator Blob() const {
-    return get();
-  }
-
-  BlobRef &operator=(std::nullptr_t) {
-    set(nullptr);
-    return *this;
-  }
-  BlobRef &operator=(const Blob &value) {
-    set(value);
-    return *this;
-  }
-
-  Blob get() const;
-  void set(std::nullptr_t) {
-    set(Blob(nullptr));
-  }
-  void set(const Blob &value);
-  void set(const void *ptr, uint64_t length) {
-    set(Blob(ptr, length));
-  }
-
-  void append(const Blob &value);
-  void append(const void *ptr, uint64_t length) {
-    append(Blob(ptr, length));
-  }
-  void prepend(const Blob &value);
-  void prepend(const void *ptr, uint64_t length) {
-    prepend(Blob(ptr, length));
-  }
-
- private:
-  BlobVector &vector_;
-  uint64_t id_;
-};
-
-typedef Vector<BlobVectorCell> BlobVectorTable;
-
-typedef Vector<char, BLOB_VECTOR_VALUE_STORE_PAGE_SIZE,
-                     BLOB_VECTOR_VALUE_STORE_TABLE_SIZE,
-                     BLOB_VECTOR_VALUE_STORE_SECONDARY_TABLE_SIZE>
-BlobVectorValueStore;
-
-typedef Vector<BlobVectorPageInfo,
-               BLOB_VECTOR_VALUE_STORE_TABLE_SIZE,
-               BLOB_VECTOR_VALUE_STORE_SECONDARY_TABLE_SIZE>
-BlobVectorIndexStore;
-
-class BlobVectorImpl {
- public:
-  static std::unique_ptr<BlobVectorImpl> create(io::Pool pool);
-  static std::unique_ptr<BlobVectorImpl> open(io::Pool pool,
-                                              uint32_t block_id);
-
-  Blob get_value(uint64_t id) {
-    return get_value(table_[id]);
-  }
-  void set_value(uint64_t id, const Blob &value);
-
-  void append(uint64_t id, const Blob &value);
-  void prepend(uint64_t id, const Blob &value);
-
-  uint32_t block_id() const {
-    return block_info_->id();
-  }
-
-  StringBuilder &write_to(StringBuilder &builder) const;
-
-  static void unlink(io::Pool pool, uint32_t block_id);
-
- private:
-  io::Pool pool_;
-  const io::BlockInfo *block_info_;
-  BlobVectorHeader *header_;
-  Recycler *recycler_;
-  BlobVectorTable table_;
-  BlobVectorValueStore value_store_;
-  BlobVectorIndexStore index_store_;
-  Mutex inter_thread_mutex_;
-
-  BlobVectorImpl();
-
-  void create_vector(io::Pool pool);
-  void open_vector(io::Pool pool, uint32_t block_id);
-
-  Blob get_value(BlobVectorCell cell);
-
-  inline BlobVectorCell create_value(const Blob &value);
-  inline BlobVectorCell join_values(const Blob &lhs, const Blob &rhs);
-
-  inline void *create_small_value(uint64_t length, BlobVectorCell *cell);
-  inline void *create_medium_value(uint64_t length, BlobVectorCell *cell);
-  inline void *create_large_value(uint64_t length, BlobVectorCell *cell);
-
-  void free_value(BlobVectorCell cell);
-
-  void register_large_value(uint32_t block_id,
-                            BlobVectorValueHeader *value_header);
-  void unregister_large_value(uint32_t block_id,
-                              BlobVectorValueHeader *value_header);
-
-  void freeze_page(uint32_t page_id);
-  void unfreeze_oldest_frozen_page();
-
-  Mutex *mutable_inter_thread_mutex() {
-    return &inter_thread_mutex_;
-  }
-  Mutex *mutable_inter_process_mutex() {
-    return header_->mutable_inter_process_mutex();
-  }
-};
-
-inline StringBuilder &operator<<(StringBuilder &builder,
-                                 const BlobVectorImpl &vector) {
-  return vector.write_to(builder);
-}
-
-class BlobVector {
- public:
-  BlobVector() = default;
-  BlobVector(const BlobVectorCreate &, io::Pool pool)
-    : impl_(BlobVectorImpl::create(pool)) {}
-  BlobVector(const BlobVectorOpen &, io::Pool pool, uint32_t block_id)
-    : impl_(BlobVectorImpl::open(pool, block_id)) {}
-
-  explicit operator bool() const {
-    return static_cast<bool>(impl_);
-  }
-
-  void create(io::Pool pool) {
-    *this = BlobVector(BLOB_VECTOR_CREATE, pool);
-  }
-  void open(io::Pool pool, uint32_t block_id) {
-    *this = BlobVector(BLOB_VECTOR_OPEN, pool, block_id);
-  }
-  void close() {
-    *this = BlobVector();
-  }
-
-  BlobRef operator[](uint64_t id) {
-    return BlobRef(this, id);
-  }
-
-  Blob get_value(uint64_t id) {
-    return impl_->get_value(id);
-  }
-  void set_value(uint64_t id, const Blob &value) {
-    impl_->set_value(id, value);
-  }
-
-  void append(uint64_t id, const Blob &value) {
-    impl_->append(id, value);
-  }
-  void prepend(uint64_t id, const Blob &value) {
-    impl_->prepend(id, value);
-  }
-
-  uint32_t block_id() const {
-    return impl_->block_id();
-  }
-
-  void swap(BlobVector &rhs) {
-    impl_.swap(rhs.impl_);
-  }
-
-  StringBuilder &write_to(StringBuilder &builder) const {
-    return impl_ ? impl_->write_to(builder) : (builder << "n/a");
-  }
-
-  static constexpr uint64_t max_id() {
-    return BLOB_VECTOR_MAX_ID;
-  }
-
-  static void unlink(io::Pool pool, uint32_t block_id) {
-    BlobVectorImpl::unlink(pool, block_id);
-  }
-
- private:
-  std::shared_ptr<BlobVectorImpl> impl_;
-};
-
-inline void swap(BlobVector &lhs, BlobVector &rhs) {
-  lhs.swap(rhs);
-}
-
-inline StringBuilder &operator<<(StringBuilder &builder,
-                                 const BlobVector &vector) {
-  return vector.write_to(builder);
-}
-
-inline Blob BlobRef::get() const {
-  return vector_.get_value(id_);
-}
-
-inline void BlobRef::set(const Blob &value) {
-  vector_.set_value(id_, value);
-}
-
-inline void BlobRef::append(const Blob &value) {
-  vector_.append(id_, value);
-}
-
-inline void BlobRef::prepend(const Blob &value) {
-  vector_.prepend(id_, value);
-}
-
-}  // namespace alpha
-}  // namespace grnxx
-
-#endif  // GRNXX_ALPHA_BLOB_VECTOR_HPP

  Modified: lib/db/blob_vector.cpp (+324 -673)
===================================================================
--- lib/db/blob_vector.cpp    2012-12-12 14:29:12 +0900 (09a4875)
+++ lib/db/blob_vector.cpp    2012-12-13 18:21:10 +0900 (a0564f8)
@@ -17,212 +17,164 @@
 */
 #include "blob_vector.hpp"
 
-#include <ostream>
-
 #include "../exception.hpp"
-#include "../logger.hpp"
 #include "../lock.hpp"
+#include "../logger.hpp"
 
 namespace grnxx {
 namespace db {
 
-void BlobVectorHeader::initialize(uint32_t cells_block_id,
-                                  Duration frozen_duration) {
-  std::memset(this, 0, sizeof(*this));
-
-  cells_block_id_ = cells_block_id;
-  frozen_duration_ = frozen_duration;
+BlobVectorCreate BLOB_VECTOR_CREATE;
+BlobVectorOpen BLOB_VECTOR_OPEN;
 
-  for (uint32_t i = 0; i < BLOB_VECTOR_MEDIUM_VALUE_STORES_NUM; ++i) {
-    medium_value_store_block_ids_[i] = io::BLOCK_INVALID_ID;
-  }
+BlobVectorHeader::BlobVectorHeader(uint32_t table_block_id)
+  : table_block_id_(table_block_id),
+    value_store_block_id_(io::BLOCK_INVALID_ID),
+    index_store_block_id_(io::BLOCK_INVALID_ID),
+    next_page_id_(0),
+    next_value_offset_(0),
+    latest_frozen_page_id_(BLOB_VECTOR_INVALID_PAGE_ID),
+    latest_large_value_block_id_(io::BLOCK_INVALID_ID),
+    inter_process_mutex_(MUTEX_UNLOCKED) {}
 
-  large_value_store_block_id_ = io::BLOCK_INVALID_ID;
-  rearmost_large_value_offset_ = BLOB_VECTOR_LARGE_VALUE_INVALID_OFFSET;
-  latest_frozen_large_value_offset_ = BLOB_VECTOR_LARGE_VALUE_INVALID_OFFSET;
-  for (uint32_t i = 0; i < BLOB_VECTOR_LARGE_VALUE_LISTS_NUM; ++i) {
-    oldest_idle_large_value_offsets_[i] =
-        BLOB_VECTOR_LARGE_VALUE_INVALID_OFFSET;
+StringBuilder &BlobVectorHeader::write_to(StringBuilder &builder) const {
+  if (!builder) {
+    return builder;
   }
 
-  inter_process_mutex_.unlock();
-  medium_value_store_mutex_.unlock();
-  large_value_store_mutex_.unlock();
-}
-
-BlobVector::BlobVector()
-  : pool_(),
-    block_info_(nullptr),
-    header_(nullptr),
-    recycler_(nullptr),
-    cells_(),
-    medium_value_stores_(),
-    large_value_store_(),
-    inter_thread_mutex_(MUTEX_UNLOCKED) {}
-
-BlobVector::~BlobVector() {}
-
-BlobVector::BlobVector(BlobVector &&rhs)
-  : pool_(std::move(rhs.pool_)),
-    block_info_(std::move(rhs.block_info_)),
-    header_(std::move(rhs.header_)),
-    recycler_(std::move(rhs.recycler_)),
-    cells_(std::move(rhs.cells_)),
-    medium_value_stores_(std::move(rhs.medium_value_stores_)),
-    large_value_store_(std::move(rhs.large_value_store_)),
-    inter_thread_mutex_(std::move(rhs.inter_thread_mutex_)) {}
-
-BlobVector &BlobVector::operator=(BlobVector &&rhs) {
-  pool_ = std::move(rhs.pool_);
-  block_info_ = std::move(rhs.block_info_);
-  header_ = std::move(rhs.header_);
-  recycler_ = std::move(rhs.recycler_);
-  cells_ = std::move(rhs.cells_);
-  medium_value_stores_ = std::move(rhs.medium_value_stores_);
-  large_value_store_ = std::move(rhs.large_value_store_);
-  inter_thread_mutex_ = std::move(rhs.inter_thread_mutex_);
-  return *this;
+  builder << "{ table_block_id = " << table_block_id_
+          << ", value_store_block_id = " << value_store_block_id_
+          << ", index_store_block_id = " << index_store_block_id_
+          << ", next_page_id = " << next_page_id_
+          << ", next_value_offset = " << next_value_offset_
+          << ", latest_large_value_block_id = " << latest_large_value_block_id_
+          << ", inter_process_mutex = " << inter_process_mutex_;
+  return builder << " }";
 }
 
-void BlobVector::create(io::Pool pool) {
-  if (!pool) {
-    GRNXX_ERROR() << "invalid argument: pool = " << pool;
+std::unique_ptr<BlobVectorImpl> BlobVectorImpl::create(io::Pool pool) {
+  std::unique_ptr<BlobVectorImpl> vector(new (std::nothrow) BlobVectorImpl);
+  if (!vector) {
+    GRNXX_ERROR() << "new grnxx::io::VectorImpl failed";
     GRNXX_THROW();
   }
-
-  BlobVector new_vector;
-  new_vector.create_vector(pool);
-  *this = std::move(new_vector);
+  vector->create_vector(pool);
+  return vector;
 }
 
-void BlobVector::open(io::Pool pool, uint32_t block_id) {
-  if (!pool) {
-    GRNXX_ERROR() << "invalid argument: pool = " << pool;
+std::unique_ptr<BlobVectorImpl> BlobVectorImpl::open(io::Pool pool,
+                                                     uint32_t block_id) {
+  std::unique_ptr<BlobVectorImpl> vector(new (std::nothrow) BlobVectorImpl);
+  if (!vector) {
+    GRNXX_ERROR() << "new grnxx::io::VectorImpl failed";
     GRNXX_THROW();
   }
-
-  BlobVector new_vector;
-  new_vector.open_vector(pool, block_id);
-  *this = std::move(new_vector);
+  vector->open_vector(pool, block_id);
+  return vector;
 }
 
-void BlobVector::close() {
-  if (!is_open()) {
-    GRNXX_ERROR() << "failed to close vector";
-    GRNXX_THROW();
+void BlobVectorImpl::set_value(uint64_t id, const Blob &value) {
+  const BlobVectorCell new_cell = create_value(value);
+  BlobVectorCell old_cell;
+  try {
+    do {
+      old_cell = table_[id];
+    } while (!atomic_compare_and_swap(old_cell, new_cell, &table_[id]));
+  } catch (...) {
+    // The new value is freed on failure.
+    free_value(new_cell);
+    throw;
   }
-  *this = BlobVector();
+  // The old value is freed on success.
+  free_value(old_cell);
 }
 
-const void *BlobVector::get_value_address(uint64_t id, uint64_t *length) {
-  const BlobVectorCell cell = cells_[id];
-  switch (cell.value_type()) {
-    case BLOB_VECTOR_SMALL_VALUE: {
-      if (length) {
-        *length = cell.small_value_cell().length();
-      }
-      // FIXME: the cell might be updated by other threads and processes.
-      return cells_[id].small_value_cell().value();
-    }
-    case BLOB_VECTOR_MEDIUM_VALUE: {
-      if (length) {
-        *length = cell.medium_value_cell().length();
-      }
-      const uint8_t store_id = cell.medium_value_cell().store_id();
-      const uint64_t offset = cell.medium_value_cell().offset();
-      BlobVectorMediumValueStore &store = medium_value_stores_[store_id];
-      if (!store) {
-        open_medium_value_store(store_id);
-      }
-      return &store[offset];
-    }
-    case BLOB_VECTOR_LARGE_VALUE: {
-      if (length) {
-        *length = cell.large_value_cell().length();
-      }
-      const uint64_t offset = cell.large_value_cell().offset();
-      if (!large_value_store_) {
-        open_large_value_store();
-      }
-      return get_large_value_header(offset)->value();
-    }
-    case BLOB_VECTOR_HUGE_VALUE: {
-      void *block_address =
-          pool_.get_block_address(cell.huge_value_cell().block_id());
-      if (length) {
-        *length = *static_cast<uint64_t *>(block_address);
-      }
-      return static_cast<uint64_t *>(block_address) + 1;
-    }
-    default: {
-      GRNXX_ERROR() << "invalid value type";
-      GRNXX_THROW();
+void BlobVectorImpl::append(uint64_t id, const Blob &value) {
+  if (!value || (value.length() == 0)) {
+    return;
+  }
+
+  for ( ; ; ) {
+    const BlobVectorCell old_cell = table_[id];
+    const Blob old_value = get_value(old_cell);
+    const BlobVectorCell new_cell = join_values(old_value, value);
+    if (atomic_compare_and_swap(old_cell, new_cell, &table_[id])) {
+      // The old value is freed on success.
+      free_value(old_cell);
+      break;
+    } else {
+      // The new value is freed on failure.
+      free_value(new_cell);
     }
   }
 }
 
-void BlobVector::set_value(uint64_t id, const void *ptr, uint64_t length) {
-  if (!ptr && (length != 0)) {
-    GRNXX_ERROR() << "invalid arguments: ptr = " << ptr
-                  << ", length = " << length;
-    GRNXX_THROW();
+void BlobVectorImpl::prepend(uint64_t id, const Blob &value) {
+  if (!value || (value.length() == 0)) {
+    return;
   }
 
-  BlobVectorCell new_cell;
-  if (length <= BLOB_VECTOR_SMALL_VALUE_LENGTH_MAX) {
-    new_cell = create_small_value_cell(ptr, length);
-  } else if (length <= BLOB_VECTOR_MEDIUM_VALUE_LENGTH_MAX) {
-    new_cell = create_medium_value_cell(ptr, length);
-  } else if (length <= BLOB_VECTOR_LARGE_VALUE_LENGTH_MAX) {
-    new_cell = create_large_value_cell(ptr, length);
-  } else {
-    new_cell = create_huge_value_cell(ptr, length);
+  for ( ; ; ) {
+    const BlobVectorCell old_cell = table_[id];
+    const Blob old_value = get_value(old_cell);
+    const BlobVectorCell new_cell = join_values(value, old_value);
+    if (atomic_compare_and_swap(old_cell, new_cell, &table_[id])) {
+      // The old value is freed on success.
+      free_value(old_cell);
+      break;
+    } else {
+      // The new value is freed on failure.
+      free_value(new_cell);
+    }
   }
+}
 
-  // The old cell is replaced with the new one.
-  // Then, the resources allocated to the old cell are freed.
-  BlobVectorCell old_cell;
-  try {
-    do {
-      old_cell = cells_[id];
-    } while (!atomic_compare_and_swap(old_cell, new_cell, &cells_[id]));
-  } catch (...) {
-    free_value(new_cell);
-    throw;
+StringBuilder &BlobVectorImpl::write_to(StringBuilder &builder) const {
+  if (!builder) {
+    return builder;
   }
-  free_value(old_cell);
-}
 
-void BlobVector::swap(BlobVector &rhs) {
-  using std::swap;
-  swap(pool_, rhs.pool_);
-  swap(block_info_, rhs.block_info_);
-  swap(header_, rhs.header_);
-  swap(recycler_, rhs.recycler_);
-  swap(cells_, rhs.cells_);
-  swap(medium_value_stores_, rhs.medium_value_stores_);
-  swap(large_value_store_, rhs.large_value_store_);
-  swap(inter_thread_mutex_, rhs.inter_thread_mutex_);
+  builder << "{ pool = " << pool_.path()
+          << ", block_info = " << *block_info_
+          << ", header = " << *header_
+          << ", inter_thread_mutex = " << inter_thread_mutex_;
+  return builder << " }";
 }
 
-void BlobVector::unlink(io::Pool pool, uint32_t block_id) {
-  if (!pool) {
-    GRNXX_ERROR() << "invalid argument: pool = " << pool;
-    GRNXX_THROW();
-  }
-
-  BlobVector vector;
-  vector.open(pool, block_id);
+void BlobVectorImpl::unlink(io::Pool pool, uint32_t block_id) {
+  std::unique_ptr<BlobVectorImpl> vector =
+      BlobVectorImpl::open(pool, block_id);
 
-  // TODO
+  if (vector->header_->latest_large_value_block_id() != io::BLOCK_INVALID_ID) {
+    uint32_t block_id = vector->header_->latest_large_value_block_id();
+    do {
+      auto value_header = static_cast<const BlobVectorValueHeader *>(
+          pool.get_block_address(block_id));
+      const uint32_t prev_block_id = value_header->prev_value_block_id();
+      pool.free_block(block_id);
+      block_id = prev_block_id;
+    } while (block_id != vector->header_->latest_large_value_block_id());
+  }
+  BlobVectorTable::unlink(pool, vector->header_->table_block_id());
+  pool.free_block(vector->block_info_->id());
 }
 
-void BlobVector::create_vector(io::Pool pool) {
+BlobVectorImpl::BlobVectorImpl()
+  : pool_(),
+    block_info_(nullptr),
+    header_(nullptr),
+    recycler_(nullptr),
+    table_(),
+    value_store_(),
+    index_store_(),
+    inter_thread_mutex_(MUTEX_UNLOCKED) {}
+
+void BlobVectorImpl::create_vector(io::Pool pool) {
   pool_ = pool;
   block_info_ = pool.create_block(sizeof(BlobVectorHeader));
 
   try {
-    cells_.create(pool_, BlobVectorCell());
+    table_.create(pool_, BlobVectorCell::null_value());
   } catch (...) {
     pool_.free_block(*block_info_);
     throw;
@@ -230,12 +182,12 @@ void BlobVector::create_vector(io::Pool pool) {
 
   void * const block_address = pool_.get_block_address(*block_info_);
   header_ = static_cast<BlobVectorHeader *>(block_address);
-  header_->initialize(cells_.block_id(), pool.options().frozen_duration());
+  *header_ = BlobVectorHeader(table_.block_id());
 
   recycler_ = pool.mutable_recycler();
 }
 
-void BlobVector::open_vector(io::Pool pool, uint32_t block_id) {
+void BlobVectorImpl::open_vector(io::Pool pool, uint32_t block_id) {
   pool_ = pool;
   block_info_ = pool.get_block_info(block_id);
   if (block_info_->size() < sizeof(BlobVectorHeader)) {
@@ -247,571 +199,270 @@ void BlobVector::open_vector(io::Pool pool, uint32_t block_id) {
   void * const block_address = pool_.get_block_address(*block_info_);
   header_ = static_cast<BlobVectorHeader *>(block_address);
 
-  // TODO: check the header!
+  // TODO: Check the format!
 
   recycler_ = pool.mutable_recycler();
 
   // Open the core table.
-  cells_.open(pool, header_->cells_block_id());
+  table_.open(pool, header_->table_block_id());
 }
 
-BlobVectorSmallValueCell BlobVector::create_small_value_cell(
-    const void *ptr, uint64_t length) {
-  return BlobVectorSmallValueCell(ptr, length);
-}
-
-BlobVectorMediumValueCell BlobVector::create_medium_value_cell(
-    const void *ptr, uint64_t length) {
-  const uint8_t store_id = get_store_id(length);
-  BlobVectorMediumValueStore &store = medium_value_stores_[store_id];
-  if (!store) {
-    open_medium_value_store(store_id);
-  }
-
-  uint64_t offset;
-  {
-    Lock lock(header_->mutable_medium_value_store_mutex());
-
-    // TODO: Reuse.
-
-    offset = header_->medium_value_store_next_offsets(store_id);
-    if (offset > store.max_id()) {
-      GRNXX_ERROR() << "store is full: offset = " << offset
-                    << ", max_id = " << store.max_id();
-      GRNXX_THROW();
+Blob BlobVectorImpl::get_value(BlobVectorCell cell) {
+  switch (cell.type()) {
+    case BLOB_VECTOR_NULL: {
+      return Blob(nullptr);
     }
-    header_->set_medium_value_store_next_offsets(store_id,
-        offset + (1 << (store_id + BLOB_VECTOR_MEDIUM_VALUE_UNIT_SIZE_BITS)));
-  }
-
-  std::memcpy(&store[offset], ptr, length);
-  return BlobVectorMediumValueCell(store_id, offset, length);
-}
-
-BlobVectorLargeValueCell BlobVector::create_large_value_cell(
-    const void *ptr, uint64_t length) {
-  typedef BlobVectorLargeValueHeader ValueHeader;
-
-  if (!large_value_store_) {
-    open_large_value_store();
-  }
-
-  const uint64_t capacity = ~(BLOB_VECTOR_LARGE_VALUE_UNIT_SIZE - 1) &
-      (length + (BLOB_VECTOR_LARGE_VALUE_UNIT_SIZE - 1));
-  const uint64_t required_size = sizeof(ValueHeader) + capacity;
-
-  uint64_t offset;
-  {
-    Lock lock(header_->mutable_large_value_store_mutex());
-
-    unfreeze_frozen_large_values();
-
-    uint8_t list_id = get_list_id(capacity - 1);
-    for (++list_id ; list_id < BLOB_VECTOR_LARGE_VALUE_LISTS_NUM; ++list_id) {
-      if (header_->oldest_idle_large_value_offsets(list_id) !=
-          BLOB_VECTOR_LARGE_VALUE_INVALID_OFFSET) {
-        offset = header_->oldest_idle_large_value_offsets(list_id);
-        break;
-      }
+    case BLOB_VECTOR_SMALL: {
+      return Blob(cell);
     }
-
-    if (list_id < BLOB_VECTOR_LARGE_VALUE_LISTS_NUM) {
-      auto header = get_large_value_header(offset);
-      if (header->capacity() > capacity) {
-        divide_idle_large_value(offset, capacity);
-      } else {
-        unregister_idle_large_value(offset);
-        header->set_type(BLOB_VECTOR_ACTIVE_VALUE);
-      }
-    } else {
-      BlobVectorLargeValueFlags flags = BlobVectorLargeValueFlags::none();
-      uint64_t prev_capacity = 0;
-
-      const uint64_t prev_offset = header_->rearmost_large_value_offset();
-      ValueHeader *prev_header = nullptr;
-      if (prev_offset == BLOB_VECTOR_LARGE_VALUE_INVALID_OFFSET) {
-        offset = 0;
-      } else {
-        prev_header = get_large_value_header(prev_offset);
-        offset = prev_offset + prev_header->capacity() + sizeof(ValueHeader);
-
-        const uint64_t size_left = BLOB_VECTOR_LARGE_VALUE_STORE_PAGE_SIZE
-            - (offset & (BLOB_VECTOR_LARGE_VALUE_STORE_PAGE_SIZE - 1));
-        if (size_left < required_size) {
-          auto header = get_large_value_header(offset);
-          header->initialize(BLOB_VECTOR_IDLE_VALUE,
-                             BLOB_VECTOR_LARGE_VALUE_HAS_PREV,
-                             size_left - sizeof(ValueHeader),
-                             prev_header->capacity());
-          prev_header->set_flags(
-              prev_header->flags() | BLOB_VECTOR_LARGE_VALUE_HAS_NEXT);
-          header_->set_rearmost_large_value_offset(offset);
-          register_idle_large_value(offset);
-          offset += size_left;
-        } else if (size_left < BLOB_VECTOR_LARGE_VALUE_STORE_PAGE_SIZE) {
-          flags |= BLOB_VECTOR_LARGE_VALUE_HAS_PREV;
-          prev_capacity = prev_header->capacity();
+    case BLOB_VECTOR_MEDIUM: {
+      if (!value_store_) {
+        Lock lock(mutable_inter_thread_mutex());
+        if (!value_store_) {
+          value_store_.open(pool_, header_->value_store_block_id());
         }
       }
-
-      auto header = get_large_value_header(offset);
-      header->initialize(BLOB_VECTOR_ACTIVE_VALUE,
-                         flags, capacity, prev_capacity);
-      if (flags & BLOB_VECTOR_LARGE_VALUE_HAS_PREV) {
-        prev_header->set_flags(
-            prev_header->flags() | BLOB_VECTOR_LARGE_VALUE_HAS_NEXT);
-      }
-      header_->set_rearmost_large_value_offset(offset);
-    }
-  }
-
-  std::memcpy(get_large_value_header(offset)->value(), ptr, length);
-  return BlobVectorLargeValueCell(offset, length);
-}
-
-BlobVectorHugeValueCell BlobVector::create_huge_value_cell(
-    const void *ptr, uint64_t length) {
-  const io::BlockInfo *block_info =
-      pool_.create_block(sizeof(uint64_t) + length);
-  void * const block_address = pool_.get_block_address(*block_info);
-  *static_cast<uint64_t *>(block_address) = length;
-  std::memcpy(static_cast<uint64_t *>(block_address) + 1, ptr, length);
-  return BlobVectorHugeValueCell(block_info->id());
-}
-
-void BlobVector::free_value(BlobVectorCell cell) {
-  switch (cell.value_type()) {
-    case BLOB_VECTOR_SMALL_VALUE: {
-      // Nothing to do.
-      break;
-    }
-    case BLOB_VECTOR_MEDIUM_VALUE: {
-      // TODO
-      break;
-    }
-    case BLOB_VECTOR_LARGE_VALUE: {
-      typedef BlobVectorLargeValueHeader ValueHeader;
-      Lock lock(header_->mutable_large_value_store_mutex());
-      if (!large_value_store_) {
-        open_large_value_store();
-      }
-      const uint64_t offset = cell.large_value_cell().offset();
-      ValueHeader * const header = get_large_value_header(offset);
-      header->set_frozen_stamp(recycler_->stamp());
-      header->set_type(BLOB_VECTOR_FROZEN_VALUE);
-      const uint64_t latest_offset =
-          header_->latest_frozen_large_value_offset();
-      if (latest_offset == BLOB_VECTOR_LARGE_VALUE_INVALID_OFFSET) {
-        header->set_next_offset(offset);
-      } else {
-        ValueHeader * const latest_header =
-            get_large_value_header(header_->latest_frozen_large_value_offset());
-        header->set_next_offset(latest_header->next_offset());
-        latest_header->set_next_offset(offset);
-      }
-      header_->set_latest_frozen_large_value_offset(offset);
-      break;
+      return Blob(&value_store_[cell.offset()], cell.medium_length());
     }
-    case BLOB_VECTOR_HUGE_VALUE: {
-      pool_.free_block(cell.huge_value_cell().block_id());
-      break;
+    case BLOB_VECTOR_LARGE: {
+      const auto value_header = static_cast<const BlobVectorValueHeader *>(
+          pool_.get_block_address(cell.block_id()));
+      return Blob(value_header + 1, value_header->length());
     }
-  }
-}
-
-void BlobVector::open_medium_value_store(uint8_t store_id) {
-  Lock inter_thread_lock(&inter_thread_mutex_);
-  BlobVectorMediumValueStore &store = medium_value_stores_[store_id];
-  if (!store) {
-    if (header_->medium_value_store_block_ids(store_id) ==
-        io::BLOCK_INVALID_ID) {
-      Lock inter_process_lock(header_->mutable_inter_process_mutex());
-      if (header_->medium_value_store_block_ids(store_id) ==
-          io::BLOCK_INVALID_ID) {
-        store.create(pool_);
-        header_->set_medium_value_store_block_ids(store_id, store.block_id());
-      }
-    }
-    if (!store) {
-      store.open(pool_, header_->medium_value_store_block_ids(store_id));
-    }
-  }
-}
-
-void BlobVector::open_large_value_store() {
-  Lock inter_thread_lock(&inter_thread_mutex_);
-  if (!large_value_store_) {
-    if (header_->large_value_store_block_id() == io::BLOCK_INVALID_ID) {
-      Lock inter_process_lock(header_->mutable_inter_process_mutex());
-      if (header_->large_value_store_block_id() == io::BLOCK_INVALID_ID) {
-        large_value_store_.create(pool_);
-        header_->set_large_value_store_block_id(large_value_store_.block_id());
-      }
-    }
-    if (!large_value_store_) {
-      large_value_store_.open(pool_, header_->large_value_store_block_id());
-    }
-  }
-}
-
-void BlobVector::unfreeze_frozen_large_values() {
-  if (header_->latest_frozen_large_value_offset() !=
-      BLOB_VECTOR_LARGE_VALUE_INVALID_OFFSET) {
-    auto latest_frozen_value_header =
-        get_large_value_header(header_->latest_frozen_large_value_offset());
-    for (int i = 0; i < 5; ++i) {
-      const uint64_t oldest_frozen_value_offset =
-          latest_frozen_value_header->next_offset();
-      auto oldest_frozen_value_header =
-          get_large_value_header(oldest_frozen_value_offset);
-      if (!recycler_->check(oldest_frozen_value_header->frozen_stamp())) {
-        break;
-      }
-      latest_frozen_value_header->set_next_offset(
-          oldest_frozen_value_header->next_offset());
-      oldest_frozen_value_header->set_type(BLOB_VECTOR_IDLE_VALUE);
-      register_idle_large_value(oldest_frozen_value_offset);
-      merge_idle_large_values(oldest_frozen_value_offset);
-      if (latest_frozen_value_header == oldest_frozen_value_header) {
-        header_->set_latest_frozen_large_value_offset(
-            BLOB_VECTOR_LARGE_VALUE_INVALID_OFFSET);
-        break;
-      }
-    }
-  }
-}
-
-void BlobVector::divide_idle_large_value(uint64_t offset, uint64_t capacity) {
-  typedef BlobVectorLargeValueHeader ValueHeader;
-
-  unregister_idle_large_value(offset);
-
-  const uint64_t next_offset = offset + capacity + sizeof(ValueHeader);
-  auto header = get_large_value_header(offset);
-  auto next_header = get_large_value_header(next_offset);
-  next_header->initialize(BLOB_VECTOR_IDLE_VALUE,
-                          BLOB_VECTOR_LARGE_VALUE_HAS_PREV |
-                          (header->flags() & BLOB_VECTOR_LARGE_VALUE_HAS_NEXT),
-                          header->capacity() - capacity - sizeof(ValueHeader),
-                          capacity);
-
-  if (header->flags() & BLOB_VECTOR_LARGE_VALUE_HAS_NEXT) {
-    const uint64_t next_next_offset =
-        offset + header->capacity() + sizeof(ValueHeader);
-    auto next_next_header = get_large_value_header(next_next_offset);
-    next_next_header->set_prev_capacity(next_header->capacity());
-  }
-
-  header->set_type(BLOB_VECTOR_ACTIVE_VALUE);
-  header->set_flags(header->flags() | BLOB_VECTOR_LARGE_VALUE_HAS_NEXT);
-  header->set_capacity(capacity);
-
-  register_idle_large_value(next_offset);
-
-  if (offset == header_->rearmost_large_value_offset()) {
-    header_->set_rearmost_large_value_offset(next_offset);
-  }
-}
-
-void BlobVector::merge_idle_large_values(uint64_t offset) {
-  typedef BlobVectorLargeValueHeader ValueHeader;
-
-  auto header = get_large_value_header(offset);
-  if (header->flags() & BLOB_VECTOR_LARGE_VALUE_HAS_NEXT) {
-    const uint64_t next_offset =
-        offset + header->capacity() + sizeof(ValueHeader);
-    auto next_header = get_large_value_header(next_offset);
-    if (next_header->type() == BLOB_VECTOR_IDLE_VALUE) {
-      merge_idle_large_values(offset, next_offset);
-    }
-  }
-  if (header->flags() & BLOB_VECTOR_LARGE_VALUE_HAS_PREV) {
-    const uint64_t prev_offset =
-        offset - header->prev_capacity() - sizeof(ValueHeader);
-    auto prev_header = get_large_value_header(prev_offset);
-    if (prev_header->type() == BLOB_VECTOR_IDLE_VALUE) {
-      merge_idle_large_values(prev_offset, offset);
+    default: {
+      GRNXX_ERROR() << "invalid value type";
+      GRNXX_THROW();
     }
   }
 }
 
-void BlobVector::merge_idle_large_values(uint64_t offset, uint64_t next_offset) {
-  typedef BlobVectorLargeValueHeader ValueHeader;
-
-  unregister_idle_large_value(offset);
-  unregister_idle_large_value(next_offset);
-
-  auto header = get_large_value_header(offset);
-  auto next_header = get_large_value_header(next_offset);
-
-  header->set_flags((header->flags() & BLOB_VECTOR_LARGE_VALUE_HAS_PREV) |
-                    (next_header->flags() & BLOB_VECTOR_LARGE_VALUE_HAS_NEXT));
-  header->set_capacity(
-      header->capacity() + next_header->capacity() + sizeof(ValueHeader));
-
-  if (next_header->flags() & BLOB_VECTOR_LARGE_VALUE_HAS_NEXT) {
-    const uint64_t next_next_offset =
-        next_offset + next_header->capacity() + sizeof(ValueHeader);
-    auto next_next_header = get_large_value_header(next_next_offset);
-    next_next_header->set_prev_capacity(header->capacity());
+BlobVectorCell BlobVectorImpl::create_value(const Blob &value) {
+  if (!value) {
+    return BlobVectorCell::null_value();
   }
 
-  register_idle_large_value(offset);
-
-  if (next_offset == header_->rearmost_large_value_offset()) {
-    header_->set_rearmost_large_value_offset(offset);
-  }
-}
-
-void BlobVector::register_idle_large_value(uint64_t offset) {
-  auto header = get_large_value_header(offset);
-  if (header->capacity() < BLOB_VECTOR_LARGE_VALUE_LENGTH_MIN) {
-    return;
-  }
-  const uint8_t list_id = get_list_id(header->capacity());
-  const uint64_t oldest_idle_value_offset =
-      header_->oldest_idle_large_value_offsets(list_id);
-  if (oldest_idle_value_offset == BLOB_VECTOR_LARGE_VALUE_INVALID_OFFSET) {
-    header->set_next_offset(offset);
-    header->set_prev_offset(offset);
-    header_->set_oldest_idle_large_value_offsets(list_id, offset);
+  BlobVectorCell cell;
+  void *address;
+  if (value.length() < BLOB_VECTOR_MEDIUM_VALUE_MIN_LENGTH) {
+    address = create_small_value(value.length(), &cell);
+  } else if (value.length() < BLOB_VECTOR_LARGE_VALUE_MIN_LENGTH) {
+    address = create_medium_value(value.length(), &cell);
   } else {
-    auto next_header = get_large_value_header(oldest_idle_value_offset);
-    auto prev_header = get_large_value_header(next_header->prev_offset());
-    header->set_next_offset(oldest_idle_value_offset);
-    header->set_prev_offset(next_header->prev_offset());
-    prev_header->set_next_offset(offset);
-    next_header->set_prev_offset(offset);
+    address = create_large_value(value.length(), &cell);
   }
+  std::memcpy(address, value.address(), value.length());
+  return cell;
 }
 
-void BlobVector::unregister_idle_large_value(uint64_t offset) {
-  auto header = get_large_value_header(offset);
-  if (header->capacity() < BLOB_VECTOR_LARGE_VALUE_LENGTH_MIN) {
-    return;
-  }
-  const uint8_t list_id = get_list_id(header->capacity());
-  if (offset == header->next_offset()) {
-    header_->set_oldest_idle_large_value_offsets(
-        list_id, BLOB_VECTOR_LARGE_VALUE_INVALID_OFFSET);
+BlobVectorCell BlobVectorImpl::join_values(const Blob &lhs, const Blob &rhs) {
+  const uint64_t length = lhs.length() + rhs.length();
+  BlobVectorCell cell;
+  void *address;
+  if (length < BLOB_VECTOR_MEDIUM_VALUE_MIN_LENGTH) {
+    address = create_small_value(length, &cell);
+  } else if (length < BLOB_VECTOR_LARGE_VALUE_MIN_LENGTH) {
+    address = create_medium_value(length, &cell);
   } else {
-    auto next_header = get_large_value_header(header->next_offset());
-    auto prev_header = get_large_value_header(header->prev_offset());
-    next_header->set_prev_offset(header->prev_offset());
-    prev_header->set_next_offset(header->next_offset());
-    if (offset == header_->oldest_idle_large_value_offsets(list_id)) {
-      header_->set_oldest_idle_large_value_offsets(
-          list_id, header->next_offset());
-    }
+    address = create_large_value(length, &cell);
   }
+  std::memcpy(address, lhs.address(), lhs.length());
+  std::memcpy(static_cast<char *>(address) + lhs.length(),
+              rhs.address(), rhs.length());
+  return cell;
 }
 
-StringBuilder &operator<<(StringBuilder &builder, BlobVectorLargeValueType type) {
-  switch (type) {
-    case BLOB_VECTOR_ACTIVE_VALUE: {
-      return builder << "BLOB_VECTOR_ACTIVE_VALUE";
-    }
-    case BLOB_VECTOR_FROZEN_VALUE: {
-      return builder << "BLOB_VECTOR_FROZEN_VALUE";
-    }
-    case BLOB_VECTOR_IDLE_VALUE: {
-      return builder << "BLOB_VECTOR_IDLE_VALUE";
-    }
-    default: {
-      return builder << "n/a";
-    }
- }
+void *BlobVectorImpl::create_small_value(uint64_t length,
+                                         BlobVectorCell *cell) {
+  *cell = BlobVectorCell::small_value(length);
+  return cell->value();
 }
 
-StringBuilder &operator<<(StringBuilder &builder, BlobVectorLargeValueFlags flags) {
-  if (!builder) {
-    return builder;
-  }
+void *BlobVectorImpl::create_medium_value(uint64_t length,
+                                          BlobVectorCell *cell) {
+  Lock lock(mutable_inter_thread_mutex());
 
-  if (flags) {
-    bool is_first = true;
-    if (flags & BLOB_VECTOR_LARGE_VALUE_HAS_NEXT) {
-      builder << "BLOB_VECTOR_LARGE_VALUE_HAS_NEXT";
-      is_first = false;
-    }
-    if (flags & BLOB_VECTOR_LARGE_VALUE_HAS_PREV) {
-      if (!is_first) {
-        builder << " | ";
+  if (!value_store_) {
+    if (header_->value_store_block_id() == io::BLOCK_INVALID_ID) {
+      Lock lock(mutable_inter_process_mutex());
+      if (header_->value_store_block_id() == io::BLOCK_INVALID_ID) {
+        value_store_.create(pool_);
+        header_->set_value_store_block_id(value_store_.block_id());
       }
-      builder << "BLOB_VECTOR_LARGE_VALUE_HAS_PREV";
     }
-    return builder;
-  } else {
-    return builder << "0";
+    if (!value_store_) {
+      value_store_.open(pool_, header_->value_store_block_id());
+    }
   }
-}
 
-StringBuilder &operator<<(StringBuilder &builder, BlobVectorValueType type) {
-  switch (type) {
-    case BLOB_VECTOR_SMALL_VALUE: {
-      return builder << "BLOB_VECTOR_SMALL_VALUE";
-    }
-    case BLOB_VECTOR_MEDIUM_VALUE: {
-      return builder << "BLOB_VECTOR_MEDIUM_VALUE";
-    }
-    case BLOB_VECTOR_LARGE_VALUE: {
-      return builder << "BLOB_VECTOR_LARGE_VALUE";
-    }
-    case BLOB_VECTOR_HUGE_VALUE: {
-      return builder << "BLOB_VECTOR_HUGE_VALUE";
+  if (!index_store_) {
+    if (header_->index_store_block_id() == io::BLOCK_INVALID_ID) {
+      Lock lock(mutable_inter_process_mutex());
+      if (header_->index_store_block_id() == io::BLOCK_INVALID_ID) {
+        index_store_.create(pool_, BlobVectorPageInfo());
+        header_->set_index_store_block_id(index_store_.block_id());
+      }
     }
-    default: {
-      return builder << "n/a";
+    if (!index_store_) {
+      index_store_.open(pool_, header_->index_store_block_id());
     }
   }
-}
 
-StringBuilder &operator<<(StringBuilder &builder, BlobVectorCellFlags flags) {
-  BlobVectorCellFlags clone = flags;
-  clone.flags &= BLOB_VECTOR_VALUE_TYPE_MASK;
-  return builder << clone.value_type;
-}
+  // Unfreeze the oldest frozen page for reuse.
+  unfreeze_oldest_frozen_page();
 
-StringBuilder &BlobVectorHeader::write_to(StringBuilder &builder) const {
-  if (!builder) {
-    return builder;
-  }
+  uint64_t offset = header_->next_value_offset();
+  const uint64_t offset_in_page =
+      ((offset - 1) & (BLOB_VECTOR_VALUE_STORE_PAGE_SIZE - 1)) + 1;
+  const uint64_t size_left_in_page =
+      BLOB_VECTOR_VALUE_STORE_PAGE_SIZE - offset_in_page;
 
-  builder << "{ cells_block_id = " << cells_block_id_;
-
-  builder << ", medium_value_store_block_ids = ";
-  bool is_empty = true;
-  for (uint32_t i = 0; i < BLOB_VECTOR_MEDIUM_VALUE_STORES_NUM; ++i) {
-    if (medium_value_store_block_ids_[i] != io::BLOCK_INVALID_ID) {
-      if (is_empty) {
-        builder << "{ ";
-        is_empty = false;
-      } else {
-        builder << ", ";
+  // Reserve a new page if there is not enough space in the current page.
+  if (length > size_left_in_page) {
+    if (offset != 0) {
+      // Freeze the current page if it is empty.
+      const uint32_t page_id = static_cast<uint32_t>(
+          (offset - 1) >> BLOB_VECTOR_VALUE_STORE_PAGE_SIZE_BITS);
+      if (index_store_[page_id].num_values() == 0) {
+        freeze_page(page_id);
       }
-      builder << '[' << i << "] = " << medium_value_store_block_ids_[i];
     }
-  }
-  builder << (is_empty ? "{}" : " }");
-
-  builder << ", medium_value_store_next_offsets = ";
-  is_empty = true;
-  for (uint32_t i = 0; i < BLOB_VECTOR_MEDIUM_VALUE_STORES_NUM; ++i) {
-    if (medium_value_store_next_offsets_[i] != 0) {
-      if (is_empty) {
-        builder << "{ ";
-        is_empty = false;
-      } else {
-        builder << ", ";
-      }
-      builder << '[' << i << "] = " << medium_value_store_next_offsets_[i];
-    }
-  }
-  builder << (is_empty ? "{}" : " }");
-
-  builder << ", large_value_store_block_id = " << large_value_store_block_id_
-          << ", rearmost_large_value_offset = " << rearmost_large_value_offset_
-          << ", latest_frozen_large_value_offset = "
-          << latest_frozen_large_value_offset_;
-
-  builder << ", oldest_idle_large_value_offsets = ";
-  is_empty = true;
-  for (uint32_t i = 0; i < BLOB_VECTOR_LARGE_VALUE_LISTS_NUM; ++i) {
-    if (oldest_idle_large_value_offsets_[i] !=
-        BLOB_VECTOR_LARGE_VALUE_INVALID_OFFSET) {
-      if (is_empty) {
-        builder << "{ ";
-        is_empty = false;
-      } else {
-        builder << ", ";
-      }
-      builder << '[' << i << "] = " << oldest_idle_large_value_offsets_[i];
-    }
-  }
-  builder << (is_empty ? "{}" : " }");
-
-  builder << ", inter_process_mutex = " << inter_process_mutex_
-          << ", large_value_store_mutex = " << large_value_store_mutex_;
-  return builder << " }";
-}
 
-StringBuilder &BlobVectorLargeValueHeader::write_to(StringBuilder &builder) const {
-  if (!builder) {
-    return builder;
+    const uint32_t page_id = header_->next_page_id();
+    offset = static_cast<uint64_t>(
+        page_id << BLOB_VECTOR_VALUE_STORE_PAGE_SIZE_BITS);
+    if (index_store_[page_id].next_page_id() != BLOB_VECTOR_INVALID_PAGE_ID) {
+      header_->set_next_page_id(index_store_[page_id].next_page_id());
+    } else {
+      header_->set_next_page_id(page_id + 1);
+    }
+    index_store_[page_id].set_num_values(0);
   }
+  header_->set_next_value_offset(offset + length);
 
-  builder << "{ type = " << type()
-          << ", flags = " << flags()
-          << ", capacity = " << capacity()
-          << ", prev_capacity = " << prev_capacity();
+  const uint32_t page_id = static_cast<uint32_t>(
+      offset >> BLOB_VECTOR_VALUE_STORE_PAGE_SIZE_BITS);
+  index_store_[page_id].set_num_values(index_store_[page_id].num_values() + 1);
 
-  switch (type()) {
-    case BLOB_VECTOR_ACTIVE_VALUE: {
-      break;
-    }
-    case BLOB_VECTOR_FROZEN_VALUE: {
-      builder << ", next_offset = " << next_offset()
-              << ", frozen_stamp = " << frozen_stamp();
-      break;
-    }
-    case BLOB_VECTOR_IDLE_VALUE: {
-      builder << ", next_offset = " << next_offset()
-              << ", prev_offset = " << prev_offset();
-      break;
-    }
-  }
-  return builder << " }";
+  *cell = BlobVectorCell::medium_value(offset, length);
+  return &value_store_[offset];
 }
 
-StringBuilder &BlobVectorCell::write_to(StringBuilder &builder) const {
-  if (!builder) {
-    return builder;
-  }
-
-  builder << "{ value_type = " << value_type();
-  switch (value_type()) {
-    case BLOB_VECTOR_SMALL_VALUE: {
-      builder << ", length = " << small_value_cell().length();
-      break;
-    }
-    case BLOB_VECTOR_MEDIUM_VALUE: {
-      builder << ", store_id = " << medium_value_cell().store_id()
-              << ", capacity = " << medium_value_cell().capacity()
-              << ", length = " << medium_value_cell().length()
-              << ", offset = " << medium_value_cell().offset();
+void *BlobVectorImpl::create_large_value(uint64_t length,
+                                         BlobVectorCell *cell) {
+  const io::BlockInfo *block_info =
+      pool_.create_block(sizeof(BlobVectorValueHeader) + length);
+  auto value_header = static_cast<BlobVectorValueHeader *>(
+      pool_.get_block_address(*block_info));
+  value_header->set_length(length);
+  register_large_value(block_info->id(), value_header);
+  *cell = BlobVectorCell::large_value(block_info->id());
+  return value_header + 1;
+}
+
+void BlobVectorImpl::free_value(BlobVectorCell cell) {
+  switch (cell.type()) {
+    case BLOB_VECTOR_NULL:
+    case BLOB_VECTOR_SMALL: {
       break;
     }
-    case BLOB_VECTOR_LARGE_VALUE: {
-      builder << ", length = " << large_value_cell().length()
-              << ", offset = " << large_value_cell().offset();
+    case BLOB_VECTOR_MEDIUM: {
+      Lock lock(mutable_inter_thread_mutex());
+
+      const uint32_t page_id = static_cast<uint32_t>(
+          cell.offset() >> BLOB_VECTOR_VALUE_STORE_PAGE_SIZE_BITS);
+      index_store_[page_id].set_num_values(
+          index_store_[page_id].num_values() - 1);
+      if (index_store_[page_id].num_values() == 0) {
+        const uint32_t current_page_id =
+            static_cast<uint32_t>(header_->next_value_offset()
+                                  >> BLOB_VECTOR_VALUE_STORE_PAGE_SIZE_BITS);
+        if (page_id != current_page_id) {
+          freeze_page(page_id);
+        }
+      }
       break;
     }
-    case BLOB_VECTOR_HUGE_VALUE: {
-      builder << ", block_id = " << huge_value_cell().block_id();
+    case BLOB_VECTOR_LARGE: {
+      const io::BlockInfo * const block_info =
+          pool_.get_block_info(cell.block_id());
+      unregister_large_value(block_info->id(),
+          static_cast<BlobVectorValueHeader *>(
+              pool_.get_block_address(*block_info)));
+      pool_.free_block(*block_info);
       break;
     }
   }
-  return builder << " }";
 }
 
-StringBuilder &BlobVector::write_to(StringBuilder &builder) const {
-  if (!builder) {
-    return builder;
-  }
-
-  if (!is_open()) {
-    return builder << "n/a";
-  }
-
-  builder << "{ pool = " << pool_.path()
-          << ", block_info = " << *block_info_
-          << ", header = ";
-  if (header_) {
-    builder << *header_;
+void BlobVectorImpl::register_large_value(uint32_t block_id,
+    BlobVectorValueHeader *value_header) {
+  Lock lock(mutable_inter_process_mutex());
+  if (header_->latest_large_value_block_id() == io::BLOCK_INVALID_ID) {
+    value_header->set_next_value_block_id(block_id);
+    value_header->set_prev_value_block_id(block_id);
   } else {
-    builder << "n/a";
+    const uint32_t prev_id = header_->latest_large_value_block_id();
+    auto prev_header = static_cast<BlobVectorValueHeader *>(
+        pool_.get_block_address(prev_id));
+    const uint32_t next_id = prev_header->next_value_block_id();
+    auto next_header = static_cast<BlobVectorValueHeader *>(
+        pool_.get_block_address(next_id));
+    value_header->set_next_value_block_id(next_id);
+    value_header->set_prev_value_block_id(prev_id);
+    prev_header->set_next_value_block_id(block_id);
+    next_header->set_prev_value_block_id(block_id);
+  }
+  header_->set_latest_large_value_block_id(block_id);
+}
+
+void BlobVectorImpl::unregister_large_value(uint32_t block_id,
+    BlobVectorValueHeader *value_header) {
+  Lock lock(mutable_inter_process_mutex());
+  const uint32_t next_id = value_header->next_value_block_id();
+  const uint32_t prev_id = value_header->prev_value_block_id();
+  auto next_header = static_cast<BlobVectorValueHeader *>(
+      pool_.get_block_address(next_id));
+  auto prev_header = static_cast<BlobVectorValueHeader *>(
+      pool_.get_block_address(prev_id));
+  next_header->set_prev_value_block_id(prev_id);
+  prev_header->set_next_value_block_id(next_id);
+  if (block_id == header_->latest_large_value_block_id()) {
+    header_->set_latest_large_value_block_id(prev_id);
+  }
+}
+
+void BlobVectorImpl::freeze_page(uint32_t page_id) {
+  BlobVectorPageInfo &page_info = index_store_[page_id];
+  if (header_->latest_frozen_page_id() != BLOB_VECTOR_INVALID_PAGE_ID) {
+    BlobVectorPageInfo &latest_frozen_page_info =
+        index_store_[header_->latest_frozen_page_id()];
+    page_info.set_next_page_id(latest_frozen_page_info.next_page_id());
+    latest_frozen_page_info.set_next_page_id(page_id);
+  } else {
+    page_info.set_next_page_id(page_id);
+  }
+  page_info.set_stamp(recycler_->stamp());
+  header_->set_latest_frozen_page_id(page_id);
+}
+
+void BlobVectorImpl::unfreeze_oldest_frozen_page() {
+  if (header_->latest_frozen_page_id() != BLOB_VECTOR_INVALID_PAGE_ID) {
+    BlobVectorPageInfo &latest_frozen_page_info =
+        index_store_[header_->latest_frozen_page_id()];
+    const uint32_t oldest_frozen_page_id =
+        latest_frozen_page_info.next_page_id();
+    BlobVectorPageInfo &oldest_frozen_page_info =
+        index_store_[oldest_frozen_page_id];
+    if (recycler_->check(oldest_frozen_page_info.stamp())) {
+      latest_frozen_page_info.set_next_page_id(
+          oldest_frozen_page_info.next_page_id());
+      oldest_frozen_page_info.set_next_page_id(header_->next_page_id());
+      header_->set_next_page_id(oldest_frozen_page_id);
+      if (oldest_frozen_page_id == header_->latest_frozen_page_id()) {
+        header_->set_latest_frozen_page_id(BLOB_VECTOR_INVALID_PAGE_ID);
+      }
+    }
   }
-  return builder << ", inter_thread_mutex = " << inter_thread_mutex_ << " }";
 }
 
 }  // namespace db

  Modified: lib/db/blob_vector.hpp (+377 -382)
===================================================================
--- lib/db/blob_vector.hpp    2012-12-12 14:29:12 +0900 (861ea53)
+++ lib/db/blob_vector.hpp    2012-12-13 18:21:10 +0900 (6026f09)
@@ -18,451 +18,391 @@
 #ifndef GRNXX_DB_BLOB_VECTOR_HPP
 #define GRNXX_DB_BLOB_VECTOR_HPP
 
-#include <array>
-
 #include "vector.hpp"
 
 namespace grnxx {
 namespace db {
 
-const uint64_t BLOB_VECTOR_SMALL_VALUE_LENGTH_MAX  = 7;
-
-const uint64_t BLOB_VECTOR_MEDIUM_VALUE_LENGTH_MIN =
-    BLOB_VECTOR_SMALL_VALUE_LENGTH_MAX + 1;
-const uint64_t BLOB_VECTOR_MEDIUM_VALUE_LENGTH_MAX = 64;
-
-const uint64_t BLOB_VECTOR_LARGE_VALUE_LENGTH_MIN  =
-    BLOB_VECTOR_MEDIUM_VALUE_LENGTH_MAX + 1;
-const uint64_t BLOB_VECTOR_LARGE_VALUE_LENGTH_MAX  = 65535;
-
-const uint64_t BLOB_VECTOR_HUGE_VALUE_LENGTH_MIN  =
-    BLOB_VECTOR_LARGE_VALUE_LENGTH_MAX + 1;
-
-// 8, 16, 32, and 64 bytes (4 size types).
-const uint8_t  BLOB_VECTOR_MEDIUM_VALUE_STORES_NUM   = 4;
-const uint8_t  BLOB_VECTOR_MEDIUM_VALUE_UNIT_SIZE_BITS = 3;
-
-const uint8_t  BLOB_VECTOR_LARGE_VALUE_UNIT_SIZE_BITS  = 4;
-const uint64_t BLOB_VECTOR_LARGE_VALUE_UNIT_SIZE       =
-    uint64_t(1) << BLOB_VECTOR_LARGE_VALUE_UNIT_SIZE_BITS;
-const uint8_t  BLOB_VECTOR_LARGE_VALUE_STORE_SIZE_BITS = 44;
-const uint64_t BLOB_VECTOR_LARGE_VALUE_STORE_SIZE      =
-    uint64_t(1) << BLOB_VECTOR_LARGE_VALUE_STORE_SIZE_BITS;
-const uint64_t BLOB_VECTOR_LARGE_VALUE_INVALID_OFFSET  =
-    BLOB_VECTOR_LARGE_VALUE_STORE_SIZE - BLOB_VECTOR_LARGE_VALUE_UNIT_SIZE;
-const uint8_t  BLOB_VECTOR_LARGE_VALUE_LISTS_NUM       = 16;
-
-// The settings of stores for medium values.
-const uint8_t  BLOB_VECTOR_MEDIUM_VALUE_STORE_PAGE_SIZE_BITS            = 18;
-const uint8_t  BLOB_VECTOR_MEDIUM_VALUE_STORE_TABLE_SIZE_BITS           = 12;
-const uint8_t  BLOB_VECTOR_MEDIUM_VALUE_STORE_SECONDARY_TABLE_SIZE_BITS = 16;
-
-const uint64_t BLOB_VECTOR_MEDIUM_VALUE_STORE_PAGE_SIZE            =
-    uint64_t(1) << BLOB_VECTOR_MEDIUM_VALUE_STORE_PAGE_SIZE_BITS;
-const uint64_t BLOB_VECTOR_MEDIUM_VALUE_STORE_TABLE_SIZE           =
-    uint64_t(1) << BLOB_VECTOR_MEDIUM_VALUE_STORE_TABLE_SIZE_BITS;
-const uint64_t BLOB_VECTOR_MEDIUM_VALUE_STORE_SECONDARY_TABLE_SIZE =
-    uint64_t(1) << BLOB_VECTOR_MEDIUM_VALUE_STORE_SECONDARY_TABLE_SIZE_BITS;
-
-typedef Vector<char, BLOB_VECTOR_MEDIUM_VALUE_STORE_PAGE_SIZE,
-                     BLOB_VECTOR_MEDIUM_VALUE_STORE_TABLE_SIZE,
-                     BLOB_VECTOR_MEDIUM_VALUE_STORE_SECONDARY_TABLE_SIZE>
-BlobVectorMediumValueStore;
-
-// The settings of the store for large values.
-const uint8_t  BLOB_VECTOR_LARGE_VALUE_STORE_PAGE_SIZE_BITS            = 19;
-const uint8_t  BLOB_VECTOR_LARGE_VALUE_STORE_TABLE_SIZE_BITS           = 12;
-const uint8_t  BLOB_VECTOR_LARGE_VALUE_STORE_SECONDARY_TABLE_SIZE_BITS = 16;
-
-const uint64_t BLOB_VECTOR_LARGE_VALUE_STORE_PAGE_SIZE            =
-    uint64_t(1) << BLOB_VECTOR_LARGE_VALUE_STORE_PAGE_SIZE_BITS;
-const uint64_t BLOB_VECTOR_LARGE_VALUE_STORE_TABLE_SIZE           =
-    uint64_t(1) << BLOB_VECTOR_LARGE_VALUE_STORE_TABLE_SIZE_BITS;
-const uint64_t BLOB_VECTOR_LARGE_VALUE_STORE_SECONDARY_TABLE_SIZE =
-    uint64_t(1) << BLOB_VECTOR_LARGE_VALUE_STORE_SECONDARY_TABLE_SIZE_BITS;
-
-typedef Vector<char, BLOB_VECTOR_LARGE_VALUE_STORE_PAGE_SIZE,
-                     BLOB_VECTOR_LARGE_VALUE_STORE_TABLE_SIZE,
-                     BLOB_VECTOR_LARGE_VALUE_STORE_SECONDARY_TABLE_SIZE>
-BlobVectorLargeValueStore;
+constexpr uint64_t BLOB_VECTOR_MAX_ID = uint64_t(1) << 40;
+
+constexpr uint32_t BLOB_VECTOR_INVALID_PAGE_ID = 0xFFFFFFFFU;
+
+constexpr uint64_t BLOB_VECTOR_SMALL_VALUE_MAX_LENGTH  = 7;
+
+constexpr uint64_t BLOB_VECTOR_MEDIUM_VALUE_MIN_LENGTH =
+    BLOB_VECTOR_SMALL_VALUE_MAX_LENGTH + 1;
+constexpr uint64_t BLOB_VECTOR_MEDIUM_VALUE_MAX_LENGTH = 65535;
+
+constexpr uint64_t BLOB_VECTOR_LARGE_VALUE_MIN_LENGTH  =
+    BLOB_VECTOR_MEDIUM_VALUE_MAX_LENGTH + 1;
+
+constexpr uint8_t  BLOB_VECTOR_UNIT_SIZE_BITS  = 3;
+constexpr uint64_t BLOB_VECTOR_UNIT_SIZE       =
+    uint64_t(1) << BLOB_VECTOR_UNIT_SIZE_BITS;
+
+constexpr uint8_t  BLOB_VECTOR_VALUE_STORE_PAGE_SIZE_BITS            = 19;
+constexpr uint8_t  BLOB_VECTOR_VALUE_STORE_TABLE_SIZE_BITS           = 12;
+constexpr uint8_t  BLOB_VECTOR_VALUE_STORE_SECONDARY_TABLE_SIZE_BITS = 16;
+
+constexpr uint64_t BLOB_VECTOR_VALUE_STORE_PAGE_SIZE            =
+    uint64_t(1) << BLOB_VECTOR_VALUE_STORE_PAGE_SIZE_BITS;
+constexpr uint64_t BLOB_VECTOR_VALUE_STORE_TABLE_SIZE           =
+    uint64_t(1) << BLOB_VECTOR_VALUE_STORE_TABLE_SIZE_BITS;
+constexpr uint64_t BLOB_VECTOR_VALUE_STORE_SECONDARY_TABLE_SIZE =
+    uint64_t(1) << BLOB_VECTOR_VALUE_STORE_SECONDARY_TABLE_SIZE_BITS;
+
+extern class BlobVectorCreate {} BLOB_VECTOR_CREATE;
+extern class BlobVectorOpen {} BLOB_VECTOR_OPEN;
 
 class BlobVectorHeader {
  public:
-  void initialize(uint32_t cells_block_id, Duration frozen_duration);
+  explicit BlobVectorHeader(uint32_t table_block_id);
 
-  uint32_t cells_block_id() const {
-    return cells_block_id_;
-  }
-  Duration frozen_duration() const {
-    return frozen_duration_;
+  uint32_t table_block_id() const {
+    return table_block_id_;
   }
-  uint32_t medium_value_store_block_ids(uint8_t store_id) const {
-    return medium_value_store_block_ids_[store_id];
+  uint32_t value_store_block_id() const {
+    return value_store_block_id_;
   }
-  uint64_t medium_value_store_next_offsets(uint8_t store_id) const {
-    return medium_value_store_next_offsets_[store_id];
+  uint32_t index_store_block_id() const {
+    return index_store_block_id_;
   }
-  uint32_t large_value_store_block_id() const {
-    return large_value_store_block_id_;
+  uint32_t next_page_id() const {
+    return next_page_id_;
   }
-  uint64_t rearmost_large_value_offset() const {
-    return rearmost_large_value_offset_;
+  uint64_t next_value_offset() const {
+    return next_value_offset_;
   }
-  uint64_t latest_frozen_large_value_offset() const {
-    return latest_frozen_large_value_offset_;
+  uint32_t latest_frozen_page_id() const {
+    return latest_frozen_page_id_;
   }
-  uint64_t oldest_idle_large_value_offsets(uint8_t list_id) const {
-    return oldest_idle_large_value_offsets_[list_id];
+  uint32_t latest_large_value_block_id() const {
+    return latest_large_value_block_id_;
   }
 
-  void set_medium_value_store_block_ids(uint8_t store_id, uint32_t value) {
-    medium_value_store_block_ids_[store_id] = value;
+  void set_value_store_block_id(uint32_t value) {
+    value_store_block_id_ = value;
   }
-  void set_medium_value_store_next_offsets(uint8_t store_id, uint64_t value) {
-    medium_value_store_next_offsets_[store_id] = value;
+  void set_index_store_block_id(uint32_t value) {
+    index_store_block_id_ = value;
   }
-  void set_large_value_store_block_id(uint32_t value) {
-    large_value_store_block_id_ = value;
+  void set_next_page_id(uint32_t value) {
+    next_page_id_ = value;
   }
-  void set_rearmost_large_value_offset(uint64_t value) {
-    rearmost_large_value_offset_ = value;
+  void set_next_value_offset(uint64_t value) {
+    next_value_offset_ = value;
   }
-  void set_latest_frozen_large_value_offset(uint64_t value) {
-    latest_frozen_large_value_offset_ = value;
+  void set_latest_frozen_page_id(uint32_t value) {
+    latest_frozen_page_id_ = value;
   }
-  void set_oldest_idle_large_value_offsets(uint8_t list_id, uint64_t value) {
-    oldest_idle_large_value_offsets_[list_id] = value;
+  void set_latest_large_value_block_id(uint32_t value) {
+    latest_large_value_block_id_ = value;
   }
 
   Mutex *mutable_inter_process_mutex() {
     return &inter_process_mutex_;
   }
-  Mutex *mutable_medium_value_store_mutex() {
-    return &medium_value_store_mutex_;
-  }
-  Mutex *mutable_large_value_store_mutex() {
-    return &large_value_store_mutex_;
-  }
 
   StringBuilder &write_to(StringBuilder &builder) const;
 
  private:
-  uint32_t cells_block_id_;
-  Duration frozen_duration_;
-  uint32_t medium_value_store_block_ids_[BLOB_VECTOR_MEDIUM_VALUE_STORES_NUM];
-  uint64_t medium_value_store_next_offsets_[BLOB_VECTOR_MEDIUM_VALUE_STORES_NUM];
-  uint32_t large_value_store_block_id_;
-  uint64_t rearmost_large_value_offset_;
-  uint64_t latest_frozen_large_value_offset_;
-  uint64_t oldest_idle_large_value_offsets_[BLOB_VECTOR_LARGE_VALUE_LISTS_NUM];
+  uint32_t table_block_id_;
+  uint32_t value_store_block_id_;
+  uint32_t index_store_block_id_;
+  uint32_t next_page_id_;
+  uint64_t next_value_offset_;
+  uint32_t latest_frozen_page_id_;
+  uint32_t latest_large_value_block_id_;
   Mutex inter_process_mutex_;
-  Mutex medium_value_store_mutex_;
-  Mutex large_value_store_mutex_;
 };
 
-enum BlobVectorLargeValueType : uint8_t {
-  BLOB_VECTOR_ACTIVE_VALUE  = 0x00,
-  BLOB_VECTOR_FROZEN_VALUE  = 0x01,
-  BLOB_VECTOR_IDLE_VALUE    = 0x02
-};
+inline StringBuilder &operator<<(StringBuilder &builder,
+                                 const BlobVectorHeader &header) {
+  return header.write_to(builder);
+}
 
-class BlobVectorLargeValueFlagsIdentifier;
-typedef FlagsImpl<BlobVectorLargeValueFlagsIdentifier, uint8_t>
-    BlobVectorLargeValueFlags;
+enum BlobVectorValueType : uint8_t {
+  BLOB_VECTOR_NULL   = 0x00,
+  BLOB_VECTOR_SMALL  = 0x10,
+  BLOB_VECTOR_MEDIUM = 0x20,
+  BLOB_VECTOR_LARGE  = 0x30
+};
 
-const BlobVectorLargeValueFlags BLOB_VECTOR_LARGE_VALUE_HAS_NEXT =
-    BlobVectorLargeValueFlags::define(0x01);
-const BlobVectorLargeValueFlags BLOB_VECTOR_LARGE_VALUE_HAS_PREV =
-    BlobVectorLargeValueFlags::define(0x02);
+constexpr uint8_t BLOB_VECTOR_TYPE_MASK = 0x30;
 
-class BlobVectorLargeValueHeader {
+class BlobVectorPageInfo {
  public:
-  void initialize(BlobVectorLargeValueType type,
-                  BlobVectorLargeValueFlags flags,
-                  uint64_t capacity,
-                  uint64_t prev_capacity) {
-    set_type(type);
-    set_flags(flags);
-    set_capacity(capacity);
-    set_prev_capacity(prev_capacity);
-    next_offset_high_ = 0;
-    prev_offset_high_ = 0;
-    next_offset_low_ = 0;
-    prev_offset_low_ = 0;
-  }
+  BlobVectorPageInfo()
+    : next_page_id_(BLOB_VECTOR_INVALID_PAGE_ID), stamp_(0), reserved_(0) {}
 
-  void *value() {
-    return this + 1;
-  }
-
-  BlobVectorLargeValueType type() const {
-    return type_;
-  }
-  BlobVectorLargeValueFlags flags() const {
-    return flags_;
-  }
-  uint64_t capacity() const {
-    return static_cast<uint64_t>(capacity_)
-         << BLOB_VECTOR_LARGE_VALUE_UNIT_SIZE_BITS;
+  uint32_t next_page_id() const {
+    return next_page_id_;
   }
-  uint64_t prev_capacity() const {
-    return static_cast<uint64_t>(prev_capacity_)
-         << BLOB_VECTOR_LARGE_VALUE_UNIT_SIZE_BITS;
+  uint32_t num_values() const {
+    return num_values_;
   }
-  uint64_t next_offset() const {
-    return ((static_cast<uint64_t>(next_offset_high_) << 32) |
-            next_offset_low_) << BLOB_VECTOR_LARGE_VALUE_UNIT_SIZE_BITS;
-  }
-  uint64_t prev_offset() const {
-    return ((static_cast<uint64_t>(prev_offset_high_) << 32) |
-            prev_offset_low_) << BLOB_VECTOR_LARGE_VALUE_UNIT_SIZE_BITS;
-  }
-  uint16_t frozen_stamp() const {
-    return static_cast<uint16_t>(frozen_stamp_);
+  uint16_t stamp() const {
+    return reserved_;
   }
 
-  void set_type(BlobVectorLargeValueType value) {
-    type_ = value;
-  }
-  void set_flags(BlobVectorLargeValueFlags value) {
-    flags_ = value;
-  }
-  void set_capacity(uint64_t value) {
-    capacity_ = static_cast<uint16_t>(
-        value >> BLOB_VECTOR_LARGE_VALUE_UNIT_SIZE_BITS);
-  }
-  void set_prev_capacity(uint64_t value) {
-    prev_capacity_ = static_cast<uint16_t>(
-        value >> BLOB_VECTOR_LARGE_VALUE_UNIT_SIZE_BITS);
+  void set_next_page_id(uint32_t value) {
+    next_page_id_ = value;
   }
-  void set_next_offset(uint64_t value) {
-    value >>= BLOB_VECTOR_LARGE_VALUE_UNIT_SIZE_BITS;
-    next_offset_high_ = static_cast<uint8_t>(value >> 32);
-    next_offset_low_ = static_cast<uint32_t>(value);
+  void set_num_values(uint32_t value) {
+    num_values_ = value;
   }
-  void set_prev_offset(uint64_t value) {
-    value >>= BLOB_VECTOR_LARGE_VALUE_UNIT_SIZE_BITS;
-    prev_offset_high_ = static_cast<uint8_t>(value >> 32);
-    prev_offset_low_ = static_cast<uint32_t>(value);
+  void set_stamp(uint16_t value) {
+    stamp_ = value;
   }
-  void set_frozen_stamp(uint16_t value) {
-    frozen_stamp_ = value;
-  }
-
-  StringBuilder &write_to(StringBuilder &builder) const;
 
  private:
-  BlobVectorLargeValueType type_;
-  BlobVectorLargeValueFlags flags_;
-  uint16_t capacity_;
-  uint16_t prev_capacity_;
-  uint8_t next_offset_high_;
-  uint8_t prev_offset_high_;
-  uint32_t next_offset_low_;
   union {
-    uint32_t frozen_stamp_;
-    uint32_t prev_offset_low_;
+    uint32_t next_page_id_;
+    uint32_t num_values_;
   };
+  uint16_t stamp_;
+  uint16_t reserved_;
 };
 
-enum BlobVectorValueType : uint8_t {
-  BLOB_VECTOR_SMALL_VALUE  = 0x00,
-  BLOB_VECTOR_MEDIUM_VALUE = 0x01,
-  BLOB_VECTOR_LARGE_VALUE  = 0x02,
-  BLOB_VECTOR_HUGE_VALUE   = 0x03
-};
-
-const uint8_t BLOB_VECTOR_VALUE_TYPE_MASK = 0x03;
-
-union BlobVectorCellFlags {
-  uint8_t flags;
-  BlobVectorValueType value_type;
-};
-
-class BlobVectorSmallValueCell {
+class BlobVectorValueHeader {
  public:
-  BlobVectorSmallValueCell(const void *ptr, uint64_t length)
-    : flags_and_length_(BLOB_VECTOR_SMALL_VALUE |
-                        (static_cast<uint8_t>(length) << 5)),
-      value_() {
-    std::memcpy(value_, ptr, length);
+  uint64_t length() const {
+    return length_;
+  }
+  uint32_t next_value_block_id() const {
+    return next_value_block_id_;
+  }
+  uint32_t prev_value_block_id() const {
+    return prev_value_block_id_;
   }
 
-  uint64_t length() const {
-    return flags_and_length_ >> 5;
+  void set_length(uint64_t value) {
+    length_ = value;
   }
-  const void *value() const {
-    return value_;
+  void set_next_value_block_id(uint32_t value) {
+    next_value_block_id_ = value;
+  }
+  void set_prev_value_block_id(uint32_t value) {
+    prev_value_block_id_ = value;
   }
 
  private:
-  uint8_t flags_and_length_;
-  uint8_t value_[7];
+  uint64_t length_;
+  uint32_t next_value_block_id_;
+  uint32_t prev_value_block_id_;
 };
 
-class BlobVectorMediumValueCell {
+const uint8_t BLOB_VECTOR_CELL_FLAGS_MASK = 0xF0;
+
+class BlobVectorCell {
  public:
-  BlobVectorMediumValueCell(uint8_t store_id, uint64_t offset, uint64_t length)
-    : flags_(BLOB_VECTOR_MEDIUM_VALUE),
-      store_id_(store_id),
-      length_(static_cast<uint8_t>(length)),
-      offset_high_(static_cast<uint8_t>(
-          offset >> (32 + store_id + BLOB_VECTOR_MEDIUM_VALUE_UNIT_SIZE_BITS))),
-      offset_low_(static_cast<uint32_t>(
-          offset >> (store_id + BLOB_VECTOR_MEDIUM_VALUE_UNIT_SIZE_BITS))) {}
+  BlobVectorCell() = default;
+  explicit BlobVectorCell(std::nullptr_t) : qword_(0) {}
 
-  uint8_t store_id() const {
-    return store_id_;
+  static BlobVectorCell null_value() {
+    return BlobVectorCell(nullptr);
   }
-  uint64_t capacity() const {
-    return uint64_t(8) << store_id_;
+  static BlobVectorCell small_value(uint64_t length) {
+    BlobVectorCell cell(nullptr);
+    cell.bytes_[0] = BLOB_VECTOR_SMALL | static_cast<uint8_t>(length);
+    return cell;
   }
-  uint64_t length() const {
-    return length_;
+  static BlobVectorCell medium_value(uint64_t offset, uint64_t length) {
+    BlobVectorCell cell;
+    cell.bytes_[0] = BLOB_VECTOR_MEDIUM | static_cast<uint8_t>(offset >> 40);
+    cell.bytes_[1] = static_cast<uint8_t>(offset >> 32);
+    cell.words_[1] = static_cast<uint16_t>(length);
+    cell.dwords_[1] = static_cast<uint32_t>(offset);
+    return cell;
   }
-  uint64_t offset() const {
-    return ((static_cast<uint64_t>(offset_high_) << 32) | offset_low_)
-        << (store_id_ + BLOB_VECTOR_MEDIUM_VALUE_UNIT_SIZE_BITS);
+  static BlobVectorCell large_value(uint32_t block_id) {
+    BlobVectorCell cell(nullptr);
+    cell.bytes_[0] = BLOB_VECTOR_LARGE;
+    cell.dwords_[1] = block_id;
+    return cell;
   }
 
- private:
-  uint8_t flags_;
-  uint8_t store_id_;
-  uint8_t length_;
-  uint8_t offset_high_;
-  uint32_t offset_low_;
-};
+  BlobVectorValueType type() const {
+    Flags flags;
+    flags.byte = flags_.byte & BLOB_VECTOR_TYPE_MASK;
+    return flags.type;
+  }
 
-class BlobVectorLargeValueCell {
- public:
-  BlobVectorLargeValueCell(uint64_t offset, uint64_t length)
-    : flags_(BLOB_VECTOR_LARGE_VALUE),
-      offset_high_(static_cast<uint8_t>(offset >> 32)),
-      length_(static_cast<uint16_t>(length)),
-      offset_low_(static_cast<uint32_t>(offset)) {}
+  // Accessors to small values.
+  uint64_t small_length() const {
+    return bytes_[0] & ~BLOB_VECTOR_CELL_FLAGS_MASK;
+  }
+  const void *value() const {
+    return &bytes_[1];
+  }
+  void *value() {
+    return &bytes_[1];
+  }
 
-  uint64_t length() const {
-    return length_;
+  // Accessors to medium values.
+  uint64_t medium_length() const {
+    return words_[1];
   }
   uint64_t offset() const {
-    return (static_cast<uint64_t>(offset_high_) << 32) | offset_low_;
+    return (static_cast<uint64_t>(bytes_[0] &
+                                  ~BLOB_VECTOR_CELL_FLAGS_MASK) << 40) |
+           (static_cast<uint64_t>(bytes_[1]) << 32) | dwords_[1];
+  }
+
+  // Accessors to large values.
+  uint32_t block_id() const {
+    return dwords_[1];
   }
 
  private:
-  uint8_t flags_;
-  uint8_t offset_high_;
-  uint16_t length_;
-  uint32_t offset_low_;
+  union Flags {
+    uint8_t byte;
+    BlobVectorValueType type;
+  };
+
+  union {
+    Flags flags_;
+    uint8_t bytes_[8];
+    uint16_t words_[4];
+    uint32_t dwords_[2];
+    uint64_t qword_;
+  };
 };
 
-class BlobVectorHugeValueCell {
+static_assert(sizeof(BlobVectorCell) == sizeof(uint64_t),
+              "sizeof(BlobVectorCell) != sizeof(uint64_t)");
+
+class Blob {
  public:
-  explicit BlobVectorHugeValueCell(uint32_t block_id)
-    : flags_(BLOB_VECTOR_HUGE_VALUE), reserved_(), block_id_(block_id) {
-    std::memset(reserved_, 0, sizeof(reserved_));
+  Blob() : address_(nullptr), length_(0), cell_() {}
+  explicit Blob(std::nullptr_t) : address_(nullptr), length_(0), cell_() {}
+  Blob(const void *address, uint64_t length)
+    : address_(address), length_(length), cell_() {}
+  explicit Blob(BlobVectorCell small_value_cell)
+    : address_(), length_(), cell_(small_value_cell) {
+    address_ = cell_.value();
+    length_ = cell_.small_length();
+  }
+
+  // Note: address_ refers to the own value if it is small.
+  Blob(const Blob &rhs) : address_(), length_(rhs.length_), cell_(rhs.cell_) {
+    if (rhs.address() == rhs.cell_.value()) {
+      address_ = cell_.value();
+    } else {
+      address_ = rhs.address_;
+    }
+  }
+  Blob &operator=(const Blob &rhs) {
+    length_ = rhs.length_;
+    cell_ = rhs.cell_;
+    if (rhs.address() == rhs.cell_.value()) {
+      address_ = cell_.value();
+    } else {
+      address_ = rhs.address_;
+    }
+    return *this;
   }
 
-  uint32_t block_id() const {
-    return block_id_;
+  Blob &operator=(std::nullptr_t) {
+    return *this = Blob(nullptr);
+  }
+
+  explicit operator bool() const {
+    return static_cast<bool>(address_);
+  }
+
+  const void *address() const {
+    return address_;
+  }
+  uint64_t length() const {
+    return length_;
+  }
+
+  void set_address(const void *value) {
+    address_ = value;
+  }
+  void set_length(uint64_t value) {
+    length_ = value;
   }
 
  private:
-  uint8_t flags_;
-  uint8_t reserved_[3];
-  uint32_t block_id_;
+  const void *address_;
+  uint64_t length_;
+  BlobVectorCell cell_;
 };
 
-class BlobVectorCell {
+class BlobVector;
+
+class BlobRef {
  public:
-  BlobVectorCell() : cell_(0) {}
-  ~BlobVectorCell() {}
+  BlobRef(BlobVector *vector, uint64_t id) : vector_(*vector), id_(id) {}
 
-  BlobVectorCell(const BlobVectorCell &rhs) : cell_(rhs.cell_) {}
-  BlobVectorCell &operator=(const BlobVectorCell &rhs) {
-    cell_ = rhs.cell_;
-    return *this;
-  }
-  BlobVectorCell &operator=(const BlobVectorSmallValueCell &rhs) {
-    small_value_cell_ = rhs;
-    return *this;
-  }
-  BlobVectorCell &operator=(const BlobVectorMediumValueCell &rhs) {
-    medium_value_cell_ = rhs;
-    return *this;
+  operator Blob() const {
+    return get();
   }
-  BlobVectorCell &operator=(const BlobVectorLargeValueCell &rhs) {
-    large_value_cell_ = rhs;
+
+  BlobRef &operator=(std::nullptr_t) {
+    set(nullptr);
     return *this;
   }
-  BlobVectorCell &operator=(const BlobVectorHugeValueCell &rhs) {
-    huge_value_cell_ = rhs;
+  BlobRef &operator=(const Blob &value) {
+    set(value);
     return *this;
   }
 
-  BlobVectorValueType value_type() const {
-    BlobVectorCellFlags clone = flags_;
-    clone.flags &= BLOB_VECTOR_VALUE_TYPE_MASK;
-    return clone.value_type;
-  }
-
-  const BlobVectorSmallValueCell &small_value_cell() const {
-    return small_value_cell_;
+  Blob get() const;
+  void set(std::nullptr_t) {
+    set(Blob(nullptr));
   }
-  const BlobVectorMediumValueCell &medium_value_cell() const {
-    return medium_value_cell_;
+  void set(const Blob &value);
+  void set(const void *ptr, uint64_t length) {
+    set(Blob(ptr, length));
   }
-  const BlobVectorLargeValueCell &large_value_cell() const {
-    return large_value_cell_;
+
+  void append(const Blob &value);
+  void append(const void *ptr, uint64_t length) {
+    append(Blob(ptr, length));
   }
-  const BlobVectorHugeValueCell &huge_value_cell() const {
-    return huge_value_cell_;
+  void prepend(const Blob &value);
+  void prepend(const void *ptr, uint64_t length) {
+    prepend(Blob(ptr, length));
   }
 
-  StringBuilder &write_to(StringBuilder &builder) const;
-
  private:
-  union {
-    uint64_t cell_;
-    BlobVectorCellFlags flags_;
-    BlobVectorSmallValueCell small_value_cell_;
-    BlobVectorMediumValueCell medium_value_cell_;
-    BlobVectorLargeValueCell large_value_cell_;
-    BlobVectorHugeValueCell huge_value_cell_;
-  };
+  BlobVector &vector_;
+  uint64_t id_;
 };
 
-class BlobVector {
- public:
-  BlobVector();
-  ~BlobVector();
+typedef Vector<BlobVectorCell> BlobVectorTable;
 
-  BlobVector(BlobVector &&rhs);
-  BlobVector &operator=(BlobVector &&rhs);
+typedef Vector<char, BLOB_VECTOR_VALUE_STORE_PAGE_SIZE,
+                     BLOB_VECTOR_VALUE_STORE_TABLE_SIZE,
+                     BLOB_VECTOR_VALUE_STORE_SECONDARY_TABLE_SIZE>
+BlobVectorValueStore;
 
-  bool is_open() const {
-    return static_cast<bool>(pool_);
-  }
+typedef Vector<BlobVectorPageInfo,
+               BLOB_VECTOR_VALUE_STORE_TABLE_SIZE,
+               BLOB_VECTOR_VALUE_STORE_SECONDARY_TABLE_SIZE>
+BlobVectorIndexStore;
 
-  void create(io::Pool pool);
-  void open(io::Pool pool, uint32_t block_id);
-  void close();
+class BlobVectorImpl {
+ public:
+  static std::unique_ptr<BlobVectorImpl> create(io::Pool pool);
+  static std::unique_ptr<BlobVectorImpl> open(io::Pool pool,
+                                              uint32_t block_id);
 
-  const void *get_value_address(uint64_t id, uint64_t *length = nullptr);
-  void set_value(uint64_t id, const void *ptr, uint64_t length);
+  Blob get_value(uint64_t id) {
+    return get_value(table_[id]);
+  }
+  void set_value(uint64_t id, const Blob &value);
 
-  // TODO
-//  void append(uint64_t id, const void *ptr, uint64_t length);
-//  void prepend(uint64_t id, const void *ptr, uint64_t length);
+  void append(uint64_t id, const Blob &value);
+  void prepend(uint64_t id, const Blob &value);
 
   uint32_t block_id() const {
-    return is_open() ? block_info_->id() : io::BLOCK_INVALID_ID;
-  }
-  uint64_t id_max() const {
-    return cells_.max_id();
+    return block_info_->id();
   }
 
-  void swap(BlobVector &rhs);
-
   StringBuilder &write_to(StringBuilder &builder) const;
 
   static void unlink(io::Pool pool, uint32_t block_id);
@@ -472,80 +412,135 @@ class BlobVector {
   const io::BlockInfo *block_info_;
   BlobVectorHeader *header_;
   Recycler *recycler_;
-  Vector<BlobVectorCell> cells_;
-  std::array<BlobVectorMediumValueStore,
-             BLOB_VECTOR_MEDIUM_VALUE_STORES_NUM> medium_value_stores_;
-  BlobVectorLargeValueStore large_value_store_;
+  BlobVectorTable table_;
+  BlobVectorValueStore value_store_;
+  BlobVectorIndexStore index_store_;
   Mutex inter_thread_mutex_;
 
+  BlobVectorImpl();
+
   void create_vector(io::Pool pool);
   void open_vector(io::Pool pool, uint32_t block_id);
 
-  BlobVectorSmallValueCell create_small_value_cell(
-      const void *ptr, uint64_t length);
-  BlobVectorMediumValueCell create_medium_value_cell(
-      const void *ptr, uint64_t length);
-  BlobVectorLargeValueCell create_large_value_cell(
-      const void *ptr, uint64_t length);
-  BlobVectorHugeValueCell create_huge_value_cell(
-      const void *ptr, uint64_t length);
+  Blob get_value(BlobVectorCell cell);
+
+  inline BlobVectorCell create_value(const Blob &value);
+  inline BlobVectorCell join_values(const Blob &lhs, const Blob &rhs);
+
+  inline void *create_small_value(uint64_t length, BlobVectorCell *cell);
+  inline void *create_medium_value(uint64_t length, BlobVectorCell *cell);
+  inline void *create_large_value(uint64_t length, BlobVectorCell *cell);
 
   void free_value(BlobVectorCell cell);
 
-  void open_medium_value_store(uint8_t store_id);
-  void open_large_value_store();
+  void register_large_value(uint32_t block_id,
+                            BlobVectorValueHeader *value_header);
+  void unregister_large_value(uint32_t block_id,
+                              BlobVectorValueHeader *value_header);
+
+  void freeze_page(uint32_t page_id);
+  void unfreeze_oldest_frozen_page();
+
+  Mutex *mutable_inter_thread_mutex() {
+    return &inter_thread_mutex_;
+  }
+  Mutex *mutable_inter_process_mutex() {
+    return header_->mutable_inter_process_mutex();
+  }
+};
+
+inline StringBuilder &operator<<(StringBuilder &builder,
+                                 const BlobVectorImpl &vector) {
+  return vector.write_to(builder);
+}
+
+class BlobVector {
+ public:
+  BlobVector() = default;
+  BlobVector(const BlobVectorCreate &, io::Pool pool)
+    : impl_(BlobVectorImpl::create(pool)) {}
+  BlobVector(const BlobVectorOpen &, io::Pool pool, uint32_t block_id)
+    : impl_(BlobVectorImpl::open(pool, block_id)) {}
+
+  explicit operator bool() const {
+    return static_cast<bool>(impl_);
+  }
+
+  void create(io::Pool pool) {
+    *this = BlobVector(BLOB_VECTOR_CREATE, pool);
+  }
+  void open(io::Pool pool, uint32_t block_id) {
+    *this = BlobVector(BLOB_VECTOR_OPEN, pool, block_id);
+  }
+  void close() {
+    *this = BlobVector();
+  }
+
+  BlobRef operator[](uint64_t id) {
+    return BlobRef(this, id);
+  }
 
-  void unfreeze_frozen_large_values();
+  Blob get_value(uint64_t id) {
+    return impl_->get_value(id);
+  }
+  void set_value(uint64_t id, const Blob &value) {
+    impl_->set_value(id, value);
+  }
 
-  void divide_idle_large_value(uint64_t offset, uint64_t capacity);
-  void merge_idle_large_values(uint64_t offset);
-  void merge_idle_large_values(uint64_t offset, uint64_t next_offset);
+  void append(uint64_t id, const Blob &value) {
+    impl_->append(id, value);
+  }
+  void prepend(uint64_t id, const Blob &value) {
+    impl_->prepend(id, value);
+  }
 
-  void register_idle_large_value(uint64_t offset);
-  void unregister_idle_large_value(uint64_t offset);
+  uint32_t block_id() const {
+    return impl_->block_id();
+  }
 
-  uint8_t get_store_id(uint64_t capacity) {
-    return bit_scan_reverse(capacity - 1)
-        - (BLOB_VECTOR_MEDIUM_VALUE_UNIT_SIZE_BITS - 1);
+  void swap(BlobVector &rhs) {
+    impl_.swap(rhs.impl_);
   }
-  uint8_t get_list_id(uint64_t capacity) {
-    return bit_scan_reverse(
-        (capacity >> BLOB_VECTOR_LARGE_VALUE_UNIT_SIZE_BITS) | 1);
+
+  StringBuilder &write_to(StringBuilder &builder) const {
+    return impl_ ? impl_->write_to(builder) : (builder << "n/a");
   }
 
-  BlobVectorLargeValueHeader *get_large_value_header(uint64_t offset) {
-    return reinterpret_cast<BlobVectorLargeValueHeader *>(
-        &large_value_store_[offset]);
+  static constexpr uint64_t max_id() {
+    return BLOB_VECTOR_MAX_ID;
   }
 
-  BlobVector(const BlobVector &);
-  BlobVector &operator=(const BlobVector &);
+  static void unlink(io::Pool pool, uint32_t block_id) {
+    BlobVectorImpl::unlink(pool, block_id);
+  }
+
+ private:
+  std::shared_ptr<BlobVectorImpl> impl_;
 };
 
 inline void swap(BlobVector &lhs, BlobVector &rhs) {
   lhs.swap(rhs);
 }
 
-StringBuilder &operator<<(StringBuilder &builder, BlobVectorLargeValueType type);
-StringBuilder &operator<<(StringBuilder &builder, BlobVectorLargeValueFlags flags);
-StringBuilder &operator<<(StringBuilder &builder, BlobVectorValueType type);
-StringBuilder &operator<<(StringBuilder &builder, BlobVectorCellFlags flags);
-
 inline StringBuilder &operator<<(StringBuilder &builder,
-                                 const BlobVectorHeader &header) {
-  return header.write_to(builder);
+                                 const BlobVector &vector) {
+  return vector.write_to(builder);
 }
-inline StringBuilder &operator<<(StringBuilder &builder,
-                                 const BlobVectorLargeValueHeader &header) {
-  return header.write_to(builder);
+
+inline Blob BlobRef::get() const {
+  return vector_.get_value(id_);
 }
-inline StringBuilder &operator<<(StringBuilder &builder,
-                                 const BlobVectorCell &cell) {
-  return cell.write_to(builder);
+
+inline void BlobRef::set(const Blob &value) {
+  vector_.set_value(id_, value);
 }
-inline StringBuilder &operator<<(StringBuilder &builder,
-                                 const BlobVector &vector) {
-  return vector.write_to(builder);
+
+inline void BlobRef::append(const Blob &value) {
+  vector_.append(id_, value);
+}
+
+inline void BlobRef::prepend(const Blob &value) {
+  vector_.prepend(id_, value);
 }
 
 }  // namespace db

  Modified: test/Makefile.am (+0 -4)
===================================================================
--- test/Makefile.am    2012-12-12 14:29:12 +0900 (19b5add)
+++ test/Makefile.am    2012-12-13 18:21:10 +0900 (248074b)
@@ -1,7 +1,6 @@
 AM_CXXFLAGS = -I../lib
 
 TESTS =				\
-	test_alpha_blob_vector	\
 	test_backtrace		\
 	test_db_array		\
 	test_db_blob_vector	\
@@ -29,9 +28,6 @@ TESTS =				\
 
 check_PROGRAMS = $(TESTS)
 
-test_alpha_blob_vector_SOURCES = test_alpha_blob_vector.cpp
-test_alpha_blob_vector_LDADD = ../lib/libgrnxx.la
-
 test_backtrace_SOURCES = test_backtrace.cpp
 test_backtrace_LDADD = ../lib/libgrnxx.la
 

  Deleted: test/test_alpha_blob_vector.cpp (+0 -360) 100644
===================================================================
--- test/test_alpha_blob_vector.cpp    2012-12-12 14:29:12 +0900 (4da48a1)
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
-  Copyright (C) 2012  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 <algorithm>
-#include <cassert>
-#include <random>
-#include <vector>
-
-#include "alpha/blob_vector.hpp"
-#include "logger.hpp"
-#include "time.hpp"
-
-void test_basics() {
-  grnxx::io::Pool::unlink_if_exists("temp.grn");
-
-  grnxx::io::Pool pool(grnxx::io::POOL_CREATE, "temp.grn");
-
-  grnxx::alpha::BlobVector vector(grnxx::alpha::BLOB_VECTOR_CREATE, pool);
-
-  GRNXX_NOTICE() << "blob_vector = " << vector;
-
-  assert(vector.block_id() == 0);
-  assert(!vector.get_value(0));
-
-  grnxx::alpha::BlobVector vector2;
-
-  vector.swap(vector2);
-  vector2.swap(vector);
-
-  assert(vector.block_id() == 0);
-
-  std::string values[5];
-  values[0].resize(0);
-  values[1].resize(1 << 2, 'S');
-  values[2].resize(1 << 4, 'M');
-  values[3].resize(1 << 12, 'M');
-  values[4].resize(1 << 20, 'L');
-
-  vector[0].set(values[0].c_str(), values[0].length());
-  vector[1000].set(values[1].c_str(), values[1].length());
-  vector[1000000].set(values[2].c_str(), values[2].length());
-  vector[1000000000].set(values[3].c_str(), values[3].length());
-  vector[1000000000000ULL].set(values[4].c_str(), values[4].length());
-
-  grnxx::alpha::Blob blob;
-
-  assert(blob = vector[0]);
-  assert(blob.length() == values[0].length());
-  assert(std::memcmp(blob.address(), values[0].c_str(), blob.length()) == 0);
-  assert(blob = vector[1000]);
-  assert(blob.length() == values[1].length());
-  assert(std::memcmp(blob.address(), values[1].c_str(), blob.length()) == 0);
-  assert(blob = vector[1000000]);
-  assert(blob.length() == values[2].length());
-  assert(std::memcmp(blob.address(), values[2].c_str(), blob.length()) == 0);
-  assert(blob = vector[1000000000]);
-  assert(blob.length() == values[3].length());
-  assert(std::memcmp(blob.address(), values[3].c_str(), blob.length()) == 0);
-  assert(blob = vector[1000000000000ULL]);
-  assert(blob.length() == values[4].length());
-  assert(std::memcmp(blob.address(), values[4].c_str(), blob.length()) == 0);
-
-  std::uint32_t block_id = vector.block_id();
-
-  vector.close();
-  pool.close();
-
-  pool.open(grnxx::io::POOL_OPEN, "temp.grn");
-  vector.open(pool, block_id);
-
-  GRNXX_NOTICE() << "blob_vector = " << vector;
-
-  assert(blob = vector[0]);
-  assert(blob.length() == values[0].length());
-  assert(std::memcmp(blob.address(), values[0].c_str(), blob.length()) == 0);
-  assert(blob = vector[1000]);
-  assert(blob.length() == values[1].length());
-  assert(std::memcmp(blob.address(), values[1].c_str(), blob.length()) == 0);
-  assert(blob = vector[1000000]);
-  assert(blob.length() == values[2].length());
-  assert(std::memcmp(blob.address(), values[2].c_str(), blob.length()) == 0);
-  assert(blob = vector[1000000000]);
-  assert(blob.length() == values[3].length());
-  assert(std::memcmp(blob.address(), values[3].c_str(), blob.length()) == 0);
-  assert(blob = vector[1000000000000ULL]);
-  assert(blob.length() == values[4].length());
-  assert(std::memcmp(blob.address(), values[4].c_str(), blob.length()) == 0);
-
-  vector[0] = grnxx::alpha::Blob("banana", 6);
-  blob = vector[0];
-  assert(blob);
-  assert(blob.length() == 6);
-  assert(std::memcmp(blob.address(), "banana", 6) == 0);
-
-  vector[0] = grnxx::alpha::Blob("xyz", 3);
-  assert(std::memcmp(blob.address(), "banana", 6) == 0);
-
-  grnxx::alpha::Blob blob2 = blob;
-  blob = nullptr;
-  assert(blob2);
-  assert(blob2.length() == 6);
-  assert(std::memcmp(blob2.address(), "banana", 6) == 0);
-
-  vector[0] = nullptr;
-  blob = vector[0];
-  assert(!blob);
-
-  vector[0].append("ABC", 3);
-  blob = vector[0];
-  assert(blob.length() == 3);
-  assert(std::memcmp(blob.address(), "ABC", 3) == 0);
-
-  vector[0].append("XYZ", 3);
-  blob = vector[0];
-  assert(blob.length() == 6);
-  assert(std::memcmp(blob.address(), "ABCXYZ", 6) == 0);
-
-  vector[0].prepend("123", 3);
-  blob = vector[0];
-  assert(blob.length() == 9);
-  assert(std::memcmp(blob.address(), "123ABCXYZ", 9) == 0);
-
-  vector.close();
-  pool.close();
-
-  grnxx::io::Pool::unlink_if_exists("temp.grn");
-}
-
-void test_sequential_access(int num_loops,
-                            std::size_t num_values,
-                            std::size_t min_value_length,
-                            std::size_t max_value_length) {
-  std::mt19937 random;
-
-  grnxx::io::PoolOptions options;
-  options.set_frozen_duration(grnxx::Duration(0));
-  grnxx::io::Pool pool(grnxx::io::POOL_TEMPORARY, "temp.grn", options);
-  grnxx::alpha::BlobVector vector(grnxx::alpha::BLOB_VECTOR_CREATE, pool);
-
-  for (int loop_id = 0; loop_id < num_loops; ++loop_id) {
-    std::vector<std::string> values(num_values);
-    for (std::size_t i = 0; i < values.size(); ++i) {
-      const size_t length = min_value_length
-          + (random() % (max_value_length - min_value_length + 1));
-      values[i].resize(length, '0' + (random() % 10));
-      if (length != 0) {
-        values[i][0] = 'a' + (random() % 26);
-        values[i][length - 1] = 'A' + (random() % 26);
-      }
-    }
-
-    for (std::size_t i = 0; i < values.size(); ++i) {
-      vector[i].set(values[i].c_str(), values[i].length());
-      grnxx::alpha::Blob blob = vector[i];
-      assert(blob);
-      assert(blob.length() == values[i].length());
-      assert(std::memcmp(blob.address(), values[i].c_str(),
-                         blob.length()) == 0);
-    }
-
-    for (std::size_t i = 0; i < values.size(); ++i) {
-      grnxx::alpha::Blob blob = vector[i];
-      assert(blob);
-      assert(blob.length() == values[i].length());
-      assert(std::memcmp(blob.address(), values[i].c_str(),
-                         blob.length()) == 0);
-    }
-
-    GRNXX_NOTICE() << "total_size = " << pool.header().total_size();
-  }
-}
-
-void test_random_access(int num_loops,
-                        std::size_t num_values,
-                        std::size_t min_value_length,
-                        std::size_t max_value_length) {
-  std::mt19937 random;
-
-  grnxx::io::PoolOptions options;
-  options.set_frozen_duration(grnxx::Duration(0));
-  grnxx::io::Pool pool(grnxx::io::POOL_TEMPORARY, "temp.grn", options);
-  grnxx::alpha::BlobVector vector(grnxx::alpha::BLOB_VECTOR_CREATE, pool);
-
-  std::vector<std::uint32_t> ids(num_values);
-  for (std::size_t i = 0; i < ids.size(); ++i) {
-    ids[i] = static_cast<std::uint32_t>(i);
-  }
-
-  for (int loop_id = 0; loop_id < num_loops; ++loop_id) {
-    std::random_shuffle(ids.begin(), ids.end());
-
-    std::vector<std::string> values(num_values);
-    for (std::size_t i = 0; i < values.size(); ++i) {
-      const size_t length = min_value_length
-          + (random() % (max_value_length - min_value_length + 1));
-      values[i].resize(length);
-      for (std::size_t j = 0; j < length; ++j) {
-        values[i][j] = 'A' + (random() % 26);
-      }
-    }
-
-    for (std::size_t i = 0; i < values.size(); ++i) {
-      vector[ids[i]].set(values[i].c_str(), values[i].length());
-      grnxx::alpha::Blob blob = vector[ids[i]];
-      assert(blob);
-      assert(blob.length() == values[i].length());
-      assert(std::memcmp(blob.address(), values[i].c_str(),
-                         blob.length()) == 0);
-    }
-
-    for (std::size_t i = 0; i < values.size(); ++i) {
-      grnxx::alpha::Blob blob = vector[ids[i]];
-      assert(blob);
-      assert(blob.length() == values[i].length());
-      assert(std::memcmp(blob.address(), values[i].c_str(),
-                         blob.length()) == 0);
-    }
-
-    GRNXX_NOTICE() << "total_size = " << pool.header().total_size();
-  }
-}
-
-void test_access_patterns(int num_loops,
-                          std::size_t num_values,
-                          std::size_t min_value_length,
-                          std::size_t max_value_length) {
-  GRNXX_NOTICE() << "num_loops = " << num_loops
-                 << ", num_values = " << num_values
-                 << ", min_value_length = " << min_value_length
-                 << ", max_value_length = " << max_value_length;
-
-  test_sequential_access(num_loops, num_values,
-                         min_value_length, max_value_length);
-  test_random_access(num_loops, num_values,
-                     min_value_length, max_value_length);
-}
-
-void test_small_values() {
-  test_access_patterns(3, 1 << 17,
-                       0,
-                       grnxx::alpha::BLOB_VECTOR_SMALL_VALUE_MAX_LENGTH);
-}
-
-void test_medium_values() {
-  test_access_patterns(3, 1 << 14,
-                       grnxx::alpha::BLOB_VECTOR_MEDIUM_VALUE_MIN_LENGTH,
-                       1 << 10);
-  test_access_patterns(3, 1 << 8,
-                       1 << 10,
-                       grnxx::alpha::BLOB_VECTOR_MEDIUM_VALUE_MAX_LENGTH);
-}
-
-void test_large_values() {
-  test_access_patterns(3, 1 << 6,
-                       grnxx::alpha::BLOB_VECTOR_LARGE_VALUE_MIN_LENGTH,
-                       grnxx::alpha::BLOB_VECTOR_LARGE_VALUE_MIN_LENGTH * 2);
-}
-
-void test_reuse(bool enable_reuse) {
-  const uint32_t NUM_LOOPS = 3;
-  const uint32_t NUM_VALUES = 1 << 14;
-  const uint32_t MAX_LENGTH = 1024;
-
-  GRNXX_NOTICE() << "enable_reuse = " << enable_reuse;
-
-  std::mt19937 random;
-
-  grnxx::io::PoolOptions options;
-  options.set_frozen_duration(
-      enable_reuse ? grnxx::Duration(0) : grnxx::Duration::days(1));
-  grnxx::io::Pool pool(grnxx::io::POOL_TEMPORARY, "temp.grn", options);
-  grnxx::alpha::BlobVector vector(grnxx::alpha::BLOB_VECTOR_CREATE, pool);
-
-  std::string value(MAX_LENGTH, 'X');
-
-  for (uint32_t loop_id = 0; loop_id < NUM_LOOPS; ++loop_id) {
-    for (uint32_t i = 0; i < NUM_VALUES; ++i) {
-      vector[0] = grnxx::alpha::Blob(&value[0], random() % MAX_LENGTH);
-    }
-    GRNXX_NOTICE() << "total_size = " << pool.header().total_size();
-  }
-}
-
-void test_mixed() {
-  const uint32_t NUM_LOOPS = 3;
-  const uint32_t NUM_VALUES = 1 << 11;
-  const uint32_t VECTOR_SIZE = 1 << 10;
-
-  std::mt19937 random;
-
-  grnxx::io::Pool pool(grnxx::io::POOL_TEMPORARY, "temp.grn");
-  grnxx::alpha::BlobVector vector(grnxx::alpha::BLOB_VECTOR_CREATE, pool);
-
-  std::string value(grnxx::alpha::BLOB_VECTOR_LARGE_VALUE_MIN_LENGTH, 'X');
-
-  for (uint32_t loop_id = 0; loop_id < NUM_LOOPS; ++loop_id) {
-    for (uint32_t i = 0; i < NUM_VALUES; ++i) {
-      const uint32_t value_id = random() % VECTOR_SIZE;
-      switch (random() & 3) {
-        case 0: {
-          vector[value_id] = nullptr;
-          break;
-        }
-        case 1: {
-          const uint32_t value_length =
-              random() % (grnxx::alpha::BLOB_VECTOR_SMALL_VALUE_MAX_LENGTH + 1);
-          vector[value_id] = grnxx::alpha::Blob(&value[0], value_length);
-          break;
-        }
-        case 2: {
-          const uint32_t value_length_range =
-              grnxx::alpha::BLOB_VECTOR_MEDIUM_VALUE_MAX_LENGTH
-              - grnxx::alpha::BLOB_VECTOR_MEDIUM_VALUE_MIN_LENGTH + 1;
-          const uint32_t value_length = (random() % value_length_range)
-              + grnxx::alpha::BLOB_VECTOR_MEDIUM_VALUE_MIN_LENGTH;
-          vector[value_id] = grnxx::alpha::Blob(&value[0], value_length);
-          break;
-        }
-        case 3: {
-          vector[value_id] = grnxx::alpha::Blob(&value[0], value.length());
-          break;
-        }
-      }
-    }
-    GRNXX_NOTICE() << "total_size = " << pool.header().total_size();
-  }
-}
-
-int main() {
-  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
-                           grnxx::LOGGER_ENABLE_COUT);
-  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
-
-  test_basics();
-
-  test_small_values();
-  test_medium_values();
-  test_large_values();
-
-  test_reuse(false);
-  test_reuse(true);
-
-  test_mixed();
-
-  return 0;
-}

  Modified: test/test_db_blob_vector.cpp (+239 -184)
===================================================================
--- test/test_db_blob_vector.cpp    2012-12-12 14:29:12 +0900 (5939cd1)
+++ test/test_db_blob_vector.cpp    2012-12-13 18:21:10 +0900 (24d5538)
@@ -15,11 +15,10 @@
   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 <algorithm>
 #include <cassert>
-#include <cstdlib>
 #include <random>
 #include <vector>
-#include <unordered_map>
 
 #include "db/blob_vector.hpp"
 #include "logger.hpp"
@@ -30,12 +29,12 @@ void test_basics() {
 
   grnxx::io::Pool pool(grnxx::io::POOL_CREATE, "temp.grn");
 
-  grnxx::db::BlobVector vector;
-  vector.create(pool);
+  grnxx::db::BlobVector vector(grnxx::db::BLOB_VECTOR_CREATE, pool);
 
   GRNXX_NOTICE() << "blob_vector = " << vector;
 
   assert(vector.block_id() == 0);
+  assert(!vector.get_value(0));
 
   grnxx::db::BlobVector vector2;
 
@@ -48,32 +47,32 @@ void test_basics() {
   values[0].resize(0);
   values[1].resize(1 << 2, 'S');
   values[2].resize(1 << 4, 'M');
-  values[3].resize(1 << 12, 'L');
-  values[4].resize(1 << 20, 'H');
-
-  vector.set_value(0, values[0].c_str(), values[0].length());
-  vector.set_value(1000, values[1].c_str(), values[1].length());
-  vector.set_value(1000000, values[2].c_str(), values[2].length());
-  vector.set_value(1000000000, values[3].c_str(), values[3].length());
-  vector.set_value(1000000000000ULL, values[4].c_str(), values[4].length());
-
-  std::uint64_t length = 0;
-
-  assert(std::memcmp(vector.get_value_address(0, &length),
-                     values[0].c_str(), values[0].length()) == 0);
-  assert(length == values[0].length());
-  assert(std::memcmp(vector.get_value_address(1000, &length),
-                     values[1].c_str(), values[1].length()) == 0);
-  assert(length == values[1].length());
-  assert(std::memcmp(vector.get_value_address(1000000, &length),
-                     values[2].c_str(), values[2].length()) == 0);
-  assert(length == values[2].length());
-  assert(std::memcmp(vector.get_value_address(1000000000, &length),
-                     values[3].c_str(), values[3].length()) == 0);
-  assert(length == values[3].length());
-  assert(std::memcmp(vector.get_value_address(1000000000000ULL, &length),
-                     values[4].c_str(), values[4].length()) == 0);
-  assert(length == values[4].length());
+  values[3].resize(1 << 12, 'M');
+  values[4].resize(1 << 20, 'L');
+
+  vector[0].set(values[0].c_str(), values[0].length());
+  vector[1000].set(values[1].c_str(), values[1].length());
+  vector[1000000].set(values[2].c_str(), values[2].length());
+  vector[1000000000].set(values[3].c_str(), values[3].length());
+  vector[1000000000000ULL].set(values[4].c_str(), values[4].length());
+
+  grnxx::db::Blob blob;
+
+  assert(blob = vector[0]);
+  assert(blob.length() == values[0].length());
+  assert(std::memcmp(blob.address(), values[0].c_str(), blob.length()) == 0);
+  assert(blob = vector[1000]);
+  assert(blob.length() == values[1].length());
+  assert(std::memcmp(blob.address(), values[1].c_str(), blob.length()) == 0);
+  assert(blob = vector[1000000]);
+  assert(blob.length() == values[2].length());
+  assert(std::memcmp(blob.address(), values[2].c_str(), blob.length()) == 0);
+  assert(blob = vector[1000000000]);
+  assert(blob.length() == values[3].length());
+  assert(std::memcmp(blob.address(), values[3].c_str(), blob.length()) == 0);
+  assert(blob = vector[1000000000000ULL]);
+  assert(blob.length() == values[4].length());
+  assert(std::memcmp(blob.address(), values[4].c_str(), blob.length()) == 0);
 
   std::uint32_t block_id = vector.block_id();
 
@@ -85,21 +84,55 @@ void test_basics() {
 
   GRNXX_NOTICE() << "blob_vector = " << vector;
 
-  assert(std::memcmp(vector.get_value_address(0, &length),
-                     values[0].c_str(), values[0].length()) == 0);
-  assert(length == values[0].length());
-  assert(std::memcmp(vector.get_value_address(1000, &length),
-                     values[1].c_str(), values[1].length()) == 0);
-  assert(length == values[1].length());
-  assert(std::memcmp(vector.get_value_address(1000000, &length),
-                     values[2].c_str(), values[2].length()) == 0);
-  assert(length == values[2].length());
-  assert(std::memcmp(vector.get_value_address(1000000000, &length),
-                     values[3].c_str(), values[3].length()) == 0);
-  assert(length == values[3].length());
-  assert(std::memcmp(vector.get_value_address(1000000000000ULL, &length),
-                     values[4].c_str(), values[4].length()) == 0);
-  assert(length == values[4].length());
+  assert(blob = vector[0]);
+  assert(blob.length() == values[0].length());
+  assert(std::memcmp(blob.address(), values[0].c_str(), blob.length()) == 0);
+  assert(blob = vector[1000]);
+  assert(blob.length() == values[1].length());
+  assert(std::memcmp(blob.address(), values[1].c_str(), blob.length()) == 0);
+  assert(blob = vector[1000000]);
+  assert(blob.length() == values[2].length());
+  assert(std::memcmp(blob.address(), values[2].c_str(), blob.length()) == 0);
+  assert(blob = vector[1000000000]);
+  assert(blob.length() == values[3].length());
+  assert(std::memcmp(blob.address(), values[3].c_str(), blob.length()) == 0);
+  assert(blob = vector[1000000000000ULL]);
+  assert(blob.length() == values[4].length());
+  assert(std::memcmp(blob.address(), values[4].c_str(), blob.length()) == 0);
+
+  vector[0] = grnxx::db::Blob("banana", 6);
+  blob = vector[0];
+  assert(blob);
+  assert(blob.length() == 6);
+  assert(std::memcmp(blob.address(), "banana", 6) == 0);
+
+  vector[0] = grnxx::db::Blob("xyz", 3);
+  assert(std::memcmp(blob.address(), "banana", 6) == 0);
+
+  grnxx::db::Blob blob2 = blob;
+  blob = nullptr;
+  assert(blob2);
+  assert(blob2.length() == 6);
+  assert(std::memcmp(blob2.address(), "banana", 6) == 0);
+
+  vector[0] = nullptr;
+  blob = vector[0];
+  assert(!blob);
+
+  vector[0].append("ABC", 3);
+  blob = vector[0];
+  assert(blob.length() == 3);
+  assert(std::memcmp(blob.address(), "ABC", 3) == 0);
+
+  vector[0].append("XYZ", 3);
+  blob = vector[0];
+  assert(blob.length() == 6);
+  assert(std::memcmp(blob.address(), "ABCXYZ", 6) == 0);
+
+  vector[0].prepend("123", 3);
+  blob = vector[0];
+  assert(blob.length() == 9);
+  assert(std::memcmp(blob.address(), "123ABCXYZ", 9) == 0);
 
   vector.close();
   pool.close();
@@ -107,180 +140,203 @@ void test_basics() {
   grnxx::io::Pool::unlink_if_exists("temp.grn");
 }
 
-void test_random_values(std::size_t num_values,
-                        std::size_t min_value_length,
-                        std::size_t max_value_length) {
+void test_sequential_access(int num_loops,
+                            std::size_t num_values,
+                            std::size_t min_value_length,
+                            std::size_t max_value_length) {
   std::mt19937 random;
 
-  std::vector<std::string> values(num_values);
-  for (std::size_t i = 0; i < values.size(); ++i) {
-    const size_t length = min_value_length
-        + (random() % (max_value_length - min_value_length + 1));
-    values[i].resize(length);
-    for (std::uint32_t j = 0; j < length; ++j) {
-      values[i][j] = 'A' + (random() % 26);
+  grnxx::io::PoolOptions options;
+  options.set_frozen_duration(grnxx::Duration(0));
+  grnxx::io::Pool pool(grnxx::io::POOL_TEMPORARY, "temp.grn", options);
+  grnxx::db::BlobVector vector(grnxx::db::BLOB_VECTOR_CREATE, pool);
+
+  for (int loop_id = 0; loop_id < num_loops; ++loop_id) {
+    std::vector<std::string> values(num_values);
+    for (std::size_t i = 0; i < values.size(); ++i) {
+      const size_t length = min_value_length
+          + (random() % (max_value_length - min_value_length + 1));
+      values[i].resize(length, '0' + (random() % 10));
+      if (length != 0) {
+        values[i][0] = 'a' + (random() % 26);
+        values[i][length - 1] = 'A' + (random() % 26);
+      }
     }
-  }
 
-  grnxx::io::Pool pool(grnxx::io::POOL_TEMPORARY, "temp.grn");
+    for (std::size_t i = 0; i < values.size(); ++i) {
+      vector[i].set(values[i].c_str(), values[i].length());
+      grnxx::db::Blob blob = vector[i];
+      assert(blob);
+      assert(blob.length() == values[i].length());
+      assert(std::memcmp(blob.address(), values[i].c_str(),
+                         blob.length()) == 0);
+    }
 
-  grnxx::db::BlobVector vector;
-  vector.create(pool);
+    for (std::size_t i = 0; i < values.size(); ++i) {
+      grnxx::db::Blob blob = vector[i];
+      assert(blob);
+      assert(blob.length() == values[i].length());
+      assert(std::memcmp(blob.address(), values[i].c_str(),
+                         blob.length()) == 0);
+    }
 
-  for (std::size_t i = 0; i < values.size(); ++i) {
-    vector.set_value(i, values[i].c_str(), values[i].length());
+    GRNXX_NOTICE() << "total_size = " << pool.header().total_size();
+  }
+}
 
-    std::uint64_t length;
-    const char *address =
-        static_cast<const char *>(vector.get_value_address(i, &length));
+void test_random_access(int num_loops,
+                        std::size_t num_values,
+                        std::size_t min_value_length,
+                        std::size_t max_value_length) {
+  std::mt19937 random;
 
-    assert(length == values[i].length());
-    assert(std::memcmp(address, values[i].c_str(), length) == 0);
+  grnxx::io::PoolOptions options;
+  options.set_frozen_duration(grnxx::Duration(0));
+  grnxx::io::Pool pool(grnxx::io::POOL_TEMPORARY, "temp.grn", options);
+  grnxx::db::BlobVector vector(grnxx::db::BLOB_VECTOR_CREATE, pool);
+
+  std::vector<std::uint32_t> ids(num_values);
+  for (std::size_t i = 0; i < ids.size(); ++i) {
+    ids[i] = static_cast<std::uint32_t>(i);
   }
 
-  for (std::size_t i = 0; i < values.size(); ++i) {
-    std::uint64_t length;
-    const char *address =
-        static_cast<const char *>(vector.get_value_address(i, &length));
+  for (int loop_id = 0; loop_id < num_loops; ++loop_id) {
+    std::random_shuffle(ids.begin(), ids.end());
+
+    std::vector<std::string> values(num_values);
+    for (std::size_t i = 0; i < values.size(); ++i) {
+      const size_t length = min_value_length
+          + (random() % (max_value_length - min_value_length + 1));
+      values[i].resize(length);
+      for (std::size_t j = 0; j < length; ++j) {
+        values[i][j] = 'A' + (random() % 26);
+      }
+    }
+
+    for (std::size_t i = 0; i < values.size(); ++i) {
+      vector[ids[i]].set(values[i].c_str(), values[i].length());
+      grnxx::db::Blob blob = vector[ids[i]];
+      assert(blob);
+      assert(blob.length() == values[i].length());
+      assert(std::memcmp(blob.address(), values[i].c_str(),
+                         blob.length()) == 0);
+    }
+
+    for (std::size_t i = 0; i < values.size(); ++i) {
+      grnxx::db::Blob blob = vector[ids[i]];
+      assert(blob);
+      assert(blob.length() == values[i].length());
+      assert(std::memcmp(blob.address(), values[i].c_str(),
+                         blob.length()) == 0);
+    }
 
-    assert(length == values[i].length());
-    assert(std::memcmp(address, values[i].c_str(), length) == 0);
+    GRNXX_NOTICE() << "total_size = " << pool.header().total_size();
   }
+}
 
-  GRNXX_NOTICE() << "pool = " << pool;
-  GRNXX_NOTICE() << "blob_vector = " << vector;
+void test_access_patterns(int num_loops,
+                          std::size_t num_values,
+                          std::size_t min_value_length,
+                          std::size_t max_value_length) {
+  GRNXX_NOTICE() << "num_loops = " << num_loops
+                 << ", num_values = " << num_values
+                 << ", min_value_length = " << min_value_length
+                 << ", max_value_length = " << max_value_length;
+
+  test_sequential_access(num_loops, num_values,
+                         min_value_length, max_value_length);
+  test_random_access(num_loops, num_values,
+                     min_value_length, max_value_length);
 }
 
 void test_small_values() {
-  test_random_values(1 << 20,
-                     0,
-                     grnxx::db::BLOB_VECTOR_SMALL_VALUE_LENGTH_MAX);
+  test_access_patterns(3, 1 << 17,
+                       0,
+                       grnxx::db::BLOB_VECTOR_SMALL_VALUE_MAX_LENGTH);
 }
 
 void test_medium_values() {
-  test_random_values(1 << 16,
-                     grnxx::db::BLOB_VECTOR_MEDIUM_VALUE_LENGTH_MIN,
-                     grnxx::db::BLOB_VECTOR_MEDIUM_VALUE_LENGTH_MAX);
+  test_access_patterns(3, 1 << 14,
+                       grnxx::db::BLOB_VECTOR_MEDIUM_VALUE_MIN_LENGTH,
+                       1 << 10);
+  test_access_patterns(3, 1 << 8,
+                       1 << 10,
+                       grnxx::db::BLOB_VECTOR_MEDIUM_VALUE_MAX_LENGTH);
 }
 
 void test_large_values() {
-  test_random_values(1 << 8,
-                     grnxx::db::BLOB_VECTOR_LARGE_VALUE_LENGTH_MIN,
-                     grnxx::db::BLOB_VECTOR_LARGE_VALUE_LENGTH_MAX);
+  test_access_patterns(3, 1 << 6,
+                       grnxx::db::BLOB_VECTOR_LARGE_VALUE_MIN_LENGTH,
+                       grnxx::db::BLOB_VECTOR_LARGE_VALUE_MIN_LENGTH * 2);
 }
 
-void test_huge_values() {
-  test_random_values(1 << 6,
-                     grnxx::db::BLOB_VECTOR_HUGE_VALUE_LENGTH_MIN,
-                     grnxx::db::BLOB_VECTOR_HUGE_VALUE_LENGTH_MIN * 4);
-}
+void test_reuse(bool enable_reuse) {
+  const uint32_t NUM_LOOPS = 3;
+  const uint32_t NUM_VALUES = 1 << 14;
+  const uint32_t MAX_LENGTH = 1024;
 
-void test_random_updates(grnxx::Duration frozen_duration) {
-  std::mt19937 random;
+  GRNXX_NOTICE() << "enable_reuse = " << enable_reuse;
 
-  std::unordered_map<std::uint64_t, std::string> map;
+  std::mt19937 random;
 
   grnxx::io::PoolOptions options;
-  options.set_frozen_duration(frozen_duration);
-
+  options.set_frozen_duration(
+      enable_reuse ? grnxx::Duration(0) : grnxx::Duration::days(1));
   grnxx::io::Pool pool(grnxx::io::POOL_TEMPORARY, "temp.grn", options);
+  grnxx::db::BlobVector vector(grnxx::db::BLOB_VECTOR_CREATE, pool);
 
-  grnxx::db::BlobVector vector;
-  vector.create(pool);
+  std::string value(MAX_LENGTH, 'X');
 
-  const std::uint8_t  LENGTH_BITS_MIN = 3;
-  const std::uint8_t  LENGTH_BITS_MAX = 10;
-  const std::uint32_t LOOP_COUNT = 1 << 16;
-  const std::uint32_t ID_MASK = (LOOP_COUNT >> 4) - 1;
-
-  std::string query;
-  for (std::uint32_t i = 0; i < LOOP_COUNT; ++i) {
-    const std::uint64_t id = random() & ID_MASK;
-
-    std::uint64_t length;
-    const char *address =
-        static_cast<const char *>(vector.get_value_address(id, &length));
-
-    auto it = map.find(id);
-    if (it == map.end()) {
-      assert(length == 0);
-    } else {
-      assert(length == it->second.length());
-      assert(std::memcmp(address, it->second.c_str(), length) == 0);
+  for (uint32_t loop_id = 0; loop_id < NUM_LOOPS; ++loop_id) {
+    for (uint32_t i = 0; i < NUM_VALUES; ++i) {
+      vector[0] = grnxx::db::Blob(&value[0], random() % MAX_LENGTH);
     }
-
-    const size_t query_length_bits = LENGTH_BITS_MIN
-        + (random() % (LENGTH_BITS_MAX - LENGTH_BITS_MIN + 1));
-    const size_t query_length = random() & ((1 << query_length_bits) - 1);
-    query.resize(query_length);
-    for (std::uint32_t j = 0; j < query_length; ++j) {
-      query[j] = 'A' + (random() % 26);
-    }
-
-    vector.set_value(id, query.c_str(), query.length());
-    map[id] = query;
+    GRNXX_NOTICE() << "total_size = " << pool.header().total_size();
   }
-
-  for (auto it = map.begin(); it != map.end(); ++it) {
-    const uint64_t key = it->first;
-    const std::string &value = it->second;
-
-    std::uint64_t length;
-    const char *address =
-        static_cast<const char *>(vector.get_value_address(key, &length));
-
-    assert(length == value.length());
-    assert(std::memcmp(address, value.c_str(), length) == 0);
-  }
-
-  GRNXX_NOTICE() << "pool = " << pool;
-  GRNXX_NOTICE() << "blob_vector = " << vector;
-}
-
-void test_rewrite() {
-  test_random_updates(grnxx::Duration::days(1));
 }
 
-void test_reuse() {
-  test_random_updates(grnxx::Duration(0));
-}
+void test_mixed() {
+  const uint32_t NUM_LOOPS = 3;
+  const uint32_t NUM_VALUES = 1 << 11;
+  const uint32_t VECTOR_SIZE = 1 << 10;
 
-void test_random() {
   std::mt19937 random;
 
-  std::vector<std::string> values(1 << 10);
-  for (std::size_t i = 0; i < values.size(); ++i) {
-    const std::uint32_t length_bits = 4 + (random() % 14);
-    const std::uint32_t length = random() & ((1 << length_bits) - 1);
-    values[i].resize(length);
-    for (std::uint32_t j = 0; j < length; ++j) {
-      values[i][j] = 'A' + (random() % 26);
-    }
-  }
-
   grnxx::io::Pool pool(grnxx::io::POOL_TEMPORARY, "temp.grn");
-
-  grnxx::db::BlobVector vector;
-  vector.create(pool);
-
-  for (std::size_t i = 0; i < values.size(); ++i) {
-    vector.set_value(i, values[i].c_str(), values[i].length());
-
-    std::uint64_t length;
-    const char *address =
-        static_cast<const char *>(vector.get_value_address(i, &length));
-
-    assert(length == values[i].length());
-    assert(std::memcmp(address, values[i].c_str(), length) == 0);
-  }
-
-  for (std::size_t i = 0; i < values.size(); ++i) {
-    std::uint64_t length;
-    const char *address =
-        static_cast<const char *>(vector.get_value_address(i, &length));
-
-    assert(length == values[i].length());
-    assert(std::memcmp(address, values[i].c_str(), length) == 0);
+  grnxx::db::BlobVector vector(grnxx::db::BLOB_VECTOR_CREATE, pool);
+
+  std::string value(grnxx::db::BLOB_VECTOR_LARGE_VALUE_MIN_LENGTH, 'X');
+
+  for (uint32_t loop_id = 0; loop_id < NUM_LOOPS; ++loop_id) {
+    for (uint32_t i = 0; i < NUM_VALUES; ++i) {
+      const uint32_t value_id = random() % VECTOR_SIZE;
+      switch (random() & 3) {
+        case 0: {
+          vector[value_id] = nullptr;
+          break;
+        }
+        case 1: {
+          const uint32_t value_length =
+              random() % (grnxx::db::BLOB_VECTOR_SMALL_VALUE_MAX_LENGTH + 1);
+          vector[value_id] = grnxx::db::Blob(&value[0], value_length);
+          break;
+        }
+        case 2: {
+          const uint32_t value_length_range =
+              grnxx::db::BLOB_VECTOR_MEDIUM_VALUE_MAX_LENGTH
+              - grnxx::db::BLOB_VECTOR_MEDIUM_VALUE_MIN_LENGTH + 1;
+          const uint32_t value_length = (random() % value_length_range)
+              + grnxx::db::BLOB_VECTOR_MEDIUM_VALUE_MIN_LENGTH;
+          vector[value_id] = grnxx::db::Blob(&value[0], value_length);
+          break;
+        }
+        case 3: {
+          vector[value_id] = grnxx::db::Blob(&value[0], value.length());
+          break;
+        }
+      }
+    }
+    GRNXX_NOTICE() << "total_size = " << pool.header().total_size();
   }
 }
 
@@ -294,12 +350,11 @@ int main() {
   test_small_values();
   test_medium_values();
   test_large_values();
-  test_huge_values();
 
-  test_rewrite();
-  test_reuse();
+  test_reuse(false);
+  test_reuse(true);
 
-  test_random();
+  test_mixed();
 
   return 0;
 }




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