[Groonga-commit] groonga/grnxx at c046b91 [master] Add modules for grnxx::Storage.

Back to archive index

susumu.yata null+****@clear*****
Mon Apr 22 21:40:59 JST 2013


susumu.yata	2013-04-22 21:40:59 +0900 (Mon, 22 Apr 2013)

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

  Message:
    Add modules for grnxx::Storage.

  Added files:
    lib/grnxx/storage/Makefile.am
    lib/grnxx/storage/file-posix.cpp
    lib/grnxx/storage/file-posix.hpp
    lib/grnxx/storage/file-windows.cpp
    lib/grnxx/storage/file-windows.hpp
    lib/grnxx/storage/file.cpp
    lib/grnxx/storage/file.hpp
    lib/grnxx/storage/path.cpp
    lib/grnxx/storage/path.hpp
    lib/grnxx/storage/view-posix.cpp
    lib/grnxx/storage/view-posix.hpp
    lib/grnxx/storage/view-windows.cpp
    lib/grnxx/storage/view-windows.hpp
    lib/grnxx/storage/view.cpp
    lib/grnxx/storage/view.hpp
    test/test_storage.cpp
  Modified files:
    .gitignore
    configure.ac
    lib/grnxx/Makefile.am
    lib/grnxx/storage.cpp
    lib/grnxx/storage.hpp
    test/Makefile.am

  Modified: .gitignore (+1 -0)
===================================================================
--- .gitignore    2013-04-22 21:29:49 +0900 (030950b)
+++ .gitignore    2013-04-22 21:40:59 +0900 (1af17e3)
@@ -53,6 +53,7 @@ test/test_mutex
 test/test_os
 test/test_recycler
 test/test_slice
+test/test_storage
 test/test_string
 test/test_string_builder
 test/test_string_format

  Modified: configure.ac (+1 -0)
===================================================================
--- configure.ac    2013-04-22 21:29:49 +0900 (278f6bb)
+++ configure.ac    2013-04-22 21:40:59 +0900 (7c774b0)
@@ -70,6 +70,7 @@ AC_CONFIG_FILES([Makefile
                  lib/grnxx/map/da/Makefile
                  lib/grnxx/map/da/basic/Makefile
                  lib/grnxx/map/da/large/Makefile
+                 lib/grnxx/storage/Makefile
                  lib/grnxx/time/Makefile
                  src/Makefile
                  test/Makefile])

  Modified: lib/grnxx/Makefile.am (+9 -1)
===================================================================
--- lib/grnxx/Makefile.am    2013-04-22 21:29:49 +0900 (0f3dea6)
+++ lib/grnxx/Makefile.am    2013-04-22 21:40:59 +0900 (74c2c57)
@@ -1,4 +1,11 @@
-SUBDIRS = alpha charset db io map time
+SUBDIRS =				\
+	alpha				\
+	charset				\
+	db				\
+	io				\
+	map				\
+	storage				\
+	time
 
 lib_LTLIBRARIES = libgrnxx.la
 
@@ -8,6 +15,7 @@ libgrnxx_la_LIBADD =			\
 	db/libgrnxx_db.la		\
 	io/libgrnxx_io.la		\
 	map/libgrnxx_map.la		\
+	storage/libgrnxx_storage.la	\
 	time/libgrnxx_time.la
 
 libgrnxx_la_LDFLAGS = @AM_LTLDFLAGS@

  Modified: lib/grnxx/storage.cpp (+14 -41)
===================================================================
--- lib/grnxx/storage.cpp    2013-04-22 21:29:49 +0900 (6b35d09)
+++ lib/grnxx/storage.cpp    2013-04-22 21:40:59 +0900 (1bf05c5)
@@ -17,40 +17,8 @@
 */
 #include "grnxx/storage.hpp"
 
-#include <ostream>
-
 namespace grnxx {
 
-#define GRNXX_FLAGS_WRITE(flag) do { \
-  if (flags & flag) { \
-    if (!is_first) { \
-      builder << " | "; \
-    } \
-    builder << #flag; \
-    is_first = false; \
-  } \
-} while (false)
-
-StringBuilder &operator<<(StringBuilder &builder, StorageFlags flags) {
-  if (flags) {
-    bool is_first = true;
-    GRNXX_FLAGS_WRITE(STORAGE_ANONYMOUS);
-    GRNXX_FLAGS_WRITE(STORAGE_HUGE_TLB);
-    GRNXX_FLAGS_WRITE(STORAGE_READ_ONLY);
-    GRNXX_FLAGS_WRITE(STORAGE_TEMPORARY);
-    return builder;
-  } else {
-    return builder << "0";
-  }
-}
-
-std::ostream &operator<<(std::ostream &stream, StorageFlags flags) {
-  char buf[256];
-  StringBuilder builder(buf);
-  builder << flags;
-  return stream.write(builder.c_str(), builder.length());
-}
-
 StorageOptions::StorageOptions()
   : max_num_files(1000),
     max_file_size(1ULL << 40),
@@ -75,34 +43,39 @@ StorageNodeHeader::StorageNodeHeader()
 Storage::Storage() {}
 Storage::~Storage() {}
 
-Storage *Storage::create(StorageFlags flags,
-                         const char *path,
+Storage *Storage::create(const char *path,
+                         StorageFlags flags,
                          const StorageOptions &options) {
   // TODO
   return nullptr;
 }
 
-Storage *Storage::open(StorageFlags flags,
-                       const char *path) {
+Storage *Storage::open(const char *path,
+                       StorageFlags flags) {
   // TODO
   return nullptr;
 }
 
-Storage *Storage::create_or_open(StorageFlags flags,
-                                 const char *path,
+Storage *Storage::open_or_create(const char *path,
+                                 StorageFlags flags,
                                  const StorageOptions &options) {
   // TODO
   return nullptr;
 }
 
 bool Storage::exists(const char *path) {
-  std::unique_ptr<Storage> storage(open(STORAGE_READ_ONLY, path));
-  return static_cast<bool>(storage);
+  std::unique_ptr<Storage> storage(open(path, STORAGE_READ_ONLY));
+  if (!storage) {
+    // TODO: Error: memory allocation failed.
+    return false;
+  }
+  return true;
 }
 
 bool Storage::unlink(const char *path) {
-  std::unique_ptr<Storage> storage(open(STORAGE_READ_ONLY, path));
+  std::unique_ptr<Storage> storage(open(path, STORAGE_READ_ONLY));
   if (!storage) {
+    // TODO: Error: memory allocation failed.
     return false;
   }
   // TODO: Remove files.

  Modified: lib/grnxx/storage.hpp (+36 -41)
===================================================================
--- lib/grnxx/storage.hpp    2013-04-22 21:29:49 +0900 (319d159)
+++ lib/grnxx/storage.hpp    2013-04-22 21:40:59 +0900 (a8649a4)
@@ -48,26 +48,27 @@ struct StorageNodeHeader {
   // The ID of the chunk to which this node belongs.
   uint16_t chunk_id;
   // (Non-phantom)
-  // The offset of this node in chunk. The actual offset is "offset" << "bits".
+  // The offset of this node in chunk.
+  // The actual offset is "offset" << "bits".
   uint32_t offset;
   // (Non-phantom)
   // The size of this node. The actual size is "size" << "bits".
   uint32_t size;
   // (Non-phantom)
-  // The ID of the next node in chunk. INVALID_ID indicates that this node is
-  // the last node in chunk.
+  // The ID of the next node in chunk.
+  // INVALID_ID indicates that this node is the last node in chunk.
   uint32_t next_node_id;
   // (Non-phantom)
-  // The ID of the previous node in chunk. INVALID_ID indicates that this node
-  // is the first node in chunk.
+  // The ID of the previous node in chunk.
+  // INVALID_ID indicates that this node is the first node in chunk.
   uint32_t prev_node_id;
   union {
     // (Phantom)
     // The ID of the next phantom node.
     uint32_t next_phantom_node_id;
     // (Active, marked, or unlinked)
-    // The ID of the latest child node. INVALID_ID indicates that this node has
-    // no children.
+    // The ID of the latest child node.
+    // INVALID_ID indicates that this node has no children.
     uint32_t child_id;
     // (Idle)
     // The ID of the next idle node.
@@ -75,12 +76,12 @@ struct StorageNodeHeader {
   };
   union {
     // (Active or marked)
-    // The ID of the next sibling node. INVALID_ID indicates that this node has
-    // no elder siblings.
+    // The ID of the next sibling node.
+    // INVALID_ID indicates that this node has no elder siblings.
     uint32_t sibling_node_id;
     // (Unlinked)
-    // The ID of the next unlinked node. INVALID_ID indicates that this node
-    // is the last unlinked node.
+    // The ID of the next unlinked node.
+    // INVALID_ID indicates that this node is the last unlinked node.
     uint32_t next_unlinked_node_id;
     // (Idle)
     // The ID of the previous idle node.
@@ -110,22 +111,16 @@ struct StorageNode {
 class Storage;
 typedef FlagsImpl<Storage> StorageFlags;
 
-// Create an anonymous (non-file-backed) temporary storage. All other flags,
-// except STORAGE_HUGE_TLB, are ignored.
-constexpr StorageFlags STORAGE_ANONYMOUS = StorageFlags::define(0x01);
-// Try to use huge pages. If huge pages are not available, regular pages will
-// be used.
-constexpr StorageFlags STORAGE_HUGE_TLB  = StorageFlags::define(0x02);
-// Open a storage in read-only mode. If this flag is not specified, a storage
-// is opened in read-write mode.
-constexpr StorageFlags STORAGE_READ_ONLY = StorageFlags::define(0x04);
-// Create a file-backed temporary storage. All other flags, except
-// STORAGE_ANONYMOUS and STORAGE_HUGE_TLB, are ignored.
-constexpr StorageFlags STORAGE_TEMPORARY = StorageFlags::define(0x08);
-
-// Generate a string from a set of flags.
-StringBuilder &operator<<(StringBuilder &builder, StorageFlags flags);
-std::ostream &operator<<(std::ostream &builder, StorageFlags flags);
+// Use the default settings.
+constexpr StorageFlags STORAGE_DEFAULT   = StorageFlags::define(0x00);
+// Use huge pages if available, or use regular pages.
+constexpr StorageFlags STORAGE_HUGE_TLB  = StorageFlags::define(0x01);
+// Open a storage in read-only mode.
+// If not specified, a storage is opened in read-write mode.
+constexpr StorageFlags STORAGE_READ_ONLY = StorageFlags::define(0x02);
+// Create an anonymous (non-file-backed) temporary storage if "path" ==
+// nullptr, or create a file-backed temporary storage.
+constexpr StorageFlags STORAGE_TEMPORARY = StorageFlags::define(0x04);
 
 struct StorageOptions {
   // The maximum number of files.
@@ -153,20 +148,20 @@ class Storage {
   Storage();
   virtual ~Storage();
 
-  // Create a storage if missing. If "flags" contains STORAGE_ANONYMOUS or
-  // STORAGE_TEMPORARY, "path" == nullptr is acceptable. Available flags are
-  // STORAGE_ANONYMOUS, STORAGE_HUGE_TLB, and STORAGE_TEMPORARY.
-  static Storage *create(StorageFlags flags,
-                         const char *path = nullptr,
+  // Create a storage.
+  // "path" == nullptr is acceptable iff "flags" contains STORAGE_TEMPORARY.
+  // Available flags are STORAGE_HUGE_TLB and STORAGE_TEMPORARY.
+  static Storage *create(const char *path,
+                         StorageFlags flags = STORAGE_DEFAULT,
                          const StorageOptions &options = StorageOptions());
-  // Open an existing storage. Available flags are STORAGE_HUGE_TLB and
-  // STORAGE_READ_ONLY.
-  static Storage *open(StorageFlags flags,
-                       const char *path = nullptr);
-  // Create a storage if missing, or open an existing storage. The available
-  // flag is STORAGE_HUGE_TLB.
-  static Storage *create_or_open(StorageFlags flags,
-                                 const char *path = nullptr,
+  // Open a storage.
+  // Available flags are STORAGE_HUGE_TLB and STORAGE_READ_ONLY.
+  static Storage *open(const char *path,
+                       StorageFlags flags = STORAGE_DEFAULT);
+  // Open or create a storage.
+  // The available flags is STORAGE_HUGE_TLB.
+  static Storage *open_or_create(const char *path,
+                                 StorageFlags flags = STORAGE_DEFAULT,
                                  const StorageOptions &options = StorageOptions());
 
   // Return true iff "path" refers to a valid storage.
@@ -186,7 +181,7 @@ class Storage {
   // Return the address to the header.
   virtual const StorageHeader *header() const = 0;
 
-  // Open an existing node.
+  // Open a node.
   virtual StorageNode open_node(uint32_t node_id) = 0;
   // Create a node of at least "size" bytes under "parent_node".
   virtual StorageNode create_node(const StorageNode &parent_node,

  Added: lib/grnxx/storage/Makefile.am (+22 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/storage/Makefile.am    2013-04-22 21:40:59 +0900 (c94aee2)
@@ -0,0 +1,22 @@
+noinst_LTLIBRARIES = libgrnxx_storage.la
+
+libgrnxx_storage_la_LDFLAGS = @AM_LTLDFLAGS@
+
+libgrnxx_storage_la_SOURCES =		\
+	file.cpp			\
+	file-posix.cpp			\
+	file-windows.cpp		\
+	path.cpp			\
+	view.cpp			\
+	view-posix.cpp			\
+	view-windows.cpp
+
+libgrnxx_storage_includedir = ${includedir}/grnxx/storage
+libgrnxx_storage_include_HEADERS =	\
+	file.hpp			\
+	file-posix.hpp			\
+	file-windows.hpp		\
+	path.hpp			\
+	view.hpp			\
+	view-posix.hpp			\
+	view-windows.hpp

  Added: lib/grnxx/storage/file-posix.cpp (+324 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/storage/file-posix.cpp    2013-04-22 21:40:59 +0900 (b00cf60)
@@ -0,0 +1,324 @@
+/*
+  Copyright (C) 2012-2013  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include "grnxx/storage/file-posix.hpp"
+
+#ifndef GRNXX_WINDOWS
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "grnxx/error.hpp"
+#include "grnxx/logger.hpp"
+#include "grnxx/storage/path.hpp"
+
+namespace grnxx {
+namespace storage {
+namespace {
+
+constexpr int UNIQUE_PATH_GENERATION_TRIAL_COUNT = 10;
+
+}  // namespace
+
+FileImpl::FileImpl()
+    : File(),
+      path_(nullptr),
+      flags_(FILE_DEFAULT),
+      fd_(-1),
+      locked_(false) {}
+
+FileImpl::~FileImpl() {
+  if (fd_ != -1) {
+    if (locked_) {
+      unlock();
+    }
+    if (::close(fd_) != 0) {
+      GRNXX_ERROR() << "failed to close file: path = " << path_.get()
+                    << ": '::close' " << Error(errno);
+    }
+  }
+}
+
+FileImpl *FileImpl::create(const char *path, FileFlags flags) {
+  std::unique_ptr<FileImpl> file(new (std::nothrow) FileImpl);
+  if (!file) {
+    GRNXX_ERROR() << "new grnxx::storage::FileImpl failed";
+    return nullptr;
+  }
+  if (~flags & FILE_TEMPORARY) {
+    if (!file->create_persistent_file(path, flags)) {
+      return nullptr;
+    }
+  } else {
+    if (!file->create_temporary_file(path, flags)) {
+      return nullptr;
+    }
+  }
+  return file.release();
+}
+
+FileImpl *FileImpl::open(const char *path, FileFlags flags) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = nullptr";
+    return nullptr;
+  }
+  std::unique_ptr<FileImpl> file(new (std::nothrow) FileImpl);
+  if (!file) {
+    GRNXX_ERROR() << "new grnxx::storage::FileImpl failed";
+    return nullptr;
+  }
+  if (!file->open_file(path, flags)) {
+    return nullptr;
+  }
+  return file.release();
+}
+
+FileImpl *FileImpl::open_or_create(const char *path, FileFlags flags) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = nullptr";
+    return nullptr;
+  }
+  std::unique_ptr<FileImpl> file(new (std::nothrow) FileImpl);
+  if (!file) {
+    GRNXX_ERROR() << "new grnxx::storage::FileImpl failed";
+    return nullptr;
+  }
+  if (!file->open_or_create_file(path, flags)) {
+    return nullptr;
+  }
+  return file.release();
+}
+
+bool FileImpl::exists(const char *path) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = nullptr";
+    return false;
+  }
+  struct stat stat;
+  if (::stat(path, &stat) != 0) {
+    return false;
+  }
+  return S_ISREG(stat.st_mode);
+}
+
+bool FileImpl::unlink(const char *path) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = nullptr";
+    return false;
+  }
+  if (::unlink(path) != 0) {
+    GRNXX_WARNING() << "failed to unlink file: path = " << path
+                    << ": '::unlink' " << Error(errno);
+    return false;
+  }
+  return true;
+}
+
+bool FileImpl::lock(FileLockFlags lock_flags) {
+  if (locked_) {
+    GRNXX_ERROR() << "already locked: path = " << path_.get();
+    return false;
+  }
+  FileLockFlags lock_type =
+      lock_flags & (FILE_LOCK_SHARED | FILE_LOCK_EXCLUSIVE);
+  if (!lock_type || (lock_type == (FILE_LOCK_SHARED | FILE_LOCK_EXCLUSIVE))) {
+    GRNXX_ERROR() << "invalid argument: lock_flags == " << lock_flags;
+    return false;
+  }
+  int operation = 0;
+  if (lock_flags & FILE_LOCK_SHARED) {
+    operation |= LOCK_SH;
+  }
+  if (lock_flags & FILE_LOCK_EXCLUSIVE) {
+    operation |= LOCK_EX;
+  }
+  if (lock_flags & FILE_LOCK_NONBLOCKING) {
+    operation |= LOCK_NB;
+  }
+  if (::flock(fd_, operation) != 0) {
+    if (errno == EWOULDBLOCK) {
+      // The file is locked by others.
+      return false;
+    }
+    GRNXX_ERROR() << "failed to lock file: path = " << path_.get()
+                  << ", lock_flags = " << lock_flags
+                  << ": '::flock' " << Error(errno);
+    return false;
+  }
+  locked_ = true;
+  return true;
+}
+
+bool FileImpl::unlock() {
+  if (!locked_) {
+    GRNXX_WARNING() << "unlocked: path = " << path_.get();
+    return false;
+  }
+  if (::flock(fd_, LOCK_UN) != 0) {
+    GRNXX_ERROR() << "failed to unlock file: path = " << path_.get()
+                  << ": '::flock' " << Error(errno);
+    return false;
+  }
+  locked_ = false;
+  return true;
+}
+
+bool FileImpl::sync() {
+  if (::fsync(fd_) != 0) {
+    GRNXX_ERROR() << "failed to sync file: path = " << path_.get()
+                  << ": '::fsync' " << Error(errno);
+    return false;
+  }
+  return true;
+}
+
+bool FileImpl::resize(int64_t size) {
+  if (flags_ & FILE_READ_ONLY) {
+    GRNXX_ERROR() << "read-only";
+    return false;
+  }
+  if ((size < 0) ||
+      (size > std::numeric_limits<off_t>::max())) {
+    GRNXX_ERROR() << "invalid argument: size = " << size;
+    return false;
+  }
+  if (::ftruncate(fd_, size) != 0) {
+    GRNXX_ERROR() << "failed to resize file: path = " << path_.get()
+                  << ", size = " << size
+                  << ": '::ftruncate' " << Error(errno);
+    return false;
+  }
+  return true;
+}
+
+int64_t FileImpl::size() const {
+  struct stat stat;
+  if (::fstat(fd_, &stat) != 0) {
+    GRNXX_ERROR() << "failed to stat file: path = " << path_.get()
+                  << ": '::fstat' " << Error(errno);
+    return -1;
+  }
+  return stat.st_size;
+}
+
+const char *FileImpl::path() const {
+  return path_.get();
+}
+
+FileFlags FileImpl::flags() const {
+  return flags_;
+}
+
+const void *FileImpl::handle() const {
+  return &fd_;
+}
+
+bool FileImpl::create_persistent_file(const char *path, FileFlags flags) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = nullptr";
+    return false;
+  }
+  if (!clone_path(path)) {
+    return false;
+  }
+  fd_ = ::open(path, O_RDWR | O_CREAT | O_EXCL, 0644);
+  if (fd_ == -1) {
+    GRNXX_ERROR() << "failed to create file: path = " << path
+                  << ", flags = " << flags
+                  << ": '::open' " << Error(errno);
+    return false;
+  }
+  return true;
+}
+
+bool FileImpl::create_temporary_file(const char *path, FileFlags flags) {
+  flags_ = FILE_TEMPORARY;
+  int posix_flags = O_RDWR | O_CREAT | O_EXCL | O_NOCTTY;
+#ifdef O_NOATIME
+  posix_flags |= O_NOATIME;
+#endif  // O_NOATIME
+#ifdef O_NOFOLLOW
+  posix_flags |= O_NOFOLLOW;
+#endif  // O_NOFOLLOW
+  for (int i = 0; i < UNIQUE_PATH_GENERATION_TRIAL_COUNT; ++i) {
+    path_.reset(Path::unique_path(path));
+    fd_ = ::open(path_.get(), posix_flags, 0600);
+    if (fd_ != -1) {
+      unlink(path_.get());
+      return true;
+    }
+    GRNXX_WARNING() << "failed to create file: path = " << path_.get()
+                    << ": '::open' " << Error(errno);
+  }
+  GRNXX_ERROR() << "failed to create temporary file: "
+                << "path = " << path
+                << ", flags = " << flags;
+  return false;
+}
+
+bool FileImpl::open_file(const char *path, FileFlags flags) {
+  if (!clone_path(path)) {
+    return false;
+  }
+  int posix_flags = O_RDWR;
+  if (flags & FILE_READ_ONLY) {
+    posix_flags = O_RDONLY;
+    flags_ |= FILE_READ_ONLY;
+  }
+  fd_ = ::open(path, posix_flags);
+  if (fd_ == -1) {
+    GRNXX_ERROR() << "failed to open file: path = " << path
+                  << ", flags = " << flags
+                  << ": '::open' " << Error(errno);
+    return false;
+  }
+  return true;
+}
+
+bool FileImpl::open_or_create_file(const char *path, FileFlags flags) {
+  if (!clone_path(path)) {
+    return false;
+  }
+  fd_ = ::open(path, O_RDWR | O_CREAT, 0644);
+  if (fd_ == -1) {
+    GRNXX_ERROR() << "failed to open file: path = " << path
+                  << ", flags = " << flags
+                  << ": '::open' " << Error(errno);
+    return false;
+  }
+  return true;
+}
+
+bool FileImpl::clone_path(const char *path) {
+  const size_t size = std::strlen(path) + 1;
+  path_.reset(new (std::nothrow) char[size]);
+  if (!path_) {
+    GRNXX_ERROR() << "new char[] failed: size = " << size;
+    return false;
+  }
+  std::memcpy(path_.get(), path, size);
+  return true;
+}
+
+}  // namespace storage
+}  // namespace grnxx
+
+#endif  // GRNXX_WINDOWS

  Added: lib/grnxx/storage/file-posix.hpp (+71 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/storage/file-posix.hpp    2013-04-22 21:40:59 +0900 (cb4e474)
@@ -0,0 +1,71 @@
+/*
+  Copyright (C) 2012-2013  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#ifndef GRNXX_STORAGE_FILE_POSIX_HPP
+#define GRNXX_STORAGE_FILE_POSIX_HPP
+
+#include "grnxx/storage/file.hpp"
+
+#ifndef GRNXX_WINDOWS
+
+namespace grnxx {
+namespace storage {
+
+class FileImpl : public File {
+ public:
+  FileImpl();
+  ~FileImpl();
+
+  static FileImpl *create(const char *path, FileFlags flags);
+  static FileImpl *open(const char *path, FileFlags flags);
+  static FileImpl *open_or_create(const char *path, FileFlags flags);
+
+  static bool exists(const char *path);
+  static bool unlink(const char *path);
+
+  bool lock(FileLockFlags lock_flags);
+  bool unlock();
+
+  bool sync();
+
+  bool resize(int64_t size);
+  int64_t size() const;
+
+  const char *path() const;
+  FileFlags flags() const;
+  const void *handle() const;
+
+ private:
+  std::unique_ptr<char[]> path_;
+  FileFlags flags_;
+  int fd_;
+  bool locked_;
+
+  bool create_persistent_file(const char *path, FileFlags flags);
+  bool create_temporary_file(const char *path_prefix, FileFlags flags);
+  bool open_file(const char *path, FileFlags flags);
+  bool open_or_create_file(const char *path, FileFlags flags);
+
+  bool clone_path(const char *path);
+};
+
+}  // namespace storage
+}  // namespace grnxx
+
+#endif  // GRNXX_WINDOWS
+
+#endif  // GRNXX_STORAGE_FILE_POSIX_HPP

  Added: lib/grnxx/storage/file-windows.cpp (+354 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/storage/file-windows.cpp    2013-04-22 21:40:59 +0900 (957ab83)
@@ -0,0 +1,354 @@
+/*
+  Copyright (C) 2012-2013  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include "grnxx/storage/file-windows.hpp"
+
+#ifdef GRNXX_WINDOWS
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <io.h>
+
+#include "grnxx/error.hpp"
+#include "grnxx/logger.hpp"
+#include "grnxx/storage/path.hpp"
+
+namespace grnxx {
+namespace storage {
+namespace {
+
+constexpr int UNIQUE_PATH_GENERATION_TRIAL_COUNT = 10;
+
+}  // namespace
+
+FileImpl::FileImpl()
+    : File(),
+      path_(nullptr),
+      flags_(FILE_DEFAULT),
+      handle_(INVALID_HANDLE_VALUE),
+      locked_(false) {}
+
+FileImpl::~FileImpl() {
+  if (handle_ != INVALID_HANDLE_VALUE) {
+    if (locked_) {
+      unlock();
+    }
+    if (!::CloseHandle(handle_)) {
+      GRNXX_ERROR() << "failed to close file: file = " << *this
+                    << ": '::CloseHandle' " << Error(::GetLastError());
+    }
+  }
+}
+
+
+FileImpl *FileImpl::create(const char *path, FileFlags flags) {
+  std::unique_ptr<FileImpl> file(new (std::nothrow) FileImpl);
+  if (!file) {
+    GRNXX_ERROR() << "new grnxx::storage::FileImpl failed";
+    return nullptr;
+  }
+  if (~flags & FILE_TEMPORARY) {
+    if (!file->create_persistent_file(path, flags)) {
+      return nullptr;
+    }
+  } else {
+    if (!file->create_temporary_file(path, flags)) {
+      return nullptr;
+    }
+  }
+  return file.release();
+}
+
+FileImpl *FileImpl::open(const char *path, FileFlags flags) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = nullptr";
+    return nullptr;
+  }
+  std::unique_ptr<FileImpl> file(new (std::nothrow) FileImpl);
+  if (!file) {
+    GRNXX_ERROR() << "new grnxx::storage::FileImpl failed";
+    return nullptr;
+  }
+  if (!file->open_file(path, flags)) {
+    return nullptr;
+  }
+  return file.release();
+}
+
+FileImpl *FileImpl::open_or_create(const char *path, FileFlags flags) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = nullptr";
+    return nullptr;
+  }
+  std::unique_ptr<FileImpl> file(new (std::nothrow) FileImpl);
+  if (!file) {
+    GRNXX_ERROR() << "new grnxx::storage::FileImpl failed";
+    return nullptr;
+  }
+  if (!file->open_or_create_file(path, flags)) {
+    return nullptr;
+  }
+  return file.release();
+}
+
+bool FileImpl::exists(const char *path) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = nullptr";
+    return false;
+  }
+  struct _stat stat;
+  if (::_stat(path, &stat) != 0) {
+    return false;
+  }
+  return stat.st_mode & _S_IFREG;
+}
+
+bool FileImpl::unlink(const char *path) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = nullptr";
+    return false;
+  }
+  if (!::DeleteFile(path)) {
+    GRNXX_WARNING() << "failed to unlink file: path = " << path
+                    << ": '::DeleteFile' " << Error(::GetLastError());
+    return false;
+  }
+  return true;
+}
+
+bool FileImpl::try_lock(FileLockMode mode) {
+  if (locked_) {
+    GRNXX_ERROR() << "already locked: path = " << path_.get();
+    return false;
+  }
+  FileLockFlags lock_type =
+      lock_flags & (FILE_LOCK_SHARED | FILE_LOCK_EXCLUSIVE);
+  if (!lock_type || (lock_type == (FILE_LOCK_SHARED | FILE_LOCK_EXCLUSIVE))) {
+    GRNXX_ERROR() << "invalid argument: lock_flags == " << lock_flags;
+    return false;
+  }
+  DWORD flags = LOCKFILE_FAIL_IMMEDIATELY;
+  if (lock_flags & FILE_LOCK_SHARED) {
+    // Nothing to do.
+  }
+  if (lock_flags & FILE_LOCK_EXCLUSIVE) {
+    flags |= LOCKFILE_EXCLUSIVE_LOCK;
+  }
+  if (lock_flags & FILE_LOCK_NONBLOCKING) {
+    flags |= LOCKFILE_FAIL_IMMEDIATELY;
+  }
+
+  OVERLAPPED overlapped;
+  overlapped.Offset = 0;
+  overlapped.OffsetHigh = 0x80000000U;
+  if (!::LockFileEx(handle_, flags, 0, 0, 0x80000000U, &overlapped)) {
+    const DWORD last_error = ::GetLastError();
+    if (last_error == ERROR_LOCK_FAILED) {
+      // The file is locked by others.
+      return false;
+    }
+    GRNXX_ERROR() << "failed to lock file: path = " << path_.get()
+                  << ", mode = " << mode
+                  << ": '::LockFileEx' " << Error(last_error);
+    return false;
+  }
+  locked_ = true;
+  return true;
+}
+
+bool FileImpl::unlock() {
+  if (!locked_) {
+    GRNXX_WARNING() << "unlocked: path = " << path_.get();
+    return false;
+  }
+
+  OVERLAPPED overlapped;
+  overlapped.Offset = 0;
+  overlapped.OffsetHigh = 0x80000000U;
+  if (!::UnlockFileEx(handle_, 0, 0, 0x80000000U, &overlapped)) {
+    GRNXX_ERROR() << "failed to unlock file: path = " << path_.get()
+                  << ": '::UnlockFileEx' " << Error(::GetLastError());
+    return false;
+  }
+  locked_ = false;
+  return true;
+}
+
+void FileImpl::sync() {
+  if (!::FlushFileBuffers(handle_)) {
+    GRNXX_ERROR() << "failed to sync file: path = " << path_.get()
+                  << ": '::FlushFileBuffers' " << Error(errno);
+    return false;
+  }
+  return true;
+}
+
+bool FileImpl::resize(int64_t size) {
+  if (flags_ & FILE_READ_ONLY) {
+    GRNXX_ERROR() << "read-only";
+    return false;
+  }
+  if ((size < 0) ||
+      (size > std::numeric_limits<off_t>::max())) {
+    GRNXX_ERROR() << "invalid argument: size = " << size;
+    return false;
+  }
+  LARGE_INTEGER request;
+  request.QuadPart = size;
+  if (!::SetFilePointerEx(handle_, request, nullptr, FILE_BEGIN)) {
+    GRNXX_ERROR() << "failed to seek file: path = " << path_.get()
+                  << ", offset = " << offset << ", whence = " << whence
+                  << ": '::SetFilePointerEx' " << Error(::GetLastError());
+    return false;
+  }
+  if (!::SetEndOfFile(handle_)) {
+    GRNXX_ERROR() << "failed to resize file: path = " << path_.get()
+                  << ", size = " << size
+                  << ": '::SetEndOfFile' " << Error(::GetLastError());
+    return false;
+  }
+  return true;
+}
+
+int64_t FileImpl::size() const {
+  LARGE_INTEGER file_size;
+  if (!::GetFileSizeEx(handle_, &file_size)) {
+    GRNXX_ERROR() << "failed to get file size: path = " << path_.get()
+                  << ": '::GetFileSizeEx' " << Error(::GetLastError());
+    return -1;
+  }
+  return file_size.QuadPart;
+}
+
+const char *FileImpl::path() const {
+  return path_.get();
+}
+
+FileFlags FileImpl::flags() const {
+  return flags_;
+}
+
+const void *FileImpl::handle() const {
+  return &handle_;
+}
+
+bool FileImpl::create_persistent_file(const char *path, FileFlags flags) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = nullptr";
+    return false;
+  }
+  if (!clone_path(path)) {
+    return false;
+  }
+  const DWORD desired_access = GENERIC_READ | GENERIC_WRITE;
+  const DWORD share_mode =
+      FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;
+  const DWORD creation_disposition = CREATE_NEW;
+  const DWORD flags_and_attributes = FILE_ATTRIBUTE_NORMAL;
+  handle_ = ::CreateFileA(path, desired_access, share_mode, nullptr,
+                          creation_disposition, flags_and_attributes, nullptr);
+  if (handle_ == INVALID_HANDLE_VALUE) {
+    GRNXX_ERROR() << "failed to open file: path = " << path
+                  << ", flags = " << flags
+                  << ": '::CreateFileA' " << Error(::GetLastError());
+    return false;
+  }
+  return true;
+}
+
+bool FileImpl::create_temporary_file(const char *path, FileFlags flags) {
+  flags_ = FILE_TEMPORARY;
+  const DWORD desired_access = GENERIC_READ | GENERIC_WRITE;
+  const DWORD share_mode = FILE_SHARE_DELETE;
+  const DWORD creation_disposition = CREATE_NEW;
+  const DWORD flags_and_attributes = FILE_ATTRIBUTE_TEMPORARY |
+                                     FILE_FLAG_DELETE_ON_CLOSE;
+  for (int i = 0; i < UNIQUE_PATH_GENERATION_TRIAL_COUNT; ++i) {
+    path_.reset(Path::unique_path(path));
+    handle_ = ::CreateFileA(path_.get(), desired_access,
+                            share_mode, nullptr, creation_disposition,
+                            flags_and_attributes, nullptr);
+    if (handle_ != INVALID_HANDLE_VALUE) {
+      return true;
+    }
+    GRNXX_WARNING() << "failed to create file: path = " << path_.get()
+                    << ": '::CreateFileA' " << Error(::GetLastError());
+  }
+  GRNXX_ERROR() << "failed to create temporary file: path = " << path
+                << ", flags = " << flags;
+  return false;
+}
+
+bool FileImpl::open_file(const char *path, FileFlags flags) {
+  if (!clone_path(path)) {
+    return false;
+  }
+  DWORD desired_access = GENERIC_READ | GENERIC_WRITE;
+  if (flags_ & FILE_READ_ONLY) {
+    flags_ |= FILE_READ_ONLY;
+    desired_access = GENERIC_READ;
+  }
+  const DWORD share_mode =
+      FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;
+  const DWORD creation_disposition = OPEN_EXISTING;
+  const DWORD flags_and_attributes = FILE_ATTRIBUTE_NORMAL;
+  handle_ = ::CreateFileA(path, desired_access, share_mode, nullptr,
+                          creation_disposition, flags_and_attributes, nullptr);
+  if (handle_ == INVALID_HANDLE_VALUE) {
+    GRNXX_ERROR() << "failed to open file: path = " << path
+                  << ", flags = " << flags
+                  << ": '::CreateFileA' " << Error(::GetLastError());
+    return false;
+  }
+  return true;
+}
+
+bool FileImpl::open_or_create_file(const char *path, FileFlags flags) {
+  if (!clone_path(path)) {
+    return false;
+  }
+  const DWORD desired_access = GENERIC_READ | GENERIC_WRITE;
+  const DWORD share_mode =
+      FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;
+  const DWORD creation_disposition = OPEN_ALWAYS;
+  const DWORD flags_and_attributes = FILE_ATTRIBUTE_NORMAL;
+  handle_ = ::CreateFileA(path, desired_access, share_mode, nullptr,
+                          creation_disposition, flags_and_attributes, nullptr);
+  if (handle_ == INVALID_HANDLE_VALUE) {
+    GRNXX_ERROR() << "failed to open file: path = " << path
+                  << ", flags = " << flags
+                  << ": '::CreateFileA' " << Error(::GetLastError());
+    return false;
+  }
+  return true;
+}
+
+bool FileImpl::clone_path(const char *path) {
+  const size_t size = std::strlen(path) + 1;
+  path_.reset(new (std::nothrow) char[size]);
+  if (!path_) {
+    GRNXX_ERROR() << "new char[] failed: size = " << size;
+    return false;
+  }
+  std::memcpy(path_.get(), path, size);
+  return true;
+}
+
+}  // namespace storage
+}  // namespace grnxx
+
+#endif  // GRNXX_WINDOWS

  Added: lib/grnxx/storage/file-windows.hpp (+79 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/storage/file-windows.hpp    2013-04-22 21:40:59 +0900 (0f4b63d)
@@ -0,0 +1,79 @@
+/*
+  Copyright (C) 2012-2013  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#ifndef GRNXX_STORAGE_FILE_WINDOWS_HPP
+#define GRNXX_STORAGE_FILE_WINDOWS_HPP
+
+#include "grnxx/storage/file.hpp"
+
+#ifdef GRNXX_WINDOWS
+
+#include <windows.h>
+
+// FILE_READ_ONLY is defined as a macro in windows.h.
+#ifdef FILE_READ_ONLY
+# undef FILE_READ_ONLY
+#endif  // FILE_READ_ONLY
+
+namespace grnxx {
+namespace storage {
+
+class FileImpl : public File {
+ public:
+  FileImpl();
+  ~FileImpl();
+
+  static FileImpl *create(const char *path, FileFlags flags);
+  static FileImpl *open(const char *path, FileFlags flags);
+  static FileImpl *open_or_create(const char *path, FileFlags flags);
+
+  static bool exists(const char *path);
+  static bool unlink(const char *path);
+
+  bool lock(FileLockFlags lock_flags);
+  bool unlock();
+
+  bool sync();
+
+  bool resize(int64_t size);
+  int64_t size() const;
+
+  const char *path() const;
+  FileFlags flags() const;
+  const void *handle() const;
+
+ private:
+  std::unique_ptr<char[]> path_;
+  FileFlags flags_;
+  HANDLE handle_;
+  int fd_;
+  bool locked_;
+
+  bool create_persistent_file(const char *path, FileFlags flags);
+  bool create_temporary_file(const char *path_prefix, FileFlags flags);
+  bool open_file(const char *path, FileFlags flags);
+  bool open_or_create_file(const char *path, FileFlags flags);
+
+  bool clone_path(const char *path);
+};
+
+}  // namespace storage
+}  // namespace grnxx
+
+#endif  // GRNXX_WINDOWS
+
+#endif  // GRNXX_STORAGE_FILE_WINDOWS_HPP

  Added: lib/grnxx/storage/file.cpp (+84 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/storage/file.cpp    2013-04-22 21:40:59 +0900 (90fbed1)
@@ -0,0 +1,84 @@
+/*
+  Copyright (C) 2012-2013  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include "grnxx/storage/file.hpp"
+
+#include "grnxx/storage/file-posix.hpp"
+#include "grnxx/storage/file-windows.hpp"
+#include "grnxx/string_builder.hpp"
+
+namespace grnxx {
+namespace storage {
+
+#define GRNXX_FLAGS_WRITE(flag) do { \
+  if (flags & flag) { \
+    if (!is_first) { \
+      builder << " | "; \
+    } \
+    builder << #flag; \
+    is_first = false; \
+  } \
+} while (false)
+
+StringBuilder &operator<<(StringBuilder &builder, FileFlags flags) {
+  if (flags) {
+    bool is_first = true;
+    GRNXX_FLAGS_WRITE(FILE_READ_ONLY);
+    GRNXX_FLAGS_WRITE(FILE_TEMPORARY);
+    return builder;
+  } else {
+    return builder << "FILE_DEFAULT";
+  }
+}
+
+StringBuilder &operator<<(StringBuilder &builder, FileLockFlags flags) {
+  if (flags) {
+    bool is_first = true;
+    GRNXX_FLAGS_WRITE(FILE_LOCK_SHARED);
+    GRNXX_FLAGS_WRITE(FILE_LOCK_EXCLUSIVE);
+    GRNXX_FLAGS_WRITE(FILE_LOCK_NONBLOCKING);
+    return builder;
+  } else {
+    return builder << "0";
+  }
+}
+
+File::File() {}
+File::~File() {}
+
+File *File::create(const char *path, FileFlags flags) {
+  return FileImpl::create(path, flags);
+}
+
+File *File::open(const char *path, FileFlags flags) {
+  return FileImpl::open(path, flags);
+}
+
+File *File::open_or_create(const char *path, FileFlags flags) {
+  return FileImpl::open_or_create(path, flags);
+}
+
+bool File::exists(const char *path) {
+  return FileImpl::exists(path);
+}
+
+bool File::unlink(const char *path) {
+  return FileImpl::unlink(path);
+}
+
+}  // namespace storage
+}  // namespace grnxx

  Added: lib/grnxx/storage/file.hpp (+105 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/storage/file.hpp    2013-04-22 21:40:59 +0900 (ceeb21a)
@@ -0,0 +1,105 @@
+/*
+  Copyright (C) 2012-2013  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#ifndef GRNXX_STORAGE_FILE_HPP
+#define GRNXX_STORAGE_FILE_HPP
+
+#include "grnxx/basic.hpp"
+#include "grnxx/flags_impl.hpp"
+
+namespace grnxx {
+
+class StringBuilder;
+
+namespace storage {
+
+class File;
+typedef FlagsImpl<File> FileFlags;
+
+// Use the default settings.
+constexpr FileFlags FILE_DEFAULT   = FileFlags::define(0x00);
+// Open a file in read-only mode.
+constexpr FileFlags FILE_READ_ONLY = FileFlags::define(0x01);
+// Create a temporary file.
+constexpr FileFlags FILE_TEMPORARY = FileFlags::define(0x02);
+
+StringBuilder &operator<<(StringBuilder &builder, FileFlags flags);
+
+class FileLock;
+typedef FlagsImpl<FileLock> FileLockFlags;
+
+// Apply an exclusive advisory lock.
+constexpr FileLockFlags FILE_LOCK_SHARED      = FileLockFlags::define(0x01);
+// Apply a shared advisory lock.
+constexpr FileLockFlags FILE_LOCK_EXCLUSIVE   = FileLockFlags::define(0x02);
+// Immediately return the result when the file is locked.
+constexpr FileLockFlags FILE_LOCK_NONBLOCKING = FileLockFlags::define(0x04);
+
+StringBuilder &operator<<(StringBuilder &builder, FileLockFlags flags);
+
+class File {
+ public:
+  File();
+  virtual ~File();
+
+  // Create a file.
+  // "path" == nullptr is acceptable iff "flags" contains FILE_TEMPORARY.
+  // The available flag is FILE_TEMPORARY.
+  static File *create(const char *path = nullptr,
+                      FileFlags flags = FILE_DEFAULT);
+  // Open a file.
+  // The available flag is FILE_READ_ONLY.
+  static File *open(const char *path = nullptr,
+                    FileFlags flags = FILE_DEFAULT);
+  // Open or create a file.
+  // "path" == nullptr is acceptable iff "flags" contains FILE_TEMPORARY.
+  // There are no available flags.
+  static File *open_or_create(const char *path = nullptr,
+                              FileFlags flags = FILE_DEFAULT);
+
+  // Return true iff "path" refers to a regular file.
+  static bool exists(const char *path);
+  // Unlink a file and return true on success.
+  static bool unlink(const char *path);
+
+  // Try to lock a file and return true on success.
+  // Note that the file is accessible even if it is locked (advisory).
+  virtual bool lock(FileLockFlags lock_flags) = 0;
+  // Unlock a file and return true on success.
+  virtual bool unlock() = 0;
+
+  // Flush modified pages and return true on success.
+  virtual bool sync() = 0;
+
+  // Extend or truncate a file to "size" bytes.
+  // Note that the contents of the extended part are undefined.
+  virtual bool resize(int64_t size) = 0;
+  // Return the file size on success, or a negative value on failure.
+  virtual int64_t size() const = 0;
+
+  // Return the file path.
+  virtual const char *path() const = 0;
+  // Return the activated flags.
+  virtual FileFlags flags() const = 0;
+  // Return a pointer to the file handle 
+  virtual const void *handle() const = 0;
+};
+
+}  // namespace storage
+}  // namespace grnxx
+
+#endif  // GRNXX_STORAGE_FILE_HPP

  Added: lib/grnxx/storage/path.cpp (+143 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/storage/path.cpp    2013-04-22 21:40:59 +0900 (4145c9b)
@@ -0,0 +1,143 @@
+/*
+  Copyright (C) 2012-2013  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include "grnxx/storage/path.hpp"
+
+#ifdef GRNXX_WINDOWS
+# include <stdlib.h>
+#else  // GRNXX_WINDOWS
+# include <unistd.h>
+#endif  // GRNXX_WINDOWS
+
+#include <cerrno>
+#include <cstdlib>
+#include <random>
+
+#include "grnxx/error.hpp"
+#include "grnxx/logger.hpp"
+#include "grnxx/string_builder.hpp"
+
+namespace grnxx {
+namespace storage {
+namespace {
+
+constexpr size_t MAX_PATH_LENGTH = 1023;
+
+}  // namespace
+
+char *Path::full_path(const char *path) {
+  if (!path) {
+    path = "";
+  }
+  char full_path[MAX_PATH_LENGTH + 1];
+  size_t full_path_length = 0;
+#ifdef GRNXX_WINDOWS
+  if (!::_fullpath(full_path, path, sizeof(full_path))) {
+    GRNXX_ERROR() << "failed to generate full path: path = " << path
+                  << ": '::_fullpath' " << Error(errno);
+    return nullptr;
+  }
+  full_path_length = std::strlen(full_path);
+#else  // GRNXX_WINDOWS
+  if (path[0] != '/') {
+    if (!::getcwd(full_path, sizeof(full_path))) {
+      GRNXX_ERROR() << "failed to get current working directory: '::getcwd' "
+                    << Error(errno);
+      return nullptr;
+    }
+    full_path_length = std::strlen(full_path);
+    full_path[full_path_length++] = '/';
+  }
+
+  const size_t path_length = std::strlen(path);
+  if ((full_path_length + path_length) >= sizeof(full_path)) {
+    GRNXX_ERROR() << "failed to generate full path: path = " << path
+                  << ": too long";
+    return nullptr;
+  }
+  std::memcpy(full_path + full_path_length, path, path_length);
+  full_path_length += path_length;
+  full_path[full_path_length] = '\0';
+
+  size_t src = 0;
+  size_t dest = 0;
+  while (full_path[src] != '\0') {
+    if (full_path[src] == '/') {
+      if (full_path[src + 1] == '\0') {
+        full_path[dest++] = full_path[src++];
+        break;
+      } else if (full_path[src + 1] == '/') {
+        ++src;
+        continue;
+      } else if (full_path[src + 1] == '.') {
+        if ((full_path[src + 2] == '/') || (full_path[src + 2] == '\0')) {
+          src += 2;
+          continue;
+        } else if (full_path[src + 2] == '.') {
+          if ((full_path[src + 3] == '/') || (full_path[src + 3] == '\0')) {
+            if (dest > 0) {
+              do {
+                --dest;
+              } while (full_path[dest] != '/');
+            }
+            src += 3;
+            continue;
+          }
+        }
+      }
+    }
+    full_path[dest++] = full_path[src++];
+  }
+  if (dest == 0) {
+    full_path[0] = '/';
+    full_path_length = 1;
+  } else {
+    full_path_length = dest;
+  }
+#endif  // GRNXX_WINDOWS
+  char * const buf = new (std::nothrow) char[full_path_length + 1];
+  std::memcpy(buf, full_path, full_path_length);
+  buf[full_path_length] = '\0';
+  return buf;
+}
+
+char *Path::unique_path(const char *prefix) {
+  if (!prefix) {
+    prefix = "";
+  }
+  constexpr size_t SUFFIX_LENGTH = 8;
+  const size_t prefix_length = std::strlen(prefix);
+  const size_t path_size = prefix_length + SUFFIX_LENGTH + 2;
+  char * const path(new (std::nothrow) char[path_size]);
+  if (!path) {
+    GRNXX_ERROR() << "new char[] failed: path_size = " << path_size;
+    return nullptr;
+  }
+  std::memcpy(path, prefix, prefix_length);
+  path[prefix_length] = '_';
+  std::random_device random;
+  for (size_t i = 0; i < SUFFIX_LENGTH; ++i) {
+    const int value = static_cast<int>(random() % 36);
+    path[prefix_length + 1 + i] =
+        (value < 10) ? ('0' + value) : ('A' + value - 10);
+  }
+  path[prefix_length + 1 + SUFFIX_LENGTH] = '\0';
+  return path;
+}
+
+}  // namespace storage
+}  // namespace grnxx

  Added: lib/grnxx/storage/path.hpp (+42 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/storage/path.hpp    2013-04-22 21:40:59 +0900 (1306535)
@@ -0,0 +1,42 @@
+/*
+  Copyright (C) 2012-2013  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#ifndef GRNXX_STORAGE_PATH_HPP
+#define GRNXX_STORAGE_PATH_HPP
+
+#include "grnxx/basic.hpp"
+
+namespace grnxx {
+namespace storage {
+
+class Path {
+ public:
+  // Generate a full path from "path" and return an allocated buffer which
+  // must be freed with delete[].
+  static char *full_path(const char *path);
+
+  // Generate an almost unique path by adding a random suffix and return an
+  // allocated buffer which must be freed with delete[].
+  // For example, when "prefix" == "temp", the result is "temp_XXXXXXXX",
+  // where X indicates a random character ('0'-'9' or 'A'-'Z').
+  static char *unique_path(const char *prefix);
+};
+
+}  // namespace storage
+}  // namespace grnxx
+
+#endif  // GRNXX_STORAGE_PATH_HPP

  Added: lib/grnxx/storage/view-posix.cpp (+157 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/storage/view-posix.cpp    2013-04-22 21:40:59 +0900 (7063728)
@@ -0,0 +1,157 @@
+/*
+  Copyright (C) 2012-2013  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include "grnxx/storage/view-posix.hpp"
+
+#ifndef GRNXX_WINDOWS
+
+#include <sys/mman.h>
+#include <cerrno>
+
+#include "grnxx/error.hpp"
+#include "grnxx/storage/file.hpp"
+#include "grnxx/logger.hpp"
+
+#ifndef MAP_ANONYMOUS
+# ifdef MAP_ANON
+#  define MAP_ANONYMOUS MAP_ANON
+# endif  // MAP_ANON
+#endif  // MAP_ANONYMOUS
+
+namespace grnxx {
+namespace storage {
+
+ViewImpl::ViewImpl() : flags_(VIEW_DEFAULT), address_(MAP_FAILED), size_(0) {}
+
+ViewImpl::~ViewImpl() {
+  if (address_ != MAP_FAILED) {
+    if (::munmap(address_, static_cast<size_t>(size_)) != 0) {
+      GRNXX_ERROR() << "failed to unmap view: '::munmap' " << Error(errno);
+    }
+  }
+}
+
+View *ViewImpl::create(File *file, int64_t offset, int64_t size,
+                       ViewFlags flags) {
+  std::unique_ptr<ViewImpl> view(new (std::nothrow) ViewImpl);
+  if (!view) {
+    GRNXX_ERROR() << "new grnxx::storage::FileImpl failed";
+    return nullptr;
+  }
+  if (file) {
+    view->create_file_backed_view(file, offset, size, flags);
+  } else {
+    view->create_anonymous_view(size, flags);
+  }
+  return view.release();
+}
+
+bool ViewImpl::sync(int64_t offset, int64_t size) {
+  if ((offset < 0) || (offset > size_) || (size > size_) ||
+      ((size >= 0) && ((offset + size) > size_))) {
+    GRNXX_ERROR() << "invalid argument: offset = " << offset
+                  << ", size = " << size << ", view_size = " << size_;
+    return false;
+  }
+  if (size < 0) {
+    size = size_ - offset;
+  }
+  if (size > 0) {
+    if (::msync(static_cast<char *>(address_) + offset, size, MS_SYNC) != 0) {
+      GRNXX_ERROR() << "failed to sync view: offset = " << offset
+                    << ", size = " << size << ": '::msync' " << Error(errno);
+      return false;
+    }
+  }
+  return true;
+}
+
+ViewFlags ViewImpl::flags() const {
+  return flags_;
+}
+
+void *ViewImpl::address() const {
+  return address_;
+}
+
+int64_t ViewImpl::size() const {
+  return size_;
+}
+
+bool ViewImpl::create_file_backed_view(File *file, int64_t offset, int64_t size,
+                                       ViewFlags flags) {
+  const int64_t file_size = file->size();
+  if ((offset < 0) || (offset >= file_size) || (size == 0)) {
+    GRNXX_ERROR() << "invalid argument: offset = " << offset
+                  << ", size = " << size << ", file_size = " << file_size;
+    return false;
+  }
+  if (file->flags() & FILE_READ_ONLY) {
+    flags_ |= VIEW_READ_ONLY;
+  }
+  size_ = size;
+  int protection_flags = PROT_READ | PROT_WRITE;
+  if (flags_ & VIEW_READ_ONLY) {
+    flags_ |= VIEW_READ_ONLY;
+    protection_flags = PROT_READ;
+  }
+  const int mmap_flags = MAP_SHARED;
+  address_ = ::mmap(nullptr, size, protection_flags, mmap_flags,
+                    *static_cast<const int *>(file->handle()), offset);
+  if (address_ == MAP_FAILED) {
+    GRNXX_ERROR() << "failed to map file-backed view: "
+                  << "file_path = " << file->path()
+                  << ", offset = " << offset << ", size = " << size
+                  << ", flags = " << flags << ": '::mmap' " << Error(errno);
+    return false;
+  }
+  return true;
+}
+
+bool ViewImpl::create_anonymous_view(int64_t size, ViewFlags flags) {
+  if (size <= 0) {
+    GRNXX_ERROR() << "invalid argument: size = " << size;
+    return false;
+  }
+  flags_ = VIEW_ANONYMOUS;
+  size_ = size;
+  const int protection_flags = PROT_READ | PROT_WRITE;
+  const int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
+#ifdef MAP_HUGETLB
+  if (flags & VIEW_HUGE_TLB) {
+    address_ = ::mmap(nullptr, size, protection_flags,
+                      mmap_flags | MAP_HUGETLB, -1, 0);
+    if (address_ != MAP_FAILED) {
+      flags_ |= VIEW_HUGE_TLB;
+    }
+  }
+#endif  // MAP_HUGETLB
+  if (address_ == MAP_FAILED) {
+    address_ = ::mmap(nullptr, size, protection_flags, mmap_flags, -1, 0);
+    if (address_ == MAP_FAILED) {
+      GRNXX_ERROR() << "failed to map anonymous view: size = " << size
+                    << ", flags = " << flags << ": '::mmap' " << Error(errno);
+      return false;
+    }
+  }
+  return true;
+}
+
+}  // namespace storage
+}  // namespace grnxx
+
+#endif  // GRNXX_WINDOWS

  Added: lib/grnxx/storage/view-posix.hpp (+57 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/storage/view-posix.hpp    2013-04-22 21:40:59 +0900 (c954ee0)
@@ -0,0 +1,57 @@
+/*
+  Copyright (C) 2012-2013  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#ifndef GRNXX_STORAGE_VIEW_POSIX_HPP
+#define GRNXX_STORAGE_VIEW_POSIX_HPP
+
+#include "grnxx/storage/view.hpp"
+
+#ifndef GRNXX_WINDOWS
+
+namespace grnxx {
+namespace storage {
+
+class ViewImpl : public View {
+ public:
+  ViewImpl();
+  ~ViewImpl();
+
+  static View *create(File *file, int64_t offset, int64_t size,
+                      ViewFlags flags);
+
+  bool sync(int64_t offset, int64_t size);
+
+  ViewFlags flags() const;
+  void *address() const;
+  int64_t size() const;
+
+ private:
+  ViewFlags flags_;
+  void *address_;
+  int64_t size_;
+
+  bool create_file_backed_view(File *file, int64_t offset, int64_t size,
+                               ViewFlags flags);
+  bool create_anonymous_view(int64_t size, ViewFlags flags);
+};
+
+}  // namespace storage
+}  // namespace grnxx
+
+#endif  // GRNXX_WINDOWS
+
+#endif  // GRNXX_STORAGE_VIEW_POSIX_HPP

  Added: lib/grnxx/storage/view-windows.cpp (+160 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/storage/view-windows.cpp    2013-04-22 21:40:59 +0900 (90ca935)
@@ -0,0 +1,160 @@
+/*
+  Copyright (C) 2012-2013  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include "grnxx/storage/view-windows.hpp"
+
+#ifdef GRNXX_WINDOWS
+
+#include "grnxx/error.hpp"
+#include "grnxx/storage/file.hpp"
+#include "grnxx/logger.hpp"
+
+namespace grnxx {
+namespace storage {
+
+ViewImpl::ViewImpl()
+    : flags_(VIEW_DEFAULT),
+      handle_(nullptr),
+      address_(nullptr),
+      size_(0) {}
+
+ViewImpl::~ViewImpl() {
+  if (address_) {
+    if (!::UnmapViewOfFile(address_)) {
+      GRNXX_ERROR() << "failed to unmap view"
+                    << ": '::UnmapViewOfFile' " << Error(::GetLastError());
+    }
+  }
+  if (handle_) {
+    if (!::CloseHandle(handle_)) {
+      GRNXX_ERROR() << "failed to close file mapping"
+                    << ": '::CloseHandle' " << Error(::GetLastError());
+    }
+  }
+}
+
+View *ViewImpl::create(File *file, int64_t offset, int64_t size,
+                       ViewFlags flags) {
+  std::unique_ptr<ViewImpl> view(new (std::nothrow) ViewImpl);
+  if (!view) {
+    GRNXX_ERROR() << "new grnxx::storage::FileImpl failed";
+    return nullptr;
+  }
+  if (file) {
+    view->create_file_backed_view(file, offset, size, flags);
+  } else {
+    view->create_anonymous_view(size, flags);
+  }
+  return view.release();
+}
+
+bool ViewImpl::sync(int64_t offset, int64_t size) {
+  if ((offset < 0) || (offset > size_) || (size > size_) ||
+      ((size >= 0) && ((offset + size) > size_))) {
+    GRNXX_ERROR() << "invalid argument: offset = " << offset
+                  << ", size = " << size << ", view_size = " << size_;
+    return false;
+  }
+  if (size < 0) {
+    size = size_ - offset;
+  }
+  if (size > 0) {
+    if (!::FlushViewOfFile(static_cast<char *>(address_) + offset, size)) {
+      GRNXX_ERROR() << "failed to sync view: offset = " << offset
+                    << ", size = " << size
+                    << ": '::FlushViewOfFile' " << Error(::GetLastError());
+      return false;
+    }
+  }
+  return true;
+}
+
+bool ViewImpl::create_file_backed_view(File *file, int64_t offset, int64_t size,
+                                       ViewFlags flags) {
+  const int64_t file_size = file->size();
+  if ((offset < 0) || (offset >= file_size) || (size == 0)) {
+    GRNXX_ERROR() << "invalid argument: offset = " << offset
+                  << ", size = " << size << ", file_size = " << file_size;
+    return false;
+  }
+  if (file->flags() & FILE_READ_ONLY) {
+    flags_ |= VIEW_READ_ONLY;
+  }
+  size_ = size;
+  int protection_mode = PAGE_READWRITE;
+  DWORD desired_access = FILE_MAP_WRITE;
+  if (flags_ & VIEW_READ_ONLY) {
+    protection_mode = PAGE_READONLY;
+    desired_access = FILE_MAP_READ;
+  }
+  const DWORD size_high = static_cast<DWORD>((offset + size) >> 32);
+  const DWORD size_low = static_cast<DWORD>(offset + size);
+  handle_ = ::CreateFileMapping(*static_cast<const HANDLE *>(file->handle()),
+                                nullptr, protection_mode, size_high, size_low,
+                                nullptr);
+  if (!handle_) {
+    GRNXX_ERROR() << "failed to create file mapping: "
+                  << "file_path = " << file->path() << ", offset = " << offset
+                  << ", size = " << size << ", flags = " << flags
+                  << ": '::CreateFileMapping' " << Error(::GetLastError());
+    return false;
+  }
+  const DWORD offset_high = static_cast<DWORD>(offset >> 32);
+  const DWORD offset_low = static_cast<DWORD>(offset);
+  address_ = ::MapViewOfFile(handle_, desired_access, offset_high, offset_low,
+                             static_cast<SIZE_T>(size));
+  if (!address_) {
+    GRNXX_ERROR() << "failed to map view: "
+                  << "file_path = " << file->path() << ", offset = " << offset
+                  << ", size = " << size << ", flags = " << flags
+                  << ": '::MapViewOfFile' " << Error(::GetLastError());
+    return false;
+  }
+  return true;
+}
+
+bool ViewImpl::create_anonymous_view(int64_t size, ViewFlags flags) {
+  if (size <= 0) {
+    GRNXX_ERROR() << "invalid argument: size = " << size;
+    return false;
+  }
+  flags_ = VIEW_ANONYMOUS;
+  size_ = size;
+  const DWORD size_high = static_cast<DWORD>(size >> 32);
+  const DWORD size_low = static_cast<DWORD>(size);
+  handle_ = ::CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE,
+                                size_high, size_low, nullptr);
+  if (!handle_) {
+    GRNXX_ERROR() << "failed to create anonymous file mapping: "
+                  << "size = " << size << ", flags = " << flags
+                  << ": '::CreateFileMapping' " << Error(::GetLastError());
+    return false;
+  }
+  address_ = ::MapViewOfFile(handle_, FILE_MAP_WRITE, 0, 0, 0);
+  if (!address_) {
+    GRNXX_ERROR() << "failed to map anonymous view: "
+                  << "size = " << size << ", flags = " << flags
+                  << ": '::MapViewOfFile' " << Error(::GetLastError());
+    return false;
+  }
+  return true;
+}
+
+}  // namespace storage
+}  // namespace grnxx
+
+#endif  // GRNXX_WINDOWS

  Added: lib/grnxx/storage/view-windows.hpp (+65 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/storage/view-windows.hpp    2013-04-22 21:40:59 +0900 (0152902)
@@ -0,0 +1,65 @@
+/*
+  Copyright (C) 2012-2013  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#ifndef GRNXX_STORAGE_VIEW_WINDOWS_HPP
+#define GRNXX_STORAGE_VIEW_WINDOWS_HPP
+
+#include "grnxx/storage/view.hpp"
+
+#ifdef GRNXX_WINDOWS
+
+#include <windows.h>
+
+// FILE_READ_ONLY is defined as a macro in windows.h.
+#ifdef FILE_READ_ONLY
+# undef FILE_READ_ONLY
+#endif  // FILE_READ_ONLY
+
+namespace grnxx {
+namespace storage {
+
+class ViewImpl : public View {
+ public:
+  ViewImpl();
+  ~ViewImpl();
+
+  static View *create(File *file, int64_t offset, int64_t size,
+                      ViewFlags flags);
+
+  bool sync(int64_t offset, int64_t size);
+
+  ViewFlags flags() const;
+  void *address() const;
+  int64_t size() const;
+
+ private:
+  ViewFlags flags_;
+  HANDLE handle_;
+  void *address_;
+  uint64_t size_;
+
+  bool create_file_backed_view(File *file, int64_t offset, int64_t size,
+                               ViewFlags flags);
+  bool create_anonymous_view(int64_t size, ViewFlags flags);
+};
+
+}  // namespace storage
+}  // namespace grnxx
+
+#endif  // GRNXX_WINDOWS
+
+#endif  // GRNXX_STORAGE_VIEW_WINDOWS_HPP

  Added: lib/grnxx/storage/view.cpp (+57 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/storage/view.cpp    2013-04-22 21:40:59 +0900 (72c3987)
@@ -0,0 +1,57 @@
+/*
+  Copyright (C) 2012-2013  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include "grnxx/storage/view.hpp"
+
+#include "grnxx/storage/view-posix.hpp"
+#include "grnxx/storage/view-windows.hpp"
+#include "grnxx/string_builder.hpp"
+
+namespace grnxx {
+namespace storage {
+
+#define GRNXX_FLAGS_WRITE(flag) do { \
+  if (flags & flag) { \
+    if (!is_first) { \
+      builder << " | "; \
+    } \
+    builder << #flag; \
+    is_first = false; \
+  } \
+} while (false)
+
+StringBuilder &operator<<(StringBuilder &builder, ViewFlags flags) {
+  if (flags) {
+    bool is_first = true;
+    GRNXX_FLAGS_WRITE(VIEW_ANONYMOUS);
+    GRNXX_FLAGS_WRITE(VIEW_HUGE_TLB);
+    GRNXX_FLAGS_WRITE(VIEW_READ_ONLY);
+    return builder;
+  } else {
+    return builder << "0";
+  }
+}
+
+View::View() {}
+View::~View() {}
+
+View *View::create(File *file, int64_t offset, int64_t size, ViewFlags flags) {
+  return ViewImpl::create(file, offset, size, flags);
+}
+
+}  // namespace storage
+}  // namespace grnxx

  Added: lib/grnxx/storage/view.hpp (+75 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx/storage/view.hpp    2013-04-22 21:40:59 +0900 (666a45e)
@@ -0,0 +1,75 @@
+/*
+  Copyright (C) 2012-2013  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#ifndef GRNXX_STORAGE_VIEW_HPP
+#define GRNXX_STORAGE_VIEW_HPP
+
+#include "grnxx/basic.hpp"
+#include "grnxx/flags_impl.hpp"
+
+namespace grnxx {
+
+class StringBuilder;
+
+namespace storage {
+
+class View;
+typedef FlagsImpl<View> ViewFlags;
+
+// Use the default settings.
+constexpr ViewFlags VIEW_DEFAULT   = ViewFlags::define(0x00);
+// Create an anonymous memory mapping.
+// This flag is automatically enabled if "file" == nullptr.
+constexpr ViewFlags VIEW_ANONYMOUS = ViewFlags::define(0x01);
+// Use huge pages if available, or use regular pages.
+constexpr ViewFlags VIEW_HUGE_TLB  = ViewFlags::define(0x02);
+// Create a read-only memory mapping.
+// This flag is automatically enabled if "file" is read-only.
+constexpr ViewFlags VIEW_READ_ONLY = ViewFlags::define(0x04);
+
+StringBuilder &operator<<(StringBuilder &builder, ViewFlags flags);
+
+class File;
+
+class View {
+ public:
+  View();
+  virtual ~View();
+
+  // Create a file-backed memory mapping on "file" if "file" != nullptr, or
+  // create an anonymous memory mapping.
+  // The available flag is VIEW_HUGE_TLB.
+  static View *create(File *file,
+                      int64_t offset = 0,
+                      int64_t size = -1,
+                      ViewFlags flags = ViewFlags());
+
+  // Flush modified pages.
+  virtual bool sync(int64_t offset = 0, int64_t size = -1) = 0;
+
+  // Return the enabled flags.
+  virtual ViewFlags flags() const = 0;
+  // Return the starting address.
+  virtual void *address() const = 0;
+  // Return the size.
+  virtual int64_t size() const = 0;
+};
+
+}  // namespace storage
+}  // namespace grnxx
+
+#endif  // GRNXX_STORAGE_VIEW_HPP

  Modified: test/Makefile.am (+4 -0)
===================================================================
--- test/Makefile.am    2013-04-22 21:29:49 +0900 (95611ab)
+++ test/Makefile.am    2013-04-22 21:40:59 +0900 (f6d9ef4)
@@ -27,6 +27,7 @@ TESTS =					\
 	test_os				\
 	test_recycler			\
 	test_slice			\
+	test_storage			\
 	test_string			\
 	test_string_builder		\
 	test_string_format		\
@@ -113,6 +114,9 @@ test_recycler_LDADD = ../lib/grnxx/libgrnxx.la
 test_slice_SOURCES = test_slice.cpp
 test_slice_LDADD = ../lib/grnxx/libgrnxx.la
 
+test_storage_SOURCES = test_storage.cpp
+test_storage_LDADD = ../lib/grnxx/libgrnxx.la
+
 test_string_SOURCES = test_string.cpp
 test_string_LDADD = ../lib/grnxx/libgrnxx.la
 

  Added: test/test_storage.cpp (+243 -0) 100644
===================================================================
--- /dev/null
+++ test/test_storage.cpp    2013-04-22 21:40:59 +0900 (8ef498b)
@@ -0,0 +1,243 @@
+/*
+  Copyright (C) 2012-2013  Brazil, Inc.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+#include <cassert>
+#include <sstream>
+
+#include "grnxx/storage/file.hpp"
+#include "grnxx/storage/path.hpp"
+#include "grnxx/logger.hpp"
+
+namespace {
+
+void test_full_path(const char *path, const char *answer) {
+  std::unique_ptr<char[]> full_path(grnxx::storage::Path::full_path(path));
+  assert(full_path);
+  assert(std::strcmp(full_path.get(), answer) == 0);
+}
+
+void test_full_path() {
+  std::unique_ptr<char[]> full_path(grnxx::storage::Path::full_path(nullptr));
+  assert(full_path);
+  GRNXX_NOTICE() << "full_path = " << full_path.get();
+
+  full_path.reset(grnxx::storage::Path::full_path("temp.grn"));
+  assert(full_path);
+  GRNXX_NOTICE() << "full_path = " << full_path.get();
+
+  test_full_path("/", "/");
+  test_full_path("/.", "/");
+  test_full_path("/..", "/");
+
+  test_full_path("/usr/local/lib", "/usr/local/lib");
+  test_full_path("/usr/local/lib/", "/usr/local/lib/");
+  test_full_path("/usr/local/lib/.", "/usr/local/lib");
+  test_full_path("/usr/local/lib/./", "/usr/local/lib/");
+  test_full_path("/usr/local/lib/..", "/usr/local");
+  test_full_path("/usr/local/lib/../", "/usr/local/");
+}
+
+void test_unique_path() {
+  std::unique_ptr<char[]> unique_path(
+      grnxx::storage::Path::unique_path(nullptr));
+  assert(unique_path);
+  GRNXX_NOTICE() << "unique_path = " << unique_path.get();
+
+  unique_path.reset(grnxx::storage::Path::unique_path("temp.grn"));
+  GRNXX_NOTICE() << "unique_path = " << unique_path.get();
+}
+
+void test_file_create() {
+  const char FILE_PATH[] = "temp.grn";
+  grnxx::storage::File::unlink(FILE_PATH);
+  std::unique_ptr<grnxx::storage::File> file;
+
+  file.reset(grnxx::storage::File::create(FILE_PATH));
+  assert(file);
+  file.reset(grnxx::storage::File::create(FILE_PATH));
+  assert(!file);
+
+  file.reset(grnxx::storage::File::create(FILE_PATH,
+                                          grnxx::storage::FILE_TEMPORARY));
+  assert(file);
+  file.reset(grnxx::storage::File::create(FILE_PATH,
+                                          grnxx::storage::FILE_TEMPORARY));
+  assert(file);
+
+  file.reset(grnxx::storage::File::create(nullptr,
+                                          grnxx::storage::FILE_TEMPORARY));
+  assert(file);
+  file.reset(grnxx::storage::File::create(nullptr,
+                                          grnxx::storage::FILE_TEMPORARY));
+  assert(file);
+
+  grnxx::storage::File::unlink(FILE_PATH);
+}
+
+void test_file_open() {
+  const char FILE_PATH[] = "temp.grn";
+  grnxx::storage::File::unlink(FILE_PATH);
+  std::unique_ptr<grnxx::storage::File> file;
+
+  file.reset(grnxx::storage::File::open(FILE_PATH));
+  assert(!file);
+
+  file.reset(grnxx::storage::File::create(FILE_PATH));
+  file.reset(grnxx::storage::File::open(FILE_PATH));
+  assert(file);
+
+  file.reset();
+  grnxx::storage::File::unlink(FILE_PATH);
+}
+
+void test_file_open_or_create() {
+  const char FILE_PATH[] = "temp.grn";
+  grnxx::storage::File::unlink(FILE_PATH);
+  std::unique_ptr<grnxx::storage::File> file;
+
+  file.reset(grnxx::storage::File::open_or_create(FILE_PATH));
+  assert(file);
+  file.reset(grnxx::storage::File::open_or_create(FILE_PATH));
+  assert(file);
+
+  file.reset();
+  grnxx::storage::File::unlink(FILE_PATH);
+}
+
+void test_file_exists_and_unlink() {
+  const char FILE_PATH[] = "temp.grn";
+  std::unique_ptr<grnxx::storage::File>(
+      grnxx::storage::File::open_or_create(FILE_PATH));
+
+  assert(grnxx::storage::File::exists(FILE_PATH));
+  assert(grnxx::storage::File::unlink(FILE_PATH));
+  assert(!grnxx::storage::File::unlink(FILE_PATH));
+  assert(!grnxx::storage::File::exists(FILE_PATH));
+}
+
+void test_file_lock_and_unlock() {
+  const char FILE_PATH[] = "temp.grn";
+  std::unique_ptr<grnxx::storage::File> file_1;
+  file_1.reset(grnxx::storage::File::open_or_create(FILE_PATH));
+  assert(file_1);
+
+  assert(file_1->lock(grnxx::storage::FILE_LOCK_SHARED));
+  assert(!file_1->lock(grnxx::storage::FILE_LOCK_SHARED));
+  assert(file_1->unlock());
+  assert(!file_1->unlock());
+
+  assert(file_1->lock(grnxx::storage::FILE_LOCK_EXCLUSIVE));
+  assert(!file_1->lock(grnxx::storage::FILE_LOCK_EXCLUSIVE));
+  assert(file_1->unlock());
+  assert(!file_1->unlock());
+
+  std::unique_ptr<grnxx::storage::File> file_2;
+  file_2.reset(grnxx::storage::File::open(FILE_PATH));
+  assert(file_2);
+
+  assert(file_1->lock(grnxx::storage::FILE_LOCK_SHARED));
+  assert(file_2->lock(grnxx::storage::FILE_LOCK_SHARED |
+                      grnxx::storage::FILE_LOCK_NONBLOCKING));
+  assert(file_2->unlock());
+  assert(!file_2->lock(grnxx::storage::FILE_LOCK_EXCLUSIVE |
+                       grnxx::storage::FILE_LOCK_NONBLOCKING));
+  assert(file_1->unlock());
+
+  assert(file_1->lock(grnxx::storage::FILE_LOCK_EXCLUSIVE));
+  assert(!file_2->lock(grnxx::storage::FILE_LOCK_SHARED |
+                       grnxx::storage::FILE_LOCK_NONBLOCKING));
+  assert(!file_2->lock(grnxx::storage::FILE_LOCK_EXCLUSIVE |
+                       grnxx::storage::FILE_LOCK_NONBLOCKING));
+  assert(file_1->unlock());
+
+  file_1.reset();
+  file_2.reset();
+  grnxx::storage::File::unlink(FILE_PATH);
+}
+
+void test_file_sync() {
+  std::unique_ptr<grnxx::storage::File> file(
+      grnxx::storage::File::create(nullptr, grnxx::storage::FILE_TEMPORARY));
+  assert(file);
+
+  assert(file->sync());
+}
+
+void test_file_resize_and_size() {
+  std::unique_ptr<grnxx::storage::File> file(
+      grnxx::storage::File::create(nullptr, grnxx::storage::FILE_TEMPORARY));
+  assert(file);
+
+  assert(file->size() == 0);
+  assert(file->resize(65536));
+  assert(file->size() == 65536);
+  assert(file->resize(1024));
+  assert(file->size() == 1024);
+  assert(!file->resize(-1));
+}
+
+void test_file_path() {
+  const char FILE_PATH[] = "temp.grn";
+  std::unique_ptr<grnxx::storage::File> file;
+
+  file.reset(grnxx::storage::File::create(FILE_PATH));
+  assert(std::strcmp(file->path(), FILE_PATH) == 0);
+
+  file.reset(grnxx::storage::File::create(FILE_PATH,
+                                          grnxx::storage::FILE_TEMPORARY));
+  assert(std::strcmp(file->path(), FILE_PATH) != 0);
+
+  assert(grnxx::storage::File::unlink(FILE_PATH));
+}
+
+void test_file_handle() {
+  std::unique_ptr<grnxx::storage::File> file(
+      grnxx::storage::File::create(nullptr, grnxx::storage::FILE_TEMPORARY));
+  assert(file);
+
+  assert(file->handle());
+}
+
+void test_path() {
+  test_full_path();
+  test_unique_path();
+}
+
+void test_file() {
+  test_file_create();
+  test_file_open();
+  test_file_open_or_create();
+  test_file_exists_and_unlink();
+  test_file_lock_and_unlock();
+  test_file_sync();
+  test_file_resize_and_size();
+  test_file_path();
+  test_file_handle();
+}
+
+}  // namespace
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  test_path();
+  test_file();
+
+  return 0;
+}




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