[Groonga-commit] groonga/grnxx [master] Copy files from the private repository.

Back to archive index

susumu.yata null+****@clear*****
Wed Nov 28 16:36:40 JST 2012


susumu.yata	2012-11-28 16:36:40 +0900 (Wed, 28 Nov 2012)

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

  Log:
    Copy files from the private repository.

  Added files:
    .gitignore
    AUTHORS
    COPYING
    COPYING-GPL-3.0
    COPYING-LGPL-2.1
    COPYING-LGPL-3.0
    ChangeLog
    INSTALL
    Makefile.am
    NEWS
    README
    configure.ac
    lib/Makefile.am
    lib/backtrace.cpp
    lib/backtrace.hpp
    lib/basic.hpp
    lib/db/Makefile.am
    lib/db/array.cpp
    lib/db/array.hpp
    lib/db/blob_vector.cpp
    lib/db/blob_vector.hpp
    lib/db/vector.hpp
    lib/db/vector_base.cpp
    lib/db/vector_base.hpp
    lib/duration.cpp
    lib/duration.hpp
    lib/error.cpp
    lib/error.hpp
    lib/exception.hpp
    lib/features.hpp
    lib/flags_impl.hpp
    lib/grnxx.cpp
    lib/grnxx.hpp
    lib/intrinsic.hpp
    lib/io/Makefile.am
    lib/io/block.cpp
    lib/io/block.hpp
    lib/io/chunk.cpp
    lib/io/chunk.hpp
    lib/io/file-posix.cpp
    lib/io/file-posix.hpp
    lib/io/file-windows.cpp
    lib/io/file-windows.hpp
    lib/io/file.cpp
    lib/io/file.hpp
    lib/io/file_info-impl.cpp
    lib/io/file_info-impl.hpp
    lib/io/file_info.cpp
    lib/io/file_info.hpp
    lib/io/flags.cpp
    lib/io/flags.hpp
    lib/io/path.cpp
    lib/io/path.hpp
    lib/io/pool-impl.cpp
    lib/io/pool-impl.hpp
    lib/io/pool.cpp
    lib/io/pool.hpp
    lib/io/view-posix.cpp
    lib/io/view-posix.hpp
    lib/io/view-windows.cpp
    lib/io/view-windows.hpp
    lib/io/view.cpp
    lib/io/view.hpp
    lib/lock.hpp
    lib/logger.cpp
    lib/logger.hpp
    lib/mutex.cpp
    lib/mutex.hpp
    lib/os.cpp
    lib/os.hpp
    lib/recycler.cpp
    lib/recycler.hpp
    lib/string.cpp
    lib/string.hpp
    lib/string_builder.cpp
    lib/string_builder.hpp
    lib/string_format.hpp
    lib/thread.cpp
    lib/thread.hpp
    lib/time.cpp
    lib/time.hpp
    test/Makefile.am
    test/test_backtrace.cpp
    test/test_db_array.cpp
    test/test_db_blob_vector.cpp
    test/test_db_vector.cpp
    test/test_duration.cpp
    test/test_error.cpp
    test/test_exception.cpp
    test/test_features.cpp
    test/test_grnxx.cpp
    test/test_intrinsic.cpp
    test/test_io_file.cpp
    test/test_io_file_info.cpp
    test/test_io_path.cpp
    test/test_io_pool.cpp
    test/test_io_view.cpp
    test/test_logger.cpp
    test/test_mutex.cpp
    test/test_os.cpp
    test/test_recycler.cpp
    test/test_string.cpp
    test/test_string_builder.cpp
    test/test_string_format.cpp
    test/test_thread.cpp
    test/test_time.cpp

  Added: .gitignore (+51 -0) 100644
===================================================================
--- /dev/null
+++ .gitignore    2012-11-28 16:36:40 +0900 (cad19e3)
@@ -0,0 +1,51 @@
+.deps/
+.libs/
+*.a
+*.dat
+*.dll
+*.exe
+*.la
+*.lai
+*.lo
+*.log
+*.o
+*.so
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache/
+config.*
+configure
+depcomp
+install-sh
+libtool
+ltmain.sh
+m4/
+missing
+stamp-h1
+test/*.grn
+test/test_backtrace
+test/test_db_array
+test/test_db_blob_vector
+test/test_db_vector
+test/test_duration
+test/test_error
+test/test_exception
+test/test_features
+test/test_grnxx
+test/test_intrinsic
+test/test_io_alpha_pool
+test/test_io_file
+test/test_io_file_info
+test/test_io_path
+test/test_io_pool
+test/test_io_view
+test/test_logger
+test/test_mutex
+test/test_os
+test/test_recycler
+test/test_string
+test/test_string_builder
+test/test_string_format
+test/test_thread
+test/test_time

  Added: AUTHORS (+0 -0) 100644
===================================================================
--- /dev/null
+++ AUTHORS    2012-11-28 16:36:40 +0900 (e69de29)

  Added: COPYING (+16 -0) 100644
===================================================================
--- /dev/null
+++ COPYING    2012-11-28 16:36:40 +0900 (96f53b9)
@@ -0,0 +1,16 @@
+grnxx - An open-source fulltext search engine and column store.
+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

  Added: COPYING-GPL-3.0 (+674 -0) 100644
===================================================================
--- /dev/null
+++ COPYING-GPL-3.0    2012-11-28 16:36:40 +0900 (94a9ed0)
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.

  Added: COPYING-LGPL-2.1 (+502 -0) 100644
===================================================================
--- /dev/null
+++ COPYING-LGPL-2.1    2012-11-28 16:36:40 +0900 (4362b49)
@@ -0,0 +1,502 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!

  Added: COPYING-LGPL-3.0 (+165 -0) 100644
===================================================================
--- /dev/null
+++ COPYING-LGPL-3.0    2012-11-28 16:36:40 +0900 (65c5ca8)
@@ -0,0 +1,165 @@
+                   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.

  Added: ChangeLog (+0 -0) 100644
===================================================================
--- /dev/null
+++ ChangeLog    2012-11-28 16:36:40 +0900 (e69de29)

  Added: INSTALL (+0 -0) 100644
===================================================================
--- /dev/null
+++ INSTALL    2012-11-28 16:36:40 +0900 (e69de29)

  Added: Makefile.am (+8 -0) 100644
===================================================================
--- /dev/null
+++ Makefile.am    2012-11-28 16:36:40 +0900 (926881d)
@@ -0,0 +1,8 @@
+ACLOCAL_AMFLAGS = -I m4
+
+SUBDIRS = lib test
+
+EXTRA_DIST =			\
+	COPYING-GPL-3.0		\
+	COPYING-LGPL-2.1	\
+	COPYING-LGPL-3.0

  Added: NEWS (+0 -0) 100644
===================================================================
--- /dev/null
+++ NEWS    2012-11-28 16:36:40 +0900 (e69de29)

  Added: README (+0 -0) 100644
===================================================================
--- /dev/null
+++ README    2012-11-28 16:36:40 +0900 (e69de29)

  Added: configure.ac (+59 -0) 100644
===================================================================
--- /dev/null
+++ configure.ac    2012-11-28 16:36:40 +0900 (94a0b41)
@@ -0,0 +1,59 @@
+#                                               -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ([2.67])
+AC_INIT([grnxx], [0.0.0], [groonga �� razil.jp])
+AC_CONFIG_SRCDIR([lib/grnxx.hpp])
+AC_CONFIG_HEADERS(config.h)
+AM_INIT_AUTOMAKE
+
+# Checks for programs.
+LT_INIT
+AC_PROG_CXX
+AC_PROG_INSTALL
+
+AC_CONFIG_MACRO_DIR([m4])
+
+CXXFLAGS="${CXXFLAGS} -Wall -Wextra -std=c++0x -fno-strict-aliasing"
+
+# Checks for libraries.
+
+AC_ARG_ENABLE([backtrace],
+              [AS_HELP_STRING([--enable-backtrace],
+                              [show backtrace on error [default=no]])],
+              [],
+              [enable_backtrace="no"])
+if test "x$enable_backtrace" != "xno"; then
+  AC_CHECK_FUNC([backtrace],
+                [AC_DEFINE([HAVE_BACKTRACE], [1],
+                           [Define to 1 if you have the `backtrace' function])])
+  AC_CHECK_LIB([bfd], [bfd_openr])
+fi
+
+AC_CHECK_LIB([rt], [clock_gettime])
+
+# Checks for header files.
+
+# Checks for typedefs, structures, and compiler characteristics.
+
+# Checks for library functions.
+
+AC_CONFIG_FILES([Makefile
+                 lib/Makefile
+                 lib/db/Makefile
+                 lib/io/Makefile
+                 test/Makefile])
+AC_OUTPUT
+
+echo
+echo "$PACKAGE_NAME $PACKAGE_VERSION configuration:"
+echo "-----------------------"
+echo "  CXX:                   ${CXX}"
+echo "  CXXFLAGS:              ${CXXFLAGS}"
+echo "  Libraries:             ${LIBS}"
+echo
+echo "Paths:"
+echo "  Install path prefix:   ${prefix}"
+echo
+
+echo "Now type 'make' to build $PACKAGE_NAME $PACKAGE_VERSION!"

  Added: lib/Makefile.am (+43 -0) 100644
===================================================================
--- /dev/null
+++ lib/Makefile.am    2012-11-28 16:36:40 +0900 (a1f1646)
@@ -0,0 +1,43 @@
+SUBDIRS = db io
+
+lib_LTLIBRARIES = libgrnxx.la
+
+libgrnxx_la_LIBADD =		\
+	db/libgrnxxdb.la	\
+	io/libgrnxxio.la
+
+libgrnxx_la_SOURCES =		\
+	backtrace.cpp		\
+	duration.cpp		\
+	error.cpp		\
+	grnxx.cpp		\
+	logger.cpp		\
+	mutex.cpp		\
+	os.cpp			\
+	recycler.cpp		\
+	string.cpp		\
+	string_builder.cpp	\
+	thread.cpp		\
+	time.cpp
+
+libgrnxx_includedir = ${includedir}/grnxx
+libgrnxx_include_HEADERS =	\
+	backtrace.hpp		\
+	basic.hpp		\
+	duration.hpp		\
+	error.hpp		\
+	exception.hpp		\
+	features.hpp		\
+	flags_impl.hpp		\
+	grnxx.hpp		\
+	intrinsic.hpp		\
+	lock.hpp		\
+	logger.hpp		\
+	mutex.hpp		\
+	os.hpp			\
+	recycler.hpp		\
+	string.hpp		\
+	string_builder.hpp	\
+	string_format.hpp	\
+	thread.hpp		\
+	time.hpp

  Added: lib/backtrace.cpp (+416 -0) 100644
===================================================================
--- /dev/null
+++ lib/backtrace.cpp    2012-11-28 16:36:40 +0900 (2fb1b2d)
@@ -0,0 +1,416 @@
+/*
+  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 "backtrace.hpp"
+
+#include "../config.h"
+
+#ifdef HAVE_BACKTRACE
+
+#include <cxxabi.h>
+#include <dlfcn.h>
+#include <execinfo.h>
+
+#ifdef HAVE_LIBBFD
+# include <bfd.h>
+# ifdef GRNXX_APPLE
+#  include <mach-o/dyld.h>
+# else  // GRNXX_APPLE
+#  include <link.h>
+# endif  // GRNXX_APPLE
+#endif  // HAVE_LIBBFD
+
+#include <cstdlib>
+#include <sstream>
+
+#include "lock.hpp"
+
+namespace grnxx {
+namespace {
+
+class Resolver {
+ public:
+  static bool resolve(void *address, std::ostream *stream);
+
+ private:
+#ifdef HAVE_LIBBFD
+  bfd *bfd_;
+  asymbol **bfd_symbols_;
+
+  static bool bfd_initialized_;
+#endif  // HAVE_LIBBFD
+
+  Resolver();
+  ~Resolver();
+
+#ifdef HAVE_LIBBFD
+  bool resolve_(const char *image_name, bfd_vma address, std::ostream *stream);
+#endif  // HAVE_LIBBFD
+
+  Resolver(const Resolver &);
+  Resolver &operator=(const Resolver &);
+};
+
+#ifdef HAVE_LIBBFD
+struct Match {
+  const char *image_name;
+  uintptr_t address;
+};
+
+// FIXME: The default image name depends on the environment.
+// http://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe
+const char DEFAULT_IMAGE_NAME[] = "/proc/self/exe";
+
+# ifdef GRNXX_APPLE
+// TODO: Not tested yet.
+bool find_match(Match *match) {
+  const uint32_t image_count = ::_dyld_image_count();
+  for (uint32_t image_id = 0; image_id < image_count; ++image_id) {
+    const struct mach_header *header = ::_dyld_get_image_header(image_id);
+    const struct load_command *command;
+    if (header->magic == MH_MAGIC_64) {
+      command = reinterpret_cast<const struct load_command *>(
+          reinterpret_cast<const struct mach_header_64 *>(header) + 1);
+    } else {
+      command = reinterpret_cast<const struct load_command *>(header + 1);
+    }
+
+    const intptr_t slide = ::_dyld_get_image_vmaddr_slide(image_id);
+    for (uint32_t command_id = 0; command_id < header->ncmds; ++command_id) {
+      switch (command->cmd) {
+        case LC_SEGMENT: {
+          const struct segment_command *segment =
+              reinterpret_cast<const struct segment_command *>(command);
+          if ((address >= (segment->vmaddr + slide)) &&
+              (address < (segment->vmaddr + slide + segment->vmsize))) {
+            match.address = segment->vmaddr - slide;
+            match.image_name = ::_dyld_get_image_name(image_id);
+            return true;
+          }
+          break;
+        }
+        case LC_SEGMENT_64: {
+          const struct segment_command_64 *segment =
+              reinterpret_cast<const struct segment_command_64 *>(command);
+          if ((address >= (segment->vmaddr + slide)) &&
+              (address < (segment->vmaddr + slide + segment->vmsize))) {
+            match.address = segment->vmaddr - slide;
+            match.image_name = ::_dyld_get_image_name(i);
+            return true;
+          }
+          break;
+        }
+      }
+      command = reinterpret_cast<const struct load_command *>(
+          reinterpret_cast<const char *>(command) + cmd->cmdsize);
+    }
+  }
+  return false;
+}
+# else  // GRNXX_APPLE
+int find_match_callback(struct dl_phdr_info *info, size_t,
+                        void *user_data) {
+  Match * const match = static_cast<Match *>(user_data);
+  for (ElfW(Half) i = 0; i < info->dlpi_phnum; ++i) {
+    if (info->dlpi_phdr[i].p_type == PT_LOAD) {
+      ElfW(Addr) address = info->dlpi_phdr[i].p_vaddr + info->dlpi_addr;
+      if ((match->address >= address) &&
+          (match->address < (address + info->dlpi_phdr[i].p_memsz))) {
+        if (!info->dlpi_name || (info->dlpi_name[0] == '\0')) {
+          match->image_name = DEFAULT_IMAGE_NAME;
+        } else {
+          match->image_name = info->dlpi_name;
+        }
+        match->address -= (uintptr_t)info->dlpi_addr;
+        return 1;
+      }
+    }
+  }
+  return 0;
+}
+
+bool find_match(Match *match) {
+  return dl_iterate_phdr(find_match_callback, match) != 0;
+}
+# endif  // GRNXX_APPLE
+
+bool Resolver::bfd_initialized_ = false;
+
+bool Resolver::resolve(void *address, std::ostream *stream) {
+  // Just to be safe, call ::bfd_init() only once.
+  if (!bfd_initialized_) {
+    ::bfd_init();
+    bfd_initialized_ = true;
+  }
+
+  Match match;
+  match.address = reinterpret_cast<uintptr_t>(address) - 1;
+  if (find_match(&match)) {
+    *stream << address;
+    return Resolver().resolve_(match.image_name,
+                               static_cast<bfd_vma>(match.address), stream);
+  }
+  return false;
+}
+
+class Detail {
+ public:
+  Detail(asymbol **symbols_, bfd_vma address_)
+    : symbols(symbols_), address(address_), filename(nullptr),
+      function(nullptr), line(0), found(false) {}
+
+  asymbol **symbols;
+  bfd_vma address;
+  const char *filename;
+  const char *function;
+  unsigned int line;
+  bool found;
+};
+
+void callback_for_each_section(bfd *bfd_, asection *section, void *user_data) {
+  Detail * const detail = static_cast<Detail *>(user_data);
+  if (detail->found) {
+    return;
+  }
+
+  if ((bfd_get_section_flags(bfd_, section) & SEC_ALLOC) == 0) {
+    return;
+  }
+
+  bfd_vma address = bfd_get_section_vma(bfd_, section);
+  if (detail->address < address) {
+    return;
+  }
+
+  bfd_size_type size = bfd_section_size(bfd_, section);
+  if (detail->address >= (address + size)) {
+    return;
+  }
+
+  if (bfd_find_nearest_line(bfd_, section, detail->symbols,
+                            detail->address - address, &detail->filename,
+                            &detail->function, &detail->line) != 0) {
+    detail->found = true;
+  }
+}
+
+Resolver::Resolver() : bfd_(nullptr), bfd_symbols_(nullptr) {}
+
+Resolver::~Resolver() {
+  if (bfd_symbols_) {
+    std::free(bfd_symbols_);
+  }
+  if (bfd_) {
+    ::bfd_close(bfd_);
+  }
+}
+
+bool Resolver::resolve_(const char *image_name, bfd_vma address,
+                        std::ostream *stream) {
+  bfd_ = ::bfd_openr(image_name, nullptr);
+  if (!bfd_) {
+    return false;
+  }
+
+  if (::bfd_check_format(bfd_, ::bfd_archive)) {
+    return false;
+  }
+
+  char **matches = nullptr;
+  if (!::bfd_check_format_matches(bfd_, ::bfd_object, &matches)) {
+    if (::bfd_get_error() == ::bfd_error_file_ambiguously_recognized) {
+      std::free(matches);
+    }
+    return false;
+  }
+
+  if ((bfd_get_file_flags(bfd_) & HAS_SYMS) == 0) {
+    return false;
+  }
+
+  unsigned int size = 0;
+  long num_symbols = bfd_read_minisymbols(bfd_, false,
+                                          (void **)&bfd_symbols_, &size);
+  if (num_symbols == 0) {
+    std::free(bfd_symbols_);
+    bfd_symbols_ = nullptr;
+    num_symbols = bfd_read_minisymbols(bfd_, true,
+                                       (void**)&bfd_symbols_, &size);
+  }
+  if (num_symbols <= 0) {
+    return false;
+  }
+
+  Detail detail(bfd_symbols_, address);
+  ::bfd_map_over_sections(bfd_, callback_for_each_section, &detail);
+  if (!detail.found) {
+    return false;
+  }
+
+  *stream << ": ";
+  if (!detail.function || (detail.function[0] == '\0')) {
+    *stream << "???";
+  } else {
+    int status = 0;
+    char *demangled_function =
+        ::abi::__cxa_demangle(detail.function, 0, 0, &status);
+    if (demangled_function) {
+      *stream << demangled_function;
+      std::free(demangled_function);
+    } else {
+      *stream << detail.function;
+    }
+  }
+
+  *stream << " (";
+  if (!detail.filename || (detail.filename[0] == '\0') || (detail.line == 0)) {
+    *stream << "???:???";
+  } else {
+    *stream << ::basename(detail.filename) << ':' << detail.line;
+  }
+
+  if (std::strcmp(image_name, DEFAULT_IMAGE_NAME) != 0) {
+    *stream << " in " << image_name;
+  }
+  *stream << ')';
+
+//  bfd_find_inliner_info(bfd_, &detail.filename, &detail.function,
+//                        &detail.line);
+
+  return true;
+}
+#else  // HAVE_LIBBFD
+bool Resolver::resolve(void *, std::ostream *) {
+  return false;
+}
+#endif  // HAVE_LIBBFD
+
+}  // namespace
+
+bool Backtrace::backtrace(int skip_count, std::vector<void *> *addresses) try {
+  static Mutex mutex;
+  Lock lock(&mutex);
+
+  if ((skip_count < BACKTRACE_MIN_SKIP_COUNT) ||
+      (skip_count > BACKTRACE_MAX_SKIP_COUNT)) {
+    return false;
+  }
+  if (!addresses) {
+    return false;
+  }
+  ++skip_count;
+
+  std::vector<void *> buf(BACKTRACE_MIN_BUF_SIZE);
+  for ( ; ; ) {
+    const int depth = ::backtrace(&buf[0], static_cast<int>(buf.size()));
+    if (depth < static_cast<int>(buf.size())) {
+      if (depth <= skip_count) {
+        return false;
+      }
+      buf.resize(depth);
+      break;
+    }
+    if (buf.size() >= BACKTRACE_MAX_BUF_SIZE) {
+      break;
+    }
+    buf.resize(buf.size() * 2);
+  }
+  addresses->assign(buf.begin() + skip_count, buf.end());
+  return true;
+} catch (...) {
+  return false;
+}
+
+bool Backtrace::resolve(void *address, std::string *entry) try {
+  static Mutex mutex;
+  Lock lock(&mutex);
+
+  if (!address || !entry) {
+    return false;
+  }
+
+  std::ostringstream stream;
+  if (Resolver::resolve(address, &stream)) {
+    entry->assign(stream.str());
+    return true;
+  }
+
+  char **symbols = ::backtrace_symbols(&address, 1);
+  if (!symbols) {
+    return false;
+  }
+  char *first_symbol = symbols[0];
+  std::free(symbols);
+  if (!first_symbol) {
+    return false;
+  }
+  entry->append(first_symbol);
+  return true;
+} catch (...) {
+  return false;
+}
+
+bool Backtrace::pretty_backtrace(int skip_count,
+                                 std::vector<std::string> *entries) try {
+  if ((skip_count < BACKTRACE_MIN_SKIP_COUNT) ||
+      (skip_count > BACKTRACE_MAX_SKIP_COUNT)) {
+    return false;
+  }
+  if (!entries) {
+    return false;
+  }
+  ++skip_count;
+
+  std::vector<void *> addresses;
+  if (!backtrace(skip_count, &addresses)) {
+    return false;
+  }
+
+  entries->clear();
+  for (std::size_t i = 0; i < addresses.size(); ++i) {
+    std::string entry;
+    if (resolve(addresses[i], &entry)) {
+      entry.insert(0, (i == 0) ? "at " : "by ", 3);
+      entries->push_back(entry);
+    }
+  }
+  return true;
+} catch (...) {
+  return false;
+}
+
+}  // namespace grnxx
+
+#else  // HAVE_BACKTRACE
+
+namespace grnxx {
+
+bool Backtrace::backtrace(int, std::vector<void *> *) {
+  return false;
+}
+
+bool Backtrace::resolve(void *, std::string *) {
+  return false;
+}
+
+bool Backtrace::pretty_backtrace(int, std::vector<std::string> *) {
+  return false;
+}
+
+}  // namespace grnxx
+
+#endif  // HAVE_BACKTRACE

  Added: lib/backtrace.hpp (+61 -0) 100644
===================================================================
--- /dev/null
+++ lib/backtrace.hpp    2012-11-28 16:36:40 +0900 (e257ecd)
@@ -0,0 +1,61 @@
+/*
+  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_BACKTRACE_HPP
+#define GRNXX_BACKTRACE_HPP
+
+#include <string>
+#include <vector>
+
+#include "basic.hpp"
+
+namespace grnxx {
+
+const int BACKTRACE_MIN_SKIP_COUNT = 0;
+const int BACKTRACE_MAX_SKIP_COUNT = 16;
+
+const size_t BACKTRACE_MIN_BUF_SIZE = 16;
+const size_t BACKTRACE_MAX_BUF_SIZE = 1024;
+
+class Backtrace {
+ public:
+  // The following functions return true on success, false on failure.
+
+  // backtrace() writes a list of addresses to function calls into 'addresses'.
+  // 'skip_count' specfies the number of function calls to be skipped.
+  static bool backtrace(int skip_count, std::vector<void *> *addresses);
+
+  // resolve() writes a function call referred to by 'address' in
+  // human-readable format into 'entry'.
+  static bool resolve(void *address, std::string *entry);
+
+  // pretty_backtrace() writes a list of function calls in human-readable
+  // format. 'skip_count' specfies the number of function calls to be skipped.
+  static bool pretty_backtrace(int skip_count,
+                               std::vector<std::string> *entries);
+
+ private:
+  Backtrace();
+  ~Backtrace();
+
+  Backtrace(const Backtrace &);
+  Backtrace &operator=(const Backtrace &);
+};
+
+}  // namespace grnxx
+
+#endif  // GRNXX_BACKTRACE_HPP

  Added: lib/basic.hpp (+53 -0) 100644
===================================================================
--- /dev/null
+++ lib/basic.hpp    2012-11-28 16:36:40 +0900 (cfe684a)
@@ -0,0 +1,53 @@
+/*
+  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_BASIC_HPP
+#define GRNXX_BASIC_HPP
+
+#include "features.hpp"
+
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
+#include <exception>
+#include <iosfwd>
+#include <limits>
+#include <memory>
+#include <new>
+#include <type_traits>
+#include <utility>
+
+namespace grnxx {
+
+using std::size_t;
+
+using std::int8_t;
+using std::int16_t;
+using std::int32_t;
+using std::int64_t;
+
+using std::uint8_t;
+using std::uint16_t;
+using std::uint32_t;
+using std::uint64_t;
+
+using std::intptr_t;
+using std::uintptr_t;
+
+}  // namespace grnxx
+
+#endif  // GRNXX_BASIC_HPP

  Added: lib/db/Makefile.am (+13 -0) 100644
===================================================================
--- /dev/null
+++ lib/db/Makefile.am    2012-11-28 16:36:40 +0900 (a6c3ab4)
@@ -0,0 +1,13 @@
+noinst_LTLIBRARIES = libgrnxxdb.la
+
+libgrnxxdb_la_SOURCES =		\
+	array.cpp		\
+	blob_vector.cpp		\
+	vector_base.cpp
+
+libgrnxxdb_includedir = ${includedir}/grnxx/db
+libgrnxxdb_include_HEADERS =	\
+	array.hpp		\
+	blob_vector.hpp		\
+	vector.hpp		\
+	vector_base.hpp

  Added: lib/db/array.cpp (+156 -0) 100644
===================================================================
--- /dev/null
+++ lib/db/array.cpp    2012-11-28 16:36:40 +0900 (00c8839)
@@ -0,0 +1,156 @@
+/*
+  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 "array.hpp"
+
+namespace grnxx {
+namespace db {
+
+void ArrayHeader::initialize(uint64_t value_size, uint64_t array_size) {
+  std::memset(this, 0, sizeof(ArrayHeader));
+  value_size_ = value_size;
+  array_size_ = array_size;
+}
+
+ArrayImpl::ArrayImpl()
+  : pool_(), block_id_(io::BLOCK_INVALID_ID),
+    header_(nullptr), address_(nullptr) {}
+
+ArrayImpl::ArrayImpl(io::Pool *pool, uint64_t value_size, uint64_t array_size)
+  : pool_(), block_id_(io::BLOCK_INVALID_ID),
+    header_(nullptr), address_(nullptr) {
+  if (!pool) {
+    GRNXX_ERROR() << "invalid argument: pool = " << pool;
+    GRNXX_THROW();
+  }
+  if (value_size == 0) {
+    GRNXX_ERROR() << "invalid argument: value_size = " << value_size;
+    GRNXX_THROW();
+  }
+
+  pool_ = *pool;
+
+  const io::BlockInfo *block_info =
+      pool->create_block(ARRAY_HEADER_SIZE + (value_size * array_size));
+
+  header_ = static_cast<ArrayHeader *>(pool->get_block_address(*block_info));
+  header_->initialize(value_size, array_size);
+
+  block_id_ = block_info->id();
+  address_ = header_ + 1;
+}
+
+ArrayImpl::ArrayImpl(io::Pool *pool, uint32_t block_id)
+  : pool_(), block_id_(io::BLOCK_INVALID_ID),
+    header_(nullptr), address_(nullptr) {
+  if (!pool) {
+    GRNXX_ERROR() << "invalid argument: pool = " << pool;
+    GRNXX_THROW();
+  }
+
+  pool_ = *pool;
+
+  const io::BlockInfo *block_info = pool->get_block_info(block_id);
+  if (block_info->size() < ARRAY_HEADER_SIZE) {
+    GRNXX_ERROR() << "too small block: block_size = " << block_info->size();
+    GRNXX_THROW();
+  }
+
+  block_id_ = block_info->id();
+  header_ = static_cast<ArrayHeader *>(pool->get_block_address(*block_info));
+  address_ = header_ + 1;
+
+  if (value_size() == 0) {
+    GRNXX_ERROR() << "invalid parameter: value_size = " << value_size();
+    GRNXX_THROW();
+  }
+
+  const uint64_t required_block_size =
+      ARRAY_HEADER_SIZE + (value_size() * array_size());
+  if (block_info->size() < required_block_size) {
+    GRNXX_ERROR() << "block size conflict: block_size = " << block_info->size()
+                  << ", required_block_size = " << required_block_size;
+    GRNXX_THROW();
+  }
+}
+
+ArrayImpl::~ArrayImpl() {}
+
+ArrayImpl::ArrayImpl(ArrayImpl &&array)
+  : pool_(std::move(array.pool_)), block_id_(std::move(array.block_id_)),
+    header_(std::move(array.header_)), address_(std::move(array.address_)) {}
+
+ArrayImpl &ArrayImpl::operator=(ArrayImpl &&array) {
+  pool_ = std::move(array.pool_);
+  block_id_ = std::move(array.block_id_);
+  header_ = std::move(array.header_);
+  address_ = std::move(array.address_);
+  return *this;
+}
+
+void ArrayImpl::create(io::Pool *pool, uint64_t value_size,
+                       uint64_t array_size) {
+  ArrayImpl(pool, value_size, array_size).swap(*this);
+}
+
+void ArrayImpl::open(io::Pool *pool, uint32_t block_id) {
+  ArrayImpl(pool, block_id).swap(*this);
+}
+
+void ArrayImpl::swap(ArrayImpl &array) {
+  using std::swap;
+  swap(pool_, array.pool_);
+  swap(block_id_, array.block_id_);
+  swap(header_, array.header_);
+  swap(address_, array.address_);
+}
+
+void ArrayImpl::unlink(io::Pool *pool, uint32_t block_id) {
+  pool->free_block(block_id);
+}
+
+StringBuilder &ArrayHeader::write_to(StringBuilder &builder) const {
+  if (!builder) {
+    return builder;
+  }
+
+  return builder << "{ value_size = " << value_size_
+                 << ", array_size = " << array_size_ << " }";
+}
+
+StringBuilder &ArrayImpl::write_to(StringBuilder &builder) const {
+  if (!builder) {
+    return builder;
+  }
+
+  if (!pool_) {
+    return builder << "n/a";
+  }
+
+  builder << "{ pool = " << pool_.path()
+          << ", block_id = " << block_id_
+          << ", header = ";
+  if (header_) {
+    builder << *header_;
+  } else {
+    builder << "n/a";
+  }
+  return builder << ", address = " << address_ << " }";
+}
+
+}  // namespace db
+}  // namespace grnxx

  Added: lib/db/array.hpp (+187 -0) 100644
===================================================================
--- /dev/null
+++ lib/db/array.hpp    2012-11-28 16:36:40 +0900 (49b513b)
@@ -0,0 +1,187 @@
+/*
+  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_DB_ARRAY_HPP
+#define GRNXX_DB_ARRAY_HPP
+
+#include "../exception.hpp"
+#include "../logger.hpp"
+#include "../io/pool.hpp"
+
+namespace grnxx {
+namespace db {
+
+const uint64_t ARRAY_HEADER_SIZE = 64;
+
+class ArrayHeader {
+ public:
+  void initialize(uint64_t value_size, uint64_t array_size);
+
+  uint64_t value_size() const {
+    return value_size_;
+  }
+  uint64_t array_size() const {
+    return array_size_;
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+ private:
+  uint64_t value_size_;
+  uint64_t array_size_;
+  uint8_t reserved_[ARRAY_HEADER_SIZE - 16];
+};
+
+static_assert(sizeof(ArrayHeader) == ARRAY_HEADER_SIZE,
+              "sizeof(ArrayHeader) is wrong");
+
+class ArrayImpl {
+ public:
+  ArrayImpl();
+  ArrayImpl(io::Pool *pool, uint64_t value_size, uint64_t array_size);
+  ArrayImpl(io::Pool *pool, uint32_t block_id);
+  ~ArrayImpl();
+
+  ArrayImpl(ArrayImpl &&array);
+  ArrayImpl &operator=(ArrayImpl &&array);
+
+  void create(io::Pool *pool, uint64_t value_size, uint64_t array_size);
+  void open(io::Pool *pool, uint32_t block_id);
+
+  uint32_t block_id() const {
+    return block_id_;
+  }
+  uint64_t value_size() const {
+    return header_->value_size();
+  }
+  uint64_t array_size() const {
+    return header_->array_size();
+  }
+  void *address() const {
+    return address_;
+  }
+
+  void swap(ArrayImpl &array);
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+  static void unlink(io::Pool *pool, uint32_t block_id);
+
+ private:
+  io::Pool pool_;
+  uint32_t block_id_;
+  ArrayHeader *header_;
+  void *address_;
+
+  ArrayImpl(const ArrayImpl &);
+  ArrayImpl &operator=(const ArrayImpl &);
+};
+
+inline void swap(ArrayImpl &lhs, ArrayImpl &rhs) {
+  lhs.swap(rhs);
+}
+
+template <typename T>
+class Array {
+ public:
+  typedef T Value;
+
+  Array() : impl_() {}
+  ~Array() {}
+
+  Array(Array &&array) : impl_(std::move(array.impl_)) {}
+  Array &operator=(Array &&array) {
+    impl_ = std::move(array.impl_);
+    return *this;
+  }
+
+  void create(io::Pool *pool, uint64_t size) {
+    impl_.create(pool, sizeof(Value), size);
+  }
+  void open(io::Pool *pool, uint32_t block_id) {
+    ArrayImpl new_impl;
+    new_impl.open(pool, block_id);
+    if (new_impl.value_size() != sizeof(Value)) {
+      GRNXX_ERROR() << "invalid value size: expected = " << sizeof(Value)
+                    << ", actual = " << new_impl.value_size();
+      GRNXX_THROW();
+    }
+    impl_ = std::move(new_impl);
+  }
+  void close() {
+    ArrayImpl().swap(impl_);
+  }
+
+  Value &operator[](uint64_t id) const {
+    return address()[id];
+  }
+
+  uint32_t block_id() const {
+    return impl_.block_id();
+  }
+  uint64_t size() const {
+    return impl_.array_size();
+  }
+  Value *address() const {
+    return static_cast<Value *>(impl_.address());
+  }
+
+  void swap(Array &array) {
+    impl_.swap(array.impl_);
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const {
+    return impl_.write_to(builder);
+  }
+
+  static void unlink(io::Pool *pool, uint32_t block_id) {
+    Array array;
+    array.open(pool, block_id);
+    array.close();
+    ArrayImpl::unlink(pool, block_id);
+  }
+
+ private:
+  ArrayImpl impl_;
+
+  Array(const Array &);
+  Array &operator=(const Array &);
+};
+
+template <typename T>
+inline void swap(Array<T> &lhs, Array<T> &rhs) {
+  lhs.swap(rhs);
+}
+
+inline StringBuilder &operator<<(StringBuilder &builder,
+                                 const ArrayHeader &header) {
+  return header.write_to(builder);
+}
+inline StringBuilder &operator<<(StringBuilder &builder,
+                                 const ArrayImpl &array) {
+  return array.write_to(builder);
+}
+template <typename T>
+inline StringBuilder &operator<<(StringBuilder &builder,
+                                 const Array<T> &array) {
+  return array.write_to(builder);
+}
+
+}  // namespace db
+}  // namespace grnxx
+
+#endif  // GRNXX_DB_ARRAY_HPP

  Added: lib/db/blob_vector.cpp (+816 -0) 100644
===================================================================
--- /dev/null
+++ lib/db/blob_vector.cpp    2012-11-28 16:36:40 +0900 (26c7f34)
@@ -0,0 +1,816 @@
+/*
+  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 <ostream>
+
+#include "../lock.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;
+
+  for (uint32_t i = 0; i < BLOB_VECTOR_MEDIUM_VALUE_STORES_NUM; ++i) {
+    medium_value_store_block_ids_[i] = io::BLOCK_INVALID_ID;
+  }
+
+  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;
+  }
+
+  inter_process_mutex_.clear();
+  medium_value_store_mutex_.clear();
+  large_value_store_mutex_.clear();
+}
+
+BlobVector::BlobVector()
+  : pool_(),
+    block_info_(nullptr),
+    header_(nullptr),
+    recycler_(nullptr),
+    cells_(),
+    medium_value_stores_(),
+    large_value_store_(),
+    inter_thread_mutex_() {}
+
+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;
+}
+
+void BlobVector::create(io::Pool pool) {
+  if (!pool) {
+    GRNXX_ERROR() << "invalid argument: pool = " << pool;
+    GRNXX_THROW();
+  }
+
+  BlobVector new_vector;
+  new_vector.create_vector(pool);
+  *this = std::move(new_vector);
+}
+
+void BlobVector::open(io::Pool pool, uint32_t block_id) {
+  if (!pool) {
+    GRNXX_ERROR() << "invalid argument: pool = " << pool;
+    GRNXX_THROW();
+  }
+
+  BlobVector new_vector;
+  new_vector.open_vector(pool, block_id);
+  *this = std::move(new_vector);
+}
+
+void BlobVector::close() {
+  if (!is_open()) {
+    GRNXX_ERROR() << "failed to close vector";
+    GRNXX_THROW();
+  }
+  *this = BlobVector();
+}
+
+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.is_open()) {
+        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_.is_open()) {
+        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 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();
+  }
+
+  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);
+  }
+
+  // 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;
+  }
+  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_);
+}
+
+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);
+
+  // TODO
+}
+
+void BlobVector::create_vector(io::Pool pool) {
+  pool_ = pool;
+  block_info_ = pool.create_block(sizeof(BlobVectorHeader));
+
+  try {
+    cells_.create(&pool_, BlobVectorCell());
+  } catch (...) {
+    pool_.free_block(*block_info_);
+    throw;
+  }
+
+  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());
+
+  recycler_ = pool.mutable_recycler();
+}
+
+void BlobVector::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 header!
+
+  recycler_ = pool.mutable_recycler();
+
+  // Open the core table.
+  cells_.open(&pool, header_->cells_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.is_open()) {
+    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.id_max()) {
+      GRNXX_ERROR() << "store is full: offset = " << offset
+                    << ", id_max = " << store.id_max();
+      GRNXX_THROW();
+    }
+    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_.is_open()) {
+    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;
+      }
+    }
+
+    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;
+      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();
+        }
+      }
+
+      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_.is_open()) {
+        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;
+    }
+    case BLOB_VECTOR_HUGE_VALUE: {
+      pool_.free_block(cell.huge_value_cell().block_id());
+      break;
+    }
+  }
+}
+
+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.is_open()) {
+    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.is_open()) {
+      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_.is_open()) {
+    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_.is_open()) {
+      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);
+    }
+  }
+}
+
+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());
+  }
+
+  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);
+  } 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);
+  }
+}
+
+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);
+  } 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());
+    }
+  }
+}
+
+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";
+    }
+ }
+}
+
+StringBuilder &operator<<(StringBuilder &builder, BlobVectorLargeValueFlags flags) {
+  if (!builder) {
+    return builder;
+  }
+
+  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 << " | ";
+      }
+      builder << "BLOB_VECTOR_LARGE_VALUE_HAS_PREV";
+    }
+    return builder;
+  } else {
+    return builder << "0";
+  }
+}
+
+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";
+    }
+    default: {
+      return builder << "n/a";
+    }
+  }
+}
+
+StringBuilder &operator<<(StringBuilder &builder, BlobVectorCellFlags flags) {
+  BlobVectorCellFlags clone = flags;
+  clone.flags &= BLOB_VECTOR_VALUE_TYPE_MASK;
+  return builder << clone.value_type;
+}
+
+StringBuilder &BlobVectorHeader::write_to(StringBuilder &builder) const {
+  if (!builder) {
+    return builder;
+  }
+
+  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 << ", ";
+      }
+      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;
+  }
+
+  builder << "{ type = " << type()
+          << ", flags = " << flags()
+          << ", capacity = " << capacity()
+          << ", prev_capacity = " << prev_capacity();
+
+  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 << " }";
+}
+
+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();
+      break;
+    }
+    case BLOB_VECTOR_LARGE_VALUE: {
+      builder << ", length = " << large_value_cell().length()
+              << ", offset = " << large_value_cell().offset();
+      break;
+    }
+    case BLOB_VECTOR_HUGE_VALUE: {
+      builder << ", block_id = " << huge_value_cell().block_id();
+      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_;
+  } else {
+    builder << "n/a";
+  }
+  return builder << ", inter_thread_mutex = " << inter_thread_mutex_ << " }";
+}
+
+}  // namespace db
+}  // namespace grnxx

  Added: lib/db/blob_vector.hpp (+554 -0) 100644
===================================================================
--- /dev/null
+++ lib/db/blob_vector.hpp    2012-11-28 16:36:40 +0900 (6bddced)
@@ -0,0 +1,554 @@
+/*
+  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_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;
+
+class BlobVectorHeader {
+ public:
+  void initialize(uint32_t cells_block_id, Duration frozen_duration);
+
+  uint32_t cells_block_id() const {
+    return cells_block_id_;
+  }
+  Duration frozen_duration() const {
+    return frozen_duration_;
+  }
+  uint32_t medium_value_store_block_ids(uint8_t store_id) const {
+    return medium_value_store_block_ids_[store_id];
+  }
+  uint64_t medium_value_store_next_offsets(uint8_t store_id) const {
+    return medium_value_store_next_offsets_[store_id];
+  }
+  uint32_t large_value_store_block_id() const {
+    return large_value_store_block_id_;
+  }
+  uint64_t rearmost_large_value_offset() const {
+    return rearmost_large_value_offset_;
+  }
+  uint64_t latest_frozen_large_value_offset() const {
+    return latest_frozen_large_value_offset_;
+  }
+  uint64_t oldest_idle_large_value_offsets(uint8_t list_id) const {
+    return oldest_idle_large_value_offsets_[list_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_medium_value_store_next_offsets(uint8_t store_id, uint64_t value) {
+    medium_value_store_next_offsets_[store_id] = value;
+  }
+  void set_large_value_store_block_id(uint32_t value) {
+    large_value_store_block_id_ = value;
+  }
+  void set_rearmost_large_value_offset(uint64_t value) {
+    rearmost_large_value_offset_ = value;
+  }
+  void set_latest_frozen_large_value_offset(uint64_t value) {
+    latest_frozen_large_value_offset_ = value;
+  }
+  void set_oldest_idle_large_value_offsets(uint8_t list_id, uint64_t value) {
+    oldest_idle_large_value_offsets_[list_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];
+  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
+};
+
+class BlobVectorLargeValueFlagsIdentifier;
+typedef FlagsImpl<BlobVectorLargeValueFlagsIdentifier, uint8_t>
+    BlobVectorLargeValueFlags;
+
+const BlobVectorLargeValueFlags BLOB_VECTOR_LARGE_VALUE_HAS_NEXT =
+    BlobVectorLargeValueFlags::define(0x01);
+const BlobVectorLargeValueFlags BLOB_VECTOR_LARGE_VALUE_HAS_PREV =
+    BlobVectorLargeValueFlags::define(0x02);
+
+class BlobVectorLargeValueHeader {
+ 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;
+  }
+
+  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;
+  }
+  uint64_t prev_capacity() const {
+    return static_cast<uint64_t>(prev_capacity_)
+         << BLOB_VECTOR_LARGE_VALUE_UNIT_SIZE_BITS;
+  }
+  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_);
+  }
+
+  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_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_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_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_;
+  };
+};
+
+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 {
+ 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 flags_and_length_ >> 5;
+  }
+  const void *value() const {
+    return value_;
+  }
+
+ private:
+  uint8_t flags_and_length_;
+  uint8_t value_[7];
+};
+
+class BlobVectorMediumValueCell {
+ 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))) {}
+
+  uint8_t store_id() const {
+    return store_id_;
+  }
+  uint64_t capacity() const {
+    return uint64_t(8) << store_id_;
+  }
+  uint64_t length() const {
+    return length_;
+  }
+  uint64_t offset() const {
+    return ((static_cast<uint64_t>(offset_high_) << 32) | offset_low_)
+        << (store_id_ + BLOB_VECTOR_MEDIUM_VALUE_UNIT_SIZE_BITS);
+  }
+
+ private:
+  uint8_t flags_;
+  uint8_t store_id_;
+  uint8_t length_;
+  uint8_t offset_high_;
+  uint32_t offset_low_;
+};
+
+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)) {}
+
+  uint64_t length() const {
+    return length_;
+  }
+  uint64_t offset() const {
+    return (static_cast<uint64_t>(offset_high_) << 32) | offset_low_;
+  }
+
+ private:
+  uint8_t flags_;
+  uint8_t offset_high_;
+  uint16_t length_;
+  uint32_t offset_low_;
+};
+
+class BlobVectorHugeValueCell {
+ public:
+  explicit BlobVectorHugeValueCell(uint32_t block_id)
+    : flags_(BLOB_VECTOR_HUGE_VALUE), reserved_(), block_id_(block_id) {
+    std::memset(reserved_, 0, sizeof(reserved_));
+  }
+
+  uint32_t block_id() const {
+    return block_id_;
+  }
+
+ private:
+  uint8_t flags_;
+  uint8_t reserved_[3];
+  uint32_t block_id_;
+};
+
+class BlobVectorCell {
+ public:
+  BlobVectorCell() : cell_(0) {}
+  ~BlobVectorCell() {}
+
+  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;
+  }
+  BlobVectorCell &operator=(const BlobVectorLargeValueCell &rhs) {
+    large_value_cell_ = rhs;
+    return *this;
+  }
+  BlobVectorCell &operator=(const BlobVectorHugeValueCell &rhs) {
+    huge_value_cell_ = rhs;
+    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_;
+  }
+  const BlobVectorMediumValueCell &medium_value_cell() const {
+    return medium_value_cell_;
+  }
+  const BlobVectorLargeValueCell &large_value_cell() const {
+    return large_value_cell_;
+  }
+  const BlobVectorHugeValueCell &huge_value_cell() const {
+    return huge_value_cell_;
+  }
+
+  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_;
+  };
+};
+
+class BlobVector {
+ public:
+  BlobVector();
+  ~BlobVector();
+
+  BlobVector(BlobVector &&rhs);
+  BlobVector &operator=(BlobVector &&rhs);
+
+  bool is_open() const {
+    return static_cast<bool>(pool_);
+  }
+
+  void create(io::Pool pool);
+  void open(io::Pool pool, uint32_t block_id);
+  void close();
+
+  const void *get_value_address(uint64_t id, uint64_t *length = nullptr);
+  void set_value(uint64_t id, const void *ptr, uint64_t length);
+
+  // TODO
+//  void append(uint64_t id, const void *ptr, uint64_t length);
+//  void prepend(uint64_t id, const void *ptr, uint64_t length);
+
+  uint32_t block_id() const {
+    return is_open() ? block_info_->id() : io::BLOCK_INVALID_ID;
+  }
+  uint64_t id_max() const {
+    return cells_.id_max();
+  }
+
+  void swap(BlobVector &rhs);
+
+  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_;
+  Vector<BlobVectorCell> cells_;
+  std::array<BlobVectorMediumValueStore,
+             BLOB_VECTOR_MEDIUM_VALUE_STORES_NUM> medium_value_stores_;
+  BlobVectorLargeValueStore large_value_store_;
+  Mutex inter_thread_mutex_;
+
+  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);
+
+  void free_value(BlobVectorCell cell);
+
+  void open_medium_value_store(uint8_t store_id);
+  void open_large_value_store();
+
+  void unfreeze_frozen_large_values();
+
+  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 register_idle_large_value(uint64_t offset);
+  void unregister_idle_large_value(uint64_t offset);
+
+  uint8_t get_store_id(uint64_t capacity) {
+    return bit_scan_reverse(capacity - 1)
+        - (BLOB_VECTOR_MEDIUM_VALUE_UNIT_SIZE_BITS - 1);
+  }
+  uint8_t get_list_id(uint64_t capacity) {
+    return bit_scan_reverse(
+        (capacity >> BLOB_VECTOR_LARGE_VALUE_UNIT_SIZE_BITS) | 1);
+  }
+
+  BlobVectorLargeValueHeader *get_large_value_header(uint64_t offset) {
+    return reinterpret_cast<BlobVectorLargeValueHeader *>(
+        &large_value_store_[offset]);
+  }
+
+  BlobVector(const BlobVector &);
+  BlobVector &operator=(const BlobVector &);
+};
+
+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);
+}
+inline StringBuilder &operator<<(StringBuilder &builder,
+                                 const BlobVectorLargeValueHeader &header) {
+  return header.write_to(builder);
+}
+inline StringBuilder &operator<<(StringBuilder &builder,
+                                 const BlobVectorCell &cell) {
+  return cell.write_to(builder);
+}
+inline StringBuilder &operator<<(StringBuilder &builder,
+                                 const BlobVector &vector) {
+  return vector.write_to(builder);
+}
+
+}  // namespace db
+}  // namespace grnxx
+
+#endif  // GRNXX_DB_BLOB_VECTOR_HPP

  Added: lib/db/vector.hpp (+168 -0) 100644
===================================================================
--- /dev/null
+++ lib/db/vector.hpp    2012-11-28 16:36:40 +0900 (f26b0cd)
@@ -0,0 +1,168 @@
+/*
+  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_DB_VECTOR_HPP
+#define GRNXX_DB_VECTOR_HPP
+
+#include "vector_base.hpp"
+
+namespace grnxx {
+namespace db {
+
+const uint64_t VECTOR_PAGE_SIZE_MIN     = uint64_t(1) << 0;
+const uint64_t VECTOR_PAGE_SIZE_MAX     = uint64_t(1) << 20;
+const uint64_t VECTOR_PAGE_SIZE_DEFAULT = uint64_t(1) << 16;
+
+const uint64_t VECTOR_TABLE_SIZE_MIN     = uint64_t(1) << 10;
+const uint64_t VECTOR_TABLE_SIZE_MAX     = uint64_t(1) << 20;
+const uint64_t VECTOR_TABLE_SIZE_DEFAULT = uint64_t(1) << 12;
+
+const uint64_t VECTOR_SECONDARY_TABLE_SIZE_MIN     = uint64_t(1) << 10;
+const uint64_t VECTOR_SECONDARY_TABLE_SIZE_MAX     = uint64_t(1) << 20;
+const uint64_t VECTOR_SECONDARY_TABLE_SIZE_DEFAULT = uint64_t(1) << 12;
+
+template <typename T,
+          uint64_t PAGE_SIZE = VECTOR_PAGE_SIZE_DEFAULT,
+          uint64_t TABLE_SIZE = VECTOR_TABLE_SIZE_DEFAULT,
+          uint64_t SECONDARY_TABLE_SIZE = VECTOR_SECONDARY_TABLE_SIZE_DEFAULT>
+class Vector {
+ public:
+  typedef T Value;
+
+  static_assert(PAGE_SIZE >= VECTOR_PAGE_SIZE_MIN, "too small PAGE_SIZE");
+  static_assert(PAGE_SIZE <= VECTOR_PAGE_SIZE_MAX, "too large PAGE_SIZE");
+
+  static_assert(TABLE_SIZE >= VECTOR_TABLE_SIZE_MIN, "too small TABLE_SIZE");
+  static_assert(TABLE_SIZE <= VECTOR_TABLE_SIZE_MAX, "too large TABLE_SIZE");
+
+  static_assert(SECONDARY_TABLE_SIZE >= VECTOR_SECONDARY_TABLE_SIZE_MIN,
+                "too small SECONDARY_TABLE_SIZE");
+  static_assert(SECONDARY_TABLE_SIZE <= VECTOR_SECONDARY_TABLE_SIZE_MAX,
+                "too large SECONDARY_TABLE_SIZE");
+
+  static_assert((PAGE_SIZE & (PAGE_SIZE - 1)) == 0,
+                "PAGE_SIZE must be a power of two");
+  static_assert((TABLE_SIZE & (TABLE_SIZE - 1)) == 0,
+                "TABLE_SIZE must be a power of two");
+  static_assert((SECONDARY_TABLE_SIZE & (SECONDARY_TABLE_SIZE - 1)) == 0,
+                "SECONDARY_TABLE_SIZE must be a power of two");
+
+  Vector() : base_() {}
+  ~Vector() {}
+
+  Vector(Vector &&vector) : base_(std::move(vector.base_)) {}
+  Vector &operator=(Vector &&vector) {
+    base_ = std::move(vector.base_);
+    return *this;
+  }
+
+  bool is_open() const {
+    return base_.is_open();
+  }
+
+  void create(io::Pool *pool) {
+    base_.create(pool, sizeof(Value),
+                 PAGE_SIZE, TABLE_SIZE, SECONDARY_TABLE_SIZE,
+                 nullptr, fill_page);
+  }
+  void create(io::Pool *pool, const Value &default_value) {
+    base_.create(pool, sizeof(Value),
+                 PAGE_SIZE, TABLE_SIZE, SECONDARY_TABLE_SIZE,
+                 &default_value, fill_page);
+  }
+  void open(io::Pool *pool, uint32_t block_id) {
+    base_.open(pool, block_id, sizeof(Value),
+               PAGE_SIZE, TABLE_SIZE, SECONDARY_TABLE_SIZE, fill_page);
+  }
+  void close() {
+    if (!is_open()) {
+      GRNXX_ERROR() << "failed to close vector";
+      GRNXX_THROW();
+    }
+    base_ = VectorBase();
+  }
+
+  Value *get_value_address(uint64_t id) {
+    return base_.get_value_address<Value, PAGE_SIZE, TABLE_SIZE,
+                                   SECONDARY_TABLE_SIZE>(id);
+  }
+  Value &get_value(uint64_t id) {
+    return *get_value_address(id);
+  }
+  Value &operator[](uint64_t id) {
+    return get_value(id);
+  }
+
+  uint32_t block_id() const {
+    return base_.block_id();
+  }
+
+  void swap(Vector &vector) {
+    base_.swap(vector.base_);
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const {
+    return base_.write_to(builder);
+  }
+
+  static uint64_t value_size() {
+    return sizeof(Value);
+  }
+  static uint64_t page_size() {
+    return PAGE_SIZE;
+  }
+  static uint64_t table_size() {
+    return TABLE_SIZE;
+  }
+  static uint64_t secondary_table_size() {
+    return SECONDARY_TABLE_SIZE;
+  }
+  static uint64_t id_max() {
+    return (PAGE_SIZE * TABLE_SIZE * SECONDARY_TABLE_SIZE) - 1;
+  }
+
+  static void unlink(io::Pool *pool, uint32_t block_id) {
+    VectorBase::unlink(pool, block_id, sizeof(Value),
+                       PAGE_SIZE, TABLE_SIZE, SECONDARY_TABLE_SIZE);
+  }
+
+ private:
+  VectorBase base_;
+
+  static void fill_page(void *page_address, const void *value) {
+    Value *values = static_cast<Value *>(page_address);
+    for (uint64_t i = 0; i < PAGE_SIZE; ++i) {
+      std::memcpy(&values[i], value, sizeof(Value));
+    }
+  }
+};
+
+template <typename T>
+inline void swap(Vector<T> &lhs, Vector<T> &rhs) {
+  lhs.swap(rhs);
+}
+
+template <typename T>
+inline StringBuilder &operator<<(StringBuilder &builder,
+                                 const Vector<T> &vector) {
+  return vector.write_to(builder);
+}
+
+}  // namespace db
+}  // namespace grnxx
+
+#endif  // GRNXX_DB_VECTOR_HPP

  Added: lib/db/vector_base.cpp (+512 -0) 100644
===================================================================
--- /dev/null
+++ lib/db/vector_base.cpp    2012-11-28 16:36:40 +0900 (f87647a)
@@ -0,0 +1,512 @@
+/*
+  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 "vector_base.hpp"
+
+#include <vector>
+
+#include "../lock.hpp"
+#include "../logger.hpp"
+
+namespace grnxx {
+namespace db {
+
+void VectorHeader::initialize(uint64_t value_size,
+                              uint64_t page_size,
+                              uint64_t table_size,
+                              uint64_t secondary_table_size,
+                              const void *default_value) {
+  std::memset(this, 0, sizeof(*this));
+
+  value_size_ = value_size;
+  page_size_ = page_size;
+  table_size_ = table_size;
+  secondary_table_size_ = secondary_table_size;
+  has_default_value_ = default_value ? 1 : 0;
+
+  first_table_block_id_ = io::BLOCK_INVALID_ID;
+  secondary_table_block_id_ = io::BLOCK_INVALID_ID;
+
+  mutex_.clear();
+}
+
+VectorBase::VectorBase()
+  : pool_(),
+    block_info_(nullptr),
+    header_(nullptr),
+    default_value_(nullptr),
+    fill_page_(nullptr),
+    table_size_bits_(0),
+    table_size_mask_(0),
+    page_id_max_(0),
+    first_table_(nullptr),
+    secondary_table_(nullptr),
+    secondary_table_cache_(),
+    first_table_cache_(),
+    tables_cache_(),
+    mutex_() {}
+
+VectorBase::~VectorBase() {}
+
+VectorBase::VectorBase(VectorBase &&rhs)
+  : pool_(std::move(rhs.pool_)),
+    block_info_(std::move(rhs.block_info_)),
+    header_(std::move(rhs.header_)),
+    default_value_(std::move(rhs.default_value_)),
+    fill_page_(std::move(rhs.fill_page_)),
+    table_size_bits_(std::move(rhs.table_size_bits_)),
+    table_size_mask_(std::move(rhs.table_size_mask_)),
+    page_id_max_(std::move(rhs.page_id_max_)),
+    first_table_(std::move(rhs.first_table_)),
+    secondary_table_(std::move(rhs.secondary_table_)),
+    secondary_table_cache_(std::move(rhs.secondary_table_cache_)),
+    first_table_cache_(std::move(rhs.first_table_cache_)),
+    tables_cache_(std::move(rhs.tables_cache_)),
+    mutex_(std::move(rhs.mutex_)) {}
+
+VectorBase &VectorBase::operator=(VectorBase &&rhs) {
+  pool_ = std::move(rhs.pool_);
+  block_info_ = std::move(rhs.block_info_);
+  header_ = std::move(rhs.header_);
+  default_value_ = std::move(rhs.default_value_);
+  fill_page_ = std::move(rhs.fill_page_);
+  table_size_bits_ = std::move(rhs.table_size_bits_);
+  table_size_mask_ = std::move(rhs.table_size_mask_);
+  page_id_max_ = std::move(rhs.page_id_max_);
+  first_table_ = std::move(rhs.first_table_);
+  secondary_table_ = std::move(rhs.secondary_table_);
+  secondary_table_cache_ = std::move(rhs.secondary_table_cache_);
+  first_table_cache_ = std::move(rhs.first_table_cache_);
+  tables_cache_ = std::move(rhs.tables_cache_);
+  mutex_ = std::move(rhs.mutex_);
+  return *this;
+}
+
+void VectorBase::create(io::Pool *pool,
+                        uint64_t value_size,
+                        uint64_t page_size,
+                        uint64_t table_size,
+                        uint64_t secondary_table_size,
+                        const void *default_value,
+                        FillPage fill_page) {
+  if (!pool) {
+    GRNXX_ERROR() << "invalid argument: pool = " << pool;
+    GRNXX_THROW();
+  } else if (!*pool) {
+    GRNXX_ERROR() << "invalid_argument: pool = " << *pool;
+    GRNXX_THROW();
+  }
+
+  VectorBase new_vector;
+  new_vector.create_vector(pool, value_size, page_size, table_size,
+                           secondary_table_size, default_value, fill_page);
+  *this = std::move(new_vector);
+}
+
+void VectorBase::open(io::Pool *pool,
+                      uint32_t block_id,
+                      uint64_t value_size,
+                      uint64_t page_size,
+                      uint64_t table_size,
+                      uint64_t secondary_table_size,
+                      FillPage fill_page) {
+  if (!pool) {
+    GRNXX_ERROR() << "invalid argument: pool = " << pool;
+    GRNXX_THROW();
+  } else if (!*pool) {
+    GRNXX_ERROR() << "invalid_argument: pool = " << *pool;
+    GRNXX_THROW();
+  }
+
+  VectorBase new_vector;
+  new_vector.open_vector(pool, block_id, value_size, page_size, table_size,
+                         secondary_table_size, fill_page);
+  *this = std::move(new_vector);
+}
+
+void VectorBase::swap(VectorBase &rhs) {
+  using std::swap;
+  swap(pool_, rhs.pool_);
+  swap(block_info_, rhs.block_info_);
+  swap(header_, rhs.header_);
+  swap(default_value_, rhs.default_value_);
+  swap(fill_page_, rhs.fill_page_);
+  swap(table_size_bits_, rhs.table_size_bits_);
+  swap(table_size_mask_, rhs.table_size_mask_);
+  swap(page_id_max_, rhs.page_id_max_);
+  swap(first_table_, rhs.first_table_);
+  swap(secondary_table_, rhs.secondary_table_);
+  swap(secondary_table_cache_, rhs.secondary_table_cache_);
+  swap(first_table_cache_, rhs.first_table_cache_);
+  swap(tables_cache_, rhs.tables_cache_);
+  swap(mutex_, rhs.mutex_);
+}
+
+void VectorBase::unlink(io::Pool *pool,
+                        uint32_t block_id,
+                        uint64_t value_size,
+                        uint64_t page_size,
+                        uint64_t table_size,
+                        uint64_t secondary_table_size) try {
+  std::vector<uint32_t> block_ids;
+
+  {
+    VectorBase vector;
+    vector.open(pool, block_id, value_size,
+                page_size, table_size, secondary_table_size, nullptr);
+    const VectorHeader * const header = vector.header_;
+
+    block_ids.push_back(block_id);
+
+    block_ids.push_back(header->first_table_block_id());
+    for (uint64_t i = 0; i < header->table_size(); ++i) {
+      if (vector.first_table_[i] != io::BLOCK_INVALID_ID) {
+        block_ids.push_back(vector.first_table_[i]);
+      }
+    }
+
+    if (header->secondary_table_block_id() != io::BLOCK_INVALID_ID) {
+      block_ids.push_back(header->secondary_table_block_id());
+      uint32_t * const secondary_table = static_cast<uint32_t *>(
+          pool->get_block_address(header->secondary_table_block_id()));
+      for (uint64_t i = 0; i < header->secondary_table_size(); ++i) {
+        if (secondary_table[i] != io::BLOCK_INVALID_ID) {
+          block_ids.push_back(secondary_table[i]);
+          uint32_t * const table = static_cast<uint32_t *>(
+              pool->get_block_address(secondary_table[i]));
+          for (uint64_t j = 0; j < header->table_size(); ++j) {
+            if (table[j] != io::BLOCK_INVALID_ID) {
+              block_ids.push_back(table[j]);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  for (size_t i = 0; i < block_ids.size(); ++i) {
+    pool->free_block(block_ids[i]);
+  }
+} catch (const std::exception &exception) {
+  GRNXX_ERROR() << exception;
+  GRNXX_THROW();
+}
+
+void VectorBase::create_vector(io::Pool *pool,
+                               uint64_t value_size,
+                               uint64_t page_size,
+                               uint64_t table_size,
+                               uint64_t secondary_table_size,
+                               const void *default_value,
+                               FillPage fill_page) {
+  pool_ = *pool;
+
+  std::unique_ptr<void *[]> first_table_cache(
+        new (std::nothrow) void *[table_size]);
+  if (!first_table_cache) {
+    GRNXX_ERROR() << "new void *[" << table_size << "] failed";
+    GRNXX_THROW();
+  }
+
+  uint64_t header_block_size = sizeof(VectorHeader);
+  if (default_value) {
+    header_block_size += value_size;
+  }
+  block_info_ = pool_.create_block(header_block_size);
+
+  const io::BlockInfo *first_table_block_info;
+  try {
+    first_table_block_info = pool_.create_block(sizeof(uint32_t) * table_size);
+  } catch (...) {
+    pool_.free_block(*block_info_);
+    throw;
+  }
+
+  void * const block_address = pool_.get_block_address(*block_info_);
+  header_ = static_cast<VectorHeader *>(block_address);
+  header_->initialize(value_size, page_size, table_size,
+                      secondary_table_size, default_value);
+  restore_from_header();
+
+  if (default_value_) {
+    std::memcpy(default_value_, default_value,
+                static_cast<size_t>(value_size));
+    fill_page_ = fill_page;
+  }
+
+  header_->set_first_table_block_id(first_table_block_info->id());
+  first_table_ = static_cast<uint32_t *>(
+      pool_.get_block_address(*first_table_block_info));
+  first_table_cache_ = std::move(first_table_cache);
+  for (uint64_t i = 0; i < header_->table_size(); ++i) {
+    first_table_[i] = io::BLOCK_INVALID_ID;
+    first_table_cache_[i] = nullptr;
+  }
+}
+
+void VectorBase::open_vector(io::Pool *pool,
+                             uint32_t block_id,
+                             uint64_t value_size,
+                             uint64_t page_size,
+                             uint64_t table_size,
+                             uint64_t secondary_table_size,
+                             FillPage fill_page) {
+  pool_ = *pool;
+  block_info_ = pool_.get_block_info(block_id);
+  if (block_info_->size() < sizeof(VectorHeader)) {
+    GRNXX_ERROR() << "invalid argument: block_info = " << *block_info_
+                  << ", header_size = " << sizeof(VectorHeader);
+    GRNXX_THROW();
+  }
+
+  void * const block_address = pool_.get_block_address(*block_info_);
+  header_ = static_cast<VectorHeader *>(block_address);
+  restore_from_header();
+
+  if (default_value_) {
+    const uint64_t header_size = sizeof(VectorHeader) + value_size;
+    if (block_info_->size() < header_size) {
+      GRNXX_ERROR() << "invalid argument: block_info = " << *block_info_
+                    << ", header_size = " << header_size;
+      GRNXX_THROW();
+    }
+    fill_page_ = fill_page;
+  }
+
+  if (value_size != header_->value_size()) {
+    GRNXX_ERROR() << "invalid value size: actual = " << header_->value_size()
+                  << ", expected = " << value_size;
+    GRNXX_THROW();
+  }
+  if (page_size != header_->page_size()) {
+    GRNXX_ERROR() << "invalid page size: actual = " << header_->page_size()
+                  << ", expected = " << page_size;
+    GRNXX_THROW();
+  }
+  if (table_size != header_->table_size()) {
+    GRNXX_ERROR() << "invalid table size: actual = " << header_->table_size()
+                  << ", expected = " << table_size;
+    GRNXX_THROW();
+  }
+  if (secondary_table_size != header_->secondary_table_size()) {
+    GRNXX_ERROR() << "invalid secondary table size: actual = "
+                  << header_->secondary_table_size()
+                  << ", expected = " << secondary_table_size;
+    GRNXX_THROW();
+  }
+
+  first_table_ = static_cast<uint32_t *>(
+      pool_.get_block_address(header_->first_table_block_id()));
+
+  first_table_cache_.reset(new (std::nothrow) void *[header_->table_size()]);
+  if (!first_table_cache_) {
+    GRNXX_ERROR() << "new void *[" << header_->table_size() << "] failed";
+    GRNXX_THROW();
+  }
+  for (uint64_t i = 0; i < header_->table_size(); ++i) {
+    first_table_cache_[i] = nullptr;
+  }
+}
+
+void VectorBase::restore_from_header() {
+  if (header_->has_default_value()) {
+    default_value_ = header_ + 1;
+  }
+
+  table_size_bits_ = bit_scan_reverse(header_->table_size());
+  table_size_mask_ = header_->table_size() - 1;
+  page_id_max_ = header_->table_size() * header_->secondary_table_size() - 1;
+}
+
+void VectorBase::initialize_secondary_table() {
+  Lock lock(inter_process_mutex());
+  if (header_->secondary_table_block_id() == io::BLOCK_INVALID_ID) {
+    const auto block_info = pool_.create_block(
+        sizeof(uint32_t) * header_->secondary_table_size());
+    uint32_t * const body = static_cast<uint32_t *>(
+        pool_.get_block_address(*block_info));
+    for (uint64_t i = 0; i < header_->secondary_table_size(); ++i) {
+      body[i] = io::BLOCK_INVALID_ID;
+    }
+    header_->set_secondary_table_block_id(block_info->id());
+  }
+}
+
+void *VectorBase::get_page_address_on_failure(uint64_t page_id) {
+  if (page_id < header_->table_size()) {
+    if (!first_table_cache_[page_id]) {
+      if (first_table_[page_id] == io::BLOCK_INVALID_ID) {
+        initialize_page(&first_table_[page_id]);
+      }
+      first_table_cache_[page_id] =
+          pool_.get_block_address(first_table_[page_id]);
+    }
+    return first_table_cache_[page_id];
+  }
+
+  if (page_id <= page_id_max_) {
+    if (!tables_cache_) {
+      if (!secondary_table_cache_) {
+        if (!secondary_table_) {
+          if (header_->secondary_table_block_id() == io::BLOCK_INVALID_ID) {
+            initialize_secondary_table();
+          }
+          secondary_table_ = static_cast<uint32_t *>(
+              pool_.get_block_address(header_->secondary_table_block_id()));
+        }
+        initialize_secondary_table_cache();
+      }
+      initialize_tables_cache();
+    }
+
+    const uint64_t table_id = page_id >> table_size_bits_;
+    std::unique_ptr<void *[]> &table_cache = tables_cache_[table_id];
+    if (!table_cache) {
+      if (secondary_table_[table_id] == io::BLOCK_INVALID_ID) {
+        initialize_table(&secondary_table_[table_id]);
+      }
+      secondary_table_cache_[table_id] = static_cast<uint32_t *>(
+          pool_.get_block_address(secondary_table_[table_id]));
+      initialize_table_cache(&table_cache);
+    }
+
+    page_id &= table_size_mask_;
+    if (!table_cache[page_id]) {
+      uint32_t * const table = secondary_table_cache_[table_id];
+      if (table[page_id] == io::BLOCK_INVALID_ID) {
+        initialize_page(&table[page_id]);
+      }
+      table_cache[page_id] = pool_.get_block_address(table[page_id]);
+    }
+    return table_cache[page_id];
+  }
+
+  GRNXX_ERROR() << "invalid argument: page_id = " << page_id
+                << ": [0, " << page_id_max_ <<']';
+  GRNXX_THROW();
+}
+
+void VectorBase::initialize_table(uint32_t *table_block_id) {
+  Lock lock(inter_process_mutex());
+  if (*table_block_id == io::BLOCK_INVALID_ID) {
+    const auto block_info = pool_.create_block(
+        sizeof(uint32_t) * header_->table_size());
+    uint32_t * const body = static_cast<uint32_t *>(
+        pool_.get_block_address(*block_info));
+    for (uint64_t i = 0; i < header_->table_size(); ++i) {
+      body[i] = io::BLOCK_INVALID_ID;
+    }
+    *table_block_id = block_info->id();
+  }
+}
+
+void VectorBase::initialize_page(uint32_t *page_block_id) {
+  Lock lock(inter_process_mutex());
+  if (*page_block_id == io::BLOCK_INVALID_ID) {
+    const io::BlockInfo *block_info = pool_.create_block(
+        header_->value_size() * header_->page_size());
+    if (fill_page_) {
+      fill_page_(pool_.get_block_address(*block_info), default_value_);
+    }
+    *page_block_id = block_info->id();
+  }
+}
+
+void VectorBase::initialize_secondary_table_cache() {
+  Lock lock(inter_thread_mutex());
+  if (!secondary_table_cache_) {
+    std::unique_ptr<uint32_t *[]> tables(
+        new (std::nothrow) uint32_t *[header_->secondary_table_size()]);
+    if (!tables) {
+      GRNXX_ERROR() << "new grnxx::uint32_t *["
+                    << header_->secondary_table_size() << "] failed";
+      GRNXX_THROW();
+    }
+    for (uint64_t i = 0; i < header_->secondary_table_size(); ++i) {
+      tables[i] = nullptr;
+    }
+    secondary_table_cache_ = std::move(tables);
+  }
+}
+
+void VectorBase::initialize_table_cache(
+    std::unique_ptr<void *[]> *table_cache) {
+  Lock lock(inter_thread_mutex());
+  if (!*table_cache) {
+    std::unique_ptr<void *[]> cache(
+        new (std::nothrow) void *[header_->table_size()]);
+    if (!cache) {
+      GRNXX_ERROR() << "new void *[" << header_->table_size() << "] failed";
+      GRNXX_THROW();
+    }
+    for (uint64_t i = 0; i < header_->table_size(); ++i) {
+      cache[i] = nullptr;
+    }
+    *table_cache = std::move(cache);
+  }
+}
+
+void VectorBase::initialize_tables_cache() {
+  Lock lock(inter_thread_mutex());
+  if (!tables_cache_) {
+    std::unique_ptr<std::unique_ptr<void *[]>[]> cache(new (std::nothrow)
+        std::unique_ptr<void *[]>[header_->secondary_table_size()]);
+    for (uint64_t i = 0; i < header_->secondary_table_size(); ++i) {
+      cache[i] = nullptr;
+    }
+    tables_cache_ = std::move(cache);
+  }
+}
+
+StringBuilder &VectorHeader::write_to(StringBuilder &builder) const {
+  if (!builder) {
+    return builder;
+  }
+
+  return builder << "{ value_size = " << value_size_
+                 << ", page_size = " << page_size_
+                 << ", table_size = " << table_size_
+                 << ", secondary_table_size = " << secondary_table_size_
+                 << ", has_default_value = " << has_default_value_
+                 << ", first_table_block_id = " << first_table_block_id_
+                 << ", secondary_table_block_id = " << secondary_table_block_id_
+                 << ", mutex = " << mutex_ << " }";
+}
+
+StringBuilder &VectorBase::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_;
+  } else {
+    builder << "n/a";
+  }
+  return builder << ", page_id_max = " << page_id_max_
+                 << ", mutex = " << mutex_ << " }";
+}
+
+}  // namespace db
+}  // namespace grnxx

  Added: lib/db/vector_base.hpp (+247 -0) 100644
===================================================================
--- /dev/null
+++ lib/db/vector_base.hpp    2012-11-28 16:36:40 +0900 (2cbac41)
@@ -0,0 +1,247 @@
+/*
+  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_DB_VECTOR_BASE_HPP
+#define GRNXX_DB_VECTOR_BASE_HPP
+
+#include "../exception.hpp"
+#include "../logger.hpp"
+#include "../io/pool.hpp"
+
+namespace grnxx {
+namespace db {
+
+class VectorHeader {
+ public:
+  void initialize(uint64_t value_size,
+                  uint64_t page_size,
+                  uint64_t table_size,
+                  uint64_t secondary_table_size,
+                  const void *default_value);
+
+  uint64_t value_size() const {
+    return value_size_;
+  }
+  uint64_t page_size() const {
+    return page_size_;
+  }
+  uint64_t table_size() const {
+    return table_size_;
+  }
+  uint64_t secondary_table_size() const {
+    return secondary_table_size_;
+  }
+  bool has_default_value() const {
+    return has_default_value_ != 0;
+  }
+  uint32_t first_table_block_id() const {
+    return first_table_block_id_;
+  }
+  uint32_t secondary_table_block_id() const {
+    return secondary_table_block_id_;
+  }
+
+  void set_first_table_block_id(uint32_t value) {
+    first_table_block_id_ = value;
+  }
+  void set_secondary_table_block_id(uint32_t value) {
+    secondary_table_block_id_ = value;
+  }
+
+  Mutex *mutex() {
+    return &mutex_;
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+ private:
+  uint64_t value_size_;
+  uint64_t page_size_;
+  uint64_t table_size_;
+  uint64_t secondary_table_size_;
+  uint8_t has_default_value_;
+  uint32_t first_table_block_id_;
+  uint32_t secondary_table_block_id_;
+  Mutex mutex_;
+
+  VectorHeader();
+  ~VectorHeader();
+
+  VectorHeader(const VectorHeader &);
+  VectorHeader &operator=(const VectorHeader &);
+};
+
+class VectorBase {
+ public:
+  typedef void (*FillPage)(void *page_address, const void *value);
+
+  VectorBase();
+  ~VectorBase();
+
+  VectorBase(VectorBase &&rhs);
+  VectorBase &operator=(VectorBase &&rhs);
+
+  bool is_open() const {
+    return static_cast<bool>(pool_);
+  }
+
+  void create(io::Pool *pool,
+              uint64_t value_size,
+              uint64_t page_size,
+              uint64_t table_size,
+              uint64_t secondary_table_size,
+              const void *default_value,
+              FillPage fill_page);
+
+  void open(io::Pool *pool,
+            uint32_t block_id,
+            uint64_t value_size,
+            uint64_t page_size,
+            uint64_t table_size,
+            uint64_t secondary_table_size,
+            FillPage fill_page);
+
+  template <typename T,
+            uint64_t PAGE_SIZE,
+            uint64_t TABLE_SIZE,
+            uint64_t SECONDARY_TABLE_SIZE>
+  T *get_value_address(uint64_t id) {
+    void * const page_address =
+       get_page_address<PAGE_SIZE, TABLE_SIZE, SECONDARY_TABLE_SIZE>(
+           id / PAGE_SIZE);
+    return static_cast<T *>(page_address) + (id % PAGE_SIZE);
+  }
+
+  template <uint64_t PAGE_SIZE,
+            uint64_t TABLE_SIZE,
+            uint64_t SECONDARY_TABLE_SIZE>
+  void *get_page_address(uint64_t page_id) {
+    if ((page_id < TABLE_SIZE) && first_table_cache_[page_id]) {
+      return first_table_cache_[page_id];
+    }
+    if ((page_id < (TABLE_SIZE * SECONDARY_TABLE_SIZE)) && tables_cache_) {
+      const uint64_t table_id = page_id / TABLE_SIZE;
+      const std::unique_ptr<void *[]> &table_cache = tables_cache_[table_id];
+      if (table_cache) {
+        const uint64_t local_page_id = page_id % TABLE_SIZE;
+        if (table_cache[local_page_id]) {
+          return table_cache[local_page_id];
+        }
+      }
+    }
+    return get_page_address_on_failure(page_id);
+  }
+
+  uint32_t block_id() const {
+    return is_open() ? block_info_->id() : io::BLOCK_INVALID_ID;
+  }
+  uint64_t value_size() const {
+    return is_open() ? header_->value_size() : 0;
+  }
+  uint64_t page_size() const {
+    return is_open() ? header_->page_size() : 0;
+  }
+  uint64_t table_size() const {
+    return is_open() ? header_->table_size() : 0;
+  }
+  uint64_t secondary_table_size() const {
+    return is_open() ? header_->secondary_table_size() : 0;
+  }
+  uint64_t id_max() const {
+    return (page_size() * table_size() * secondary_table_size()) - 1;
+  }
+
+  void swap(VectorBase &rhs);
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+  static void unlink(io::Pool *pool,
+                     uint32_t block_id,
+                     uint64_t value_size,
+                     uint64_t page_size,
+                     uint64_t table_size,
+                     uint64_t secondary_table_size);
+
+ private:
+  io::Pool pool_;
+  const io::BlockInfo *block_info_;
+  VectorHeader *header_;
+  void *default_value_;
+  FillPage fill_page_;
+  uint8_t table_size_bits_;
+  uint64_t table_size_mask_;
+  uint64_t page_id_max_;
+  uint32_t *first_table_;
+  uint32_t *secondary_table_;
+  std::unique_ptr<uint32_t *[]> secondary_table_cache_;
+  std::unique_ptr<void *[]> first_table_cache_;
+  std::unique_ptr<std::unique_ptr<void *[]>[]> tables_cache_;
+  Mutex mutex_;
+
+  void create_vector(io::Pool *pool,
+                     uint64_t value_size,
+                     uint64_t page_size,
+                     uint64_t table_size,
+                     uint64_t secondary_table_size,
+                     const void *default_value,
+                     FillPage fill_page);
+  void open_vector(io::Pool *pool,
+                   uint32_t block_id,
+                   uint64_t value_size,
+                   uint64_t page_size,
+                   uint64_t table_size,
+                   uint64_t secondary_table_size,
+                   FillPage fill_page);
+
+  void restore_from_header();
+  uint64_t header_size() const;
+
+  void *get_page_address_on_failure(uint64_t page_id);
+
+  void initialize_secondary_table();
+  void initialize_table(uint32_t *table_block_id);
+  void initialize_page(uint32_t *page_block_id);
+
+  void initialize_secondary_table_cache();
+  void initialize_table_cache(
+      std::unique_ptr<void *[]> *table_cache);
+  void initialize_tables_cache();
+
+  Mutex *inter_thread_mutex() {
+    return &mutex_;
+  }
+  Mutex *inter_process_mutex() {
+    return header_->mutex();
+  }
+
+  VectorBase(const VectorBase &);
+  VectorBase &operator=(const VectorBase &);
+};
+
+inline StringBuilder &operator<<(StringBuilder &builder,
+                                 const VectorHeader &header) {
+  return header.write_to(builder);
+}
+inline StringBuilder &operator<<(StringBuilder &builder,
+                                 const VectorBase &vector) {
+  return vector.write_to(builder);
+}
+
+}  // namespace db
+}  // namespace grnxx
+
+#endif  // GRNXX_DB_VECTOR_BASE_HPP

  Added: lib/duration.cpp (+53 -0) 100644
===================================================================
--- /dev/null
+++ lib/duration.cpp    2012-11-28 16:36:40 +0900 (c1215f3)
@@ -0,0 +1,53 @@
+/*
+  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 "duration.hpp"
+
+#include <ostream>
+
+#include "string_format.hpp"
+
+namespace grnxx {
+
+StringBuilder &Duration::write_to(StringBuilder &builder) const {
+  if (!builder) {
+    return builder;
+  }
+
+  uint64_t nanoseconds;
+  if (nanoseconds_ >= 0) {
+    nanoseconds = nanoseconds_;
+  } else {
+    builder << '-';
+    nanoseconds = -nanoseconds_;
+  }
+  builder << (nanoseconds / 1000000000);
+  nanoseconds %= 1000000000;
+  if (nanoseconds != 0) {
+    builder << '.' << StringFormat::align_right(nanoseconds, 9, '0');
+  }
+  return builder;
+}
+
+std::ostream &operator<<(std::ostream &stream, Duration duration) {
+  char buf[32];
+  StringBuilder builder(buf);
+  builder << duration;
+  return stream.write(builder.c_str(), builder.length());
+}
+
+}  // namespace grnxx

  Added: lib/duration.hpp (+155 -0) 100644
===================================================================
--- /dev/null
+++ lib/duration.hpp    2012-11-28 16:36:40 +0900 (f6b83ff)
@@ -0,0 +1,155 @@
+/*
+  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_DURATION_HPP
+#define GRNXX_DURATION_HPP
+
+#include "basic.hpp"
+#include "string_builder.hpp"
+
+namespace grnxx {
+
+class Duration {
+ public:
+  GRNXX_CONSTEXPR Duration() : nanoseconds_(0) {}
+  explicit GRNXX_CONSTEXPR Duration(int64_t nanoseconds)
+    : nanoseconds_(nanoseconds) {}
+
+  static GRNXX_CONSTEXPR Duration nanoseconds(int64_t nanoseconds) {
+    return Duration(nanoseconds);
+  }
+  static GRNXX_CONSTEXPR Duration microseconds(int64_t microseconds) {
+    return Duration(microseconds * 1000);
+  }
+  static GRNXX_CONSTEXPR Duration milliseconds(int64_t milliseconds) {
+    return Duration(milliseconds * 1000000);
+  }
+  static GRNXX_CONSTEXPR Duration seconds(int64_t seconds) {
+    return Duration(seconds * 1000000000);
+  }
+  static GRNXX_CONSTEXPR Duration minutes(int64_t minutes) {
+    return Duration(minutes * 1000000000 * 60);
+  }
+  static GRNXX_CONSTEXPR Duration hours(int64_t hours) {
+    return Duration(hours * 1000000000 * 60 * 60);
+  }
+  static GRNXX_CONSTEXPR Duration days(int64_t days) {
+    return Duration(days * 1000000000 * 60 * 60 * 24);
+  }
+  static GRNXX_CONSTEXPR Duration weeks(int64_t weeks) {
+    return Duration(weeks * 1000000000 * 60 * 60 * 24 * 7);
+  }
+
+  GRNXX_CONSTEXPR int64_t nanoseconds() const {
+    return nanoseconds_;
+  }
+  void set_nanoseconds(int64_t nanoseconds) {
+    nanoseconds_ = nanoseconds;
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+ private:
+  int64_t nanoseconds_;
+
+  // Copyable.
+};
+
+inline GRNXX_CONSTEXPR Duration operator+(Duration duration) {
+  return duration;
+}
+inline GRNXX_CONSTEXPR Duration operator-(Duration duration) {
+  return Duration(-duration.nanoseconds());
+}
+
+inline Duration &operator+=(Duration &lhs, Duration rhs) {
+  lhs.set_nanoseconds(lhs.nanoseconds() + rhs.nanoseconds());
+  return lhs;
+}
+inline Duration &operator-=(Duration &lhs, Duration rhs) {
+  lhs.set_nanoseconds(lhs.nanoseconds() - rhs.nanoseconds());
+  return lhs;
+}
+inline Duration &operator*=(Duration &lhs, int64_t rhs) {
+  lhs.set_nanoseconds(lhs.nanoseconds() * rhs);
+  return lhs;
+}
+inline Duration &operator/=(Duration &lhs, int64_t rhs) {
+  if (rhs == 0) {
+    lhs.set_nanoseconds(0);
+  } else {
+    lhs.set_nanoseconds(lhs.nanoseconds() / rhs);
+  }
+  return lhs;
+}
+inline Duration &operator%=(Duration &lhs, Duration rhs) {
+  if (rhs.nanoseconds() == 0) {
+    lhs.set_nanoseconds(0);
+  } else {
+    lhs.set_nanoseconds(lhs.nanoseconds() % rhs.nanoseconds());
+  }
+  return lhs;
+}
+
+inline GRNXX_CONSTEXPR Duration operator+(Duration lhs, Duration rhs) {
+  return Duration(lhs.nanoseconds() + rhs.nanoseconds());
+}
+inline GRNXX_CONSTEXPR Duration operator-(Duration lhs, Duration rhs) {
+  return Duration(lhs.nanoseconds() - rhs.nanoseconds());
+}
+inline GRNXX_CONSTEXPR Duration operator*(Duration lhs, int64_t rhs) {
+  return Duration(lhs.nanoseconds() * rhs);
+}
+inline GRNXX_CONSTEXPR Duration operator*(int64_t lhs, Duration rhs) {
+  return Duration(lhs * rhs.nanoseconds());
+}
+inline GRNXX_CONSTEXPR Duration operator/(Duration lhs, int64_t rhs) {
+  return (rhs != 0) ? Duration(lhs.nanoseconds() / rhs) : Duration(0);
+}
+inline GRNXX_CONSTEXPR Duration operator%(Duration lhs, Duration rhs) {
+  return (rhs.nanoseconds() != 0) ?
+      Duration(lhs.nanoseconds() % rhs.nanoseconds()) : Duration(0);
+}
+
+inline GRNXX_CONSTEXPR bool operator==(Duration lhs, Duration rhs) {
+  return lhs.nanoseconds() == rhs.nanoseconds();
+}
+inline GRNXX_CONSTEXPR bool operator!=(Duration lhs, Duration rhs) {
+  return lhs.nanoseconds() != rhs.nanoseconds();
+}
+inline GRNXX_CONSTEXPR bool operator<(Duration lhs, Duration rhs) {
+  return lhs.nanoseconds() < rhs.nanoseconds();
+}
+inline GRNXX_CONSTEXPR bool operator<=(Duration lhs, Duration rhs) {
+  return lhs.nanoseconds() <= rhs.nanoseconds();
+}
+inline GRNXX_CONSTEXPR bool operator>(Duration lhs, Duration rhs) {
+  return lhs.nanoseconds() > rhs.nanoseconds();
+}
+inline GRNXX_CONSTEXPR bool operator>=(Duration lhs, Duration rhs) {
+  return lhs.nanoseconds() >= rhs.nanoseconds();
+}
+
+inline StringBuilder &operator<<(StringBuilder &builder, Duration duration) {
+  return duration.write_to(builder);
+}
+
+std::ostream &operator<<(std::ostream &stream, Duration duration);
+
+}  // namespace grnxx
+
+#endif  // GRNXX_TIME_HPP

  Added: lib/error.cpp (+81 -0) 100644
===================================================================
--- /dev/null
+++ lib/error.cpp    2012-11-28 16:36:40 +0900 (af13207)
@@ -0,0 +1,81 @@
+/*
+  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 "error.hpp"
+
+#ifdef GRNXX_WINDOWS
+# include <windows.h>
+#endif  // GRNXX_WINDOWS
+
+#include <ostream>
+
+namespace grnxx {
+namespace {
+
+#ifdef GRNXX_WINDOWS
+class Freer {
+ public:
+  void operator()(void *ptr) const {
+    ::LocalFree(ptr);
+  }
+};
+#endif  // GRNXX_WINDOWS
+
+}  // namespace
+
+StringBuilder &Error::write_to(StringBuilder &builder) const {
+  switch (type_) {
+    case POSIX_ERROR: {
+      return builder << '(' << std::strerror(posix_error_) << ')';
+    }
+#ifdef GRNXX_WINDOWS
+    case WINDOWS_ERROR: {
+      char *message;
+      if (::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                           FORMAT_MESSAGE_FROM_SYSTEM |
+                           FORMAT_MESSAGE_IGNORE_INSERTS,
+                           nullptr, windows_error_,
+                           MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
+                           reinterpret_cast<LPSTR>(&message),
+                           0, nullptr) != 0) {
+        std::unique_ptr<char[], Freer> message_freer(message);
+        builder << '(' << message << ')';
+      } else {
+        builder << "(n/a)";
+      }
+      return builder;
+    }
+#endif  // GRNXX_WINDOWS
+    case GRNXX_ERROR: {
+      // TODO
+      // grnxx_error_
+      return builder << "(undefined)";
+    }
+    default: {
+      return builder << "(undefined)";
+    }
+  }
+}
+
+std::ostream &operator<<(std::ostream &stream, const Error &error) {
+  char buf[64];
+  StringBuilder builder(buf, STRING_BUILDER_AUTO_RESIZE);
+  builder << error;
+  return stream.write(builder.c_str(), builder.length());
+}
+
+}  // namespace grnxx

  Added: lib/error.hpp (+89 -0) 100644
===================================================================
--- /dev/null
+++ lib/error.hpp    2012-11-28 16:36:40 +0900 (0787390)
@@ -0,0 +1,89 @@
+/*
+  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_ERROR_HPP
+#define GRNXX_ERROR_HPP
+
+#include "basic.hpp"
+#include "string_builder.hpp"
+
+namespace grnxx {
+
+enum ErrorType {
+  POSIX_ERROR,
+#ifdef GRNXX_WINDOWS
+  WINDOWS_ERROR,
+#endif  // GRNXX_WINDOWS
+  GRNXX_ERROR
+};
+
+enum ErrorCode {
+  // TODO
+};
+
+class Error {
+ public:
+  // For errno.
+  explicit Error(int error_code)
+    : type_(POSIX_ERROR), posix_error_(error_code) {}
+#ifdef GRNXX_WINDOWS
+  // For DWORD returned by ::GetLastError().
+  explicit Error(unsigned long error_code)
+    : type_(WINDOWS_ERROR), windows_error_(error_code) {}
+#endif  // GRNXX_WINDOWS
+  explicit Error(ErrorCode error_code)
+    : type_(GRNXX_ERROR), grnxx_error_(error_code) {}
+
+  ErrorType type() const {
+    return type_;
+  }
+  int posix_error() const {
+    return posix_error_;
+  }
+#ifdef GRNXX_WINDOWS
+  unsigned long windows_error() const {
+    return windows_error_;
+  }
+#endif  // GRNXX_WINDOWS
+  ErrorCode grnxx_error() const {
+    return grnxx_error_;
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+ private:
+  ErrorType type_;
+  union {
+    int posix_error_;
+#ifdef GRNXX_WINDOWS
+    unsigned long windows_error_;
+#endif  // GRNXX_WINDOWS
+    ErrorCode grnxx_error_;
+  };
+
+  // Copyable.
+};
+
+inline StringBuilder &operator<<(StringBuilder &builder, const Error &error) {
+  return error.write_to(builder);
+}
+
+std::ostream &operator<<(std::ostream &stream, const Error &error);
+
+}  // namespace grnxx
+
+#endif  // GRNXX_ERROR_HPP

  Added: lib/exception.hpp (+47 -0) 100644
===================================================================
--- /dev/null
+++ lib/exception.hpp    2012-11-28 16:36:40 +0900 (ef5824c)
@@ -0,0 +1,47 @@
+/*
+  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_EXCEPTION_HPP
+#define GRNXX_EXCEPTION_HPP
+
+#include "basic.hpp"
+
+#define GRNXX_THROW() (throw ::grnxx::Exception())
+
+namespace grnxx {
+
+class Exception : std::exception {
+ public:
+  Exception() GRNXX_NOEXCEPT {}
+  virtual ~Exception() GRNXX_NOEXCEPT {}
+
+  Exception(const Exception &) GRNXX_NOEXCEPT {}
+  Exception &operator=(const Exception &) GRNXX_NOEXCEPT {
+    return *this;
+  }
+
+  virtual const char *what() const GRNXX_NOEXCEPT {
+    return "";
+  }
+
+ private:
+  // TODO.
+};
+
+}  // namespace grnxx
+
+#endif  // GRNXX_EXCEPTION_HPP

  Added: lib/features.hpp (+232 -0) 100644
===================================================================
--- /dev/null
+++ lib/features.hpp    2012-11-28 16:36:40 +0900 (a4f8d1d)
@@ -0,0 +1,232 @@
+/*
+  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_FEATURES_HPP
+#define GRNXX_FEATURES_HPP
+
+// Operating system features.
+
+#ifdef _WIN32
+# define GRNXX_WINDOWS
+# ifdef _WIN64
+#  define GRNXX_WIN64 _WIN64
+# else  // _WIN64
+#  define GRNXX_WIN32 _WIN32
+# endif  // _WIN64
+#endif  // _WIN32
+
+#if defined(__APPLE__) && defined(__MACH__)
+# define GRNXX_APPLE
+#endif  // defined(__APPLE__) && defined(__MACH__)
+
+#if defined(sun) || defined(__sun)
+# define GRNXX_SOLARIS
+#endif  // defined(sun) || defined(__sun)
+
+// Compiler features.
+
+#ifdef _MSC_VER
+# define GRNXX_MSC
+# define GRNXX_MSC_VERSION _MSC_VER
+# ifdef GRNXX_WIN32
+#  define GRNXX_MSC32
+# else  // GRNXX_WIN32
+#  define GRNXX_MSC64
+# endif  // GRNXX_WIN32
+#endif  // _MSC_VER
+
+#ifdef __MINGW32__
+# define GRNXX_MINGW
+# ifdef __MINGW64__
+#  define GRNXX_MINGW64
+# else  // __MINGW64__
+#  define GRNXX_MINGW32
+# endif  // __MINGW64__
+#endif  // __MINGW32__
+
+#ifdef __clang__
+# define GRNXX_CLANG
+# define GRNXX_CLANG_MAJOR __clang_major__
+# define GRNXX_CLANG_MINOR __clang_minor__
+# define GRNXX_CLANG_PATCH_LEVEL __clang_patchlevel__
+# define GRNXX_CLANG_VERSION __clang_version__
+#endif  // __clang__
+
+#ifdef __GNUC__
+# define GRNXX_GNUC
+# define GRNXX_GNUC_MAJOR __GNUC__
+# ifdef __GNUC_MINOR__
+#  define GRNXX_GNUC_MINOR __GNUC_MINOR__
+# else  // __GNUC_MINOR__
+#  define GRNXX_GNUC_MINOR 0
+# endif  // __GNUC_MINOR__
+# ifdef __GNUC_PATCHLEVEL__
+#  define GRNXX_GNUC_PATCH_LEVEL __GNUC_PATCHLEVEL__
+# else  // __GNUC_PATCHLEVEL__
+#  define GRNXX_GNUC_PATCH_LEVEL 0
+# endif  // __GNUC_PATCHLEVEL__
+# define GRNXX_GNUC_MAKE_VERSION(major, minor, patch_level)\
+    ((major * 10000) + (minor * 100) + patch_level)
+# define GRNXX_GNUC_VERSION GRNXX_GNUC_MAKE_VERSION(\
+    GRNXX_GNUC_MAJOR, GRNXX_GNUC_MINOR, GRNXX_GNUC_PATCH_LEVEL)
+#endif  // defined(__GNUC__)
+
+#ifdef GRNXX_CLANG
+# ifdef __has_builtin
+#  define GRNXX_CLANG_HAS_BUILTIN(builtin) __has_builtin(builtin)
+# else
+#  define GRNXX_CLANG_HAS_BUILTIN(builtin) false
+# endif  // __has_builtin
+# ifdef __has_feature
+#  define GRNXX_CLANG_HAS_FEATURE(feature) __has_feature(feature)
+# else
+#  define GRNXX_CLANG_HAS_FEATURE(feature) false
+# endif  // __has_feature
+# ifdef __has_extension
+#  define GRNXX_CLANG_HAS_EXTENSION(extension) __has_extension(extension)
+# else
+#  define GRNXX_CLANG_HAS_EXTENSION(extension) false
+# endif  // __has_extension
+# define GRNXX_CLANG_HAS(thing) (GRNXX_CLANG_HAS_BUILTIN(thing) |\
+    GRNXX_CLANG_HAS_FEATURE(thing) | GRNXX_CLANG_HAS_EXTENSION(thing))
+#endif  // GRNXX_CLANG
+
+#ifdef GRNXX_CLANG
+# if GRNXX_CLANG_HAS(__builtin_clz)
+#  define GRNXX_HAS_GNUC_BUILTIN_CLZ
+# endif  // GRNXX_CLANG_HAS(__builtin_clz)
+# if GRNXX_CLANG_HAS(__sync_bool_compare_and_swap)
+#  define GRNXX_HAS_GNUC_BUILTIN_SYNC
+# endif  // GRNXX_CLANG_HAS(__sync_bool_compare_and_swap)
+# if GRNXX_CLANG_HAS(__atomic_compare_exchange_n)
+#  define GRNXX_HAS_GNUC_BUILTIN_ATOMIC
+# endif  // GRNXX_CLANG_HAS(__atomic_compare_exchange_n)
+# if GRNXX_CLANG_HAS(c_atomic)
+#  define GRNXX_HAS_CLANG_BUILTIN_ATOMIC
+# endif  // GRNXX_CLANG_HAS(c_atomic)
+# if GRNXX_CLANG_HAS(cxx_constexpr)
+#  define GRNXX_HAS_CONSTEXPR
+# endif  // GRNXX_CLANG_HAS(cxx_constexpr)
+# if GRNXX_CLANG_HAS(cxx_explicit_conversions)
+#  define GRNXX_HAS_EXPLICIT_CONVERSION
+# endif  // GRNXX_CLANG_HAS(cxx_explicit_conversions)
+# if GRNXX_CLANG_HAS(cxx_noexcept)
+#  define GRNXX_HAS_NOEXCEPT
+# endif  // GRNXX_CLANG_HAS(cxx_noexcept)
+#elif defined(GRNXX_GNUC)
+# define GRNXX_HAS_GNUC_BUILTIN_CLZ
+# if GRNXX_GNUC_VERSION >= GRNXX_GNUC_MAKE_VERSION(4, 2, 0)
+#  define GRNXX_HAS_GNUC_BUILTIN_SYNC
+# endif  // GRNXX_GNUC_VERSION >= GRNXX_GNUC_MAKE_VERSION(4, 2, 0)
+# if GRNXX_GNUC_VERSION >= GRNXX_GNUC_MAKE_VERSION(4, 5, 0)
+#  define GRNXX_HAS_EXPLICIT_CONVERSION
+# endif  // GRNXX_GNUC_VERSION >= GRNXX_GNUC_MAKE_VERSION(4, 5, 0)
+# if GRNXX_GNUC_VERSION >= GRNXX_GNUC_MAKE_VERSION(4, 6, 0)
+#  define GRNXX_HAS_CONSTEXPR
+#  define GRNXX_HAS_NOEXCEPT
+# endif  // GRNXX_GNUC_VERSION >= GRNXX_GNUC_MAKE_VERSION(4, 6, 0)
+# if GRNXX_GNUC_VERSION >= GRNXX_GNUC_MAKE_VERSION(4, 7, 0)
+#  define GRNXX_HAS_GNUC_BUILTIN_ATOMIC
+# endif  // GRNXX_GNUC_VERSION >= GRNXX_GNUC_MAKE_VERSION(4, 7, 0)
+#endif  // defined(GRNXX_GNUC)
+
+#ifdef GRNXX_HAS_CONSTEXPR
+# define GRNXX_CONSTEXPR constexpr
+#else  // GRNXX_HAS_CONSTEXPR
+# define GRNXX_CONSTEXPR
+#endif  // GRNXX_HAS_CONSTEXPR
+
+#ifdef GRNXX_HAS_EXPLICIT_CONVERSION
+# define GRNXX_EXPLICIT_CONVERSION explicit
+#else  // GRNXX_HAS_EXPLICIT_CONVERSION
+# define GRNXX_EXPLICIT_CONVERSION
+#endif  // GRNXX_HAS_EXPLICIT_CONVERSION
+
+#ifdef GRNXX_HAS_NOEXCEPT
+# define GRNXX_NOEXCEPT noexcept
+#else  // GRNXX_HAS_NOEXCEPT
+# define GRNXX_NOEXCEPT throw()
+#endif  // GRNXX_HAS_NOEXCEPT
+
+// Source features.
+
+#ifdef _POSIX_C_SOURCE
+# define GRNXX_POSIX_C_SOURCE _POSIX_C_SOURCE
+#else  // _POSIX_C_SOURCE
+# define GRNXX_POSIX_C_SOURCE 0
+#endif  // _POSIX_C_SOURCE
+
+#ifdef _XOPEN_SOURCE
+# define GRNXX_XOPEN_SOURCE _XOPEN_SOURCE
+#else  // _XOPEN_SOURCE
+# define GRNXX_XOPEN_SOURCE 0
+#endif  // _XOPEN_SOURCE
+
+#ifdef _XOPEN_SOURCE_EXTENDED
+# define GRNXX_XOPEN_SOURCE_EXTENDED _XOPEN_SOURCE_EXTENDED
+#else  // _XOPEN_SOURCE_EXTENDED
+# define GRNXX_XOPEN_SOURCE_EXTENDED 0
+#endif  // _XOPEN_SOURCE_EXTENDED
+
+#ifdef _BSD_SOURCE
+# define GRNXX_BSD_SOURCE _BSD_SOURCE
+#else  // _BSD_SOURCE
+# define GRNXX_BSD_SOURCE 0
+#endif  // _BSD_SOURCE
+
+#ifdef _SVID_SOURCE
+# define GRNXX_SVID_SOURCE _SVID_SOURCE
+#else  // _SVID_SOURCE
+# define GRNXX_SVID_SOURCE 0
+#endif  // _SVID_SOURCE
+
+#ifdef _POSIX_SOURCE
+# define GRNXX_POSIX_SOURCE _POSIX_SOURCE
+#else  // _POSIX_SOURCE
+# define GRNXX_POSIX_SOURCE 0
+#endif  // _POSIX_SOURCE
+
+// Available functions.
+
+#if GRNXX_POSIX_C_SOURCE >= 199309L
+# define GRNXX_HAS_NANOSLEEP
+#endif  // GRNXX_POSIX_C_SOURCE >= 199309L
+
+#if (GRNXX_POSIX_C_SOURCE >= 1) || GRNXX_XOPEN_SOURCE || GRNXX_BSD_SOURCE ||\
+    GRNXX_SVID_SOURCE || GRNXX_POSIX_SOURCE
+# define GRNXX_HAS_LOCALTIME_R
+#endif  // (GRNXX_POSIX_C_SOURCE >= 1) || ...
+
+#if (GRNXX_XOPEN_SOURCE >= 500) || (GRNXX_POSIX_C_SOURCE >= 200809L)
+# define GRNXX_HAS_PREAD
+# define GRNXX_HAS_PWRITE
+#endif  // (GRNXX_XOPEN_SOURCE >= 500) || (GRNXX_POSIX_C_SOURCE >= 200809L)
+
+#ifndef GRNXX_WINDOWS
+# include <unistd.h>
+# ifdef _POSIX_TIMERS
+#  if _POSIX_TIMERS > 0
+#   define GRNXX_HAS_POSIX_TIMER
+#   define GRNXX_HAS_CLOCK_GETTIME
+#  endif  // _POSIX_TIMERS > 0
+# endif  // _POSIX_TIMERS
+# ifdef _POSIX_PRIORITY_SCHEDULING
+#  define GRNXX_HAS_SCHED_YIELD
+# endif  // _POSIX_PRIORITY_SCHEDULING
+#endif  // GRNXX_WINDOWS
+
+#endif  // GRNXX_FEATURES_HPP

  Added: lib/flags_impl.hpp (+83 -0) 100644
===================================================================
--- /dev/null
+++ lib/flags_impl.hpp    2012-11-28 16:36:40 +0900 (722ec4c)
@@ -0,0 +1,83 @@
+/*
+  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_FLAGS_IMPL_HPP
+#define GRNXX_FLAGS_IMPL_HPP
+
+#include "basic.hpp"
+
+namespace grnxx {
+
+template <typename T, typename U = uint32_t>
+class FlagsImpl {
+ public:
+  typedef T Identifier;
+  typedef U Type;
+
+  GRNXX_CONSTEXPR FlagsImpl() : flags_(0) {}
+  GRNXX_CONSTEXPR FlagsImpl(const FlagsImpl &flags) : flags_(flags.flags_) {}
+
+  GRNXX_CONSTEXPR GRNXX_EXPLICIT_CONVERSION operator bool() const {
+    return flags_ != 0;
+  }
+
+  GRNXX_CONSTEXPR FlagsImpl operator&(FlagsImpl rhs) const {
+    return FlagsImpl(flags_ & rhs.flags_);
+  }
+  GRNXX_CONSTEXPR FlagsImpl operator|(FlagsImpl rhs) const {
+    return FlagsImpl(flags_ | rhs.flags_);
+  }
+  GRNXX_CONSTEXPR FlagsImpl operator^(FlagsImpl rhs) const {
+    return FlagsImpl(flags_ ^ rhs.flags_);
+  }
+  GRNXX_CONSTEXPR FlagsImpl operator~() const {
+    return FlagsImpl(~flags_);
+  }
+
+  GRNXX_CONSTEXPR bool operator==(FlagsImpl rhs) const {
+    return flags_ == rhs.flags_;
+  }
+  GRNXX_CONSTEXPR bool operator!=(FlagsImpl rhs) const {
+    return flags_ == rhs.flags_;
+  }
+
+  FlagsImpl &operator&=(FlagsImpl rhs) {
+    flags_ &= rhs.flags_;
+    return *this;
+  }
+  FlagsImpl &operator|=(FlagsImpl rhs) {
+    flags_ |= rhs.flags_;
+    return *this;
+  }
+  FlagsImpl &operator^=(FlagsImpl rhs) {
+    flags_ ^= rhs.flags_;
+    return *this;
+  }
+
+  static GRNXX_CONSTEXPR FlagsImpl define(Type flags) {
+    return FlagsImpl(flags);
+  }
+
+ private:
+  Type flags_;
+
+  explicit GRNXX_CONSTEXPR FlagsImpl(Type flags) : flags_(flags) {}
+};
+
+}  // namespace grnxx
+
+#endif  // GRNXX_FLAGS_IMPL_HPP

  Added: lib/grnxx.cpp (+32 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx.cpp    2012-11-28 16:36:40 +0900 (dd03a13)
@@ -0,0 +1,32 @@
+/*
+  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 "grnxx.hpp"
+
+#include "../config.h"
+
+namespace grnxx {
+
+const char *Grnxx::bugreport() {
+  return PACKAGE_BUGREPORT;
+}
+
+const char *Grnxx::version() {
+  return PACKAGE_VERSION;
+}
+
+}  // namespace grnxx

  Added: lib/grnxx.hpp (+41 -0) 100644
===================================================================
--- /dev/null
+++ lib/grnxx.hpp    2012-11-28 16:36:40 +0900 (f0393f5)
@@ -0,0 +1,41 @@
+/*
+  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_GRNXX_HPP
+#define GRNXX_GRNXX_HPP
+
+namespace grnxx {
+
+class Grnxx {
+ public:
+  // The e-mail address for bug reports.
+  static const char *bugreport();
+
+  // The version.
+  static const char *version();
+
+ private:
+  Grnxx();
+  ~Grnxx();
+
+  Grnxx(const Grnxx &);
+  Grnxx &operator=(const Grnxx &);
+};
+
+}  // namespace grnxx
+
+#endif  // GRNXX_GRNXX_HPP

  Added: lib/intrinsic.hpp (+790 -0) 100644
===================================================================
--- /dev/null
+++ lib/intrinsic.hpp    2012-11-28 16:36:40 +0900 (3f60fa2)
@@ -0,0 +1,790 @@
+/*
+  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_INTRINSIC_HPP
+#define GRNXX_INTRINSIC_HPP
+
+#include "basic.hpp"
+
+#ifdef GRNXX_MSC
+# include <intrin.h>
+# pragma intrinsic(_BitScanReverse)
+# pragma intrinsic(_BitScanForward)
+# pragma intrinsic(_InterlockedExchangeAdd)
+# pragma intrinsic(_InterlockedOr)
+# pragma intrinsic(_InterlockedAnd)
+# pragma intrinsic(_InterlockedXor)
+# pragma intrinsic(_InterlockedCompareExchange)
+# pragma intrinsic(_InterlockedCompareExchange64)
+# ifdef GRNXX_MSC64
+#  pragma intrinsic(_BitScanReverse64)
+#  pragma intrinsic(_BitScanForward64)
+#  pragma intrinsic(_InterlockedOr64)
+#  pragma intrinsic(_InterlockedAnd64)
+#  pragma intrinsic(_InterlockedXor64)
+#  pragma intrinsic(_InterlockedExchangeAdd64)
+# endif  // GRNXX_MSC64
+#endif  // GRNXX_MSC
+
+namespace grnxx {
+
+// bit_scan_reverse() returns the position of the most significant 1 bit.
+// For example, if value == 0x1010, bit_scan_reverse() returns 12.
+// Note that if value == 0, the result is undefined.
+template <typename Value>
+inline uint8_t bit_scan_reverse(Value value);
+
+// bit_scan_forward() returns the position of the most insignificant 1 bit.
+// For example, if value == 0x1010, bit_scan_forward() returns 4.
+// Note that if value == 0, the result is undefined.
+template <typename Value>
+inline uint8_t bit_scan_forward(Value value);
+
+// atomic_compare_and_swap() atomically performs the following.
+//  if (*value == expected) {
+//    *value = desired;
+//    return true;
+//  } else {
+//    return false;
+//  }
+template <typename Value>
+inline bool atomic_compare_and_swap(Value expected, Value desired,
+                                    volatile Value *value);
+
+// atomic_fetch_and_add() atomically performs the following:
+//  temp = *value;
+//  *value += plus;
+//  return temp;
+template <typename Plus, typename Value>
+inline Value atomic_fetch_and_add(Plus plus, volatile Value *value);
+
+// atomic_fetch_or() atomically performs the following:
+//  temp = *value;
+//  *value |= plus;
+//  return temp;
+template <typename Value>
+inline Value atomic_fetch_and_or(Value mask, volatile Value *value);
+
+// atomic_fetch_and() atomically performs the following:
+//  temp = *value;
+//  *value &= plus;
+//  return temp;
+template <typename Value>
+inline Value atomic_fetch_and_and(Value mask, volatile Value *value);
+
+// atomic_fetch_xor() atomically performs the following:
+//  temp = *value;
+//  *value ^= plus;
+//  return temp;
+template <typename Value>
+inline Value atomic_fetch_and_xor(Value mask, volatile Value *value);
+
+// Implementation of bit_scan_reverse.
+
+#if defined(GRNXX_MSC) || defined(GRNXX_HAS_GNUC_BUILTIN_CLZ)
+
+template <size_t ValueSize>
+class BitScanReverse {
+ public:
+  template <typename Value>
+  uint8_t operator()(Value value) const {
+# ifdef GRNXX_MSC
+    unsigned long index;
+    ::_BitScanReverse(&index, static_cast<unsigned long>(value));
+    return static_cast<uint8_t>(index);
+# else  // GRNXX_MSC
+    return static_cast<uint8_t>(
+        31 - ::__builtin_clz(static_cast<unsigned int>(value)));
+# endif  // GRNXX_MSC
+  }
+};
+
+template <>
+class BitScanReverse<8> {
+ public:
+  template <typename Value>
+  uint8_t operator()(Value value) const {
+# ifdef GRNXX_MSC
+#  ifdef GRNXX_MSC64
+    unsigned long index;
+    ::_BitScanReverse64(&index, static_cast<unsigned __int64>(value));
+    return static_cast<uint8_t>(index);
+#  else  // GRNXX_MSC64
+    if ((value >> 32) != 0) {
+      return static_cast<uint8_t>(BitScanReverse<4>((value >> 32) + 32));
+    }
+    return BitScanReverse<4>(value);
+#  endif  // GRNXX_MSC64
+# else  // GRNXX_MSC
+    return static_cast<uint8_t>(
+        63 - ::__builtin_clzll(static_cast<unsigned long long>(value)));
+# endif  // GRNXX_MSC
+  }
+};
+
+#endif  // defined(GRNXX_MSC) || defined(GRNXX_HAS_GNUC_BUILTIN_CLZ)
+
+template <typename Value>
+inline uint8_t bit_scan_reverse(Value value) {
+  static_assert(std::is_integral<Value>::value &&
+                std::is_unsigned<Value>::value,
+                "bit_scan_reverse accepts only unsigned integer types");
+
+#if defined(GRNXX_MSC) || defined(GRNXX_HAS_GNUC_BUILTIN_CLZ)
+  return BitScanReverse<sizeof(Value)>()(value);
+#else  // defined(GRNXX_MSC) || defined(GRNXX_HAS_GNUC_BUILTIN_CLZ)
+  uint8_t result = 0;
+  for (uint8_t shift = static_cast<uint8_t>(sizeof(Value) * 4);
+       shift != 0; shift /= 2) {
+    if ((value >> shift) != 0) {
+      value >>= shift;
+      result += shift;
+    }
+  }
+  return result;
+#endif  // defined(GRNXX_MSC) || defined(GRNXX_HAS_GNUC_BUILTIN_CLZ)
+}
+
+// Implementation of bit_scan_forward.
+
+#if defined(GRNXX_MSC) || defined(GRNXX_HAS_GNUC_BUILTIN_CLZ)
+
+template <size_t ValueSize>
+class BitScanForward {
+ public:
+  template <typename Value>
+  uint8_t operator()(Value value) const {
+# ifdef GRNXX_MSC
+    unsigned long index;
+    ::_BitScanForward(&index, static_cast<unsigned long>(value));
+    return static_cast<uint8_t>(index);
+# else  // GRNXX_MSC
+    return static_cast<uint8_t>(
+        ::__builtin_ctz(static_cast<unsigned int>(value)));
+# endif  // GRNXX_MSC
+  }
+};
+
+template <>
+class BitScanForward<8> {
+ public:
+  template <typename Value>
+  uint8_t operator()(Value value) const {
+# ifdef GRNXX_MSC
+#  ifdef GRNXX_MSC64
+    unsigned long index;
+    ::_BitScanForward64(&index, static_cast<unsigned __int64>(value));
+    return static_cast<uint8_t>(index);
+#  else  // GRNXX_MSC64
+    if ((value & 0xFFFFFFFFU) == 0) {
+      return static_cast<uint8_t>(BitScanForward<4>((value >> 32) + 32));
+    }
+    return BitScanForward<4>(value);
+#  endif  // GRNXX_MSC64
+# else  // GRNXX_MSC
+    return static_cast<uint8_t>(
+        ::__builtin_ctzll(static_cast<unsigned long long>(value)));
+# endif  // GRNXX_MSC
+  }
+};
+
+#endif  // defined(GRNXX_MSC) || defined(GRNXX_HAS_GNUC_BUILTIN_CLZ)
+
+template <typename Value>
+inline uint8_t bit_scan_forward(Value value) {
+  static_assert(std::is_integral<Value>::value &&
+                std::is_unsigned<Value>::value,
+                "bit_scan_forward accepts only unsigned integer types");
+
+#if defined(GRNXX_MSC) || defined(GRNXX_HAS_GNUC_BUILTIN_CLZ)
+  return BitScanForward<sizeof(Value)>()(value);
+#else  // defined(GRNXX_MSC) || defined(GRNXX_HAS_GNUC_BUILTIN_CLZ)
+  uint8_t result = static_cast<uint8_t>(sizeof(Value) * 8) - 1;
+  for (uint8_t shift = static_cast<uint8_t>(sizeof(Value) * 4);
+       shift != 0; shift /= 2) {
+    if (static_cast<Value>(value << shift) != 0) {
+      value <<= shift;
+      result -= shift;
+    }
+  }
+  return result;
+#endif  // defined(GRNXX_MSC) || defined(GRNXX_HAS_GNUC_BUILTIN_CLZ)
+}
+
+// Helper classes.
+
+template <size_t ValueSize> class IntrinsicType;
+
+#ifdef GRNXX_MSC
+template <> class IntrinsicType<1> { public: typedef char Value; };
+template <> class IntrinsicType<2> { public: typedef short Value; };
+template <> class IntrinsicType<4> { public: typedef long Value; };
+template <> class IntrinsicType<8> { public: typedef __int64 Value; };
+#else  // GRNXX_MSC
+template <> class IntrinsicType<1> { public: typedef uint8_t Value; };
+template <> class IntrinsicType<2> { public: typedef uint16_t Value; };
+template <> class IntrinsicType<4> { public: typedef uint32_t Value; };
+template <> class IntrinsicType<8> { public: typedef uint64_t Value; };
+#endif  // GRNXX_MSC
+
+template <typename Value>
+class Intrinsic {
+ public:
+  typedef typename IntrinsicType<sizeof(Value)>::Value InternalValue;
+  typedef volatile InternalValue *InternalPointer;
+};
+
+// Implementation of atomic_compare_and_swap.
+
+#ifdef GRNXX_MSC
+
+template <typename Value>
+class AtomicCompareAndSwap;
+
+template <>
+class AtomicCompareAndSwap<long> {
+ public:
+  bool operator()(long expected, long desired,
+                  volatile long *value) const {
+    return ::_InterlockedCompareExchange(value, desired, expected) == expected;
+  }
+};
+
+template <>
+class AtomicCompareAndSwap<__int64> {
+ public:
+  bool operator()(__int64 expected, __int64 desired,
+                  volatile __int64 *value) const {
+    return ::_InterlockedCompareExchange64(value, desired, expected) == expected;
+  }
+};
+
+template <typename Value>
+class AtomicCompareAndSwap {
+ public:
+  typedef typename Intrinsic<Value>::InternalValue InternalValue;
+  typedef typename Intrinsic<Value>::InternalPointer InternalPointer;
+
+  bool operator()(Value expected, Value desired, volatile Value *value) const {
+    return AtomicCompareAndSwap<InternalValue>()(
+        reinterpret_cast<const InternalValue &>(expected),
+        reinterpret_cast<const InternalValue &>(desired),
+        reinterpret_cast<InternalPointer>(value));
+  }
+};
+
+#elif defined(GRNXX_HAS_GNUC_BUILTIN_ATOMIC) ||\
+      defined(GRNXX_HAS_GNUC_BUILTIN_SYNC)
+
+// GNUC builtin functions for atomic operations accept integer types and
+// pointer types. Other types require type casting.
+template <typename Value, bool RequiresCast>
+class InternalAtomicCompareAndSwap;
+
+template <typename Value>
+class InternalAtomicCompareAndSwap<Value, false> {
+ public:
+  bool operator()(Value expected, Value desired,
+                  volatile Value *value) const {
+#ifdef GRNXX_HAS_GNUC_BUILTIN_ATOMIC
+    return ::__atomic_compare_exchange_n(
+        value, &expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
+#else  // GRNXX_HAS_GNUC_BUILTIN_ATOMIC
+    return ::__sync_bool_compare_and_swap(value, expected, desired);
+#endif  // GRNXX_HAS_GNUC_BUILTIN_ATOMIC
+  }
+};
+
+template <typename Value>
+class InternalAtomicCompareAndSwap<Value, true> {
+ public:
+  typedef typename Intrinsic<Value>::InternalValue InternalValue;
+  typedef typename Intrinsic<Value>::InternalPointer InternalPointer;
+
+  bool operator()(Value expected, Value desired, volatile Value *value) const {
+    return InternalAtomicCompareAndSwap<InternalValue, false>()(
+        reinterpret_cast<const InternalValue &>(expected),
+        reinterpret_cast<const InternalValue &>(desired),
+        reinterpret_cast<InternalPointer>(value));
+  }
+};
+
+template <typename Value>
+class AtomicCompareAndSwap {
+ public:
+  typedef typename Intrinsic<Value>::InternalValue InternalValue;
+  typedef typename Intrinsic<Value>::InternalPointer InternalPointer;
+
+  bool operator()(Value expected, Value desired, volatile Value *value) const {
+    return InternalAtomicCompareAndSwap<Value,
+        !std::is_integral<Value>::value && !std::is_pointer<Value>::value>()(
+            expected, desired, value);
+  }
+};
+
+#else  // defined(GRNXX_HAS_GNUC_BUILTIN_ATOMIC) || ...
+
+template <typename Value>
+class AtomicCompareAndSwap {
+ public:
+  bool operator()(Value expected, Value desired, volatile Value *value) const {
+    // Non-atomic implementation.
+    if (*value == expected) {
+      *value = desired;
+      return true;
+    } else {
+      return false;
+    }
+  }
+};
+
+#endif  // defined(GRNXX_HAS_GNUC_BUILTIN_ATOMIC) || ...
+
+template <typename Value>
+inline bool atomic_compare_and_swap(Value expected, Value desired,
+                                    volatile Value *value) {
+  static_assert((sizeof(Value) == 4) || (sizeof(Value) == 8),
+      "atomic_compare_and_swap is supported only for 4/8 bytes values.");
+
+  return AtomicCompareAndSwap<Value>()(expected, desired, value);
+}
+
+// Implementation of atomic_fetch_and_add.
+
+#ifdef GRNXX_MSC
+
+template <typename Value>
+class InternalAtomicFetchAndAdd;
+
+template <>
+class InternalAtomicFetchAndAdd<long> {
+ public:
+  long operator()(long plus, volatile long *value) const {
+    return ::_InterlockedExchangeAdd(plus, value);
+  }
+};
+
+template <>
+class InternalAtomicFetchAndAdd<__int64> {
+ public:
+  __int64 operator()(__int64 plus, volatile __int64 *value) const {
+#ifdef GRNXX_MSC64
+    return ::_InterlockedExchangeAdd64(plus, value);
+#else  // GRNXX_MSC64
+    for ( ; ; ) {
+      const __int64 expected = *value;
+      const __int64 desired = expected + plus;
+      if (atomic_compare_and_swap(expected, desired, value)) {
+        return expected;
+      }
+    }
+#endif  // GRNXX_MSC64
+  }
+};
+
+template <typename Value>
+class InternalAtomicFetchAndAdd {
+ public:
+  typedef typename Intrinsic<Value>::InternalValue InternalValue;
+  typedef typename Intrinsic<Value>::InternalPointer InternalPointer;
+
+  Value operator()(Value plus, volatile Value *value) const {
+    return static_cast<Value>(InternalAtomicFetchAndAdd<InternalValue>()(
+        static_cast<InternalValue>(plus),
+        reinterpret_cast<InternalPointer>(value)));
+  }
+};
+
+#elif defined(GRNXX_HAS_GNUC_BUILTIN_ATOMIC) ||\
+      defined(GRNXX_HAS_GNUC_BUILTIN_SYNC)
+
+template <typename Value>
+class InternalAtomicFetchAndAdd {
+ public:
+  Value operator()(Value plus, volatile Value *value) const {
+#ifdef GRNXX_HAS_GNUC_BUILTIN_ATOMIC
+    return ::__atomic_fetch_add(value, plus, __ATOMIC_SEQ_CST);
+#else  // GRNXX_HAS_GNUC_BUILTIN_ATOMIC
+    return ::__sync_fetch_and_add(value, plus);
+#endif  // GRNXX_HAS_GNUC_BUILTIN_ATOMIC
+  }
+};
+
+#else  // defined(GRNXX_HAS_GNUC_BUILTIN_ATOMIC) || ...
+
+template <typename Value>
+class InternalAtomicFetchAndAdd {
+ public:
+  Value operator()(Value plus, volatile Value *value) const {
+    for ( ; ; ) {
+      const Value expected = *value;
+      const Value desired = expected + plus;
+      if (atomic_compare_and_swap(expected, desired, value)) {
+        return expected;
+      }
+    }
+  }
+};
+
+#endif
+
+template <typename Plus, typename Value, bool PlusAndValueAreIntegers>
+class AtomicFetchAndAdd;
+
+template <typename Plus, typename Value>
+class AtomicFetchAndAdd<Plus, Value, true> {
+ public:
+  Value operator()(Plus plus, volatile Value *value) const {
+    return InternalAtomicFetchAndAdd<Value>()(static_cast<Value>(plus), value);
+  }
+};
+
+template <typename Plus, typename Value>
+class AtomicFetchAndAdd<Plus, Value, false> {
+ public:
+  Value operator()(Plus plus, volatile Value *value) const {
+    for ( ; ; ) {
+      const Value expected = *value;
+      const Value desired = expected + plus;
+      if (atomic_compare_and_swap(expected, desired, value)) {
+        return expected;
+      }
+    }
+  }
+};
+
+template <typename Plus, typename Value>
+inline Value atomic_fetch_and_add(Plus plus, volatile Value *value) {
+  static_assert((sizeof(Value) == 4) || (sizeof(Value) == 8),
+      "atomic_fetch_and_add is supported only for 4/8 bytes values.");
+
+  return AtomicFetchAndAdd<Plus, Value, std::is_integral<Plus>::value &&
+                           std::is_integral<Value>::value>()(plus, value);
+}
+
+// Implementation of atomic_fetch_and_or.
+
+#ifdef GRNXX_MSC
+
+template <typename Value>
+class AtomicFetchAndOr;
+
+template <>
+class AtomicFetchAndOr<char> {
+ public:
+  char operator()(char mask, volatile char *value) const {
+    return ::_InterlockedOr8(mask, value);
+  }
+};
+
+template <>
+class AtomicFetchAndOr<short> {
+ public:
+  short operator()(short mask, volatile short *value) const {
+    return ::_InterlockedOr16(mask, value);
+  }
+};
+
+template <>
+class AtomicFetchAndOr<long> {
+ public:
+  long operator()(long mask, volatile long *value) const {
+    return ::_InterlockedOr(mask, value);
+  }
+};
+
+template <>
+class AtomicFetchAndOr<__int64> {
+ public:
+  __int64 operator()(__int64 mask, volatile __int64 *value) const {
+#ifdef GRNXX_MSC64
+    return ::_InterlockedOr64(mask, value);
+#else  // GRNXX_MSC64
+    for ( ; ; ) {
+      const __int64 expected = *value;
+      const __int64 desired = expected | mask;
+      if (atomic_compare_and_swap(expected, desired, value)) {
+        return expected;
+      }
+    }
+#endif  // GRNXX_MSC64
+  }
+};
+
+template <typename Value>
+class AtomicFetchAndOr {
+ public:
+  typedef typename Intrinsic<Value>::InternalValue InternalValue;
+  typedef typename Intrinsic<Value>::InternalPointer InternalPointer;
+
+  Value operator()(Value plus, volatile Value *value) const {
+    return static_cast<Value>(AtomicFetchAndOr<InternalValue>()(
+        static_cast<InternalValue>(mask),
+        reinterpret_cast<InternalPointer>(value)));
+  }
+};
+
+#elif defined(GRNXX_HAS_GNUC_BUILTIN_ATOMIC) ||\
+      defined(GRNXX_HAS_GNUC_BUILTIN_SYNC)
+
+template <typename Value>
+class AtomicFetchAndOr {
+ public:
+  Value operator()(Value mask, volatile Value *value) const {
+#ifdef GRNXX_HAS_GNUC_BUILTIN_ATOMIC
+    return ::__atomic_fetch_or(value, mask, __ATOMIC_SEQ_CST);
+#else  // GRNXX_HAS_GNUC_BUILTIN_ATOMIC
+    return ::__sync_fetch_and_or(value, mask);
+#endif  // GRNXX_HAS_GNUC_BUILTIN_ATOMIC
+  }
+};
+
+#else  // defined(GRNXX_HAS_GNUC_BUILTIN_ATOMIC) || ...
+
+template <typename Value>
+class AtomicFetchAndOr {
+ public:
+  Value operator()(Value mask, volatile Value *value) const {
+    for ( ; ; ) {
+      const Value expected = *value;
+      const Value desired = expected | mask;
+      if (atomic_compare_and_swap(expected, desired, value)) {
+        return expected;
+      }
+    }
+  }
+};
+
+#endif
+
+template <typename Value>
+inline Value atomic_fetch_and_or(Value mask, volatile Value *value) {
+  static_assert(std::is_integral<Value>::value,
+                "atomic_fetch_and_or accepts only integer types.");
+
+  return AtomicFetchAndOr<Value>()(mask, value);
+}
+
+// Implementation of atomic_fetch_and_and.
+
+#ifdef GRNXX_MSC
+
+template <typename Value>
+class AtomicFetchAndAnd;
+
+template <>
+class AtomicFetchAndAnd<char> {
+ public:
+  char operator()(char mask, volatile char *value) const {
+    return ::_InterlockedAnd8(mask, value);
+  }
+};
+
+template <>
+class AtomicFetchAndAnd<short> {
+ public:
+  short operator()(short mask, volatile short *value) const {
+    return ::_InterlockedAnd16(mask, value);
+  }
+};
+
+template <>
+class AtomicFetchAndAnd<long> {
+ public:
+  long operator()(long mask, volatile long *value) const {
+    return ::_InterlockedAnd(mask, value);
+  }
+};
+
+template <>
+class AtomicFetchAndAnd<__int64> {
+ public:
+  __int64 operator()(__int64 mask, volatile __int64 *value) const {
+#ifdef GRNXX_MSC64
+    return ::_InterlockedAnd64(mask, value);
+#else  // GRNXX_MSC64
+    for ( ; ; ) {
+      const __int64 expected = *value;
+      const __int64 desired = expected & mask;
+      if (atomic_compare_and_swap(expected, desired, value)) {
+        return expected;
+      }
+    }
+#endif  // GRNXX_MSC64
+  }
+};
+
+template <typename Value>
+class AtomicFetchAndAnd {
+ public:
+  typedef typename Intrinsic<Value>::InternalValue InternalValue;
+  typedef typename Intrinsic<Value>::InternalPointer InternalPointer;
+
+  Value operator()(Value plus, volatile Value *value) const {
+    return static_cast<Value>(AtomicFetchAndAnd<InternalValue>()(
+        static_cast<InternalValue>(mask),
+        reinterpret_cast<InternalPointer>(value)));
+  }
+};
+
+#elif defined(GRNXX_HAS_GNUC_BUILTIN_ATOMIC) ||\
+      defined(GRNXX_HAS_GNUC_BUILTIN_SYNC)
+
+template <typename Value>
+class AtomicFetchAndAnd {
+ public:
+  Value operator()(Value mask, volatile Value *value) const {
+#ifdef GRNXX_HAS_GNUC_BUILTIN_ATOMIC
+    return ::__atomic_fetch_and(value, mask, __ATOMIC_SEQ_CST);
+#else  // GRNXX_HAS_GNUC_BUILTIN_ATOMIC
+    return ::__sync_fetch_and_and(value, mask);
+#endif  // GRNXX_HAS_GNUC_BUILTIN_ATOMIC
+  }
+};
+
+#else  // defined(GRNXX_HAS_GNUC_BUILTIN_ATOMIC) || ...
+
+template <typename Value>
+class AtomicFetchAndAnd {
+ public:
+  Value operator()(Value mask, volatile Value *value) const {
+    for ( ; ; ) {
+      const Value expected = *value;
+      const Value desired = expected & mask;
+      if (atomic_compare_and_swap(expected, desired, value)) {
+        return expected;
+      }
+    }
+  }
+};
+
+#endif
+
+template <typename Value>
+inline Value atomic_fetch_and_and(Value mask, volatile Value *value) {
+  static_assert(std::is_integral<Value>::value,
+                "atomic_fetch_and_and accepts only integer types.");
+
+  return AtomicFetchAndAnd<Value>()(mask, value);
+}
+
+// Implementation of atomic_fetch_and_xor.
+
+#ifdef GRNXX_MSC
+
+template <typename Value>
+class AtomicFetchAndXor;
+
+template <>
+class AtomicFetchAndXor<char> {
+ public:
+  char operator()(char mask, volatile char *value) const {
+    return ::_InterlockedXor8(mask, value);
+  }
+};
+
+template <>
+class AtomicFetchAndXor<short> {
+ public:
+  short operator()(short mask, volatile short *value) const {
+    return ::_InterlockedXor16(mask, value);
+  }
+};
+
+template <>
+class AtomicFetchAndXor<long> {
+ public:
+  long operator()(long mask, volatile long *value) const {
+    return ::_InterlockedXor(mask, value);
+  }
+};
+
+template <>
+class AtomicFetchAndXor<__int64> {
+ public:
+  __int64 operator()(__int64 mask, volatile __int64 *value) const {
+#ifdef GRNXX_MSC64
+    return ::_InterlockedXor64(mask, value);
+#else  // GRNXX_MSC64
+    for ( ; ; ) {
+      const __int64 expected = *value;
+      const __int64 desired = expected ^ mask;
+      if (atomic_compare_and_swap(expected, desired, value)) {
+        return expected;
+      }
+    }
+#endif  // GRNXX_MSC64
+  }
+};
+
+template <typename Value>
+class AtomicFetchAndXor {
+ public:
+  typedef typename Intrinsic<Value>::InternalValue InternalValue;
+  typedef typename Intrinsic<Value>::InternalPointer InternalPointer;
+
+  Value operator()(Value plus, volatile Value *value) const {
+    return static_cast<Value>(AtomicFetchAndXor<InternalValue>()(
+        static_cast<InternalValue>(mask),
+        reinterpret_cast<InternalPointer>(value)));
+  }
+};
+
+#elif defined(GRNXX_HAS_GNUC_BUILTIN_ATOMIC) ||\
+      defined(GRNXX_HAS_GNUC_BUILTIN_SYNC)
+
+template <typename Value>
+class AtomicFetchAndXor {
+ public:
+  Value operator()(Value mask, volatile Value *value) const {
+#ifdef GRNXX_HAS_GNUC_BUILTIN_ATOMIC
+    return ::__atomic_fetch_xor(value, mask, __ATOMIC_SEQ_CST);
+#else  // GRNXX_HAS_GNUC_BUILTIN_ATOMIC
+    return ::__sync_fetch_and_xor(value, mask);
+#endif  // GRNXX_HAS_GNUC_BUILTIN_ATOMIC
+  }
+};
+
+#else  // defined(GRNXX_HAS_GNUC_BUILTIN_ATOMIC) || ...
+
+template <typename Value>
+class AtomicFetchAndXor {
+ public:
+  Value operator()(Value mask, volatile Value *value) const {
+    for ( ; ; ) {
+      const Value expected = *value;
+      const Value desired = expected ^ mask;
+      if (atomic_compare_and_swap(expected, desired, value)) {
+        return expected;
+      }
+    }
+  }
+};
+
+#endif
+
+template <typename Value>
+inline Value atomic_fetch_and_xor(Value mask, volatile Value *value) {
+  static_assert(std::is_integral<Value>::value,
+                "atomic_fetch_and_xor accepts only integer types.");
+
+  return AtomicFetchAndXor<Value>()(mask, value);
+}
+
+}  // namespace grnxx
+
+#endif  // INTRINSIC_HPP

  Added: lib/io/Makefile.am (+35 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/Makefile.am    2012-11-28 16:36:40 +0900 (187db54)
@@ -0,0 +1,35 @@
+noinst_LTLIBRARIES = libgrnxxio.la
+
+libgrnxxio_la_SOURCES =		\
+	block.cpp		\
+	chunk.cpp		\
+	file-posix.cpp		\
+	file-windows.cpp	\
+	file.cpp		\
+	file_info.cpp		\
+	file_info-impl.cpp	\
+	flags.cpp		\
+	path.cpp		\
+	pool.cpp		\
+	pool-impl.cpp		\
+	view-posix.cpp		\
+	view-windows.cpp	\
+	view.cpp
+
+libgrnxxio_includedir = ${includedir}/grnxx/io
+libgrnxxio_include_HEADERS =	\
+	block.hpp		\
+	chunk.hpp		\
+	file-posix.hpp		\
+	file-windows.hpp	\
+	file.hpp		\
+	file_info.hpp		\
+	file_info-impl.hpp	\
+	flags.hpp		\
+	path.hpp		\
+	pool.hpp		\
+	pool-impl.hpp		\
+	view-posix.hpp		\
+	view-windows.hpp	\
+	view.hpp
+

  Added: lib/io/block.cpp (+89 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/block.cpp    2012-11-28 16:36:40 +0900 (6ab9d4d)
@@ -0,0 +1,89 @@
+/*
+  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 "block.hpp"
+
+#include <ostream>
+
+namespace grnxx {
+namespace io {
+
+StringBuilder &operator<<(StringBuilder &builder, BlockStatus status) {
+  switch (status) {
+    case BLOCK_PHANTOM: {
+      return builder << "BLOCK_PHANTOM";
+    }
+    case BLOCK_ACTIVE: {
+      return builder << "BLOCK_ACTIVE";
+    }
+    case BLOCK_FROZEN: {
+      return builder << "BLOCK_FROZEN";
+    }
+    case BLOCK_IDLE: {
+      return builder << "BLOCK_IDLE";
+    }
+  }
+  return builder << "n/a";
+}
+
+std::ostream &operator<<(std::ostream &stream, BlockStatus status) {
+  char buf[32];
+  StringBuilder builder(buf);
+  builder << status;
+  return stream.write(builder.c_str(), builder.length());
+}
+
+StringBuilder &BlockInfo::write_to(StringBuilder &builder) const {
+  if (!builder) {
+    return builder;
+  }
+
+  builder << "{ id = " << id()
+          << ", status = " << status();
+
+  if (status_ != BLOCK_PHANTOM) {
+    builder << ", chunk_id = " << chunk_id()
+            << ", offset = " << offset()
+            << ", size = " << size()
+            << ", next_block_id = " << next_block_id()
+            << ", prev_block_id = " << prev_block_id();
+  }
+
+  switch (status_) {
+    case BLOCK_PHANTOM: {
+      builder << ", next_phantom_block_id = " << next_phantom_block_id();
+      break;
+    }
+    case BLOCK_ACTIVE: {
+      break;
+    }
+    case BLOCK_FROZEN: {
+      builder << ", next_frozen_block_id = " << next_frozen_block_id()
+              << ", frozen_stamp = " << frozen_stamp();
+      break;
+    }
+    case BLOCK_IDLE: {
+      builder << ", next_idle_block_id = " << next_idle_block_id()
+              << ", prev_idle_block_id = " << prev_idle_block_id();
+      break;
+    }
+  }
+  return builder << " }";
+}
+
+}  // namespace io
+}  // namespace grnxx

  Added: lib/io/block.hpp (+193 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/block.hpp    2012-11-28 16:36:40 +0900 (176c196)
@@ -0,0 +1,193 @@
+/*
+  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_IO_BLOCK_HPP
+#define GRNXX_IO_BLOCK_HPP
+
+#include "../string_builder.hpp"
+
+namespace grnxx {
+namespace io {
+
+const uint8_t  BLOCK_UNIT_SIZE_BITS = 12;
+const uint64_t BLOCK_UNIT_SIZE      = uint64_t(1) << BLOCK_UNIT_SIZE_BITS;
+
+const uint8_t  BLOCK_INFO_SIZE_BITS = 5;
+const uint64_t BLOCK_INFO_SIZE      = uint64_t(1) << BLOCK_INFO_SIZE_BITS;
+
+const uint32_t BLOCK_MAX_ID     = 0xFFFFFFFEU;
+const uint32_t BLOCK_INVALID_ID = 0xFFFFFFFFU;
+
+const uint64_t BLOCK_MAX_OFFSET = uint64_t(0xFFFFFFFFU) << BLOCK_UNIT_SIZE_BITS;
+const uint64_t BLOCK_MAX_SIZE   = uint64_t(0xFFFFFFFFU) << BLOCK_UNIT_SIZE_BITS;
+
+enum BlockStatus : uint8_t {
+  BLOCK_PHANTOM = 0,
+  BLOCK_ACTIVE  = 1,
+  BLOCK_FROZEN  = 2,
+  BLOCK_IDLE    = 3
+};
+
+class BlockInfo {
+ public:
+  BlockInfo()
+    : id_(0), status_(BLOCK_PHANTOM), reserved_(0),
+      chunk_id_(0), offset_(0), size_(0),
+      next_block_id_(0), prev_block_id_(0),
+      next_idle_block_id_(0), prev_idle_block_id_(0) {}
+
+  // Return the ID of the block.
+  uint32_t id() const {
+    return id_;
+  }
+  // Return the status of the block.
+  BlockStatus status() const {
+    return status_;
+  }
+  // Return the ID of the chunk that contains the block.
+  uint16_t chunk_id() const {
+    return chunk_id_;
+  }
+  // Return the offset of the block in the chunk (bytes).
+  uint64_t offset() const {
+    return static_cast<uint64_t>(offset_) << BLOCK_UNIT_SIZE_BITS;
+  }
+  // Return the size of the block (bytes).
+  uint64_t size() const {
+    return static_cast<uint64_t>(size_) << BLOCK_UNIT_SIZE_BITS;
+  }
+  // Return the ID of the next block in the same chunk.
+  // If the block is the rearmost block in the chunk, this function returns
+  // io::BLOCK_INVALID_ID.
+  uint32_t next_block_id() const {
+    return next_block_id_;
+  }
+  // Return the ID of the previous block in the same chunk.
+  // If the block is the first block in the chunk, this function returns
+  // io::BLOCK_INVALID_ID.
+  uint32_t prev_block_id() const {
+    return prev_block_id_;
+  }
+  // Return the ID of the next (older) phantom block.
+  // If the block is the oldest phantom block, this function returns
+  // io::BLOCK_INVALID_ID.
+  // Note: Available iff the block is a phantom block.
+  uint32_t next_phantom_block_id() const {
+    return next_phantom_block_id_;
+  }
+  // Return the ID of the next (newer) frozen block.
+  // If the block is the latest frozen block, this function returns
+  // the ID of the oldest frozen block.
+  // Note: Available iff the block is a frozen block.
+  uint32_t next_frozen_block_id() const {
+    return next_frozen_block_id_;
+  }
+  // Return the ID of the next (newer) idle block.
+  // If the block is the latest idle block, this function returns
+  // the ID of the oldest idle block.
+  // Note: Available iff the block is an idle block.
+  uint32_t next_idle_block_id() const {
+    return next_idle_block_id_;
+  }
+  // Return the stamp generated when the block was frozen.
+  // Note; Available iff the block is a frozen block.
+  uint16_t frozen_stamp() const {
+    return static_cast<uint16_t>(frozen_stamp_);
+  }
+  // Return the ID of the previous (older) idle block.
+  // If the block is the oldest idle block, this function returns
+  // the ID of the latest idle block.
+  // Note: Available iff the block is an idle block.
+  uint32_t prev_idle_block_id() const {
+    return prev_idle_block_id_;
+  }
+
+  void set_id(uint32_t value) {
+    id_ = value;
+  }
+  void set_status(BlockStatus value) {
+    status_ = value;
+  }
+  void set_chunk_id(uint16_t value) {
+    chunk_id_ = static_cast<uint16_t>(value);
+  }
+  void set_offset(uint64_t value) {
+    offset_ = static_cast<uint32_t>(value >> BLOCK_UNIT_SIZE_BITS);
+  }
+  void set_size(uint64_t value) {
+    size_ = static_cast<uint32_t>(value >> BLOCK_UNIT_SIZE_BITS);
+  }
+  void set_next_block_id(uint32_t value) {
+    next_block_id_ = value;
+  }
+  void set_prev_block_id(uint32_t value) {
+    prev_block_id_ = value;
+  }
+  void set_next_phantom_block_id(uint32_t value) {
+    next_phantom_block_id_ = value;
+  }
+  void set_next_frozen_block_id(uint32_t value) {
+    next_frozen_block_id_ = value;
+  }
+  void set_next_idle_block_id(uint32_t value) {
+    next_idle_block_id_ = value;
+  }
+  void set_frozen_stamp(uint16_t value) {
+    frozen_stamp_ = value;
+  }
+  void set_prev_idle_block_id(uint32_t value) {
+    prev_idle_block_id_ = value;
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+ private:
+  uint32_t id_;
+  BlockStatus status_;
+  uint8_t reserved_;
+  uint16_t chunk_id_;
+  uint32_t offset_;
+  uint32_t size_;
+  uint32_t next_block_id_;
+  uint32_t prev_block_id_;
+  union {
+    uint32_t next_phantom_block_id_;
+    uint32_t next_frozen_block_id_;
+    uint32_t next_idle_block_id_;
+  };
+  union {
+    uint32_t frozen_stamp_;
+    uint32_t prev_idle_block_id_;
+  };
+};
+
+static_assert(sizeof(BlockInfo) == BLOCK_INFO_SIZE,
+              "sizeof(BlockInfo) != BLOCK_INFO_SIZE");
+
+StringBuilder &operator<<(StringBuilder &builder, BlockStatus status);
+
+std::ostream &operator<<(std::ostream &stream, BlockStatus status);
+
+inline StringBuilder &operator<<(StringBuilder &builder,
+                                 const BlockInfo &block_info) {
+  return block_info.write_to(builder);
+}
+
+}  // namespace io
+}  // namespace grnxx
+
+#endif  // GRNXX_IO_BLOCK_HPP

  Added: lib/io/chunk.cpp (+35 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/chunk.cpp    2012-11-28 16:36:40 +0900 (7ed0a51)
@@ -0,0 +1,35 @@
+/*
+  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 "chunk.hpp"
+
+namespace grnxx {
+namespace io {
+
+StringBuilder &ChunkInfo::write_to(StringBuilder &builder) const {
+  if (!builder) {
+    return builder;
+  }
+
+  return builder << "{ id = " << id()
+                 << ", file_id = " << file_id()
+                 << ", offset = " << offset()
+                 << ", size = " << size() << " }";
+}
+
+}  // namespace io
+}  // namespace grnxx

  Added: lib/io/chunk.hpp (+138 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/chunk.hpp    2012-11-28 16:36:40 +0900 (912dd47)
@@ -0,0 +1,138 @@
+/*
+  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_IO_CHUNK_HPP
+#define GRNXX_IO_CHUNK_HPP
+
+#include "block.hpp"
+#include "view.hpp"
+
+namespace grnxx {
+namespace io {
+
+const uint8_t  CHUNK_UNIT_SIZE_BITS = 16;
+const uint64_t CHUNK_UNIT_SIZE      = uint64_t(1) << CHUNK_UNIT_SIZE_BITS;
+
+const uint8_t  CHUNK_INFO_SIZE_BITS = 4;
+const uint64_t CHUNK_INFO_SIZE      = uint64_t(1) << CHUNK_INFO_SIZE_BITS;
+
+const uint16_t CHUNK_MAX_ID     = 0xFFFE;
+const uint16_t CHUNK_INVALID_ID = 0xFFFF;
+
+const uint64_t CHUNK_MAX_OFFSET = uint64_t(0xFFFFFFFFU) << CHUNK_UNIT_SIZE_BITS;
+const uint64_t CHUNK_MAX_SIZE   = BLOCK_MAX_OFFSET;
+
+class Chunk {
+ public:
+  Chunk() : view_(), address_(nullptr) {}
+
+  Chunk &operator=(View &&view) {
+    view_ = std::move(view);
+    address_ = std::move(view_.address());
+    return *this;
+  }
+
+  // Return true iff its view is available.
+  GRNXX_EXPLICIT_CONVERSION operator bool() const {
+    return static_cast<bool>(view_);
+  }
+
+  // Return the associated view of the chunk.
+  const View &view() const {
+    return view_;
+  }
+  // Return the address of the chunk.
+  void *address() const {
+    return address_;
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const {
+    return builder << view_;
+  }
+
+ private:
+  View view_;
+  void *address_;
+
+  Chunk(const Chunk &);
+  Chunk &operator=(const Chunk &);
+};
+
+inline StringBuilder &operator<<(StringBuilder &builder, const Chunk &chunk) {
+  return chunk.write_to(builder);
+}
+
+class ChunkInfo {
+ public:
+  ChunkInfo() : id_(0), file_id_(0), offset_(0), size_(0), reserved_(0) {}
+
+  GRNXX_EXPLICIT_CONVERSION operator bool() const {
+    return size_ != 0;
+  }
+
+  // Return the ID of the chunk.
+  uint16_t id() const {
+    return id_;
+  }
+  // Return the ID of the file that contains the chunk.
+  uint16_t file_id() const {
+    return file_id_;
+  }
+  // Return the offset of the chunk in the file (bytes).
+  uint64_t offset() const {
+    return static_cast<uint64_t>(offset_) << CHUNK_UNIT_SIZE_BITS;
+  }
+  // Return the size of the chunk (bytes).
+  uint64_t size() const {
+    return static_cast<uint64_t>(size_) << CHUNK_UNIT_SIZE_BITS;
+  }
+
+  void set_id(uint16_t value) {
+    id_ = value;
+  }
+  void set_file_id(uint16_t value) {
+    file_id_ = value;
+  }
+  void set_offset(uint64_t value) {
+    offset_ = static_cast<uint32_t>(value >> CHUNK_UNIT_SIZE_BITS);
+  }
+  void set_size(uint64_t value) {
+    size_ = static_cast<uint32_t>(value >> CHUNK_UNIT_SIZE_BITS);
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+ private:
+  uint16_t id_;
+  uint16_t file_id_;
+  uint32_t offset_;
+  uint32_t size_;
+  uint32_t reserved_;
+};
+
+static_assert(sizeof(ChunkInfo) == CHUNK_INFO_SIZE,
+              "sizeof(ChunkInfo) != CHUNK_INFO_SIZE");
+
+inline StringBuilder &operator<<(StringBuilder &builder,
+                                 const ChunkInfo &chunk_info) {
+  return chunk_info.write_to(builder);
+}
+
+}  // namespace io
+}  // namespace grnxx
+
+#endif  // GRNXX_IO_CHUNK_HPP

  Added: lib/io/file-posix.cpp (+470 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/file-posix.cpp    2012-11-28 16:36:40 +0900 (7d676e8)
@@ -0,0 +1,470 @@
+/*
+  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 "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 "../error.hpp"
+#include "../exception.hpp"
+#include "../logger.hpp"
+#include "../thread.hpp"
+#include "path.hpp"
+
+namespace grnxx {
+namespace io {
+
+FileImpl::~FileImpl() {
+  if (fd_ != -1) {
+    unlock();
+    if (::close(fd_) != 0) {
+      GRNXX_ERROR() << "failed to close file: file = " << *this
+                    << ": '::close' " << Error(errno);
+    }
+  }
+  if (unlink_at_close_ && (~flags_ & GRNXX_IO_TEMPORARY)) {
+    unlink_if_exists(path_.c_str());
+  }
+}
+
+std::unique_ptr<FileImpl> FileImpl::open(const char *path, Flags flags, 
+                                         int permission) {
+  std::unique_ptr<FileImpl> file(new (std::nothrow) FileImpl);
+  if (!file) {
+    GRNXX_ERROR() << "new grnxx::io::FileImpl failed";
+    GRNXX_THROW();
+  }
+
+  if (flags & GRNXX_IO_TEMPORARY) {
+    file->open_temporary_file(path, flags, permission);
+  } else {
+    file->open_regular_file(path, flags, permission);
+  }
+  return file;
+}
+
+bool FileImpl::lock(LockMode mode, int sleep_count,
+                    Duration sleep_duration) {
+  if (locked_) {
+    return false;
+  }
+
+  for (int i = 0; i < sleep_count; ++i) {
+    if (try_lock(mode)) {
+      return true;
+    }
+    Thread::sleep(sleep_duration);
+  }
+  return false;
+}
+
+bool FileImpl::try_lock(LockMode mode) {
+  if (locked_) {
+    return false;
+  }
+
+  int operation = LOCK_NB;
+  switch (mode) {
+    case GRNXX_IO_SHARED_LOCK: {
+      operation |= LOCK_SH;
+      break;
+    }
+    case GRNXX_IO_EXCLUSIVE_LOCK: {
+      operation |= LOCK_EX;
+      break;
+    }
+    default: {
+      GRNXX_ERROR() << "invalid argument: mode = " << mode;
+      GRNXX_THROW();
+    }
+  }
+
+  if (::flock(fd_, operation) != 0) {
+    if (errno == EWOULDBLOCK) {
+      return false;
+    }
+    GRNXX_ERROR() << "failed to lock file: file = " << *this
+                  << ", mode = " << mode << ": '::flock' " << Error(errno);
+    GRNXX_THROW();
+  }
+  locked_ = true;
+  return true;
+}
+
+bool FileImpl::unlock() {
+  if (!locked_) {
+    return false;
+  }
+
+  if (::flock(fd_, LOCK_UN) != 0) {
+    GRNXX_ERROR() << "failed to unlock file: file = " << *this
+                  << ": '::flock' " << Error(errno);
+    GRNXX_THROW();
+  }
+  locked_ = false;
+  return true;
+}
+
+const uint64_t FILE_IMPL_MAX_OFFSET = std::numeric_limits<off_t>::max();
+const uint64_t FILE_IMPL_MAX_SIZE = std::numeric_limits<ssize_t>::max();
+
+uint64_t FileImpl::read(void *buf, uint64_t size) {
+  if (flags_ & GRNXX_IO_WRITE_ONLY) {
+    GRNXX_ERROR() << "file is write-only";
+    GRNXX_THROW();
+  }
+
+  const size_t chunk_size =
+      static_cast<size_t>(std::min(size, FILE_IMPL_MAX_SIZE));
+  const ssize_t result = ::read(fd_, buf, chunk_size);
+  if (result < 0) {
+    GRNXX_ERROR() << "failed to read from file: file = " << *this
+                  << ", size = " << size << ": '::read' " << Error(errno);
+    GRNXX_THROW();
+  }
+  return result;
+}
+
+uint64_t FileImpl::read(void *buf, uint64_t size,
+                             uint64_t offset) {
+  if (flags_ & GRNXX_IO_WRITE_ONLY) {
+    GRNXX_ERROR() << "file is write-only";
+    GRNXX_THROW();
+  }
+
+  if (offset > FILE_IMPL_MAX_OFFSET) {
+    GRNXX_ERROR() << "invalid argument: offset = " << offset
+                  << ", max_offset = " << FILE_IMPL_MAX_OFFSET;
+    GRNXX_THROW();
+  }
+  const size_t chunk_size =
+      static_cast<size_t>(std::min(size, FILE_IMPL_MAX_SIZE));
+#ifdef GRNXX_HAS_PREAD
+  const ssize_t result =
+      ::pread(fd_, buf, chunk_size, static_cast<off_t>(offset));
+  if (result < 0) {
+    GRNXX_ERROR() << "failed to read from file: file = " << *this
+                  << ", size = " << size << ", offset = " << offset
+                  << ": '::pwrite' " << Error(errno);
+  }
+#else  // GRNXX_HAS_PREAD
+  // TODO: To be thread-safe and error-tolerant.
+  const uint64_t current_position = tell();
+  seek(offset, SEEK_SET);
+  uint64_t result;
+  try {
+    result = read(buf, chunk_size);
+  } catch (...) {
+    seek(current_position, SEEK_SET);
+    throw;
+  }
+  seek(current_position, SEEK_SET);
+#endif  // GRNXX_HAS_PREAD
+  return result;
+}
+
+uint64_t FileImpl::write(const void *buf, uint64_t size) {
+  if (flags_ & GRNXX_IO_READ_ONLY) {
+    GRNXX_ERROR() << "file is read-only";
+    GRNXX_THROW();
+  }
+
+  const size_t chunk_size =
+      static_cast<size_t>(std::min(size, FILE_IMPL_MAX_SIZE));
+  const ssize_t result = ::write(fd_, buf, chunk_size);
+  if (result < 0) {
+    GRNXX_ERROR() << "failed to write to file: file = " << *this
+                  << ", size = " << size << ": '::write' " << Error(errno);
+    GRNXX_THROW();
+  }
+  return result;
+}
+
+uint64_t FileImpl::write(const void *buf, uint64_t size,
+                              uint64_t offset) {
+  if (flags_ & GRNXX_IO_READ_ONLY) {
+    GRNXX_ERROR() << "file is read-only";
+    GRNXX_THROW();
+  }
+
+  if (offset > FILE_IMPL_MAX_OFFSET) {
+    GRNXX_ERROR() << "invalid argument: offset = " << offset
+                  << ", max_offset = " << FILE_IMPL_MAX_OFFSET;
+    GRNXX_THROW();
+  }
+  const size_t chunk_size =
+      static_cast<size_t>(std::min(size, FILE_IMPL_MAX_SIZE));
+#ifdef GRNXX_HAS_PWRITE
+  const ssize_t result =
+      ::pwrite(fd_, buf, chunk_size, static_cast<off_t>(offset));
+  if (result < 0) {
+    GRNXX_ERROR() << "failed to write file: file = " << *this
+                  << ", size = " << size << ", offset = " << offset
+                  << ": '::pwrite' " << Error(errno);
+  }
+#else  // GRNXX_HAS_PWRITE
+  // TODO: To be thread-safe and error-tolerant.
+  const uint64_t current_position = tell();
+  seek(offset, SEEK_SET);
+  uint64_t result;
+  try {
+    result = write(buf, chunk_size);
+  } catch (...) {
+    seek(current_position, SEEK_SET);
+    throw;
+  }
+  seek(current_position, SEEK_SET);
+#endif  // GRNXX_HAS_PWRITE
+  return result;
+}
+
+void FileImpl::sync() {
+  if (::fsync(fd_) != 0) {
+    GRNXX_ERROR() << "failed to sync file: file = " << *this
+                  << ": '::fsync' " << Error(errno);
+    GRNXX_THROW();
+  }
+}
+
+uint64_t FileImpl::seek(int64_t offset, int whence) {
+  if ((offset < std::numeric_limits<off_t>::min()) ||
+      (offset > std::numeric_limits<off_t>::max())) {
+    GRNXX_ERROR() << "invalid argument: offset = " << offset
+                  << ": [" << std::numeric_limits<off_t>::min() << ", "
+                  << std::numeric_limits<off_t>::max() << ']';
+    GRNXX_THROW();
+  }
+
+  switch (whence) {
+    case SEEK_SET:
+    case SEEK_CUR:
+    case SEEK_END: {
+      break;
+    }
+    default: {
+      GRNXX_ERROR() << "invalid argument: whence = " << whence;
+      GRNXX_THROW();
+    }
+  }
+
+  const off_t result = ::lseek(fd_, offset, whence);
+  if (result == -1) {
+    GRNXX_ERROR() << "failed to seek file: file = " << *this
+                  << ", offset = " << offset << ", whence = " << whence
+                  << ": '::lseek' " << Error(errno);
+    GRNXX_THROW();
+  }
+  return result;
+}
+
+uint64_t FileImpl::tell() const {
+  const off_t result = ::lseek(fd_, 0, SEEK_CUR);
+  if (result == -1) {
+    GRNXX_ERROR() << "failed to get current position: file = " << *this
+                  << ": '::lseek' " << Error(errno);
+    GRNXX_THROW();
+  }
+  return result;
+}
+
+void FileImpl::resize(uint64_t size) {
+  if (flags_ & GRNXX_IO_READ_ONLY) {
+    GRNXX_ERROR() << "file is read-only";
+    GRNXX_THROW();
+  }
+
+  const uint64_t MAX_SIZE = std::numeric_limits<int64_t>::max();
+  if (size > MAX_SIZE) {
+    GRNXX_ERROR() << "invalid argument: size = " << size
+                  << ": [0, " << MAX_SIZE << ']';
+    GRNXX_THROW();
+  }
+  seek(static_cast<int64_t>(size), SEEK_SET);
+
+  if (::ftruncate(fd_, size) != 0) {
+    GRNXX_ERROR() << "failed to resize file: file = " << *this
+                  << ", size = " << size
+                  << ": '::ftruncate' " << Error(errno);
+    GRNXX_THROW();
+  }
+}
+
+uint64_t FileImpl::size() const {
+  struct stat stat;
+  if (::fstat(fd_, &stat) != 0) {
+    GRNXX_ERROR() << "failed to stat file: file = " << *this
+                  << ": '::fstat' " << Error(errno);
+    GRNXX_THROW();
+  }
+  return stat.st_size;
+}
+
+bool FileImpl::exists(const char *path) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = " << path;
+    GRNXX_THROW();
+  }
+
+  struct stat stat;
+  if (::stat(path, &stat) != 0) {
+    return false;
+  }
+  return S_ISREG(stat.st_mode);
+}
+
+void FileImpl::unlink(const char *path) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = " << path;
+    GRNXX_THROW();
+  }
+
+  if (::unlink(path) != 0) {
+    GRNXX_ERROR() << "failed to unlink file: path = " << path
+                  << ": '::unlink' " << Error(errno);
+    GRNXX_THROW();
+  }
+}
+
+bool FileImpl::unlink_if_exists(const char *path) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = " << path;
+    GRNXX_THROW();
+  }
+
+  return ::unlink(path) == 0;
+}
+
+FileImpl::FileImpl()
+  : path_(), flags_(), fd_(-1), locked_(false),
+    unlink_at_close_(false) {}
+
+void FileImpl::open_regular_file(const char *path, Flags flags,
+                                 int permission) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = " << path;
+    GRNXX_THROW();
+  }
+  path_ = path;
+
+  int posix_flags = O_RDWR;
+
+  if ((~flags & GRNXX_IO_CREATE) && (flags & GRNXX_IO_READ_ONLY)) {
+    flags_ |= GRNXX_IO_READ_ONLY;
+    posix_flags = O_RDONLY;
+  } else if (flags & GRNXX_IO_WRITE_ONLY) {
+    flags_ |= GRNXX_IO_WRITE_ONLY;
+    posix_flags = O_WRONLY;
+  }
+
+  if ((~flags_ & GRNXX_IO_READ_ONLY) && (flags & GRNXX_IO_APPEND)) {
+    flags_ |= GRNXX_IO_APPEND;
+    posix_flags |= O_APPEND;
+  }
+
+  if (flags & GRNXX_IO_CREATE) {
+    flags_ |= GRNXX_IO_CREATE;
+    posix_flags |= O_CREAT;
+    if (flags & GRNXX_IO_OPEN) {
+      flags_ |= GRNXX_IO_OPEN;
+    } else {
+      posix_flags |= O_EXCL;
+    }
+  } else {
+    flags_ |= GRNXX_IO_OPEN;
+  }
+
+  if ((flags_ & GRNXX_IO_OPEN) && (flags & GRNXX_IO_TRUNCATE)) {
+    flags_ |= GRNXX_IO_TRUNCATE;
+    posix_flags |= O_TRUNC;
+  }
+
+  fd_ = ::open(path, posix_flags, permission);
+  if (fd_ == -1) {
+    GRNXX_ERROR() << "failed to open file: path = " << path
+                  << ", flags = " << flags
+                  << ", permission = " << permission
+                  << ": '::open' " << Error(errno);
+    GRNXX_THROW();
+  }
+}
+
+void FileImpl::open_temporary_file(const char *path, Flags flags, int) {
+  flags_ = GRNXX_IO_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 < FILE_UNIQUE_PATH_GENERATION_MAX_NUM_TRIALS; ++i) {
+    path_ = Path::unique_path(path);
+    fd_ = ::open(path_.c_str(), posix_flags, 0600);
+    if (fd_ != -1) {
+      unlink(path_.c_str());
+      return;
+    }
+    GRNXX_WARNING() << "failed to create temporary file: path = " << path
+                    << ", unique_path = " << path_
+                    << ": '::open' " << Error(errno);
+  }
+  GRNXX_ERROR() << "failed to create temporary file: path = " << path
+                << ", flags = " << flags;
+  GRNXX_THROW();
+}
+
+StringBuilder &FileImpl::write_to(StringBuilder &builder) const {
+  if (!builder) {
+    return builder;
+  }
+
+  builder << "{ path = " << path_
+          << ", flags = " << flags_
+          << ", fd = " << fd_;
+
+  builder << ", size = ";
+  struct stat stat;
+  if (::fstat(fd_, &stat) != 0) {
+    builder << "n/a";
+  } else {
+    builder << stat.st_size;
+  }
+
+  builder << ", offset = ";
+  const off_t result = ::lseek(fd_, 0, SEEK_CUR);
+  if (result == -1) {
+    builder << "n/a";
+  } else {
+    builder << result;
+  }
+
+  return builder << ", locked = " << locked_
+                 << ", unlink_at_close = " << unlink_at_close_ << " }";
+}
+
+}  // namespace io
+}  // namespace grnxx
+
+#endif  // GRNXX_WINDOWS

  Added: lib/io/file-posix.hpp (+108 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/file-posix.hpp    2012-11-28 16:36:40 +0900 (5fe479d)
@@ -0,0 +1,108 @@
+/*
+  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_IO_FILE_POSIX_HPP
+#define GRNXX_IO_FILE_POSIX_HPP
+
+#include "file.hpp"
+
+#ifndef GRNXX_WINDOWS
+
+namespace grnxx {
+namespace io {
+
+class FileImpl {
+ public:
+  ~FileImpl();
+
+  static std::unique_ptr<FileImpl> open(const char *path, Flags flags,
+                                        int permission);
+
+  bool lock(LockMode mode, int sleep_count, Duration sleep_duration);
+  bool try_lock(LockMode mode);
+  bool unlock();
+
+  bool locked() const {
+    return locked_;
+  }
+  bool unlocked() const {
+    return !locked_;
+  }
+
+  uint64_t read(void *buf, uint64_t size);
+  uint64_t read(void *buf, uint64_t size, uint64_t offset);
+  uint64_t write(const void *buf, uint64_t size);
+  uint64_t write(const void *buf, uint64_t size, uint64_t offset);
+
+  void sync();
+
+  uint64_t seek(int64_t offset, int whence);
+  uint64_t tell() const;
+
+  void resize(uint64_t size);
+  uint64_t size() const;
+
+  bool unlink_at_close() const {
+    return unlink_at_close_;
+  }
+  void set_unlink_at_close(bool value) {
+    unlink_at_close_ = value;
+  }
+
+  String path() const {
+    return path_;
+  }
+  Flags flags() const {
+    return flags_;
+  }
+
+  const void *handle() const {
+   return &fd_;
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+  static bool exists(const char *path);
+  static void unlink(const char *path);
+  static bool unlink_if_exists(const char *path);
+
+ private:
+  String path_;
+  Flags flags_;
+  int fd_;
+  bool locked_;
+  bool unlink_at_close_;
+
+  FileImpl();
+
+  void open_regular_file(const char *path, Flags flags, int permission);
+  void open_temporary_file(const char *path, Flags flags, int permission);
+
+  FileImpl(const FileImpl &);
+  FileImpl &operator=(const FileImpl &);
+};
+
+inline StringBuilder &operator<<(StringBuilder &builder, const FileImpl &file) {
+  return file.write_to(builder);
+}
+
+}  // namespace io
+}  // namespace grnxx
+
+#endif  // GRNXX_WINDOWS
+
+#endif  // GRNXX_IO_FILE_POSIX_HPP

  Added: lib/io/file-windows.cpp (+466 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/file-windows.cpp    2012-11-28 16:36:40 +0900 (1532d4c)
@@ -0,0 +1,466 @@
+/*
+  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 "file-windows.hpp"
+
+#ifdef GRNXX_WINDOWS
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <io.h>
+
+#include "../error.hpp"
+#include "../exception.hpp"
+#include "../logger.hpp"
+#include "../thread.hpp"
+#include "path.hpp"
+
+namespace grnxx {
+namespace io {
+
+FileImpl::~FileImpl() {
+  if (handle_ != INVALID_HANDLE_VALUE) {
+    unlock();
+    if (!::CloseHandle(handle_)) {
+      GRNXX_ERROR() << "failed to close file: file = " << *this
+                    << ": '::CloseHandle' " << Error(::GetLastError());
+    }
+  }
+  if (unlink_at_close_ && (~flags_ & GRNXX_IO_TEMPORARY)) {
+    unlink_if_exists(path_.c_str());
+  }
+}
+
+std::unique_ptr<FileImpl> FileImpl::open(const char *path, Flags flags,
+                                         int permission) {
+  std::unique_ptr<FileImpl> file(new (std::nothrow) FileImpl);
+  if (!file) {
+    GRNXX_ERROR() << "new grnxx::io::FileImpl failed";
+    GRNXX_THROW();
+  }
+  if (flags & GRNXX_IO_TEMPORARY) {
+    file->open_temporary_file(path, flags, permission);
+  } else {
+    file->open_regular_file(path, flags, permission);
+  }
+  return file;
+}
+
+bool FileImpl::lock(LockMode mode, int sleep_count,
+                    Duration sleep_duration) {
+  if (locked_) {
+    return false;
+  }
+
+  for (int i = 0; i < sleep_count; ++i) {
+    if (try_lock(mode)) {
+      return true;
+    }
+    Thread::sleep(sleep_duration);
+  }
+  return false;
+}
+
+bool FileImpl::try_lock(LockMode mode) {
+  if (locked_) {
+    return false;
+  }
+
+  DWORD flags = LOCKFILE_FAIL_IMMEDIATELY;
+  switch (mode) {
+    case GRNXX_IO_SHARED_LOCK: {
+      break;
+    }
+    case GRNXX_IO_EXCLUSIVE_LOCK: {
+      flags |= LOCKFILE_EXCLUSIVE_LOCK;
+      break;
+    }
+    default: {
+      GRNXX_ERROR() << "invalid argument: mode = " << mode;
+      GRNXX_THROW();
+    }
+  }
+
+  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) {
+      return false;
+    }
+    GRNXX_ERROR() << "failed to lock file: file = " << *this
+                  << ", mode = " << mode
+                  << ": '::LockFileEx' " << Error(last_error);
+    GRNXX_THROW();
+  }
+  locked_ = true;
+  return true;
+}
+
+bool FileImpl::unlock() {
+  if (!locked_) {
+    return false;
+  }
+
+  OVERLAPPED overlapped;
+  overlapped.Offset = 0;
+  overlapped.OffsetHigh = 0x80000000U;
+  if (!::UnlockFileEx(handle_, 0, 0, 0x80000000U, &overlapped)) {
+    GRNXX_ERROR() << "failed to unlock file: file = " << *this
+                  << ": '::UnlockFileEx' " << Error(::GetLastError());
+    GRNXX_THROW();
+  }
+  locked_ = false;
+  return true;
+}
+
+uint64_t FileImpl::read(void *buf, uint64_t size) {
+  if (flags_ & GRNXX_IO_WRITE_ONLY) {
+    GRNXX_ERROR() << "file is write-only";
+    GRNXX_THROW();
+  }
+
+  uint64_t offset = 0;
+  while (offset < size) {
+    const DWORD chunk_size = static_cast<DWORD>(std::min(size - offset,
+        static_cast<uint64_t>(std::numeric_limits<DWORD>::max())));
+    DWORD input_size;
+    if (!::ReadFile(handle_, buf, chunk_size, &input_size, nullptr)) {
+      GRNXX_ERROR() << "failed to read from file: file = " << *this
+                    << ", size = " << size
+                    << ": '::ReadFile' " << Error(::GetLastError());
+      GRNXX_THROW();
+    } else if (input_size == 0) {
+      break;
+    }
+    offset += input_size;
+  }
+  return offset;
+}
+
+uint64_t FileImpl::read(void *buf, uint64_t size,
+                             uint64_t offset) {
+  if (flags_ & GRNXX_IO_WRITE_ONLY) {
+    GRNXX_ERROR() << "file is write-only";
+    GRNXX_THROW();
+  }
+
+  // TODO: must be thread-safe.
+  const uint64_t current_position = seek(0, SEEK_CUR);
+  seek(offset, SEEK_SET);
+  uint64_t result;
+  try {
+    result = read(buf, size);
+  } catch (...) {
+    seek(current_position, SEEK_SET);
+    throw;
+  }
+  seek(current_position, SEEK_SET);
+  return result;
+}
+
+uint64_t FileImpl::write(const void *buf, uint64_t size) {
+  if (flags_ & GRNXX_IO_READ_ONLY) {
+    GRNXX_ERROR() << "file is read-only";
+    GRNXX_THROW();
+  }
+
+  if (append_mode_) { 
+    seek(0, SEEK_END);
+  }
+  uint64_t offset = 0;
+  while (offset < size) {
+    const DWORD chunk_size = static_cast<DWORD>(std::min(size - offset,
+        static_cast<uint64_t>(std::numeric_limits<DWORD>::max())));
+    DWORD output_size;
+    if (!::WriteFile(handle_, buf, chunk_size, &output_size, nullptr)) {
+      GRNXX_ERROR() << "failed to write to file: file = " << *this
+                    << ", size = " << size
+                    << ": '::WriteFile' " << Error(::GetLastError());
+      GRNXX_THROW();
+    } else if (output_size == 0) {
+      break;
+    }
+    offset += output_size;
+  }
+  return offset;
+}
+
+uint64_t FileImpl::write(const void *buf, uint64_t size,
+                              uint64_t offset) {
+  if (flags_ & GRNXX_IO_READ_ONLY) {
+    GRNXX_ERROR() << "file is read-only";
+    GRNXX_THROW();
+  }
+
+  // TODO: must be thread-safe.
+  const uint64_t current_position = seek(0, SEEK_CUR);
+  seek(offset, SEEK_SET);
+  uint64_t result;
+  try {
+    result = write(buf, size);
+  } catch (...) {
+    seek(current_position, SEEK_SET);
+    throw;
+  }
+  seek(current_position, SEEK_SET);
+  return result;
+}
+
+void FileImpl::sync() {
+  if (!::FlushFileBuffers(handle_)) {
+    GRNXX_ERROR() << "failed to sync file: file = " << *this
+                  << ": '::FlushFileBuffers' " << Error(errno);
+    GRNXX_THROW();
+  }
+}
+
+uint64_t FileImpl::seek(int64_t offset, int whence) {
+  DWORD move_method;
+  switch (whence) {
+    case SEEK_SET: {
+      move_method = FILE_BEGIN;
+      break;
+    }
+    case SEEK_CUR: {
+      move_method = FILE_CURRENT;
+      break;
+    }
+    case SEEK_END: {
+      move_method = FILE_END;
+      break;
+    }
+    default: {
+      GRNXX_ERROR() << "invalid argument: whence = " << whence;
+      GRNXX_THROW();
+    }
+  }
+
+  LARGE_INTEGER request;
+  LARGE_INTEGER new_position;
+  request.QuadPart = offset;
+  if (!::SetFilePointerEx(handle_, request, &new_position, move_method)) {
+    GRNXX_ERROR() << "failed to seek file: file = " << *this
+                  << ", offset = " << offset << ", whence = " << whence
+                  << ": '::SetFilePointerEx' " << Error(::GetLastError());
+    GRNXX_THROW();
+  }
+  return new_position.QuadPart;
+}
+
+uint64_t FileImpl::tell() const {
+  LARGE_INTEGER zero;
+  zero.QuadPart = 0;
+  LARGE_INTEGER current_position;
+  if (!::SetFilePointerEx(handle_, zero, &current_position, FILE_CURRENT)) {
+    GRNXX_ERROR() << "failed to get current position: file = " << *this
+                  << ": '::SetFilePointerEx' " << Error(::GetLastError());
+    GRNXX_THROW();
+  }
+  return current_position.QuadPart;
+}
+
+void FileImpl::resize(uint64_t size) {
+  if (flags_ & GRNXX_IO_READ_ONLY) {
+    GRNXX_ERROR() << "file is read-only";
+    GRNXX_THROW();
+  }
+
+  seek(size, SEEK_SET);
+  if (!::SetEndOfFile(handle_)) {
+    GRNXX_ERROR() << "failed to resize file: file = " << *this
+                  << ", size = " << size
+                  << ": '::SetEndOfFile' " << Error(::GetLastError());
+    GRNXX_THROW();
+  }
+}
+
+uint64_t FileImpl::size() const {
+  LARGE_INTEGER file_size;
+  if (!::GetFileSizeEx(handle_, &file_size)) {
+    GRNXX_ERROR() << "failed to get file size: file = " << *this
+                  << ": '::GetFileSizeEx' " << Error(::GetLastError());
+    GRNXX_THROW();
+  }
+  return file_size.QuadPart;
+}
+
+bool FileImpl::exists(const char *path) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = " << path;
+    GRNXX_THROW();
+  }
+
+  struct _stat stat;
+  if (::_stat(path, &stat) != 0) {
+    return false;
+  }
+  return stat.st_mode & _S_IFREG;
+}
+
+void FileImpl::unlink(const char *path) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = " << path;
+    GRNXX_THROW();
+  }
+
+  if (!::DeleteFile(path)) {
+    GRNXX_ERROR() << "failed to unlink file: path = " << path
+                  << ": '::DeleteFile' " << Error(::GetLastError());
+    GRNXX_THROW();
+  }
+}
+
+bool FileImpl::unlink_if_exists(const char *path) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = " << path;
+    GRNXX_THROW();
+  }
+
+  return ::DeleteFile(path);
+}
+
+FileImpl::FileImpl()
+  : path_(), flags_(), handle_(INVALID_HANDLE_VALUE),
+    append_mode_(false), locked_(false), unlink_at_close_(false) {}
+
+void FileImpl::open_regular_file(const char *path, Flags flags,
+                                 int permission) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = " << path;
+    GRNXX_THROW();
+  }
+  path_ = path;
+
+  DWORD desired_access = GENERIC_READ | GENERIC_WRITE;
+  if ((~flags & GRNXX_IO_CREATE) && (flags & GRNXX_IO_READ_ONLY)) {
+    flags_ |= GRNXX_IO_READ_ONLY;
+    desired_access = GENERIC_READ;
+  } else if (flags & GRNXX_IO_WRITE_ONLY) {
+    flags_ |= GRNXX_IO_WRITE_ONLY;
+    desired_access = GENERIC_WRITE;
+  }
+
+  if ((~flags_ & GRNXX_IO_READ_ONLY) && (flags & GRNXX_IO_APPEND)) {
+    flags_ |= GRNXX_IO_APPEND;
+    append_mode_ = true;
+  }
+
+  const DWORD share_mode =
+      FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;
+
+  DWORD creation_disposition;
+  if (flags & GRNXX_IO_CREATE) {
+    flags_ |= GRNXX_IO_CREATE;
+    creation_disposition = CREATE_NEW;
+    if (flags & GRNXX_IO_OPEN) {
+      flags_ |= GRNXX_IO_OPEN;
+      creation_disposition = OPEN_ALWAYS;
+      if (flags & GRNXX_IO_TRUNCATE) {
+        flags_ |= GRNXX_IO_TRUNCATE;
+        creation_disposition = CREATE_ALWAYS;
+      }
+    }
+  } else {
+    flags_ |= GRNXX_IO_OPEN;
+    creation_disposition = OPEN_EXISTING;
+    if (flags & GRNXX_IO_TRUNCATE) {
+      flags_ |= GRNXX_IO_TRUNCATE;
+      creation_disposition = TRUNCATE_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
+                  << ", permission = " << permission
+                  << ": '::CreateFileA' " << Error(::GetLastError());
+    GRNXX_THROW();
+  }
+}
+
+void FileImpl::open_temporary_file(const char *path, Flags flags,
+                                   int permission) try {
+  flags_ = GRNXX_IO_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 < FILE_UNIQUE_PATH_GENERATION_MAX_NUM_TRIALS; ++i) {
+    path_ = Path::unique_path(path);
+    handle_ = ::CreateFileA(path_.c_str(), desired_access,
+                            share_mode, nullptr, creation_disposition,
+                            flags_and_attributes, nullptr);
+    if (handle_ != INVALID_HANDLE_VALUE) {
+      return;
+    }
+    GRNXX_WARNING() << "failed to create temporary file: path = " << path
+                    << ", unique_path = " << path_
+                    << ": '::open' " << Error(::GetLastError());
+  }
+  GRNXX_ERROR() << "failed to create temporary file: path = " << path
+                << ", flags = " << flags
+                << ", permission = " << permission;
+  GRNXX_THROW();
+} catch (const std::exception &exception) {
+  GRNXX_ERROR() << exception;
+  GRNXX_THROW();
+}
+
+StringBuilder &FileImpl::write_to(StringBuilder &builder) const {
+  if (!builder) {
+    return builder;
+  }
+
+  builder << "{ path = " << path_
+          << ", flags = " << flags_;
+
+  builder << ", size = ";
+  LARGE_INTEGER file_size;
+  if (!::GetFileSizeEx(handle_, &file_size)) {
+    builder << "n/a";
+  } else {
+    builder << file_size.QuadPart;
+  }
+
+  builder << ", offset = ";
+  LARGE_INTEGER zero;
+  zero.QuadPart = 0;
+  LARGE_INTEGER current_position;
+  if (!::SetFilePointerEx(handle_, zero, &current_position, FILE_CURRENT)) {
+    builder << "n/a";
+  } else {
+    builder << current_position.QuadPart;
+  }
+
+  return builder << ", append_mode = " << append_mode_
+                 << ", locked = " << locked_
+                 << ", unlink_at_close = " << unlink_at_close_ << " }";
+}
+
+}  // namespace io
+}  // namespace grnxx
+
+#endif  // GRNXX_WINDOWS

  Added: lib/io/file-windows.hpp (+112 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/file-windows.hpp    2012-11-28 16:36:40 +0900 (3144a5b)
@@ -0,0 +1,112 @@
+/*
+  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_IO_FILE_WINDOWS_HPP
+#define GRNXX_IO_FILE_WINDOWS_HPP
+
+#include "file.hpp"
+
+#ifdef GRNXX_WINDOWS
+
+#include <windows.h>
+
+namespace grnxx {
+namespace io {
+
+class FileImpl {
+ public:
+  ~FileImpl();
+
+  static std::unique_ptr<FileImpl> open(const char *path, Flags flags,
+                                        int permission);
+
+  bool lock(LockMode mode, int sleep_count, Duration sleep_duration);
+  bool try_lock(LockMode mode);
+  bool unlock();
+
+  bool locked() const {
+    return locked_;
+  }
+  bool unlocked() const {
+    return !locked_;
+  }
+
+  uint64_t read(void *buf, uint64_t size);
+  uint64_t read(void *buf, uint64_t size, uint64_t offset);
+  uint64_t write(const void *buf, uint64_t size);
+  uint64_t write(const void *buf, uint64_t size,
+                      uint64_t offset);
+
+  void sync();
+
+  uint64_t seek(int64_t offset, int whence);
+  uint64_t tell() const;
+
+  void resize(uint64_t size);
+  uint64_t size() const;
+
+  bool unlink_at_close() const {
+    return unlink_at_close_;
+  }
+  void set_unlink_at_close(bool value) {
+    unlink_at_close_ = value;
+  }
+
+  String path() const {
+    return path_;
+  }
+  Flags flags() const {
+    return flags_;
+  }
+
+  const void *handle() const {
+   return &handle_;
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+  static bool exists(const char *path);
+  static void unlink(const char *path);
+  static bool unlink_if_exists(const char *path);
+
+ private:
+  String path_;
+  Flags flags_;
+  HANDLE handle_;
+  bool append_mode_;
+  bool locked_;
+  bool unlink_at_close_;
+
+  FileImpl();
+
+  void open_regular_file(const char *path, Flags flags, int permission);
+  void open_temporary_file(const char *path, Flags flags, int permission);
+
+  FileImpl(const FileImpl &);
+  FileImpl &operator=(const FileImpl &);
+};
+
+inline StringBuilder &operator<<(StringBuilder &builder, const FileImpl &file) {
+  return file.write_to(builder);
+}
+
+}  // namespace io
+}  // namespace grnxx
+
+#endif  // GRNXX_WINDOWS
+
+#endif  // GRNXX_IO_FILE_WINDOWS_HPP

  Added: lib/io/file.cpp (+169 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/file.cpp    2012-11-28 16:36:40 +0900 (b5980e8)
@@ -0,0 +1,169 @@
+/*
+  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 "file.hpp"
+#include "file-posix.hpp"
+#include "file-windows.hpp"
+
+#include "../exception.hpp"
+#include "../logger.hpp"
+
+namespace grnxx {
+namespace io {
+
+File::File() : impl_() {}
+
+File::File(const char *path, Flags flags, int permission)
+  : impl_(FileImpl::open(path, flags, permission)) {}
+
+File::~File() {}
+
+File::File(const File &file) : impl_(file.impl_) {}
+
+File &File::operator=(const File &file) {
+  impl_ = file.impl_;
+  return *this;
+}
+
+File::File(File &&file) : impl_(std::move(file.impl_)) {}
+
+File &File::operator=(File &&file) {
+  impl_ = std::move(file.impl_);
+  return *this;
+}
+
+bool File::lock(LockMode mode, int sleep_count, Duration sleep_duration) {
+  throw_if_impl_is_invalid();
+  return impl_->lock(mode, sleep_count, sleep_duration);
+}
+
+bool File::try_lock(LockMode mode) {
+  throw_if_impl_is_invalid();
+  return impl_->try_lock(mode);
+}
+
+bool File::unlock() {
+  throw_if_impl_is_invalid();
+  return impl_->unlock();
+}
+
+bool File::locked() const {
+  throw_if_impl_is_invalid();
+  return impl_->locked();
+}
+
+bool File::unlocked() const {
+  throw_if_impl_is_invalid();
+  return impl_->unlocked();
+}
+
+uint64_t File::read(void *buf, uint64_t size) {
+  throw_if_impl_is_invalid();
+  return impl_->read(buf, size);
+}
+
+uint64_t File::read(void *buf, uint64_t size, uint64_t offset) {
+  throw_if_impl_is_invalid();
+  return impl_->read(buf, size, offset);
+}
+
+uint64_t File::write(const void *buf, uint64_t size) {
+  throw_if_impl_is_invalid();
+  return impl_->write(buf, size);
+}
+
+uint64_t File::write(const void *buf, uint64_t size,
+                          uint64_t offset) {
+  throw_if_impl_is_invalid();
+  return impl_->write(buf, size, offset);
+}
+
+void File::sync() {
+  throw_if_impl_is_invalid();
+  impl_->sync();
+}
+
+uint64_t File::seek(int64_t offset, int whence) {
+  throw_if_impl_is_invalid();
+  return impl_->seek(offset, whence);
+}
+
+uint64_t File::tell() const {
+  throw_if_impl_is_invalid();
+  return impl_->tell();
+}
+
+void File::resize(uint64_t size) {
+  throw_if_impl_is_invalid();
+  impl_->resize(size);
+}
+
+uint64_t File::size() const {
+  throw_if_impl_is_invalid();
+  return impl_->size();
+}
+
+bool File::unlink_at_close() const {
+  return impl_ ? impl_->unlink_at_close() : false;
+}
+
+void File::set_unlink_at_close(bool value) {
+  throw_if_impl_is_invalid();
+  impl_->set_unlink_at_close(value);
+}
+
+String File::path() const {
+  return impl_ ? impl_->path() : String();
+}
+
+Flags File::flags() const {
+  return impl_ ? impl_->flags() : Flags();
+}
+
+const void *File::handle() const {
+  return impl_ ? impl_->handle() : nullptr;
+}
+
+void File::swap(File &file) {
+  impl_.swap(file.impl_);
+}
+
+bool File::exists(const char *path) {
+  return FileImpl::exists(path);
+}
+
+void File::unlink(const char *path) {
+  FileImpl::unlink(path);
+}
+
+bool File::unlink_if_exists(const char *path) {
+  return FileImpl::unlink_if_exists(path);
+}
+
+void File::throw_if_impl_is_invalid() const {
+  if (!impl_) {
+    GRNXX_ERROR() << "invalid instance: file = " << *this;
+    GRNXX_THROW();
+  }
+}
+
+StringBuilder &File::write_to(StringBuilder &builder) const {
+  return impl_ ? impl_->write_to(builder) : (builder << "n/a");
+}
+
+}  // namespace io
+}  // namespace grnxx

  Added: lib/io/file.hpp (+126 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/file.hpp    2012-11-28 16:36:40 +0900 (34978fd)
@@ -0,0 +1,126 @@
+/*
+  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_IO_FILE_HPP
+#define GRNXX_IO_FILE_HPP
+
+#include "flags.hpp"
+#include "../duration.hpp"
+
+namespace grnxx {
+namespace io {
+
+const int FILE_UNIQUE_PATH_GENERATION_MAX_NUM_TRIALS = 10;
+
+class FileImpl;
+
+class File {
+ public:
+  File();
+  // Available flags are as follows:
+  //  GRNXX_IO_READ_ONLY, GRNXX_IO_WRITE_ONLY, GRNXX_IO_APPEND,
+  //  GRNXX_IO_CREATE, GRNXX_IO_OPEN, GRNXX_IO_TEMPORARY, GRNXX_IO_TRUNCATE.
+  // Windows ignores permission.
+  explicit File(const char *path, Flags flags = Flags(),
+                int permission = 0644);
+  ~File();
+
+  File(const File &file);
+  File &operator=(const File &file);
+
+  File(File &&file);
+  File &operator=(File &&file);
+
+  GRNXX_EXPLICIT_CONVERSION operator bool() const {
+    return static_cast<bool>(impl_);
+  }
+
+  // The following functions operate advisory locks for files, not for
+  // FileImple instances. The word "advisory" indicates that the file is
+  // accessible even if it is locked.
+
+  // lock() returns false on time-out or deadlock.
+  bool lock(LockMode mode, int sleep_count = 1000,
+            Duration sleep_duration = Duration::milliseconds(1));
+  // try_lock() returns false if the file is already locked.
+  bool try_lock(LockMode mode);
+  // unlock() returns false if the file is not locked.
+  bool unlock();
+
+  bool locked() const;
+  bool unlocked() const;
+
+  // The following functions are not thread-safe.
+
+  // read() reads data from file at most size bytes and returns the number of
+  // actually read bytes.
+  uint64_t read(void *buf, uint64_t size);
+  uint64_t read(void *buf, uint64_t size, uint64_t offset);
+  // write() writes data into file at most size bytes and returns the number
+  // of actually written bytes.
+  uint64_t write(const void *buf, uint64_t size);
+  uint64_t write(const void *buf, uint64_t size, uint64_t offset);
+
+  void sync();
+
+  // seek() moves the file pointer and returns the new position.
+  uint64_t seek(int64_t offset, int whence = SEEK_SET);
+  // tell() returns the current position.
+  uint64_t tell() const;
+
+  // resize() resizes the file and moves the file pointer to the new
+  // end-of-file.
+  void resize(uint64_t size);
+  // size() returns the file size in bytes.
+  uint64_t size() const;
+
+  // If true, the associated path will be unlinked after closing the file
+  // handle.
+  bool unlink_at_close() const;
+  void set_unlink_at_close(bool value);
+
+  String path() const;
+  Flags flags() const;
+
+  const void *handle() const;
+
+  void swap(File &file);
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+  static bool exists(const char *path);
+  static void unlink(const char *path);
+  static bool unlink_if_exists(const char *path);
+
+ private:
+  std::shared_ptr<FileImpl> impl_;
+
+  void throw_if_impl_is_invalid() const;
+};
+
+inline void swap(File &lhs, File &rhs) {
+  lhs.swap(rhs);
+}
+
+inline StringBuilder &operator<<(StringBuilder &builder, const File &file) {
+  return file.write_to(builder);
+}
+
+}  // namespace io
+}  // namespace grnxx
+
+#endif  // GRNXX_IO_FILE_HPP

  Added: lib/io/file_info-impl.cpp (+106 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/file_info-impl.cpp    2012-11-28 16:36:40 +0900 (de90257)
@@ -0,0 +1,106 @@
+/*
+  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 "file_info-impl.hpp"
+
+#include <cerrno>
+
+#include "../error.hpp"
+#include "../exception.hpp"
+#include "../logger.hpp"
+
+namespace grnxx {
+namespace io {
+
+std::unique_ptr<FileInfoImpl> FileInfoImpl::stat(const char *path) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = " << path;
+    GRNXX_THROW();
+  }
+
+  Stat stat;
+#ifdef GRNXX_WINDOWS
+  if (::_stat(path, &stat) != 0) {
+    if (errno != ENOENT) {
+      GRNXX_WARNING() << "failed to get file information: path = <" << path
+                      << ">: '::_stat' " << Error(errno);
+#else  // GRNXX_WINDOWS
+  if (::stat(path, &stat) != 0) {
+    if (errno != ENOENT) {
+      GRNXX_WARNING() << "failed to get file information: path = <" << path
+                      << ">: '::stat' " << Error(errno);
+#endif  // GRNXX_WINDOWS
+    }
+    return nullptr;
+  }
+
+  std::unique_ptr<FileInfoImpl> file_info(
+      new (std::nothrow) FileInfoImpl(stat));
+  if (!file_info) {
+    GRNXX_ERROR() << "new grnxx::io::FileInfoImpl failed";
+    GRNXX_THROW();
+  }
+  return file_info;
+}
+
+std::unique_ptr<FileInfoImpl> FileInfoImpl::stat(const File &file) {
+  Stat stat;
+#ifdef GRNXX_WINDOWS
+  if (::_stat(file.path().c_str(), &stat) != 0) {
+    if (errno != ENOENT) {
+      GRNXX_WARNING() << "failed to get file information: file = " << file
+                      << ": '::_stat' " << Error(errno);
+    }
+#else  // GRNXX_WINDOWS
+  if (::fstat(*static_cast<const int *>(file.handle()), &stat) != 0) {
+    GRNXX_WARNING() << "failed to get file information: file = " << file
+                    << ": '::fstat' " << Error(errno);
+#endif  // GRNXX_WINDOWS
+    return nullptr;
+  }
+
+  std::unique_ptr<FileInfoImpl> file_info(
+      new (std::nothrow) FileInfoImpl(stat));
+  if (!file_info) {
+    GRNXX_ERROR() << "new grnxx::io::FileInfoImpl failed";
+    GRNXX_THROW();
+  }
+  return file_info;
+}
+
+StringBuilder &FileInfoImpl::write_to(StringBuilder &builder) const {
+  if (!builder) {
+    return builder;
+  }
+
+  return builder << "{ is_file = " << is_file()
+                 << ", is_directory = " << is_directory()
+                 << ", device_id = " << device_id()
+                 << ", inode_id = " << inode_id()
+                 << ", mode_flags = " << mode_flags()
+                 << ", num_links = " << num_links()
+                 << ", user_id = " << user_id()
+                 << ", group_id = " << group_id()
+                 << ", size = " << size()
+                 << ", last_access_time = " << last_access_time()
+                 << ", last_modification_time = " << last_modification_time()
+                 << ", last_status_change_time = " << last_status_change_time()
+                 << " }";
+}
+
+}  // namespace io
+}  // namespace grnxx

  Added: lib/io/file_info-impl.hpp (+102 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/file_info-impl.hpp    2012-11-28 16:36:40 +0900 (195ea0a)
@@ -0,0 +1,102 @@
+/*
+  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_IO_FILE_INFO_IMPL_HPP
+#define GRNXX_IO_FILE_INFO_IMPL_HPP
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "../time.hpp"
+#include "file.hpp"
+
+namespace grnxx {
+namespace io {
+
+#ifdef GRNXX_WINDOWS
+typedef struct _stat Stat;
+#else  // GRNXX_WINDOWS
+typedef struct stat Stat;
+#endif  // GRNXX_WINDOWS
+
+class FileInfoImpl {
+ public:
+  static std::unique_ptr<FileInfoImpl> stat(const char *path);
+  static std::unique_ptr<FileInfoImpl> stat(const File &file);
+
+  bool is_file() const {
+#ifdef GRNXX_WINDOWS
+    return (stat_.st_mode & _S_IFREG) != 0;
+#else  // GRNXX_WINDOWS
+    return S_ISREG(stat_.st_mode) != 0;
+#endif  // GRNXX_WINDOWS
+  }
+
+  bool is_directory() const {
+#ifdef GRNXX_WINDOWS
+    return (stat_.st_mode & _S_IFDIR) != 0;
+#else  // GRNXX_WINDOWS
+    return S_ISDIR(stat_.st_mode) != 0;
+#endif  // GRNXX_WINDOWS
+  }
+
+  int64_t device_id() const {
+    return stat_.st_dev;
+  }
+  int64_t inode_id() const {
+    return stat_.st_ino;
+  }
+  int64_t mode_flags() const {
+    return stat_.st_mode;
+  }
+  int64_t num_links() const {
+    return stat_.st_nlink;
+  }
+  int64_t user_id() const {
+    return stat_.st_uid;
+  }
+  int64_t group_id() const {
+    return stat_.st_gid;
+  }
+  uint64_t size() const {
+    return stat_.st_size;
+  }
+  Time last_access_time() const {
+    return Time(static_cast<int64_t>(stat_.st_atime) * 1000000000);
+  }
+  Time last_modification_time() const {
+    return Time(static_cast<int64_t>(stat_.st_mtime) * 1000000000);
+  }
+  Time last_status_change_time() const {
+    return Time(static_cast<int64_t>(stat_.st_ctime) * 1000000000);
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+ private:
+  Stat stat_;
+
+  FileInfoImpl(const Stat &stat) : stat_(stat) {}
+
+  FileInfoImpl(const FileInfoImpl &);
+  FileInfoImpl &operator=(const FileInfoImpl &);
+};
+
+}  // namespace io
+}  // namespace grnxx
+
+#endif  // GRNXX_IO_FILE_INFO_IMPL_HPP

  Added: lib/io/file_info.cpp (+90 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/file_info.cpp    2012-11-28 16:36:40 +0900 (b222fb8)
@@ -0,0 +1,90 @@
+/*
+  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 "file_info.hpp"
+#include "file_info-impl.hpp"
+
+namespace grnxx {
+namespace io {
+
+FileInfo::FileInfo() : impl_() {}
+
+FileInfo::FileInfo(const char *path) : impl_(FileInfoImpl::stat(path)) {}
+
+FileInfo::FileInfo(const File &file) : impl_(FileInfoImpl::stat(file)) {}
+
+FileInfo::FileInfo(const FileInfo &file_info) : impl_(file_info.impl_) {}
+
+FileInfo &FileInfo::operator=(const FileInfo &file_info) {
+  impl_ = file_info.impl_;
+  return *this;
+}
+
+FileInfo::FileInfo(FileInfo &&file_info) : impl_(std::move(file_info.impl_)) {}
+
+FileInfo &FileInfo::operator=(FileInfo &&file_info) {
+  impl_ = std::move(file_info.impl_);
+  return *this;
+}
+
+bool FileInfo::is_file() const {
+  return impl_ ? impl_->is_file() : false;
+}
+bool FileInfo::is_directory() const {
+  return impl_ ? impl_->is_directory() : false;
+}
+int64_t FileInfo::device_id() const {
+  return impl_ ? impl_->device_id() : 0;
+}
+int64_t FileInfo::inode_id() const {
+  return impl_ ? impl_->inode_id() : 0;
+}
+int64_t FileInfo::mode_flags() const {
+  return impl_ ? impl_->mode_flags() : 0;
+}
+int64_t FileInfo::num_links() const {
+  return impl_ ? impl_->num_links() : 0;
+}
+int64_t FileInfo::user_id() const {
+  return impl_ ? impl_->user_id() : 0;
+}
+int64_t FileInfo::group_id() const {
+  return impl_ ? impl_->group_id() : 0;
+}
+uint64_t FileInfo::size() const {
+  return impl_ ? impl_->size() : 0;
+}
+Time FileInfo::last_access_time() const {
+  return impl_ ? impl_->last_access_time() : Time();
+}
+Time FileInfo::last_modification_time() const {
+  return impl_ ? impl_->last_modification_time() : Time();
+}
+Time FileInfo::last_status_change_time() const {
+  return impl_ ? impl_->last_status_change_time() : Time();
+}
+
+void FileInfo::swap(FileInfo &rhs) {
+  impl_.swap(rhs.impl_);
+}
+
+StringBuilder &FileInfo::write_to(StringBuilder &builder) const {
+  return impl_ ? impl_->write_to(builder) : (builder << "n/a");
+}
+
+}  // namespace io
+}  // namespace grnxx

  Added: lib/io/file_info.hpp (+80 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/file_info.hpp    2012-11-28 16:36:40 +0900 (b4cd16e)
@@ -0,0 +1,80 @@
+/*
+  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_IO_FILE_INFO_HPP
+#define GRNXX_IO_FILE_INFO_HPP
+
+#include "../time.hpp"
+#include "file.hpp"
+
+namespace grnxx {
+namespace io {
+
+class FileInfoImpl;
+
+class FileInfo {
+ public:
+  FileInfo();
+  explicit FileInfo(const char *path);
+  explicit FileInfo(const File &file);
+
+  FileInfo(const FileInfo &file_info);
+  FileInfo &operator=(const FileInfo &file_info);
+
+  FileInfo(FileInfo &&file_info);
+  FileInfo &operator=(FileInfo &&file_info);
+
+  GRNXX_EXPLICIT_CONVERSION operator bool() const {
+    return static_cast<bool>(impl_);
+  }
+
+  bool is_file() const;
+  bool is_directory() const;
+  int64_t device_id() const;
+  int64_t inode_id() const;
+  int64_t mode_flags() const;
+  int64_t num_links() const;
+  int64_t user_id() const;
+  int64_t group_id() const;
+  uint64_t size() const;
+  Time last_access_time() const;
+  Time last_modification_time() const;
+  Time last_status_change_time() const;
+
+  void swap(FileInfo &rhs);
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+ private:
+  std::shared_ptr<FileInfoImpl> impl_;
+
+  // Copyable.
+};
+
+inline void swap(FileInfo &lhs, FileInfo &rhs) {
+  lhs.swap(rhs);
+}
+
+inline StringBuilder &operator<<(StringBuilder &builder,
+                                 const FileInfo &file_info) {
+  return file_info.write_to(builder);
+}
+
+}  // namespace io
+}  // namespace grnxx
+
+#endif  // GRNXX_IO_FILE_INFO_HPP

  Added: lib/io/flags.cpp (+63 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/flags.cpp    2012-11-28 16:36:40 +0900 (13b2930)
@@ -0,0 +1,63 @@
+/*
+  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 "flags.hpp"
+
+#include <ostream>
+
+namespace grnxx {
+namespace io {
+
+#define GRNXX_FLAGS_WRITE(flag) do { \
+  if (flags & flag) { \
+    if (!is_first) { \
+      builder << " | "; \
+    } \
+    builder << #flag; \
+    is_first = false; \
+  } \
+} while (false)
+
+StringBuilder &operator<<(StringBuilder &builder, Flags flags) {
+  if (flags) {
+    bool is_first = true;
+    GRNXX_FLAGS_WRITE(GRNXX_IO_READ_ONLY);
+    GRNXX_FLAGS_WRITE(GRNXX_IO_WRITE_ONLY);
+    GRNXX_FLAGS_WRITE(GRNXX_IO_ANONYMOUS);
+    GRNXX_FLAGS_WRITE(GRNXX_IO_APPEND);
+    GRNXX_FLAGS_WRITE(GRNXX_IO_CREATE);
+    GRNXX_FLAGS_WRITE(GRNXX_IO_HUGE_TLB);
+    GRNXX_FLAGS_WRITE(GRNXX_IO_OPEN);
+    GRNXX_FLAGS_WRITE(GRNXX_IO_TEMPORARY);
+    GRNXX_FLAGS_WRITE(GRNXX_IO_TRUNCATE);
+    GRNXX_FLAGS_WRITE(GRNXX_IO_PRIVATE);
+    GRNXX_FLAGS_WRITE(GRNXX_IO_SHARED);
+    return builder;
+  } else {
+    return builder << "0";
+  }
+}
+
+std::ostream &operator<<(std::ostream &stream, Flags flags) {
+  char buf[256];
+  StringBuilder builder(buf);
+  builder << flags;
+  return stream.write(builder.c_str(), builder.length());
+}
+
+}  // namespace io
+}  // namespace grnxx

  Added: lib/io/flags.hpp (+83 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/flags.hpp    2012-11-28 16:36:40 +0900 (146f390)
@@ -0,0 +1,83 @@
+/*
+  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_IO_FLAGS_HPP
+#define GRNXX_IO_FLAGS_HPP
+
+#include "../string_builder.hpp"
+
+namespace grnxx {
+namespace io {
+
+class FlagsIdentifier {};
+typedef FlagsImpl<FlagsIdentifier> Flags;
+
+// GRNXX_IO_WRITE_ONLY is ignored if GRNXX_IO_READ_ONLY is enabled.
+// GRNXX_IO_READ_ONLY is disabled if GRNXX_IO_CREATE is specified.
+// If both flags are not set, and object is created/opened/mapped in
+// read-write mode.
+
+// Read-only mode.
+const Flags GRNXX_IO_READ_ONLY  = Flags::define(0x0001);
+// Write-only mode.
+const Flags GRNXX_IO_WRITE_ONLY = Flags::define(0x0002);
+
+// GRNXX_IO_ANONYMOUS disables all the flags other than GRNXX_HUGE_TLB and
+// enables GRNXX_IO_PRIVATE.
+// GRNXX_IO_APPEND is ignored if GRNXX_IO_READ_ONLY is enabled.
+// GRNXX_IO_CREATE disables GRNXX_IO_READ_ONLY.
+// GRNXX_IO_OPEN is enabled if GRNXX_IO_CREATE is not specified.
+// If both GRNXX_IO_CREATE and GRNXX_IO_OPEN are set, it first tries to create
+// an object and, if already exists, then tries to open the existing object.
+// GRNXX_IO_TEMPORARY disables other flags.
+
+// Anonymous (non-file-backed) mode.
+const Flags GRNXX_IO_ANONYMOUS  = Flags::define(0x0010);
+// Append mode.
+const Flags GRNXX_IO_APPEND     = Flags::define(0x0020);
+// Create an object if it does not exist.
+const Flags GRNXX_IO_CREATE     = Flags::define(0x0040);
+// Try to use huge pages.
+const Flags GRNXX_IO_HUGE_TLB   = Flags::define(0x0080);
+// Open an existing object.
+const Flags GRNXX_IO_OPEN       = Flags::define(0x0100);
+// Create a temporary object.
+const Flags GRNXX_IO_TEMPORARY  = Flags::define(0x0200);
+// Truncate an existing object.
+const Flags GRNXX_IO_TRUNCATE   = Flags::define(0x0400);
+
+// GRNXX_IO_PRIVATE is ignored if GRNXX_IO_SHARED is enabled.
+
+// Private mode.
+const Flags GRNXX_IO_PRIVATE    = Flags::define(0x1000);
+// Shared mode.
+const Flags GRNXX_IO_SHARED     = Flags::define(0x2000);
+
+StringBuilder &operator<<(StringBuilder &builder, Flags flags);
+
+std::ostream &operator<<(std::ostream &stream, Flags flags);
+
+// One of these modes must be specified.
+enum LockMode {
+  GRNXX_IO_EXCLUSIVE_LOCK = 0x10000,  // Create an exclusive lock.
+  GRNXX_IO_SHARED_LOCK    = 0x20000   // Create a shared lock.
+};
+
+}  // namespace io
+}  // namespace grnxx
+
+#endif  // GRNXX_IO_FLAGS_HPP

  Added: lib/io/path.cpp (+132 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/path.cpp    2012-11-28 16:36:40 +0900 (1a76c0d)
@@ -0,0 +1,132 @@
+/*
+  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 "path.hpp"
+
+#include <cerrno>
+#include <cstdlib>
+#include <random>
+
+#include "../error.hpp"
+#include "../exception.hpp"
+#include "../logger.hpp"
+
+namespace grnxx {
+namespace io {
+namespace {
+
+class Freer {
+ public:
+  void operator()(void *ptr) const {
+    std::free(ptr);
+  }
+};
+
+}  // namespace
+
+String Path::full_path(const char *path) {
+  if (!path) {
+    path = "";
+  }
+
+#ifdef GRNXX_WINDOWS
+  std::unique_ptr<char[], Freer> full_path(::_fullpath(nullptr, path, 0));
+  if (!full_path) {
+    GRNXX_ERROR() << "failed to generate full path: path = " << path
+                  << ": '::_fullpath' " << Error(errno);
+    GRNXX_THROW();
+  }
+  return String(full_path.get());
+#else  // GRNXX_WINDOWS
+  StringBuilder full_path(STRING_BUILDER_AUTO_RESIZE);
+  if (path[0] != '/') {
+    std::unique_ptr<char[], Freer> working_directory(::getcwd(nullptr, 0));
+    if (!working_directory) {
+      GRNXX_ERROR() << "failed to get working directory: '::getcwd' "
+                    << Error(errno);
+      GRNXX_THROW();
+    }
+    full_path << working_directory.get() << '/';
+  }
+  full_path << path;
+  if (!full_path) {
+    GRNXX_ERROR() << "failed to generate full path: path = " << path;
+    GRNXX_THROW();
+  }
+
+  size_t src = 0, 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) {
+    return "/";
+  }
+  return String(full_path.c_str(), dest);
+#endif  // GRNXX_WINDOWS
+}
+
+String Path::unique_path(const char *path) {
+  if (!path) {
+    path = "";
+  }
+
+  const size_t SUFFIX_LENGTH = 8;
+
+  const size_t length = std::strlen(path);
+  StringBuilder unique_path(length + SUFFIX_LENGTH + 2);
+  unique_path.append(path, length);
+  unique_path.append('_');
+
+  std::random_device random;
+  for (size_t i = 0; i < SUFFIX_LENGTH; ++i) {
+    const int value = static_cast<int>(random() % 36);
+    unique_path.append((value < 10) ? ('0' + value) : ('A' + value - 10));
+  }
+
+  if (!unique_path) {
+    GRNXX_ERROR() << "failed to generate unique path: path = " << path;
+    GRNXX_THROW();
+  }
+  return unique_path.str();
+}
+
+}  // namespace io
+}  // namespace grnxx

  Added: lib/io/path.hpp (+48 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/path.hpp    2012-11-28 16:36:40 +0900 (ee99aa4)
@@ -0,0 +1,48 @@
+/*
+  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_IO_PATH_HPP
+#define GRNXX_IO_PATH_HPP
+
+#include "../string.hpp"
+
+namespace grnxx {
+namespace io {
+
+class Path {
+ public:
+  // Generate a full path from a path.
+  // Return a copy of the given path if it is a full path.
+  static String full_path(const char *path);
+
+  // Generate an almost unique path by adding a random suffix.
+  // If the given path is "temp", this function generates "temp_XXXXXXXX",
+  // where X indicates a random character ('0'-'9' or 'A'-'Z').
+  static String unique_path(const char *path);
+
+ private:
+  Path();
+  ~Path();
+
+  Path(const Path &);
+  Path &operator=(const Path &);
+};
+
+}  // namespace io
+}  // namespace grnxx
+
+#endif  // GRNXX_IO_PATH_HPP

  Added: lib/io/pool-impl.cpp (+876 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/pool-impl.cpp    2012-11-28 16:36:40 +0900 (946ca3b)
@@ -0,0 +1,876 @@
+/*
+  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 "pool-impl.hpp"
+
+#include <vector>
+
+#include "../exception.hpp"
+#include "../lock.hpp"
+#include "../logger.hpp"
+#include "../string_format.hpp"
+#include "../time.hpp"
+#include "path.hpp"
+
+namespace grnxx {
+namespace io {
+
+PoolImpl::~PoolImpl() {}
+
+std::unique_ptr<PoolImpl> PoolImpl::open(const char *path, Flags flags,
+                                         const PoolOptions &options) {
+  std::unique_ptr<PoolImpl> pool(new (std::nothrow) PoolImpl);
+  if (!pool) {
+    GRNXX_ERROR() << "new grnxx::io::PoolImpl failed";
+    GRNXX_THROW();
+  }
+
+  if (flags & GRNXX_IO_ANONYMOUS) {
+    pool->open_anonymous_pool(flags, options);
+  } else if (flags & GRNXX_IO_TEMPORARY) {
+    pool->open_temporary_pool(path, flags, options);
+  } else {
+    pool->open_regular_pool(path, flags, options);
+  }
+
+  return pool;
+}
+
+BlockInfo *PoolImpl::create_block(uint64_t size) {
+  if (flags_ & GRNXX_IO_READ_ONLY) {
+    GRNXX_ERROR() << "invalid operation: flags = " << flags_;
+    GRNXX_THROW();
+  }
+
+  if (size > options().max_block_size()) {
+    GRNXX_ERROR() << "invalid argument: size = " << size
+                  << ", max_block_size = " << options().max_block_size();
+    GRNXX_THROW();
+  }
+
+  // A block size must be a multiple of BLOCK_UNIT_SIZE.
+  if (size == 0) {
+    size = BLOCK_UNIT_SIZE;
+  } else {
+    size = (size + (BLOCK_UNIT_SIZE - 1)) & ~(BLOCK_UNIT_SIZE - 1);
+  }
+
+  Lock lock(mutable_inter_process_data_mutex());
+  if (!lock) {
+    GRNXX_ERROR() << "failed to lock data";
+    GRNXX_THROW();
+  }
+
+  unfreeze_oldest_frozen_blocks(options().unfreeze_count_per_operation());
+
+  BlockInfo * const block_info = find_idle_block(size);
+  if (block_info) {
+    return activate_idle_block(block_info, size);
+  } else {
+    return create_active_block(size);
+  }
+}
+
+BlockInfo *PoolImpl::get_block_info(uint32_t block_id) {
+  if (block_id >= header_->num_blocks()) {
+    GRNXX_ERROR() << "invalid argument: block_id = " << block_id
+                  << ", num_blocks = " << header_->num_blocks();
+    GRNXX_THROW();
+  }
+
+  const uint8_t block_info_chunk_size_bits =
+      bit_scan_reverse(block_id | POOL_MIN_BLOCK_INFO_CHUNK_SIZE);
+  const uint16_t block_info_chunk_id = static_cast<uint16_t>(
+      (block_id >> block_info_chunk_size_bits)
+      + block_info_chunk_size_bits - POOL_MIN_BLOCK_INFO_CHUNK_SIZE_BITS);
+  const uint32_t block_info_chunk_size =
+      uint32_t(1) << block_info_chunk_size_bits;
+
+  if (!block_info_chunks_[block_info_chunk_id]) {
+    mmap_block_info_chunk(block_info_chunk_id);
+  }
+
+  const uint32_t offset_mask = block_info_chunk_size - 1;
+  BlockInfo * const block_infos = static_cast<BlockInfo *>(
+      block_info_chunks_[block_info_chunk_id].address());
+  return &block_infos[block_id & offset_mask];
+}
+
+void PoolImpl::free_block(BlockInfo *block_info) {
+  if (flags_ & GRNXX_IO_READ_ONLY) {
+    GRNXX_ERROR() << "invalid operation: flags = " << flags_;
+    GRNXX_THROW();
+  }
+
+  Lock lock(mutable_inter_process_data_mutex());
+  if (!lock) {
+    GRNXX_ERROR() << "failed to lock data";
+    GRNXX_THROW();
+  }
+
+  unfreeze_oldest_frozen_blocks(options().unfreeze_count_per_operation());
+
+  switch (block_info->status()) {
+    case BLOCK_ACTIVE: {
+      block_info->set_frozen_stamp(mutable_recycler()->stamp());
+      block_info->set_status(BLOCK_FROZEN);
+      if (header_->latest_frozen_block_id() != BLOCK_INVALID_ID) {
+        BlockInfo * const latest_frozen_block_info =
+            get_block_info(header_->latest_frozen_block_id());
+        block_info->set_next_frozen_block_id(
+            latest_frozen_block_info->next_frozen_block_id());
+        latest_frozen_block_info->set_next_frozen_block_id(block_info->id());
+      } else {
+        block_info->set_next_frozen_block_id(block_info->id());
+      }
+      header_->set_latest_frozen_block_id(block_info->id());
+      break;
+    }
+    case BLOCK_FROZEN: {
+      break;
+    }
+    default: {
+      GRNXX_ERROR() << "invalid argument: block_info = " << *block_info;
+      GRNXX_THROW();
+    }
+  }
+}
+
+StringBuilder &PoolImpl::write_to(StringBuilder &builder) const {
+  if (!builder) {
+    return builder;
+  }
+
+  builder << "{ path = " << path_
+          << ", flags = " << flags_
+          << ", header = " << *header_;
+
+  builder << ", oldest_idle_block_ids = ";
+  bool is_empty = true;
+  for (uint32_t i = 0; i < POOL_MAX_NUM_FILES; ++i) {
+    if (files_[i]) {
+      if (is_empty) {
+        builder << "{ ";
+        is_empty = false;
+      } else {
+        builder << ", ";
+      }
+      builder << '[' << i << "] = " << files_[i];
+    }
+  }
+  builder << (is_empty ? "{}" : " }");
+
+  builder << ", header_chunk = " << header_chunk_;
+
+  builder << ", block_chunks = ";
+  is_empty = true;
+  for (uint32_t i = 0; i < POOL_MAX_NUM_BLOCK_CHUNKS; ++i) {
+    if (block_chunks_[i]) {
+      if (is_empty) {
+        builder << "{ ";
+        is_empty = false;
+      } else {
+        builder << ", ";
+      }
+      builder << i;
+    }
+  }
+  builder << (is_empty ? "{}" : " }");
+
+  builder << ", block_info_chunks = ";
+  is_empty = true;
+  for (uint32_t i = 0; i < POOL_MAX_NUM_BLOCK_INFO_CHUNKS; ++i) {
+    if (block_info_chunks_[i]) {
+      if (is_empty) {
+        builder << "{ ";
+        is_empty = false;
+      } else {
+        builder << ", ";
+      }
+      builder << i;
+    }
+  }
+  builder << (is_empty ? "{}" : " }");
+
+  return builder << " }";
+}
+
+bool PoolImpl::exists(const char *path) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = " << path;
+    GRNXX_THROW();
+  }
+  // TODO: Check the file format.
+  return File::exists(path);
+}
+
+void PoolImpl::unlink(const char *path) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = " << path;
+    GRNXX_THROW();
+  } else {
+    // FIXME
+    File file(path);
+    if (!file.try_lock(GRNXX_IO_EXCLUSIVE_LOCK)) {
+      GRNXX_ERROR() << "failed to lock file: path = " << path;
+      GRNXX_THROW();
+    }
+  }
+
+  std::vector<String> paths;
+  try {
+    std::unique_ptr<PoolImpl> pool = PoolImpl::open(path, GRNXX_IO_READ_ONLY);
+    const PoolOptions &options = pool->options();
+    const PoolHeader &header = pool->header();
+    const uint16_t max_file_id = static_cast<uint16_t>(
+        (header.total_size() - 1) / options.max_file_size());
+    for (uint16_t file_id = 0; file_id <= max_file_id; ++file_id) {
+      paths.push_back(pool->generate_path(file_id));
+    }
+  } catch (const std::exception &exception) {
+    GRNXX_ERROR() << exception;
+    GRNXX_THROW();
+  }
+
+  File::unlink(paths[0].c_str());
+  for (std::size_t path_id = 1; path_id < paths.size(); ++path_id) {
+    File::unlink_if_exists(paths[path_id].c_str());
+  }
+}
+
+bool PoolImpl::unlink_if_exists(const char *path) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = " << path;
+    GRNXX_THROW();
+  }
+  // TODO: Check the file format.
+  if (exists(path)) {
+    unlink(path);
+    return true;
+  }
+  return false;
+}
+
+PoolImpl::PoolImpl()
+  : path_(),
+    flags_(),
+    header_(nullptr),
+    files_(),
+    header_chunk_(),
+    block_chunks_(),
+    block_info_chunks_(),
+    inter_thread_chunk_mutex_() {}
+
+void PoolImpl::open_anonymous_pool(Flags flags, const PoolOptions &options) {
+  flags_ = GRNXX_IO_ANONYMOUS;
+  if (flags & GRNXX_IO_HUGE_TLB) {
+    flags_ |= GRNXX_IO_HUGE_TLB;
+  }
+  setup_header(options);
+}
+
+void PoolImpl::open_temporary_pool(const char *path, Flags,
+                                   const PoolOptions &options) {
+  path_ = Path::full_path(path);
+  flags_ = GRNXX_IO_TEMPORARY;
+  files_[0] = File(path_.c_str(), GRNXX_IO_TEMPORARY);
+  setup_header(options);
+}
+
+void PoolImpl::open_regular_pool(const char *path, Flags flags,
+                                 const PoolOptions &options) {
+  if (!path) {
+    GRNXX_ERROR() << "invalid argument: path = " << path;
+    GRNXX_THROW();
+  }
+  path_ = Path::full_path(path);
+
+  Flags file_flags;
+  if ((~flags & GRNXX_IO_CREATE) && (flags & GRNXX_IO_READ_ONLY)) {
+    flags_ |= GRNXX_IO_READ_ONLY;
+    file_flags |= GRNXX_IO_READ_ONLY;
+  }
+  if (flags & GRNXX_IO_CREATE) {
+    flags_ |= GRNXX_IO_CREATE;
+    file_flags |= GRNXX_IO_CREATE;
+  }
+  if ((~flags & GRNXX_IO_CREATE) || flags & GRNXX_IO_OPEN) {
+    flags_ |= GRNXX_IO_OPEN;
+    file_flags |= GRNXX_IO_OPEN;
+  }
+  files_[0] = File(path_.c_str(), file_flags);
+  files_[0].set_unlink_at_close(true);
+
+  if (flags & GRNXX_IO_CREATE) {
+    if (files_[0].size() == 0) {
+      if (files_[0].lock(GRNXX_IO_EXCLUSIVE_LOCK)) {
+        if (files_[0].size() == 0) {
+          setup_header(options);
+          files_[0].unlock();
+          if (!files_[0].lock(GRNXX_IO_SHARED_LOCK)) {
+            GRNXX_ERROR() << "failed to lock file: path = " << path
+                          << ", full_path = " << path_
+                          << ", flags = " << flags;
+            GRNXX_THROW();
+          }
+        } else {
+          files_[0].unlock();
+        }
+      }
+    }
+  }
+
+  if (!header_) {
+    if ((flags & GRNXX_IO_OPEN) || (~flags & GRNXX_IO_CREATE)) {
+      const Time start_time = Time::now();
+      while ((Time::now() - start_time) < Duration::seconds(10)) {
+        if (files_[0].size() != 0) {
+          break;
+        }
+        Thread::sleep(Duration::milliseconds(10));
+      }
+    }
+    if (files_[0].lock(GRNXX_IO_SHARED_LOCK)) {
+      check_header();
+    }
+  }
+
+  if (!header_) {
+    GRNXX_ERROR() << "failed to open pool: path = " << path
+                  << ", full_path = " << path_ << ", flags = " << flags;
+    GRNXX_THROW();
+  }
+
+  files_[0].set_unlink_at_close(false);
+}
+
+void PoolImpl::setup_header(const PoolOptions &options) {
+  if (files_[0]) {
+    files_[0].resize(POOL_HEADER_CHUNK_SIZE);
+    header_chunk_ = View(files_[0], get_view_flags(),
+                         0, POOL_HEADER_CHUNK_SIZE);
+  } else {
+    header_chunk_ = View(get_view_flags(), POOL_HEADER_CHUNK_SIZE);
+  }
+  std::memset(header_chunk_.address(), 0, POOL_HEADER_CHUNK_SIZE);
+  header_ = static_cast<PoolHeader *>(header_chunk_.address());
+  *header_ = PoolHeader(options);
+}
+
+void PoolImpl::check_header() {
+  header_chunk_ = View(files_[0], get_view_flags(), 0, POOL_HEADER_CHUNK_SIZE);
+  header_ = static_cast<PoolHeader *>(header_chunk_.address());
+
+  // TODO: Check the header.
+}
+
+void PoolImpl::mmap_block_chunk(uint16_t chunk_id) {
+  Lock lock(mutable_inter_thread_chunk_mutex());
+  if (!lock) {
+    GRNXX_ERROR() << "failed to lock chunks";
+    GRNXX_THROW();
+  }
+
+  if (block_chunks_[chunk_id]) {
+    return;
+  }
+
+  const ChunkInfo &chunk_info = header_->block_chunk_infos(chunk_id);
+  if (!chunk_info) {
+    GRNXX_ERROR() << "invalid argument: chunk_id = " << chunk_id;
+    GRNXX_THROW();
+  }
+
+  block_chunks_[chunk_id] = mmap_chunk(chunk_info);
+}
+
+void PoolImpl::mmap_block_info_chunk(uint16_t chunk_id) {
+  Lock lock(mutable_inter_thread_chunk_mutex());
+  if (!lock) {
+    GRNXX_ERROR() << "failed to lock chunks";
+    GRNXX_THROW();
+  }
+
+  if (block_info_chunks_[chunk_id]) {
+    return;
+  }
+
+  const ChunkInfo &chunk_info = header_->block_info_chunk_infos(chunk_id);
+  if (!chunk_info) {
+    GRNXX_ERROR() << "invalid argument: chunk_id = " << chunk_id;
+    GRNXX_THROW();
+  }
+
+  block_info_chunks_[chunk_id] = mmap_chunk(chunk_info);
+}
+
+View PoolImpl::mmap_chunk(const ChunkInfo &chunk_info) {
+  if (flags_ & GRNXX_IO_ANONYMOUS) {
+    return View(get_view_flags(), chunk_info.size());
+  } else {
+    File &file = files_[chunk_info.file_id()];
+    if (!file) {
+      Flags file_flags = GRNXX_IO_CREATE | GRNXX_IO_OPEN;
+      if (flags_ & GRNXX_IO_TEMPORARY) {
+        file_flags = GRNXX_IO_TEMPORARY;
+      } else if (flags_ & GRNXX_IO_READ_ONLY) {
+        file_flags = GRNXX_IO_READ_ONLY;
+      }
+      const String &path = generate_path(chunk_info.file_id());
+      file = File(path.c_str(), file_flags);
+    }
+
+    const uint64_t min_file_size = chunk_info.offset() + chunk_info.size();
+    if (file.size() < min_file_size) {
+      Lock lock(mutable_inter_process_file_mutex());
+      if (!lock) {
+        GRNXX_ERROR() << "failed to lock files";
+        GRNXX_THROW();
+      }
+      if (file.size() < min_file_size) {
+        file.resize(min_file_size);
+      }
+    }
+
+    return View(file, get_view_flags(),
+                chunk_info.offset(), chunk_info.size());
+  }
+}
+
+Flags PoolImpl::get_view_flags() const {
+  if (flags_ & GRNXX_IO_ANONYMOUS) {
+    return (flags_ & GRNXX_IO_HUGE_TLB) ? GRNXX_IO_HUGE_TLB : Flags();
+  } else {
+    Flags view_flags = GRNXX_IO_SHARED;
+    if (flags_ & GRNXX_IO_READ_ONLY) {
+      view_flags |= GRNXX_IO_READ_ONLY;
+    }
+    return view_flags;
+  }
+}
+
+String PoolImpl::generate_path(uint16_t file_id) const {
+  if (file_id == 0) {
+    return path_;
+  }
+
+  // If path_ ends with ".grn", the result also ends with ".grn".
+  // In other words, file_id is inserted before the ".grn".
+  // Otherwise, fild_id is appended as suffix.
+  const size_t FILE_ID_WIDTH = 3;
+
+  StringBuilder path(path_.length() + FILE_ID_WIDTH + 2);
+  const bool has_extension = path_.ends_with(".grn");
+  if (has_extension) {
+    path.append(path_.c_str(), path_.length() - 4);
+  } else {
+    path.append(path_.c_str(), path_.length());
+  }
+  path << '_' << StringFormat::align_right(file_id, FILE_ID_WIDTH, '0');
+  if (has_extension) {
+    path.append(".grn", 4);
+  }
+  if (!path) {
+    GRNXX_ERROR() << "failed to generate path: file_id = " << file_id;
+    GRNXX_THROW();
+  }
+  return path.str();
+}
+
+BlockInfo *PoolImpl::create_phantom_block() {
+  if (header_->num_blocks() >= POOL_MAX_NUM_BLOCKS) {
+    GRNXX_ERROR() << "too many blocks: num_blocks = " << header_->num_blocks()
+                  << ", max_num_blocks = " << POOL_MAX_NUM_BLOCKS;
+    GRNXX_THROW();
+  }
+
+  const uint32_t block_id = header_->num_blocks();
+
+  const uint8_t block_info_chunk_size_bits =
+      bit_scan_reverse(block_id | POOL_MIN_BLOCK_INFO_CHUNK_SIZE);
+  const uint16_t block_info_chunk_id = static_cast<uint16_t>(
+      (block_id >> block_info_chunk_size_bits)
+      + block_info_chunk_size_bits - POOL_MIN_BLOCK_INFO_CHUNK_SIZE_BITS);
+  const uint32_t block_info_chunk_size =
+      uint32_t(1) << block_info_chunk_size_bits;
+
+  if (block_id == header_->max_num_blocks()) {
+    const uint64_t block_info_chunk_size_in_bytes =
+        BLOCK_INFO_SIZE << block_info_chunk_size_bits;
+    if (block_info_chunk_size_in_bytes > options().max_file_size()) {
+      GRNXX_ERROR() << "too large chunk: chunk_size = "
+                    << block_info_chunk_size_in_bytes
+                    << ", max_file_size = " << options().max_file_size();
+      GRNXX_THROW();
+    }
+
+    const uint16_t file_id =
+        header_->total_size() / options().max_file_size();
+    const uint64_t file_size =
+        header_->total_size() % options().max_file_size();
+    const uint64_t file_size_left = options().max_file_size() - file_size;
+
+    ChunkInfo chunk_info;
+    chunk_info.set_id(block_info_chunk_id);
+    if (file_size_left < block_info_chunk_size_in_bytes) {
+      if (file_id >= POOL_MAX_NUM_FILES) {
+        GRNXX_ERROR() << "too many files: fild_id = " << file_id
+                      << ", max_num_files = " << POOL_MAX_NUM_FILES;
+        GRNXX_THROW();
+      }
+      chunk_info.set_file_id(file_id + 1);
+      chunk_info.set_offset(0);
+    } else {
+      chunk_info.set_file_id(file_id);
+      chunk_info.set_offset(file_size);
+    }
+    chunk_info.set_size(block_info_chunk_size_in_bytes);
+    header_->set_block_info_chunk_infos(chunk_info);
+
+    header_->set_total_size((chunk_info.file_id() * options().max_file_size())
+        + chunk_info.offset() + chunk_info.size());
+
+    // Note: block_id == header_->max_num_blocks().
+    const uint32_t num_blocks_left = POOL_MAX_NUM_BLOCKS - block_id;
+    if (num_blocks_left > block_info_chunk_size) {
+      header_->set_max_num_blocks(block_id + block_info_chunk_size);
+    } else {
+      header_->set_max_num_blocks(POOL_MAX_NUM_BLOCKS);
+    }
+
+    if (file_id != chunk_info.file_id()) {
+      if (header_->next_block_chunk_id() >= POOL_MAX_NUM_BLOCK_CHUNKS) {
+        GRNXX_ERROR() << "too many block chunks: next_block_chunk_id = "
+                      << header_->next_block_chunk_id()
+                      << ", max_num_block_chunks = "
+                      << POOL_MAX_NUM_BLOCK_CHUNKS;
+        GRNXX_THROW();
+      }
+
+      ChunkInfo idle_chunk_info;
+      idle_chunk_info.set_id(header_->next_block_chunk_id());
+      idle_chunk_info.set_file_id(file_id);
+      idle_chunk_info.set_offset(file_size);
+      idle_chunk_info.set_size(file_size_left);
+      header_->set_block_chunk_infos(idle_chunk_info);
+      header_->set_next_block_chunk_id(idle_chunk_info.id() + 1);
+
+      // The following create_idle_block() calls this function but it never
+      // comes here and never makes an endless loop.
+      BlockInfo * const idle_block_info = create_idle_block();
+      idle_block_info->set_chunk_id(idle_chunk_info.id());
+      idle_block_info->set_offset(0);
+      idle_block_info->set_size(idle_chunk_info.size());
+      idle_block_info->set_next_block_id(BLOCK_INVALID_ID);
+      idle_block_info->set_prev_block_id(BLOCK_INVALID_ID);
+      register_idle_block(idle_block_info);
+    }
+  }
+
+  if (!block_info_chunks_[block_info_chunk_id]) {
+    mmap_block_info_chunk(block_info_chunk_id);
+  }
+
+  const uint32_t offset_mask = block_info_chunk_size - 1;
+  BlockInfo * const block_infos = static_cast<BlockInfo *>(
+      block_info_chunks_[block_info_chunk_id].address());
+
+  BlockInfo * const block_info = &block_infos[block_id & offset_mask];
+  block_info->set_id(block_id);
+  phantomize_block(block_info);
+
+  header_->set_num_blocks(block_id + 1);
+  header_->set_latest_phantom_block_id(block_id);
+  return block_info;
+}
+
+BlockInfo *PoolImpl::create_active_block(uint64_t size) {
+  if (header_->next_block_chunk_id() >= POOL_MAX_NUM_BLOCK_CHUNKS) {
+    GRNXX_ERROR() << "too many block chunks: next_block_chunk_id = "
+                  << header_->next_block_chunk_id()
+                  << ", max_num_block_chunks = "
+                  << POOL_MAX_NUM_BLOCK_CHUNKS;
+    GRNXX_THROW();
+  }
+
+  uint64_t chunk_size = static_cast<uint64_t>(
+      header_->total_size() * options().next_block_chunk_size_ratio());
+  if (chunk_size < size) {
+    chunk_size = size;
+  }
+  chunk_size = (chunk_size + (CHUNK_UNIT_SIZE - 1)) & ~(CHUNK_UNIT_SIZE - 1);
+  if (chunk_size < options().min_block_chunk_size()) {
+    chunk_size = options().min_block_chunk_size();
+  }
+  if (chunk_size > options().max_block_chunk_size()) {
+    chunk_size = options().max_block_chunk_size();
+  }
+
+  const uint16_t file_id = header_->total_size() / options().max_file_size();
+  const uint64_t file_size = header_->total_size() % options().max_file_size();
+  const uint64_t file_size_left = options().max_file_size() - file_size;
+
+  ChunkInfo chunk_info;
+  chunk_info.set_id(header_->next_block_chunk_id());
+  if (file_size_left < chunk_size) {
+    if (file_id >= POOL_MAX_NUM_FILES) {
+      GRNXX_ERROR() << "too many files: fild_id = " << file_id
+                    << ", max_num_files = " << POOL_MAX_NUM_FILES;
+      GRNXX_THROW();
+    }
+    chunk_info.set_file_id(file_id + 1);
+    chunk_info.set_offset(0);
+  } else {
+    chunk_info.set_file_id(file_id);
+    chunk_info.set_offset(file_size);
+  }
+  chunk_info.set_size(chunk_size);
+  header_->set_block_chunk_infos(chunk_info);
+  header_->set_next_block_chunk_id(chunk_info.id() + 1);
+
+  header_->set_total_size((chunk_info.file_id() * options().max_file_size())
+      + chunk_info.offset() + chunk_info.size());
+
+  BlockInfo *block_info;
+  if (header_->latest_phantom_block_id() != BLOCK_INVALID_ID) {
+    block_info = get_block_info(header_->latest_phantom_block_id());
+  } else {
+    block_info = create_phantom_block();
+  }
+  header_->set_latest_phantom_block_id(block_info->next_phantom_block_id());
+  block_info->set_status(BLOCK_ACTIVE);
+  block_info->set_chunk_id(chunk_info.id());
+  block_info->set_offset(0);
+  block_info->set_size(size);
+  block_info->set_next_block_id(BLOCK_INVALID_ID);
+  block_info->set_prev_block_id(BLOCK_INVALID_ID);
+
+  if (size < chunk_size) {
+    // Create an idle block for the left space.
+    BlockInfo * const idle_block_info = create_idle_block();
+    idle_block_info->set_chunk_id(chunk_info.id());
+    idle_block_info->set_offset(block_info->size());
+    idle_block_info->set_size(chunk_info.size() - block_info->size());
+    idle_block_info->set_next_block_id(BLOCK_INVALID_ID);
+    idle_block_info->set_prev_block_id(block_info->id());
+    register_idle_block(idle_block_info);
+
+    block_info->set_next_block_id(idle_block_info->id());
+  }
+
+  if (file_id != chunk_info.file_id()) {
+    // Create a block chunk and an idle block for the left space.
+    // In this case, the block chunk size might be less than
+    // options().min_block_chunk_size().
+    if (header_->next_block_chunk_id() >= POOL_MAX_NUM_BLOCK_CHUNKS) {
+      GRNXX_ERROR() << "too many block chunks: next_block_chunk_id = "
+                    << header_->next_block_chunk_id()
+                    << ", max_num_block_chunks = "
+                    << POOL_MAX_NUM_BLOCK_CHUNKS;
+      GRNXX_THROW();
+    }
+
+    ChunkInfo chunk_info;
+    chunk_info.set_id(header_->next_block_chunk_id());
+    chunk_info.set_file_id(file_id);
+    chunk_info.set_offset(file_size);
+    chunk_info.set_size(file_size_left);
+    header_->set_block_chunk_infos(chunk_info);
+    header_->set_next_block_chunk_id(chunk_info.id() + 1);
+
+    BlockInfo * const idle_block_info = create_idle_block();
+    idle_block_info->set_chunk_id(chunk_info.id());
+    idle_block_info->set_offset(0);
+    idle_block_info->set_size(chunk_info.size());
+    idle_block_info->set_next_block_id(BLOCK_INVALID_ID);
+    idle_block_info->set_prev_block_id(BLOCK_INVALID_ID);
+    register_idle_block(idle_block_info);
+  }
+
+  return block_info;
+}
+
+BlockInfo *PoolImpl::create_idle_block() {
+  BlockInfo *block_info;
+  if (header_->latest_phantom_block_id() != BLOCK_INVALID_ID) {
+    block_info = get_block_info(header_->latest_phantom_block_id());
+  } else {
+    block_info = create_phantom_block();
+  }
+  header_->set_latest_phantom_block_id(block_info->next_phantom_block_id());
+  block_info->set_status(BLOCK_IDLE);
+  return block_info;
+}
+
+BlockInfo *PoolImpl::find_idle_block(uint64_t size) {
+  for (uint8_t list_id = bit_scan_reverse(size >> BLOCK_UNIT_SIZE_BITS);
+       list_id < 32; ++list_id) {
+    if (header_->oldest_idle_block_ids(list_id) != BLOCK_INVALID_ID) {
+      BlockInfo * const block_info =
+          get_block_info(header_->oldest_idle_block_ids(list_id));
+      if (block_info->size() >= size) {
+        return block_info;
+      }
+    }
+  }
+  return nullptr;
+}
+
+void PoolImpl::phantomize_block(BlockInfo *block_info) {
+  block_info->set_status(BLOCK_PHANTOM);
+  block_info->set_next_phantom_block_id(header_->latest_phantom_block_id());
+  header_->set_latest_phantom_block_id(block_info->id());
+}
+
+uint32_t PoolImpl::unfreeze_oldest_frozen_blocks(uint32_t max_count) {
+  for (uint32_t count = 0; count < max_count; ++count) {
+    if (!unfreeze_oldest_frozen_block()) {
+      return count;
+    }
+  }
+  return max_count;
+}
+
+bool PoolImpl::unfreeze_oldest_frozen_block() {
+  if (header_->latest_frozen_block_id() == BLOCK_INVALID_ID) {
+    return false;
+  }
+
+  BlockInfo * const latest_frozen_block_info =
+      get_block_info(header_->latest_frozen_block_id());
+  BlockInfo * const oldest_frozen_block_info =
+      get_block_info(latest_frozen_block_info->next_frozen_block_id());
+
+  // Recently frozen blocks are rejected.
+  if (!mutable_recycler()->check(oldest_frozen_block_info->frozen_stamp())) {
+    return false;
+  }
+
+  if (latest_frozen_block_info == oldest_frozen_block_info) {
+    header_->set_latest_frozen_block_id(BLOCK_INVALID_ID);
+  } else {
+    latest_frozen_block_info->set_next_frozen_block_id(
+        oldest_frozen_block_info->next_frozen_block_id());
+  }
+  oldest_frozen_block_info->set_status(BLOCK_IDLE);
+  register_idle_block(oldest_frozen_block_info);
+  merge_idle_blocks(oldest_frozen_block_info);
+
+  return true;
+}
+
+BlockInfo *PoolImpl::activate_idle_block(BlockInfo *block_info,
+                                         uint64_t size) {
+  unregister_idle_block(block_info);
+  if (size < block_info->size()) {
+    BlockInfo * const idle_block_info = create_idle_block();
+    idle_block_info->set_chunk_id(block_info->chunk_id());
+    idle_block_info->set_offset(block_info->offset() + size);
+    idle_block_info->set_size(block_info->size() - size);
+    idle_block_info->set_next_block_id(block_info->next_block_id());
+    idle_block_info->set_prev_block_id(block_info->id());
+    register_idle_block(idle_block_info);
+
+    if (block_info->next_block_id() != BLOCK_INVALID_ID) {
+      BlockInfo * const next_block_info =
+          get_block_info(block_info->next_block_id());
+      next_block_info->set_prev_block_id(idle_block_info->id());
+    }
+    block_info->set_size(size);
+    block_info->set_next_block_id(idle_block_info->id());
+  }
+  block_info->set_status(BLOCK_ACTIVE);
+  return block_info;
+}
+
+void PoolImpl::merge_idle_blocks(BlockInfo *center_block_info) {
+  if (center_block_info->next_block_id() != BLOCK_INVALID_ID) {
+    BlockInfo * const next_block_info =
+        get_block_info(center_block_info->next_block_id());
+    if (next_block_info->status() == BLOCK_IDLE) {
+      merge_idle_blocks(center_block_info, next_block_info);
+    }
+  }
+
+  if (center_block_info->prev_block_id() != BLOCK_INVALID_ID) {
+    BlockInfo * const prev_block_info =
+        get_block_info(center_block_info->prev_block_id());
+    if (prev_block_info->status() == BLOCK_IDLE) {
+      merge_idle_blocks(prev_block_info, center_block_info);
+    }
+  }
+}
+
+void PoolImpl::merge_idle_blocks(BlockInfo *block_info,
+                                 BlockInfo *next_block_info) {
+  unregister_idle_block(block_info);
+  unregister_idle_block(next_block_info);
+
+  block_info->set_next_block_id(next_block_info->next_block_id());
+  if (next_block_info->next_block_id() != BLOCK_INVALID_ID) {
+    BlockInfo * const next_next_block_info =
+        get_block_info(next_block_info->next_block_id());
+    next_next_block_info->set_prev_block_id(block_info->id());
+  }
+  block_info->set_size(block_info->size() + next_block_info->size());
+
+  phantomize_block(next_block_info);
+
+  register_idle_block(block_info);
+}
+
+void PoolImpl::register_idle_block(BlockInfo *block_info) {
+  const uint8_t list_id =
+      bit_scan_reverse(block_info->size() >> BLOCK_UNIT_SIZE_BITS);
+  if (header_->oldest_idle_block_ids(list_id) == BLOCK_INVALID_ID) {
+    block_info->set_next_idle_block_id(block_info->id());
+    block_info->set_prev_idle_block_id(block_info->id());
+    header_->set_oldest_idle_block_ids(list_id, block_info->id());
+  } else {
+    // latest_idle_block <-> new_idle_block <-> oldest_idle_block.
+    BlockInfo * const oldest_idle_block_info =
+        get_block_info(header_->oldest_idle_block_ids(list_id));
+    BlockInfo * const latest_idle_block_info = 
+        get_block_info(oldest_idle_block_info->prev_idle_block_id());
+    block_info->set_next_idle_block_id(oldest_idle_block_info->id());
+    block_info->set_prev_idle_block_id(latest_idle_block_info->id());
+    latest_idle_block_info->set_next_idle_block_id(block_info->id());
+    oldest_idle_block_info->set_prev_idle_block_id(block_info->id());
+  }
+}
+
+void PoolImpl::unregister_idle_block(BlockInfo *block_info) {
+  const uint8_t list_id =
+      bit_scan_reverse(block_info->size() >> BLOCK_UNIT_SIZE_BITS);
+  if (block_info->id() == block_info->next_idle_block_id()) {
+    header_->set_oldest_idle_block_ids(list_id, BLOCK_INVALID_ID);
+  } else {
+    // prev_idle_block <-> next_idle_block.
+    BlockInfo * const next_idle_block_info =
+        get_block_info(block_info->next_idle_block_id());
+    BlockInfo * const prev_idle_block_info =
+        get_block_info(block_info->prev_idle_block_id());
+    next_idle_block_info->set_prev_idle_block_id(prev_idle_block_info->id());
+    prev_idle_block_info->set_next_idle_block_id(next_idle_block_info->id());
+    if (block_info->id() == header_->oldest_idle_block_ids(list_id)) {
+      header_->set_oldest_idle_block_ids(list_id, next_idle_block_info->id());
+    }
+  }
+}
+
+}  // namespace io
+}  // namespace grnxx

  Added: lib/io/pool-impl.hpp (+148 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/pool-impl.hpp    2012-11-28 16:36:40 +0900 (557ae5a)
@@ -0,0 +1,148 @@
+/*
+  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_IO_POOL_IMPL_HPP
+#define GRNXX_IO_POOL_IMPL_HPP
+
+#include "pool.hpp"
+
+namespace grnxx {
+namespace io {
+
+class PoolImpl {
+ public:
+  ~PoolImpl();
+
+  static std::unique_ptr<PoolImpl> open(
+      const char *path, Flags flags,
+      const PoolOptions &options = PoolOptions());
+
+  BlockInfo *create_block(uint64_t size);
+
+  BlockInfo *get_block_info(uint32_t block_id);
+
+  void *get_block_address(uint32_t block_id) {
+    return get_block_address(*get_block_info(block_id));
+  }
+  void *get_block_address(const BlockInfo &block_info) {
+    if (!block_chunks_[block_info.chunk_id()]) {
+      mmap_block_chunk(block_info.chunk_id());
+    }
+    return static_cast<char *>(block_chunks_[block_info.chunk_id()].address())
+        + block_info.offset();
+  }
+
+  void free_block(uint32_t block_id) {
+    free_block(get_block_info(block_id));
+  }
+  void free_block(const BlockInfo &block_info) {
+    free_block(const_cast<BlockInfo *>(&block_info));
+  }
+  void free_block(BlockInfo *block_info);
+
+  String path() const {
+    return path_;
+  }
+  Flags flags() const {
+    return flags_;
+  }
+  const PoolOptions &options() const {
+    return header_->options();
+  }
+  const PoolHeader &header() const {
+    return *header_;
+  }
+  Recycler *mutable_recycler() {
+    return header_->mutable_recycler();
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+  static bool exists(const char *path);
+  static void unlink(const char *path);
+  static bool unlink_if_exists(const char *path);
+
+ private:
+  String path_;
+  Flags flags_;
+  PoolHeader *header_;
+  File files_[POOL_MAX_NUM_FILES];
+  Chunk header_chunk_;
+  Chunk block_chunks_[POOL_MAX_NUM_BLOCK_CHUNKS];
+  Chunk block_info_chunks_[POOL_MAX_NUM_BLOCK_INFO_CHUNKS];
+  Mutex inter_thread_chunk_mutex_;
+
+  PoolImpl();
+
+  void open_anonymous_pool(Flags flags, const PoolOptions &options);
+  void open_temporary_pool(const char *path, Flags flags,
+                           const PoolOptions &options);
+  void open_regular_pool(const char *path, Flags flags,
+                         const PoolOptions &options);
+
+  void setup_header(const PoolOptions &options);
+  void check_header();
+
+  void mmap_block_chunk(uint16_t chunk_id);
+  void mmap_block_info_chunk(uint16_t chunk_id);
+  View mmap_chunk(const ChunkInfo &chunk_info);
+
+  Flags get_view_flags() const;
+
+  String generate_path(uint16_t file_id) const;
+
+  BlockInfo *create_phantom_block();
+  BlockInfo *create_active_block(uint64_t size);
+  BlockInfo *create_idle_block();
+
+  void phantomize_block(BlockInfo *block_info);
+
+  uint32_t unfreeze_oldest_frozen_blocks(uint32_t max_count);
+  bool unfreeze_oldest_frozen_block();
+
+  BlockInfo *find_idle_block(uint64_t size);
+  BlockInfo *activate_idle_block(BlockInfo *block_info, uint64_t size);
+
+  void merge_idle_blocks(BlockInfo *center_block_info);
+  void merge_idle_blocks(BlockInfo *block_info, BlockInfo *next_block_info);
+
+  void register_idle_block(BlockInfo *block_info);
+  void unregister_idle_block(BlockInfo *block_info);
+
+  Mutex *mutable_inter_process_data_mutex() {
+    return header_->mutable_inter_process_data_mutex();
+  }
+  Mutex *mutable_inter_process_file_mutex() {
+    return header_->mutable_inter_process_file_mutex();
+  }
+  Mutex *mutable_inter_thread_chunk_mutex() {
+    return &inter_thread_chunk_mutex_;
+  }
+
+  PoolImpl(const PoolImpl &);
+  PoolImpl &operator=(const PoolImpl &);
+};
+
+inline StringBuilder &operator<<(StringBuilder &builder,
+                                 const PoolImpl &pool) {
+  return pool.write_to(builder);
+}
+
+}  // namespace io
+}  // namespace grnxx
+
+#endif  // GRNXX_IO_ALPHA_POOL_IMPL_HPP

  Added: lib/io/pool.cpp (+334 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/pool.cpp    2012-11-28 16:36:40 +0900 (3627423)
@@ -0,0 +1,334 @@
+/*
+  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 "pool-impl.hpp"
+
+#include "../exception.hpp"
+#include "../logger.hpp"
+
+namespace grnxx {
+namespace io {
+
+PoolOptions::PoolOptions()
+  : max_block_size_(0),
+    min_block_chunk_size_(0),
+    max_block_chunk_size_(0),
+    max_file_size_(0),
+    next_block_chunk_size_ratio_(-1.0),
+    frozen_duration_(-1),
+    unfreeze_count_per_operation_(POOL_DEFAULT_UNFREEZE_COUNT_PER_OPERATION) {}
+
+void PoolOptions::adjust() {
+  if (max_file_size_ == 0) {
+    max_file_size_ = POOL_DEFAULT_MAX_FILE_SIZE;
+  } else {
+    max_file_size_ = (max_file_size_ >> CHUNK_UNIT_SIZE_BITS)
+        << CHUNK_UNIT_SIZE_BITS;
+    if (max_file_size_ < CHUNK_UNIT_SIZE) {
+      max_file_size_ = CHUNK_UNIT_SIZE;
+    } else if (max_file_size_ > POOL_MAX_FILE_SIZE) {
+      max_file_size_ = POOL_MAX_FILE_SIZE;
+    }
+  }
+
+  if (max_block_chunk_size_ == 0) {
+    max_block_chunk_size_ = max_file_size_;
+  } else {
+    max_block_chunk_size_ = (max_block_chunk_size_ >> CHUNK_UNIT_SIZE_BITS)
+        << CHUNK_UNIT_SIZE_BITS;
+    if (max_block_chunk_size_ < CHUNK_UNIT_SIZE) {
+      max_block_chunk_size_ = CHUNK_UNIT_SIZE;
+    } else if (max_block_chunk_size_ > CHUNK_MAX_SIZE) {
+      max_block_chunk_size_ = CHUNK_MAX_SIZE;
+    }
+  }
+  if (max_block_chunk_size_ > max_file_size_) {
+    max_block_chunk_size_ = max_file_size_;
+  }
+
+  if (min_block_chunk_size_ == 0) {
+    min_block_chunk_size_ = POOL_DEFAULT_MIN_BLOCK_CHUNK_SIZE;
+  } else {
+    min_block_chunk_size_ = (min_block_chunk_size_ >> CHUNK_UNIT_SIZE_BITS)
+        << CHUNK_UNIT_SIZE_BITS;
+    if (min_block_chunk_size_ < CHUNK_UNIT_SIZE) {
+      min_block_chunk_size_ = CHUNK_UNIT_SIZE;
+    } else if (min_block_chunk_size_ > CHUNK_MAX_SIZE) {
+      min_block_chunk_size_ = CHUNK_MAX_SIZE;
+    }
+  }
+  if (min_block_chunk_size_ > max_block_chunk_size_) {
+    min_block_chunk_size_ = max_block_chunk_size_;
+  }
+
+  if (max_block_size_ == 0) {
+    max_block_size_ = max_block_chunk_size_;
+  } else {
+    max_block_size_ = (max_block_size_ >> BLOCK_UNIT_SIZE_BITS)
+        << BLOCK_UNIT_SIZE_BITS;
+    if (max_block_size_ < BLOCK_UNIT_SIZE) {
+      max_block_size_ = BLOCK_UNIT_SIZE;
+    } else if (max_block_size_ > BLOCK_MAX_SIZE) {
+      max_block_size_ = BLOCK_MAX_SIZE;
+    }
+  }
+  if (max_block_size_ > max_block_chunk_size_) {
+    max_block_size_ = max_block_chunk_size_;
+  }
+
+  if (next_block_chunk_size_ratio_ < 0.0) {
+    next_block_chunk_size_ratio_ = POOL_DEFAULT_NEXT_BLOCK_CHUNK_SIZE_RATIO;
+  } else if (next_block_chunk_size_ratio_ >
+             POOL_MAX_NEXT_BLOCK_CHUNK_SIZE_RATIO) {
+    next_block_chunk_size_ratio_ = POOL_MAX_NEXT_BLOCK_CHUNK_SIZE_RATIO;
+  }
+
+  if (frozen_duration_ < Duration(0)) {
+    frozen_duration_ = POOL_DEFAULT_FROZEN_DURATION;
+  } else if (frozen_duration_ > POOL_MAX_FROZEN_DURATION) {
+    frozen_duration_ = POOL_MAX_FROZEN_DURATION;
+  }
+}
+
+StringBuilder &PoolOptions::write_to(StringBuilder &builder) const {
+  if (!builder) {
+    return builder;
+  }
+
+  builder << "{ max_block_size = " << max_block_size()
+          << ", min_block_chunk_size = " << min_block_chunk_size()
+          << ", max_block_chunk_size = " << max_block_chunk_size()
+          << ", max_file_size = " << max_file_size()
+          << ", next_block_chunk_size_ratio = "
+          << next_block_chunk_size_ratio()
+          << ", frozen_duration = " << frozen_duration()
+          << ", unfreeze_count_per_operation = "
+          << unfreeze_count_per_operation();
+  return builder << " }";
+}
+
+PoolHeader::PoolHeader(const PoolOptions &options)
+  : format_string_(),
+    version_string_(),
+    options_(options),
+    total_size_(POOL_HEADER_CHUNK_SIZE),
+    num_blocks_(0),
+    max_num_blocks_(0),
+    next_block_chunk_id_(0),
+    latest_phantom_block_id_(BLOCK_INVALID_ID),
+    latest_frozen_block_id_(BLOCK_INVALID_ID),
+    oldest_idle_block_ids_(),
+    block_chunk_infos_(),
+    block_info_chunk_infos_(),
+    recycler_(),
+    inter_process_data_mutex_(),
+    inter_process_file_mutex_() {
+  std::memcpy(format_string_, POOL_HEADER_FORMAT_STRING,
+              sizeof(format_string_));
+  std::memcpy(version_string_, POOL_HEADER_VERSION_STRING,
+              sizeof(version_string_));
+
+  options_.adjust();
+
+  for (uint8_t list_id = 0; list_id < 32; ++list_id) {
+    oldest_idle_block_ids_[list_id] = BLOCK_INVALID_ID;
+  }
+  for (uint16_t chunk_id = 0; chunk_id < POOL_MAX_NUM_BLOCK_CHUNKS;
+       ++chunk_id) {
+    block_chunk_infos_[chunk_id].set_id(chunk_id);
+  }
+  for (uint16_t chunk_id = 0; chunk_id < POOL_MAX_NUM_BLOCK_INFO_CHUNKS;
+       ++chunk_id) {
+    block_info_chunk_infos_[chunk_id].set_id(chunk_id);
+  }
+
+  recycler_ = Recycler(options_.frozen_duration());
+}
+
+StringBuilder &PoolHeader::write_to(StringBuilder &builder) const {
+  if (!builder) {
+    return builder;
+  }
+
+  builder << "{ format_string = " << format_string()
+          << ", version_string = " << version_string()
+          << ", options = " << options()
+          << ", total_size = " << total_size()
+          << ", num_blocks = " << num_blocks()
+          << ", max_num_blocks = " << max_num_blocks()
+          << ", next_block_chunk_id = " << next_block_chunk_id()
+          << ", latest_phantom_block_id = " << latest_phantom_block_id()
+          << ", latest_frozen_block_id = " << latest_frozen_block_id();
+
+  builder << ", oldest_idle_block_ids = ";
+  bool is_empty = true;
+  for (uint32_t i = 0; i < 32; ++i) {
+    if (oldest_idle_block_ids_[i] != BLOCK_INVALID_ID) {
+      if (is_empty) {
+        builder << "{ ";
+        is_empty = false;
+      } else {
+        builder << ", ";
+      }
+      builder << '[' << i << "] = " << oldest_idle_block_ids_[i];
+    }
+  }
+  builder << (is_empty ? "{}" : " }");
+
+  builder << ", block_chunk_infos = ";
+  is_empty = true;
+  for (uint32_t i = 0; i < POOL_MAX_NUM_BLOCK_CHUNKS; ++i) {
+    if (block_chunk_infos_[i]) {
+      if (is_empty) {
+        builder << "{ ";
+        is_empty = false;
+      } else {
+        builder << ", ";
+      }
+      builder << '[' << i << "] = " << block_chunk_infos_[i];
+    }
+  }
+  builder << (is_empty ? "{}" : " }");
+
+  builder << ", block_info_chunk_infos = ";
+  is_empty = true;
+  for (uint32_t i = 0; i < POOL_MAX_NUM_BLOCK_INFO_CHUNKS; ++i) {
+    if (block_info_chunk_infos_[i]) {
+      if (is_empty) {
+        builder << "{ ";
+        is_empty = false;
+      } else {
+        builder << ", ";
+      }
+      builder << '[' << i << "] = " << block_info_chunk_infos_[i];
+    }
+  }
+  builder << (is_empty ? "{}" : " }");
+
+  builder << ", recycler = " << recycler_
+          << ", inter_process_data_mutex = " << inter_process_data_mutex_
+          << ", inter_process_file_mutex = " << inter_process_file_mutex_;
+  return builder << " }";
+}
+
+Pool::Pool() : impl_() {}
+
+Pool::Pool(const char *path, Flags flags, const PoolOptions &options)
+  : impl_(PoolImpl::open(path, flags, options)) {}
+
+Pool::~Pool() {}
+
+Pool::Pool(const Pool &pool) : impl_(pool.impl_) {}
+
+Pool &Pool::operator=(const Pool &pool) {
+  impl_ = pool.impl_;
+  return *this;
+}
+
+Pool::Pool(Pool &&pool) : impl_(std::move(pool.impl_)) {}
+
+Pool &Pool::operator=(Pool &&pool) {
+  impl_ = std::move(pool.impl_);
+  return *this;
+}
+
+#define GRNXX_IO_POOL_THROW_IF_IMPL_IS_INVALID() do {\
+  if (!impl_) {\
+    GRNXX_ERROR() << "invalid instance: pool = " << *this;\
+    GRNXX_THROW();\
+  }\
+} while (false)
+
+const BlockInfo *Pool::create_block(uint64_t size) {
+  GRNXX_IO_POOL_THROW_IF_IMPL_IS_INVALID();
+  return impl_->create_block(size);
+}
+
+const BlockInfo *Pool::get_block_info(uint32_t block_id) {
+  GRNXX_IO_POOL_THROW_IF_IMPL_IS_INVALID();
+  return impl_->get_block_info(block_id);
+}
+
+void *Pool::get_block_address(uint32_t block_id) {
+  GRNXX_IO_POOL_THROW_IF_IMPL_IS_INVALID();
+  return impl_->get_block_address(block_id);
+}
+
+void *Pool::get_block_address(const BlockInfo &block_info) {
+  GRNXX_IO_POOL_THROW_IF_IMPL_IS_INVALID();
+  return impl_->get_block_address(block_info);
+}
+
+void Pool::free_block(uint32_t block_id) {
+  GRNXX_IO_POOL_THROW_IF_IMPL_IS_INVALID();
+  return impl_->free_block(block_id);
+}
+
+void Pool::free_block(const BlockInfo &block_info) {
+  GRNXX_IO_POOL_THROW_IF_IMPL_IS_INVALID();
+  return impl_->free_block(block_info);
+}
+
+String Pool::path() const {
+  GRNXX_IO_POOL_THROW_IF_IMPL_IS_INVALID();
+  return impl_->path();
+}
+
+Flags Pool::flags() const {
+  GRNXX_IO_POOL_THROW_IF_IMPL_IS_INVALID();
+  return impl_->flags();
+}
+
+const PoolOptions &Pool::options() const {
+  GRNXX_IO_POOL_THROW_IF_IMPL_IS_INVALID();
+  return impl_->options();
+}
+
+const PoolHeader &Pool::header() const {
+  GRNXX_IO_POOL_THROW_IF_IMPL_IS_INVALID();
+  return impl_->header();
+}
+
+Recycler *Pool::mutable_recycler() {
+  GRNXX_IO_POOL_THROW_IF_IMPL_IS_INVALID();
+  return impl_->mutable_recycler();
+}
+
+#undef GRNXX_IO_POOL_THROW_IF_IMPL_IS_INVALID
+
+void Pool::swap(Pool &rhs) {
+  impl_.swap(rhs.impl_);
+}
+
+StringBuilder &Pool::write_to(StringBuilder &builder) const {
+  return impl_ ? impl_->write_to(builder) : (builder << "n/a");
+}
+
+bool Pool::exists(const char *path) {
+  return PoolImpl::exists(path);
+}
+
+void Pool::unlink(const char *path) {
+  PoolImpl::unlink(path);
+}
+
+bool Pool::unlink_if_exists(const char *path) {
+  return PoolImpl::unlink_if_exists(path);
+}
+
+}  // namespace io
+}  // namespace grnxx

  Added: lib/io/pool.hpp (+307 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/pool.hpp    2012-11-28 16:36:40 +0900 (0eb5129)
@@ -0,0 +1,307 @@
+/*
+  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_IO_POOL_HPP
+#define GRNXX_IO_POOL_HPP
+
+#include "../mutex.hpp"
+#include "../recycler.hpp"
+#include "chunk.hpp"
+
+namespace grnxx {
+namespace io {
+
+const uint64_t POOL_MAX_FILE_SIZE = CHUNK_MAX_OFFSET;
+const uint16_t POOL_MAX_NUM_FILES = 1000;
+
+const uint32_t POOL_MAX_NUM_BLOCKS            = BLOCK_MAX_ID + 1;
+const uint16_t POOL_MAX_NUM_BLOCK_CHUNKS      = uint16_t(1) << 11;
+const uint16_t POOL_MAX_NUM_BLOCK_INFO_CHUNKS =
+    32 - (CHUNK_UNIT_SIZE_BITS - BLOCK_INFO_SIZE_BITS) + 1;
+
+const uint8_t  POOL_MIN_BLOCK_INFO_CHUNK_SIZE_BITS =
+    CHUNK_UNIT_SIZE_BITS - BLOCK_INFO_SIZE_BITS;
+const uint64_t POOL_MIN_BLOCK_INFO_CHUNK_SIZE      =
+    uint64_t(1) << POOL_MIN_BLOCK_INFO_CHUNK_SIZE_BITS;
+
+// For PoolOptions.
+
+const uint64_t POOL_DEFAULT_MAX_FILE_SIZE        = uint64_t(1) << 40;
+
+const uint64_t POOL_DEFAULT_MIN_BLOCK_CHUNK_SIZE = uint64_t(1) << 22;
+
+const double POOL_MAX_NEXT_BLOCK_CHUNK_SIZE_RATIO     = 1.0;
+const double POOL_DEFAULT_NEXT_BLOCK_CHUNK_SIZE_RATIO = 1.0 / 64;
+
+const Duration POOL_MAX_FROZEN_DURATION     = Duration::days(1);
+const Duration POOL_DEFAULT_FROZEN_DURATION = Duration::minutes(10);
+
+const uint32_t POOL_DEFAULT_UNFREEZE_COUNT_PER_OPERATION = 32;
+
+// For PoolHeader.
+
+const uint8_t  POOL_HEADER_CHUNK_SIZE_BITS = CHUNK_UNIT_SIZE_BITS;
+const uint64_t POOL_HEADER_CHUNK_SIZE      = CHUNK_UNIT_SIZE;
+
+const char POOL_HEADER_FORMAT_STRING[64]  = "grnxx::io::Pool";
+const char POOL_HEADER_VERSION_STRING[64] = "0.0.0";
+
+class PoolOptions {
+ public:
+  PoolOptions();
+
+  void adjust();
+
+  uint64_t max_block_size() const {
+    return max_block_size_;
+  }
+  uint64_t min_block_chunk_size() const {
+    return min_block_chunk_size_;
+  }
+  uint64_t max_block_chunk_size() const {
+    return max_block_chunk_size_;
+  }
+  uint64_t max_file_size() const {
+    return max_file_size_;
+  }
+  double next_block_chunk_size_ratio() const {
+    return next_block_chunk_size_ratio_;
+  }
+  Duration frozen_duration() const {
+    return frozen_duration_;
+  }
+  uint32_t unfreeze_count_per_operation() const {
+    return unfreeze_count_per_operation_;
+  }
+
+  void set_max_block_size(uint64_t value) {
+    max_block_size_ = value;
+  }
+  void set_min_block_chunk_size(uint64_t value) {
+    min_block_chunk_size_ = value;
+  }
+  void set_max_block_chunk_size(uint64_t value) {
+    max_block_chunk_size_ = value;
+  }
+  void set_max_file_size(uint64_t value) {
+    max_file_size_ = value;
+  }
+  void set_next_block_chunk_size_ratio(double value) {
+    next_block_chunk_size_ratio_ = value;
+  }
+  void set_frozen_duration(Duration value) {
+    frozen_duration_ = value;
+  }
+  void set_unfreeze_count_per_operation(uint32_t value) {
+    unfreeze_count_per_operation_ = value;
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+ private:
+  uint64_t max_block_size_;
+  uint64_t min_block_chunk_size_;
+  uint64_t max_block_chunk_size_;
+  uint64_t max_file_size_;
+  // The ratio of the next block size to the total size.
+  double next_block_chunk_size_ratio_;
+  Duration frozen_duration_;
+  uint32_t unfreeze_count_per_operation_;
+};
+
+inline StringBuilder &operator<<(StringBuilder &builder,
+                                 const PoolOptions &options) {
+  return options.write_to(builder);
+}
+
+class PoolHeader {
+ public:
+  explicit PoolHeader(const PoolOptions &options = PoolOptions());
+
+  const char *format_string() const {
+    return format_string_;
+  }
+  const char *version_string() const {
+    return format_string_;
+  }
+  const PoolOptions &options() const {
+    return options_;
+  }
+
+  uint64_t total_size() const {
+    return total_size_;
+  }
+  uint32_t num_blocks() const {
+    return num_blocks_;
+  };
+  uint32_t max_num_blocks() const {
+    return max_num_blocks_;
+  }
+  uint16_t next_block_chunk_id() const {
+    return next_block_chunk_id_;
+  }
+  uint32_t latest_phantom_block_id() const {
+    return latest_phantom_block_id_;
+  }
+  uint32_t latest_frozen_block_id() const {
+    return latest_frozen_block_id_;
+  }
+  uint32_t oldest_idle_block_ids(uint8_t list_id) const {
+    return oldest_idle_block_ids_[list_id];
+  }
+  const ChunkInfo &block_chunk_infos(uint16_t chunk_id) const {
+    return block_chunk_infos_[chunk_id];
+  }
+  const ChunkInfo &block_info_chunk_infos(uint16_t chunk_id) const {
+    return block_info_chunk_infos_[chunk_id];
+  }
+
+  void set_total_size(uint64_t value) {
+    total_size_ = value;
+  }
+  void set_num_blocks(uint32_t value) {
+    num_blocks_ = value;
+  };
+  void set_max_num_blocks(uint32_t value) {
+    max_num_blocks_ = value;
+  }
+  void set_next_block_chunk_id(uint16_t value) {
+    next_block_chunk_id_ = value;
+  }
+  void set_latest_phantom_block_id(uint32_t value) {
+    latest_phantom_block_id_ = value;
+  }
+  void set_latest_frozen_block_id(uint32_t value) {
+    latest_frozen_block_id_ = value;
+  }
+  void set_oldest_idle_block_ids(uint8_t list_id, uint32_t value) {
+    oldest_idle_block_ids_[list_id] = value;
+  }
+  void set_block_chunk_infos(const ChunkInfo &value) {
+    block_chunk_infos_[value.id()] = value;
+  }
+  void set_block_info_chunk_infos(const ChunkInfo & value) {
+    block_info_chunk_infos_[value.id()] = value;
+  }
+
+  Recycler *mutable_recycler() {
+    return &recycler_;
+  }
+  Mutex *mutable_inter_process_data_mutex() {
+    return &inter_process_data_mutex_;
+  }
+  Mutex *mutable_inter_process_file_mutex() {
+    return &inter_process_file_mutex_;
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+ private:
+  char format_string_[sizeof(POOL_HEADER_FORMAT_STRING)];
+  char version_string_[sizeof(POOL_HEADER_VERSION_STRING)];
+  PoolOptions options_;
+  uint64_t total_size_;
+  uint32_t num_blocks_;
+  uint32_t max_num_blocks_;
+  uint16_t next_block_chunk_id_;
+  uint32_t latest_phantom_block_id_;
+  uint32_t latest_frozen_block_id_;
+  uint32_t oldest_idle_block_ids_[32];
+  ChunkInfo block_chunk_infos_[POOL_MAX_NUM_BLOCK_CHUNKS];
+  ChunkInfo block_info_chunk_infos_[POOL_MAX_NUM_BLOCK_INFO_CHUNKS];
+  Recycler recycler_;
+  Mutex inter_process_data_mutex_;
+  Mutex inter_process_file_mutex_;
+};
+
+static_assert(sizeof(PoolHeader) <= POOL_HEADER_CHUNK_SIZE,
+              "sizeof(PoolHeader) > POOL_HEADER_CHUNK_SIZE");
+
+inline StringBuilder &operator<<(StringBuilder &builder,
+                                 const PoolHeader &header) {
+  return header.write_to(builder);
+}
+
+class PoolImpl;
+
+class Pool {
+ public:
+  Pool();
+  // Available flags are as follows:
+  //  GRNXX_IO_READ_ONLY, GRNXX_IO_ANONYMOUS, GRNXX_IO_CREATE,
+  //  GRNXX_IO_OPEN, GRNXX_IO_TEMPORARY.
+  Pool(const char *path, Flags flags = Flags(),
+       const PoolOptions &options = PoolOptions());
+  ~Pool();
+
+  Pool(const Pool &pool);
+  Pool &operator=(const Pool &pool);
+
+  Pool(Pool &&pool);
+  Pool &operator=(Pool &&pool);
+
+  GRNXX_EXPLICIT_CONVERSION operator bool() const {
+    return static_cast<bool>(impl_);
+  }
+
+  bool operator==(const Pool &rhs) const {
+    return impl_ == rhs.impl_;
+  }
+  bool operator!=(const Pool &rhs) const {
+    return impl_ != rhs.impl_;
+  }
+
+  const BlockInfo *create_block(uint64_t size);
+
+  const BlockInfo *get_block_info(uint32_t block_id);
+
+  void *get_block_address(uint32_t block_id);
+  void *get_block_address(const BlockInfo &block_info);
+
+  void free_block(uint32_t block_id);
+  void free_block(const BlockInfo &block_info);
+
+  String path() const;
+  Flags flags() const;
+  const PoolOptions &options() const;
+  const PoolHeader &header() const;
+  Recycler *mutable_recycler();
+
+  void swap(Pool &rhs);
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+  static bool exists(const char *path);
+  static void unlink(const char *path);
+  static bool unlink_if_exists(const char *path);
+
+ private:
+  std::shared_ptr<PoolImpl> impl_;
+};
+
+inline void swap(Pool &lhs, Pool &rhs) {
+  lhs.swap(rhs);
+}
+
+inline StringBuilder &operator<<(StringBuilder &builder, const Pool &pool) {
+  return pool.write_to(builder);
+}
+
+}  // namespace io
+}  // namespace grnxx
+
+#endif  // GRNXX_IO_POOL_HPP

  Added: lib/io/view-posix.cpp (+200 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/view-posix.cpp    2012-11-28 16:36:40 +0900 (f33aaf4)
@@ -0,0 +1,200 @@
+/*
+  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 "view-posix.hpp"
+
+#ifndef GRNXX_WINDOWS
+
+#include <sys/mman.h>
+#include <errno.h>
+
+#include "../error.hpp"
+#include "../exception.hpp"
+#include "../logger.hpp"
+
+#ifndef MAP_ANONYMOUS
+# ifdef MAP_ANON
+#  define MAP_ANONYMOUS MAP_ANON
+# endif  // MAP_ANON
+#endif  // MAP_ANONYMOUS
+
+namespace grnxx {
+namespace io {
+
+ViewImpl::~ViewImpl() {
+  if (address_ != MAP_FAILED) {
+    if (::munmap(address_, static_cast<size_t>(size_)) != 0) {
+      GRNXX_ERROR() << "failed to unmap view: view = " << *this
+                    << ": '::munmap' " << Error(errno);
+    }
+  }
+}
+
+std::unique_ptr<ViewImpl> ViewImpl::map(Flags flags, uint64_t size) {
+  std::unique_ptr<ViewImpl> view(new (std::nothrow) ViewImpl);
+  if (!view) {
+    GRNXX_ERROR() << "new grnxx::io::ViewImpl failed";
+    GRNXX_THROW();
+  }
+  view->map_on_memory(flags, size);
+  return view;
+}
+
+std::unique_ptr<ViewImpl> ViewImpl::map(const File &file, Flags flags) {
+  std::unique_ptr<ViewImpl> view(new (std::nothrow) ViewImpl);
+  if (!view) {
+    GRNXX_ERROR() << "new grnxx::io::ViewImpl failed";
+    GRNXX_THROW();
+  }
+  view->map_on_file(file, flags, 0, file.size());
+  return view;
+}
+
+std::unique_ptr<ViewImpl> ViewImpl::map(const File &file, Flags flags,
+                                        uint64_t offset, uint64_t size) {
+  std::unique_ptr<ViewImpl> view(new (std::nothrow) ViewImpl);
+  if (!view) {
+    GRNXX_ERROR() << "new grnxx::io::ViewImpl failed";
+    GRNXX_THROW();
+  }
+  view->map_on_file(file, flags, offset, size);
+  return view;
+}
+
+void ViewImpl::sync() {
+  sync(0, size_);
+}
+
+void ViewImpl::sync(uint64_t offset, uint64_t size) {
+  if ((offset > size_) || (size > size_) || ((offset + size) > size_)) {
+    GRNXX_ERROR() << "invalid arguments: view = " << *this
+                  << ", offset = " << offset << ", size = " << size;
+    GRNXX_THROW();
+  }
+
+  if (size != 0) {
+    if (::msync(static_cast<char *>(address_) + offset, size, MS_SYNC) != 0) {
+      GRNXX_ERROR() << "failed to sync memory mapping: view = " << *this
+                    << ", offset = " << offset << ", size = " << size
+                    << ": '::msync' " << Error(errno);
+      GRNXX_THROW();
+    }
+  }
+}
+
+ViewImpl::ViewImpl()
+  : file_(), flags_(), address_(MAP_FAILED), offset_(0), size_(0) {}
+
+void ViewImpl::map_on_memory(Flags flags, uint64_t size) {
+  if ((size == 0) || (size > std::numeric_limits<size_t>::max())) {
+    GRNXX_ERROR() << "invalid argument: size = " << size << ": (0, "
+                  << std::numeric_limits<size_t>::max() << ']';
+    GRNXX_THROW();
+  }
+
+  flags_ = GRNXX_IO_PRIVATE | GRNXX_IO_ANONYMOUS;
+  size_ = size;
+
+  int map_flags = MAP_PRIVATE | MAP_ANONYMOUS;
+#ifdef MAP_HUGETLB
+  if (flags & GRNXX_IO_HUGE_TLB) {
+    address_ = ::mmap(nullptr, size, PROT_READ | PROT_WRITE,
+                      map_flags | MAP_HUGETLB, -1, 0);
+    if (address_ != MAP_FAILED) {
+      flags_ |= GRNXX_IO_HUGE_TLB;
+    }
+  }
+#endif  // MAP_HUGETLB
+  if (address_ == MAP_FAILED) {
+    address_ = ::mmap(nullptr, size, PROT_READ | PROT_WRITE, map_flags, -1, 0);
+  }
+
+  if (address_ == MAP_FAILED) {
+    GRNXX_ERROR() << "failed to map anonymous view: size = " << size
+                  << ": '::mmap' " << Error(errno);
+    GRNXX_THROW();
+  }
+}
+
+void ViewImpl::map_on_file(const File &file, Flags flags, uint64_t offset,
+                           uint64_t size) {
+  if ((size == 0) || (size > std::numeric_limits<size_t>::max())) {
+    GRNXX_ERROR() << "invalid argument: size = " << size << ": (0, "
+                  << std::numeric_limits<size_t>::max() << ']';
+    GRNXX_THROW();
+  }
+  if (offset > static_cast<uint64_t>(std::numeric_limits<off_t>::max())) {
+    GRNXX_ERROR() << "invalid argument: offset = " << offset << ": [0, "
+                  << std::numeric_limits<off_t>::max() << ']';
+    GRNXX_THROW();
+  }
+
+  file_ = file;
+  offset_ = offset;
+  size_ = size;
+
+  int protection_flags = PROT_READ | PROT_WRITE;
+  if ((file.flags() & GRNXX_IO_READ_ONLY) ||
+      ((~file.flags() & GRNXX_IO_WRITE_ONLY) &&
+       (flags & GRNXX_IO_READ_ONLY))) {
+    flags_ |= GRNXX_IO_READ_ONLY;
+    protection_flags = PROT_READ;
+  } else if ((file.flags() & GRNXX_IO_WRITE_ONLY) ||
+             (flags & GRNXX_IO_WRITE_ONLY)) {
+    flags_ |= GRNXX_IO_WRITE_ONLY;
+    protection_flags = PROT_WRITE;
+  }
+
+  int map_flags;
+  if ((flags & GRNXX_IO_SHARED) || (~flags & GRNXX_IO_PRIVATE)) {
+    flags_ |= GRNXX_IO_SHARED;
+    map_flags = MAP_SHARED;
+  } else {
+    flags_ |= GRNXX_IO_PRIVATE;
+    map_flags = MAP_PRIVATE;
+  }
+
+  address_ = ::mmap(nullptr, size, protection_flags, map_flags,
+                    *static_cast<const int *>(file.handle()), offset);
+  if (address_ == MAP_FAILED) {
+    GRNXX_ERROR() << "failed to map view: file = " << file
+                  << ", flags = " << flags << ", offset = " << offset
+                  << ", size = " << size << ": '::mmap' " << Error(errno);
+    GRNXX_THROW();
+  }
+}
+
+StringBuilder &ViewImpl::write_to(StringBuilder &builder) const {
+  if (!builder) {
+    return builder;
+  }
+
+  if (file_) {
+    builder << "{ file = " << file_.path();
+  } else {
+    builder << "{ file = n/a";
+  }
+  return builder << ", flags = " << flags_
+                 << ", address = " << address_
+                 << ", offset = " << offset_
+                 << ", size = " << size_ << " }";
+}
+
+}  // namespace io
+}  // namespace grnxx
+
+#endif  // GRNXX_WINDOWS

  Added: lib/io/view-posix.hpp (+84 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/view-posix.hpp    2012-11-28 16:36:40 +0900 (eb5a80d)
@@ -0,0 +1,84 @@
+/*
+  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_IO_VIEW_POSIX_HPP
+#define GRNXX_IO_VIEW_POSIX_HPP
+
+#include "view.hpp"
+
+#ifndef GRNXX_WINDOWS
+
+namespace grnxx {
+namespace io {
+
+class ViewImpl {
+ public:
+  ~ViewImpl();
+
+  static std::unique_ptr<ViewImpl> map(Flags flags, uint64_t size);
+  static std::unique_ptr<ViewImpl> map(const File &file, Flags flags);
+  static std::unique_ptr<ViewImpl> map(const File &file, Flags flags,
+                                       uint64_t offset, uint64_t size);
+
+  void sync();
+  void sync(uint64_t offset, uint64_t size);
+
+  File file() const {
+    return file_;
+  }
+  Flags flags() const {
+    return flags_;
+  }
+  void *address() const {
+    return address_;
+  }
+  uint64_t offset() const {
+    return offset_;
+  }
+  uint64_t size() const {
+    return size_;
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+ private:
+  File file_;
+  Flags flags_;
+  void *address_;
+  uint64_t offset_;
+  uint64_t size_;
+
+  ViewImpl();
+
+  void map_on_memory(Flags flags, uint64_t size);
+  void map_on_file(const File &file, Flags flags, uint64_t offset,
+                   uint64_t size);
+
+  ViewImpl(const ViewImpl &);
+  ViewImpl &operator=(const ViewImpl &);
+};
+
+inline StringBuilder &operator<<(StringBuilder &builder, const ViewImpl &view) {
+  return view.write_to(builder);
+}
+
+}  // namespace io
+}  // namespace grnxx
+
+#endif  // GRNXX_WINDOWS
+
+#endif  // GRNXX_IO_VIEW_POSIX_HPP

  Added: lib/io/view-windows.cpp (+237 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/view-windows.cpp    2012-11-28 16:36:40 +0900 (d938e5b)
@@ -0,0 +1,237 @@
+/*
+  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 "view-windows.hpp"
+
+#ifdef GRNXX_WINDOWS
+
+#include "../error.hpp"
+#include "../exception.hpp"
+#include "../logger.hpp"
+
+namespace grnxx {
+namespace io {
+
+ViewImpl::~ViewImpl() {
+  if (address_) {
+    if (!::UnmapViewOfFile(address_)) {
+      GRNXX_ERROR() << "failed to unmap view: view = " << *this
+                    << ": '::UnmapViewOfFile' " << Error(::GetLastError());
+    }
+  }
+
+  if (handle_) {
+    if (!::CloseHandle(handle_)) {
+      GRNXX_ERROR() << "failed to close file mapping: view = " << *this
+                    << ": '::CloseHandle' " << Error(::GetLastError());
+    }
+  }
+}
+
+std::unique_ptr<ViewImpl> ViewImpl::map(Flags flags, uint64_t size) {
+  if (size == 0) {
+    GRNXX_ERROR() << "invalid argument: size = " << size;
+    GRNXX_THROW();
+  }
+
+  std::unique_ptr<ViewImpl> view(new (std::nothrow) ViewImpl);
+  if (!view) {
+    GRNXX_ERROR() << "new grnxx::io::ViewImpl failed";
+    GRNXX_THROW();
+  }
+  view->map_on_memory(flags, size);
+  return view;
+}
+
+std::unique_ptr<ViewImpl> ViewImpl::map(const File &file, Flags flags) {
+  std::unique_ptr<ViewImpl> view(new (std::nothrow) ViewImpl);
+  if (!view) {
+    GRNXX_ERROR() << "new grnxx::io::ViewImpl failed";
+    GRNXX_THROW();
+  }
+  view->map_on_file(file, flags, 0, 0);
+  return view;
+}
+
+std::unique_ptr<ViewImpl> ViewImpl::map(const File &file, Flags flags,
+                                        uint64_t offset, uint64_t size) {
+  if (size == 0) {
+    GRNXX_ERROR() << "invalid argument: size = " << size;
+    GRNXX_THROW();
+  }
+
+  std::unique_ptr<ViewImpl> view(new (std::nothrow) ViewImpl);
+  if (!view) {
+    GRNXX_ERROR() << "new grnxx::io::ViewImpl failed";
+    GRNXX_THROW();
+  }
+  view->map_on_file(file, flags, offset, size);
+  return view;
+}
+
+void ViewImpl::sync() {
+  if (!::FlushViewOfFile(address_, 0)) {
+    GRNXX_ERROR() << "failed to sync memory mapping: view = " << *this
+                  << ": '::FlushViewOfFile' " << Error(::GetLastError());
+    GRNXX_THROW();
+  }
+}
+
+void ViewImpl::sync(uint64_t offset, uint64_t size) {
+  if ((offset > size_) || (size > size_) || ((offset + size) > size_)) {
+    GRNXX_ERROR() << "invalid arguments: view = " << *this
+                  << ", offset = " << offset << ", size = " << size;
+    GRNXX_THROW();
+  }
+
+  if (size != 0) {
+    if (!::FlushViewOfFile(static_cast<char *>(address_) + offset, size)) {
+      GRNXX_ERROR() << "failed to sync memory mapping: view = " << *this
+                    << ", offset = " << offset << ", size = " << size
+                    << ": '::FlushViewOfFile' " << Error(::GetLastError());
+      GRNXX_THROW();
+    }
+  }
+}
+
+ViewImpl::ViewImpl()
+  : file_(), flags_(), handle_(nullptr),
+    address_(nullptr), offset_(0), size_(0) {}
+
+void ViewImpl::map_on_memory(Flags, uint64_t size) {
+  flags_ = GRNXX_IO_PRIVATE | GRNXX_IO_ANONYMOUS;
+  size_ = size;
+
+  const DWORD size_high = static_cast<DWORD>(size >> 32);
+  const DWORD size_low = static_cast<DWORD>(size & 0xFFFFFFFFU);
+  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 << ": '::CreateFileMapping' "
+                  << Error(::GetLastError());
+    GRNXX_THROW();
+  }
+
+  address_ = ::MapViewOfFile(handle_, FILE_MAP_WRITE, 0, 0, 0);
+  if (!address_) {
+    GRNXX_ERROR() << "failed to map anonymous view: size = " << size
+                  << ": '::MapViewOfFile' " << Error(::GetLastError());
+    GRNXX_THROW();
+  }
+}
+
+void ViewImpl::map_on_file(const File &file, Flags flags, uint64_t offset,
+                           uint64_t size) {
+  const uint64_t file_size = file.size();
+  if (file_size == 0) {
+    GRNXX_ERROR() << "invalid argument: file = " << file;
+    GRNXX_THROW();
+  }
+  if (flags & (GRNXX_IO_ANONYMOUS | GRNXX_IO_HUGE_TLB)) {
+    GRNXX_ERROR() << "invalid argument: flags = " << flags;
+    GRNXX_THROW();
+  }
+  if (size >= std::numeric_limits<SIZE_T>::max()) {
+    GRNXX_ERROR() << "invalid argument: size = " << size
+                  << ", max_size = " << std::numeric_limits<SIZE_T>::max();
+    GRNXX_THROW();
+  }
+  if ((size > file_size) || (offset > (file_size - size))) {
+    GRNXX_ERROR() << "invalid argument: size = " << size
+                  << ", offset = " << offset << ", file_size = " << file_size;
+    GRNXX_THROW();
+  }
+
+  file_ = file;
+  offset_ = offset;
+  size_ = (size != 0) ? size : file_size;
+
+  int protection_mode = PAGE_READWRITE;
+  DWORD desired_access = FILE_MAP_WRITE;
+  if ((file.flags() & GRNXX_IO_READ_ONLY) ||
+      ((~file.flags() & GRNXX_IO_WRITE_ONLY) &&
+       (flags & GRNXX_IO_READ_ONLY))) {
+    flags_ |= GRNXX_IO_READ_ONLY;
+    protection_mode = PAGE_READONLY;
+    desired_access = FILE_MAP_READ;
+  } else if (file.flags() & GRNXX_IO_WRITE_ONLY) {
+    // Write-only memory mapping is not supported on Windows.
+    GRNXX_ERROR() << "mapping file is write-only: file = " << file;
+    GRNXX_THROW();
+  } else {
+    // GRNXX_IO_WRITE_ONLY is ignored because write-only memory mapping is not
+    // supported on Windows.
+    protection_mode = PAGE_READWRITE;
+    if (flags & GRNXX_IO_PRIVATE) {
+      protection_mode = PAGE_WRITECOPY;
+      desired_access = FILE_MAP_COPY;
+    }
+  }
+
+  if ((flags & GRNXX_IO_SHARED) || (~flags & GRNXX_IO_PRIVATE)) {
+    flags_ |= GRNXX_IO_SHARED;
+  } else {
+    flags_ |= GRNXX_IO_PRIVATE;
+  }
+
+  const DWORD size_high = static_cast<DWORD>((offset + size) >> 32);
+  const DWORD size_low = static_cast<DWORD>((offset + size) & 0xFFFFFFFFU);
+  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 = " << file
+                  << ", flags = " << flags << ", offset = " << offset
+                  << ", size = " << size
+                  << ": '::CreateFileMapping' " << Error(::GetLastError());
+    GRNXX_THROW();
+  }
+
+  const DWORD offset_high = static_cast<DWORD>(offset >> 32);
+  const DWORD offset_low = static_cast<DWORD>(offset & 0xFFFFFFFFU);
+  address_ = ::MapViewOfFile(handle_, desired_access, offset_high, offset_low,
+                             static_cast<SIZE_T>(size));
+  if (!address_) {
+    GRNXX_ERROR() << "failed to map view: file = " << file
+                  << ", flags = " << flags << ", offset = " << offset
+                  << ", size = " << size
+                  << ": '::MapViewOfFile' " << Error(::GetLastError());
+    GRNXX_THROW();
+  }
+}
+
+StringBuilder &ViewImpl::write_to(StringBuilder &builder) const {
+  if (!builder) {
+    return builder;
+  }
+
+  if (file_) {
+    builder << "{ file = " << file_.path();
+  } else {
+    builder << "{ file = n/a";
+  }
+  return builder << ", flags = " << flags_
+                 << ", address = " << address_
+                 << ", offset = " << offset_
+                 << ", size = " << size_ << " }";
+}
+
+}  // namespace io
+}  // namespace grnxx
+
+#endif  // GRNXX_WINDOWS

  Added: lib/io/view-windows.hpp (+87 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/view-windows.hpp    2012-11-28 16:36:40 +0900 (929efe2)
@@ -0,0 +1,87 @@
+/*
+  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_IO_VIEW_WINDOWS_HPP
+#define GRNXX_IO_VIEW_WINDOWS_HPP
+
+#include "view.hpp"
+
+#ifdef GRNXX_WINDOWS
+
+#include <windows.h>
+
+namespace grnxx {
+namespace io {
+
+class ViewImpl {
+ public:
+  ~ViewImpl();
+
+  static std::unique_ptr<ViewImpl> map(Flags flags, uint64_t size);
+  static std::unique_ptr<ViewImpl> map(const File &file, Flags flags);
+  static std::unique_ptr<ViewImpl> map(const File &file, Flags flags,
+                                       uint64_t offset, uint64_t size);
+
+  void sync();
+  void sync(uint64_t offset, uint64_t size);
+
+  File file() const {
+    return file_;
+  }
+  Flags flags() const {
+    return flags_;
+  }
+  void *address() const {
+    return address_;
+  }
+  uint64_t offset() const {
+    return offset_;
+  }
+  uint64_t size() const {
+    return size_;
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+ private:
+  File file_;
+  Flags flags_;
+  HANDLE handle_;
+  void *address_;
+  uint64_t offset_;
+  uint64_t size_;
+
+  ViewImpl();
+
+  void map_on_memory(Flags flags, uint64_t size);
+  void map_on_file(const File &file, Flags flags, uint64_t offset,
+                   uint64_t size);
+
+  ViewImpl(const ViewImpl &);
+  ViewImpl &operator=(const ViewImpl &);
+};
+
+inline StringBuilder &operator<<(StringBuilder &builder, const ViewImpl &view) {
+  return view.write_to(builder);
+}
+
+}  // namespace io
+}  // namespace grnxx
+
+#endif  // GRNXX_WINDOWS
+
+#endif  // GRNXX_IO_VIEW_WINDOWS_HPP

  Added: lib/io/view.cpp (+97 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/view.cpp    2012-11-28 16:36:40 +0900 (bbcede3)
@@ -0,0 +1,97 @@
+/*
+  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 "view.hpp"
+#include "view-posix.hpp"
+#include "view-windows.hpp"
+
+#include "../exception.hpp"
+#include "../logger.hpp"
+
+namespace grnxx {
+namespace io {
+
+View::View() : impl_() {}
+
+View::View(Flags flags, uint64_t size)
+  : impl_(ViewImpl::map(flags, size)) {}
+
+View::View(const File &file, Flags flags)
+  : impl_(ViewImpl::map(file, flags)) {}
+
+View::View(const File &file, Flags flags, uint64_t offset, uint64_t size)
+  : impl_(ViewImpl::map(file, flags, offset, size)) {}
+
+View::~View() {}
+
+View::View(const View &view) : impl_(view.impl_) {}
+
+View &View::operator=(const View &view) {
+  impl_ = view.impl_;
+  return *this;
+}
+
+View::View(View &&view)
+  : impl_(std::move(view.impl_)) {}
+
+View &View::operator=(View &&view) {
+  impl_ = std::move(view.impl_);
+  return *this;
+}
+
+void View::sync() {
+  if (impl_) {
+    impl_->sync();
+  }
+}
+
+void View::sync(uint64_t offset, uint64_t size) {
+  if (impl_) {
+    impl_->sync(offset, size);
+  }
+}
+
+File View::file() const {
+  return impl_ ? impl_->file() : File();
+}
+
+Flags View::flags() const {
+  return impl_ ? impl_->flags() : Flags();
+}
+
+void *View::address() const {
+  return impl_ ? impl_->address() : nullptr;
+}
+
+uint64_t View::offset() const {
+  return impl_ ? impl_->offset() : 0;
+}
+
+uint64_t View::size() const {
+  return impl_ ? impl_->size() : 0;
+}
+
+void View::swap(View &view) {
+  impl_.swap(view.impl_);
+}
+
+StringBuilder &View::write_to(StringBuilder &builder) const {
+  return impl_ ? impl_->write_to(builder) : (builder << "n/a");
+}
+
+}  // namespace io
+}  // namespace grnxx

  Added: lib/io/view.hpp (+80 -0) 100644
===================================================================
--- /dev/null
+++ lib/io/view.hpp    2012-11-28 16:36:40 +0900 (87bc37d)
@@ -0,0 +1,80 @@
+/*
+  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_IO_VIEW_HPP
+#define GRNXX_IO_VIEW_HPP
+
+#include "file.hpp"
+
+namespace grnxx {
+namespace io {
+
+class ViewImpl;
+
+class View {
+ public:
+  View();
+  // Create an anonymou memory mapping.
+  // Available flags are GRNXX_IO_HUGE_TLB only.
+  explicit View(Flags flags, uint64_t size);
+  // Create a file-backed memory mapping.
+  // Available flags are as follows:
+  //  GRNXX_IO_READ_ONLY, GRNXX_IO_WRITE_ONLY, GRNXX_IO_SHARED,
+  //  GRNXX_IO_PRIVATE.
+  View(const File &file, Flags flags);
+  View(const File &file, Flags flags, uint64_t offset, uint64_t size);
+  ~View();
+
+  View(const View &view);
+  View &operator=(const View &view);
+
+  View(View &&view);
+  View &operator=(View &&view);
+
+  GRNXX_EXPLICIT_CONVERSION operator bool() const {
+    return static_cast<bool>(impl_);
+  }
+
+  void sync();
+  void sync(uint64_t offset, uint64_t size);
+
+  File file() const;
+  Flags flags() const;
+  void *address() const;
+  uint64_t offset() const;
+  uint64_t size() const;
+
+  void swap(View &view);
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+ private:
+  std::shared_ptr<ViewImpl> impl_;
+};
+
+inline void swap(View &lhs, View &rhs) {
+  lhs.swap(rhs);
+}
+
+inline StringBuilder &operator<<(StringBuilder &builder, const View &view) {
+  return view.write_to(builder);
+}
+
+}  // namespace io
+}  // namespace grnxx
+
+#endif  // GRNXX_IO_VIEW_HPP

  Added: lib/lock.hpp (+52 -0) 100644
===================================================================
--- /dev/null
+++ lib/lock.hpp    2012-11-28 16:36:40 +0900 (f3c1e88)
@@ -0,0 +1,52 @@
+/*
+  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_LOCK_HPP
+#define GRNXX_LOCK_HPP
+
+#include "basic.hpp"
+#include "mutex.hpp"
+
+namespace grnxx {
+
+class Lock {
+ public:
+  explicit Lock(Mutex *mutex)
+    : mutex_object_(mutex->lock() ?
+                    reinterpret_cast<Mutex::Object *>(mutex) : nullptr) {}
+  explicit Lock(Mutex::Object *mutex_object)
+    : mutex_object_(Mutex::lock(mutex_object) ? mutex_object : nullptr) {}
+  ~Lock() {
+    if (mutex_object_) {
+      Mutex::unlock(mutex_object_);
+    }
+  }
+
+  GRNXX_EXPLICIT_CONVERSION operator bool() const {
+    return mutex_object_ != nullptr;
+  }
+
+ private:
+  Mutex::Object *mutex_object_;
+
+  Lock(const Lock &);
+  Lock &operator=(const Lock &);
+};
+
+}  // namespace grnxx
+
+#endif  // GRNXX_LOCK_HPP

  Added: lib/logger.cpp (+225 -0) 100644
===================================================================
--- /dev/null
+++ lib/logger.cpp    2012-11-28 16:36:40 +0900 (dcc225a)
@@ -0,0 +1,225 @@
+/*
+  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 "logger.hpp"
+
+#include <ctime>
+#include <fstream>
+#include <iostream>
+
+#include "backtrace.hpp"
+#include "lock.hpp"
+#include "time.hpp"
+
+namespace grnxx {
+
+class LoggerSingleton {
+ public:
+  static bool write(const StringBuilder &builder) {
+    initialize_once();
+    Lock lock(&mutex_);
+    if (!instance_) {
+      return false;
+    }
+    static const LoggerFlags OUTPUT_FLAGS =
+        LOGGER_ENABLE_COUT | LOGGER_ENABLE_CERR | LOGGER_ENABLE_CLOG;
+    const LoggerFlags flags = Logger::flags();
+    if (!(flags & OUTPUT_FLAGS)) {
+      if (!instance_->file_ || !*instance_->file_) {
+        return false;
+      }
+    }
+
+    if (instance_->file_ && *instance_->file_) {
+      instance_->file_->write(builder.c_str(), builder.length()) << std::endl;
+    }
+    if (flags & LOGGER_ENABLE_COUT) {
+      std::cout.write(builder.c_str(), builder.length()) << '\n';
+    }
+    if (flags & LOGGER_ENABLE_CERR) {
+      std::cerr.write(builder.c_str(), builder.length()) << '\n';
+    }
+    if (flags & LOGGER_ENABLE_CLOG) {
+      std::clog.write(builder.c_str(), builder.length()) << '\n';
+    }
+    return true;
+  }
+
+  static bool open(const char *path) {
+    if (!path) {
+      return false;
+    }
+    initialize_once();
+    Lock lock(&mutex_);
+    if (!instance_) {
+      return false;
+    }
+    try {
+      String path_dummy(path);
+      std::unique_ptr<std::ofstream> file_dummy(
+          new (std::nothrow) std::ofstream(path_dummy.c_str(),
+              std::ios::out | std::ios::app | std::ios::binary));
+      if (!file_dummy || !*file_dummy) {
+        return false;
+      }
+      instance_->path_.swap(path_dummy);
+      instance_->file_.swap(file_dummy);
+      return true;
+    } catch (...) {
+      return false;
+    }
+  }
+  static void close() {
+    initialize_once();
+    Lock lock(&mutex_);
+    if (instance_) {
+      instance_->file_.reset();
+      instance_->path_ = String();
+    }
+  }
+
+ private:
+  String path_;
+  std::unique_ptr<std::ofstream> file_;
+
+  // These variables may be used even after the instance termination.
+  static volatile bool initialized_;
+  static LoggerSingleton * volatile instance_;
+  static Mutex::Object mutex_;
+
+  LoggerSingleton() : path_(), file_() {}
+  ~LoggerSingleton() {
+    Lock lock(&mutex_);
+    instance_ = nullptr;
+  }
+
+  static void initialize_once() {
+    if (!initialized_) {
+      // C++11 guarantees that a static local variable is initialized once.
+      // However, some compilers don't provide the guarantee.
+      static Mutex::Object mutex = Mutex::UNLOCKED;
+      Lock lock(&mutex);
+      if (!initialized_) {
+        initialize();
+      }
+    }
+  }
+  static void initialize() {
+    static LoggerSingleton instance;
+    instance_ = &instance;
+    initialized_ = true;
+  }
+
+  LoggerSingleton(const LoggerSingleton &);
+  LoggerSingleton &operator=(const LoggerSingleton &);
+};
+
+volatile bool LoggerSingleton::initialized_ = false;
+LoggerSingleton * volatile LoggerSingleton::instance_ = nullptr;
+Mutex::Object LoggerSingleton::mutex_ = Mutex::UNLOCKED;
+
+LoggerFlags Logger::flags_ = LoggerFlags();
+int Logger::max_level_ = NOTICE_LOGGER;
+int Logger::backtrace_level_ = ERROR_LOGGER;
+
+Logger::Logger(const char *file, int line, const char *func, int level)
+  : buf_(),
+    builder_(buf_, (Logger::flags() & LOGGER_ENABLE_AUTO_RESIZE) ?
+                   STRING_BUILDER_AUTO_RESIZE : StringBuilderFlags()),
+    file_(file),
+    line_(line),
+    func_(func),
+    level_(level) {
+  append_line_header();
+}
+
+Logger::~Logger() {
+  if (level_ <= backtrace_level()) {
+    std::vector<std::string> backtrace;
+    if (grnxx::Backtrace::pretty_backtrace(1, &backtrace)) {
+      builder_ << new_line() << backtrace[0].c_str();
+      for (size_t i = 1; i < backtrace.size(); ++i) {
+        builder_ << ", " << backtrace[i].c_str();
+      }
+    }
+  }
+  LoggerSingleton::write(builder_);
+}
+
+bool Logger::open(const char *path) {
+  return LoggerSingleton::open(path);
+}
+
+void Logger::close() {
+  return LoggerSingleton::close();
+}
+
+void Logger::append_line_header() {
+  if (!builder_) {
+    return;
+  }
+
+  const LoggerFlags flags = Logger::flags();
+  if (flags & LOGGER_WITH_DATE_TIME) {
+    builder_ << Time::now() << ": ";
+  }
+  if (flags & LOGGER_WITH_LOCATION) {
+    builder_ << file_ << ':' << line_ << ": In " << func_ << "(): ";
+  }
+  if (flags & LOGGER_WITH_LEVEL) {
+    switch (level_) {
+      case ERROR_LOGGER: {
+        builder_ << "error: ";
+        break;
+      }
+      case WARNING_LOGGER: {
+        builder_ << "warning: ";
+        break;
+      }
+      case NOTICE_LOGGER: {
+        builder_ << "notice: ";
+        break;
+      }
+      default: {
+        builder_ << "unknown (" << level_ << "): ";
+        break;
+      }
+    }
+  }
+}
+
+StringBuilder &operator<<(StringBuilder &builder, const Logger::NewLine &) {
+  builder << '\n';
+
+  Logger * const logger = reinterpret_cast<Logger *>(
+      (reinterpret_cast<char *>(&builder) - LOGGER_BUF_SIZE));
+  logger->append_line_header();
+  return builder;
+}
+
+StringBuilder &operator<<(StringBuilder &builder, const Logger::Backtrace &) {
+  std::vector<std::string> backtrace;
+  if (Backtrace::pretty_backtrace(1, &backtrace)) {
+    builder << backtrace[0].c_str();
+    for (size_t i = 1; i < backtrace.size(); ++i) {
+      builder << ", " << backtrace[i].c_str();
+    }
+  }
+  return builder;
+}
+
+}  // namespace grnxx

  Added: lib/logger.hpp (+129 -0) 100644
===================================================================
--- /dev/null
+++ lib/logger.hpp    2012-11-28 16:36:40 +0900 (7fc9191)
@@ -0,0 +1,129 @@
+/*
+  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_LOGGER_HPP
+#define GRNXX_LOGGER_HPP
+
+#include "basic.hpp"
+#include "string_builder.hpp"
+
+#define GRNXX_ERROR()   GRNXX_LOGGER(::grnxx::ERROR_LOGGER)
+#define GRNXX_WARNING() GRNXX_LOGGER(::grnxx::WARNING_LOGGER)
+#define GRNXX_NOTICE()  GRNXX_LOGGER(::grnxx::NOTICE_LOGGER)
+
+// TODO: This level check should be inline.
+#define GRNXX_LOGGER(level)\
+  ((level) > ::grnxx::Logger::max_level()) ? (void)0 :\
+  ::grnxx::Logger::Voidify() &\
+  ::grnxx::Logger(__FILE__, __LINE__, __func__, (level)).builder()
+
+namespace grnxx {
+
+const size_t LOGGER_BUF_SIZE = 4096;
+
+enum LoggerLevel {
+  ERROR_LOGGER   = 0x0000,
+  WARNING_LOGGER = 0x0001,
+  NOTICE_LOGGER  = 0x0002
+};
+
+class LoggerFlagsIdentifier {};
+typedef FlagsImpl<LoggerFlagsIdentifier> LoggerFlags;
+
+const LoggerFlags LOGGER_WITH_DATE_TIME = LoggerFlags::define(0x0001);
+const LoggerFlags LOGGER_WITH_LOCATION  = LoggerFlags::define(0x0002);
+const LoggerFlags LOGGER_WITH_LEVEL     = LoggerFlags::define(0x0004);
+const LoggerFlags LOGGER_WITH_ALL       = LoggerFlags::define(0x0007);
+
+const LoggerFlags LOGGER_ENABLE_COUT = LoggerFlags::define(0x0100);
+const LoggerFlags LOGGER_ENABLE_CERR = LoggerFlags::define(0x0200);
+const LoggerFlags LOGGER_ENABLE_CLOG = LoggerFlags::define(0x0400);
+
+const LoggerFlags LOGGER_ENABLE_AUTO_RESIZE = LoggerFlags::define(0x1000);
+
+class Logger {
+ public:
+  Logger(const char *file, int line, const char *func, int level);
+  ~Logger();
+
+  static bool open(const char *path);
+  static void close();
+
+  static LoggerFlags flags() {
+    return flags_;
+  }
+  static int max_level() {
+    return max_level_;
+  }
+  static int backtrace_level() {
+    return backtrace_level_;
+  }
+
+  static void set_flags(LoggerFlags value) {
+    flags_ = value;
+  }
+  static void set_max_level(int value) {
+    max_level_ = value;
+  }
+  static void set_backtrace_level(int value) {
+    backtrace_level_ = value;
+  }
+
+  struct NewLine {};
+  struct Backtrace {};
+
+  static NewLine new_line() {
+    return NewLine();
+  }
+  static Backtrace backtrace() {
+    return Backtrace();
+  }
+
+  class Voidify {
+   public:
+    void operator&(StringBuilder &) const {}
+  };
+
+  StringBuilder &builder() {
+    return builder_;
+  }
+
+  void append_line_header();
+
+ private:
+  char buf_[LOGGER_BUF_SIZE];
+  StringBuilder builder_;
+  const char * const file_;
+  const int line_;
+  const char * const func_;
+  const int level_;
+
+  static LoggerFlags flags_;
+  static int max_level_;
+  static int backtrace_level_;
+
+  Logger(const Logger &);
+  Logger &operator=(const Logger &);
+};
+
+// These operators must not be called with a regular (non-logger) builder.
+StringBuilder &operator<<(StringBuilder &builder, const Logger::NewLine &);
+StringBuilder &operator<<(StringBuilder &builder, const Logger::Backtrace &);
+
+}  // namespace grnxx
+
+#endif  // GRNXX_LOGGER_HPP

  Added: lib/mutex.cpp (+36 -0) 100644
===================================================================
--- /dev/null
+++ lib/mutex.cpp    2012-11-28 16:36:40 +0900 (61032d3)
@@ -0,0 +1,36 @@
+/*
+  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 "mutex.hpp"
+
+namespace grnxx {
+
+StringBuilder &Mutex::write_to(StringBuilder &builder) const {
+  switch (object_) {
+    case Mutex::UNLOCKED: {
+      return builder << "unlocked";
+    }
+    case Mutex::LOCKED: {
+      return builder << "locked";
+    }
+    default: {
+      return builder << "{ value = " << object_ << " (undefined) }";
+    }
+  }
+}
+
+}  // namespace grnxx

  Added: lib/mutex.hpp (+120 -0) 100644
===================================================================
--- /dev/null
+++ lib/mutex.hpp    2012-11-28 16:36:40 +0900 (c9ade45)
@@ -0,0 +1,120 @@
+/*
+  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_MUTEX_HPP
+#define GRNXX_MUTEX_HPP
+
+#include "basic.hpp"
+#include "intrinsic.hpp"
+#include "string_builder.hpp"
+#include "thread.hpp"
+
+namespace grnxx {
+
+const int      MUTEX_THREAD_SWITCH_COUNT_DEFAULT = 1000;
+const int      MUTEX_SLEEP_COUNT_DEFAULT         = 6000;
+const Duration MUTEX_SLEEP_DURATION_DEFAULT      = Duration::milliseconds(10);
+
+class Mutex {
+ public:
+  typedef uint32_t Value;
+  typedef volatile Value Object;
+
+  static const Value UNLOCKED = 0;
+  static const Value LOCKED = 1;
+
+  Mutex() : object_(UNLOCKED) {}
+
+  Mutex(Mutex &&mutex) : object_(std::move(mutex.object_)) {}
+  Mutex &operator=(Mutex &&mutex) {
+    object_ = std::move(mutex.object_);
+    return *this;
+  }
+
+  bool lock(int thread_switch_count = MUTEX_THREAD_SWITCH_COUNT_DEFAULT,
+            int sleep_count = MUTEX_SLEEP_COUNT_DEFAULT,
+            Duration sleep_duration = MUTEX_SLEEP_DURATION_DEFAULT) {
+    return lock(&object_, thread_switch_count, sleep_count, sleep_duration);
+  }
+  bool try_lock() {
+    return try_lock(&object_);
+  }
+  bool unlock() {
+    return unlock(&object_);
+  }
+
+  Value value() const {
+    return object_;
+  }
+
+  static bool lock(Object *object,
+                   int thread_switch_count = MUTEX_THREAD_SWITCH_COUNT_DEFAULT,
+                   int sleep_count = MUTEX_SLEEP_COUNT_DEFAULT,
+                   Duration sleep_duration = MUTEX_SLEEP_DURATION_DEFAULT) {
+    for (int i = 0; i < thread_switch_count; ++i) {
+      if (try_lock(object)) {
+        return true;
+      }
+      Thread::switch_to_others();
+    }
+    for (int i = 0; i < sleep_count; ++i) {
+      if (try_lock(object)) {
+        return true;
+      }
+      Thread::sleep(sleep_duration);
+    }
+    return false;
+  }
+  static bool try_lock(Object *object) {
+    return atomic_compare_and_swap(UNLOCKED, LOCKED, object);
+  }
+  static bool unlock(Object *object) {
+    if (*object == UNLOCKED) {
+      return false;
+    }
+    *object = UNLOCKED;
+    return true;
+  }
+
+  void clear() {
+    object_ = UNLOCKED;
+  }
+  void swap(Mutex &mutex) {
+    using std::swap;
+    swap(object_, mutex.object_);
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+ private:
+  Object object_;
+
+  Mutex(const Mutex &);
+  Mutex &operator=(const Mutex &);
+};
+
+inline void swap(Mutex &lhs, Mutex &rhs) {
+  lhs.swap(rhs);
+}
+
+inline StringBuilder &operator<<(StringBuilder &builder, const Mutex &mutex) {
+  return mutex.write_to(builder);
+}
+
+}  // namespace grnxx
+
+#endif  // GRNXX_MUTEX_HPP

  Added: lib/os.cpp (+64 -0) 100644
===================================================================
--- /dev/null
+++ lib/os.cpp    2012-11-28 16:36:40 +0900 (c949ba3)
@@ -0,0 +1,64 @@
+/*
+  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 "os.hpp"
+
+#include <cstdlib>
+#include <cerrno>
+
+#include "error.hpp"
+#include "exception.hpp"
+#include "lock.hpp"
+#include "logger.hpp"
+
+namespace grnxx {
+
+uint64_t OS::get_page_size() {
+#if defined(GRNXX_WINDOWS) || !defined(_SC_PAGESIZE)
+  static const uint64_t page_size = 4096;
+#else  // defined(GRNXX_WINDOWS) || !defined(_SC_PAGESIZE)
+  static const uint64_t page_size = ::sysconf(_SC_PAGESIZE);
+#endif  // defined(GRNXX_WINDOWS) || !defined(_SC_PAGESIZE)
+  return page_size;
+}
+
+String OS::get_environment_variable(const char *name) {
+  if (!name) {
+    GRNXX_ERROR() << "invalid argument: name = " << name;
+    GRNXX_THROW();
+  }
+
+  static Mutex mutex;
+  Lock lock(&mutex);
+
+#ifdef GRNXX_MSC
+  char *value;
+  size_t value_size;
+  if (::_dupenv_s(&value, &value_size, name) != 0) {
+    GRNXX_ERROR() << "failed to get environment variable: name = " << name
+                  << "'::_dupenv_s' " << Error(errno);
+    GRNXX_THROW();
+  }
+  String result(value, value_size);
+  std::free(value);
+  return result;
+#else  // GRNXX_MSC
+  return std::getenv(name);
+#endif  // GRNXX_MSC
+}
+
+}  // namespace grnxx

  Added: lib/os.hpp (+41 -0) 100644
===================================================================
--- /dev/null
+++ lib/os.hpp    2012-11-28 16:36:40 +0900 (7ba4ea2)
@@ -0,0 +1,41 @@
+/*
+  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_OS_HPP
+#define GRNXX_OS_HPP
+
+#include "basic.hpp"
+#include "string.hpp"
+
+namespace grnxx {
+
+class OS {
+ public:
+  static uint64_t get_page_size();
+
+  static String get_environment_variable(const char *name);
+
+ private:
+  OS();
+
+  OS(const OS &);
+  OS &operator=(const OS &);
+};
+
+}  // namespace grnxx
+
+#endif  // GRNXX_OS_HPP

  Added: lib/recycler.cpp (+106 -0) 100644
===================================================================
--- /dev/null
+++ lib/recycler.cpp    2012-11-28 16:36:40 +0900 (f496686)
@@ -0,0 +1,106 @@
+/*
+  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 "recycler.hpp"
+
+
+namespace grnxx {
+
+void Recycler::update() {
+  // First, zero-clear the internal counter so that other threads and processes
+  // will not come into this function. However, this is not a perfect barrier.
+  count_ = 0;
+
+  // Time::now() takes around 1 microsecond on Core2 Duo 1.6GHz. So, if
+  // RECYCLER_STAMP_COUNT_PER_UPDATE == 500, stamp() spends 2ns/call for
+  // Time::now() on average.
+  const Time now = Time::now();
+
+  StampPair current_stamp_pair = stamp_pair_;
+
+  // Update stamps iff enough time has passed after the latest update.
+  const uint16_t current_time_id =
+      current_stamp_pair.current & RECYCLER_STAMP_MASK;
+  const Duration step_duration =
+      frozen_duration_ / (RECYCLER_STAMP_BUF_SIZE / 2);
+  if (now > (times_[current_time_id] + step_duration)) {
+    // Use a compare-and-swap (CAS) to avoid a collision.
+    StampPair next_stamp_pair;
+    next_stamp_pair.current = current_stamp_pair.current + 1;
+    next_stamp_pair.threshold = current_stamp_pair.threshold;
+    if (!atomic_compare_and_swap(current_stamp_pair, next_stamp_pair,
+                                 &stamp_pair_)) {
+      return;
+    }
+
+    // There exists a moment when stamps_ is updated but times_ is not updated
+    // yet. So, times_ must be initialized with a future time.
+    times_[next_stamp_pair.current & RECYCLER_STAMP_MASK] = now;
+
+    // Update stamp_pair_.threshold for check().
+    const Time threshold_time = now - frozen_duration_;
+    for (current_stamp_pair = next_stamp_pair;
+         current_stamp_pair.threshold < current_stamp_pair.current;
+         current_stamp_pair = next_stamp_pair) {
+
+      // Use the time associated with the next stamp.
+      const uint16_t threshold_time_id =
+          ++next_stamp_pair.threshold & RECYCLER_STAMP_MASK;
+      if (threshold_time < times_[threshold_time_id]) {
+        break;
+      }
+
+      // Use a compare-and-swap (CAS) to avoid a collision.
+      if (!atomic_compare_and_swap(current_stamp_pair, next_stamp_pair,
+                                   &stamp_pair_)) {
+        break;
+      }
+
+      // Initialize times_ with a future time.
+      times_[current_stamp_pair.threshold & RECYCLER_STAMP_MASK] =
+          RECYCLER_FUTURE_TIME;
+    }
+  }
+}
+
+StringBuilder &Recycler::write_to(StringBuilder &builder) const {
+  if (!builder) {
+    return builder;
+  }
+
+  const StampPair stamp_pair = stamp_pair_;
+
+  builder << "{ count = " << count_
+          << ", current = " << stamp_pair.current
+          << ", threshold = " << stamp_pair.threshold
+          << ", frozen_duration = " << frozen_duration_;
+
+  builder << ", times = { ";
+  for (uint16_t stamp = stamp_pair.threshold;
+       stamp <= stamp_pair.current; ++stamp) {
+    const uint16_t time_id = stamp & RECYCLER_STAMP_MASK;
+    if (stamp != stamp_pair.threshold) {
+      builder << ", ";
+    }
+    builder << '[' << stamp << "] = " << times_[time_id];
+  }
+  builder << " }";
+
+  return builder << " }";
+}
+
+}  // namespace grnxx

  Added: lib/recycler.hpp (+100 -0) 100644
===================================================================
--- /dev/null
+++ lib/recycler.hpp    2012-11-28 16:36:40 +0900 (60312ec)
@@ -0,0 +1,100 @@
+/*
+  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_RECYCLER_HPP
+#define GRNXX_RECYCLER_HPP
+
+#include "time.hpp"
+
+namespace grnxx {
+
+const uint8_t  RECYCLER_STAMP_BUF_SIZE_BITS = 6;
+const uint16_t RECYCLER_STAMP_BUF_SIZE      =
+    uint16_t(1 << RECYCLER_STAMP_BUF_SIZE_BITS);
+const uint16_t RECYCLER_STAMP_MASK          = RECYCLER_STAMP_BUF_SIZE - 1;
+
+const uint32_t RECYCLER_STAMP_COUNT_PER_UPDATE = 512;
+
+const Time RECYCLER_FUTURE_TIME = Time(std::numeric_limits<int64_t>::max());
+
+class Recycler {
+ public:
+  Recycler() : count_(), stamp_pair_(), frozen_duration_(), times_() {}
+  explicit Recycler(Duration frozen_duration)
+    : count_(0), stamp_pair_(), frozen_duration_(frozen_duration), times_() {
+    stamp_pair_.current = 0;
+    stamp_pair_.threshold = 0;
+
+    times_[0] = Time(0);
+    for (uint16_t i = 1; i < RECYCLER_STAMP_BUF_SIZE; ++i) {
+      times_[i] = Time(RECYCLER_FUTURE_TIME);
+    }
+  }
+
+  std::uint16_t stamp() {
+    // Update stamp_pair_ and times_, once per RECYCLER_STAMP_COUNT_PER_UPDATE.
+    // Note that count_ is zero-cleared in update(), but in a multi-threaded
+    // case, the zero-clear might be ignored by ++count_.
+    //  temp = count_ + 1;
+    //  count_ = 0;  // Zero-cleared in update().
+    //  count_ = temp;
+    if (++count_ >= RECYCLER_STAMP_COUNT_PER_UPDATE) {
+      update();
+    }
+    return stamp_pair_.current;
+  }
+
+  bool check(uint16_t stamp) {
+    // In a multi-threaded case, stamp_pair_ might be updated during check().
+    // When a stamp causes an over-flow, the update may result in a critical
+    // problem. So, this function uses a copy of stamp_pair_.
+    StampPair stamp_pair = stamp_pair_;
+    stamp_pair.current = this->stamp();
+    stamp_pair.threshold = stamp_pair_.threshold;
+    if (stamp_pair.current < stamp_pair.threshold) {
+      return (stamp > stamp_pair.current) && (stamp < stamp_pair.threshold);
+    } else {
+      return (stamp > stamp_pair.current) || (stamp < stamp_pair.threshold);
+    }
+  }
+
+  Duration frozen_duration() const {
+    return frozen_duration_;
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+ private:
+  uint32_t count_;
+  struct StampPair {
+    uint16_t current;
+    uint16_t threshold;
+  } stamp_pair_;
+  Duration frozen_duration_;
+  Time times_[RECYCLER_STAMP_BUF_SIZE];
+
+  void update();
+};
+
+inline StringBuilder &operator<<(StringBuilder &builder,
+                                 const Recycler &recycler) {
+  return recycler.write_to(builder);
+}
+
+}  // namespace grnxx
+
+#endif  // GRNXX_RECYCLER_HPP

  Added: lib/string.cpp (+81 -0) 100644
===================================================================
--- /dev/null
+++ lib/string.cpp    2012-11-28 16:36:40 +0900 (d9a7af1)
@@ -0,0 +1,81 @@
+/*
+  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 "string.hpp"
+
+#include <ostream>
+
+#include "exception.hpp"
+#include "logger.hpp"
+
+namespace grnxx {
+
+StringImpl *StringImpl::create(const char *ptr, size_t length) {
+  const size_t size = buf_offset() + length + 1;
+  StringImpl * const new_impl =
+      reinterpret_cast<StringImpl *>(new (std::nothrow) char[size]);
+  if (!new_impl) {
+    GRNXX_ERROR() << "memory allocation failed: size = " << size;
+    GRNXX_THROW();
+  }
+  new_impl->length_ = length;
+  new_impl->reference_count_ = 1;
+  std::memcpy(new_impl->buf_, ptr, length);
+  new_impl->buf_[length] = '\0';
+  return new_impl;
+}
+
+String::String(const char *str) : impl_(StringImpl::default_instance()) {
+  if (str) {
+    const size_t length = std::strlen(str);
+    if (length != 0) {
+      impl_ = StringImpl::create(str, length);
+    }
+  }
+}
+
+String::String(const char *ptr, size_t length)
+  : impl_(StringImpl::default_instance()) {
+  if (ptr) {
+    if (length != 0) {
+      impl_ = StringImpl::create(ptr, length);
+    }
+  } else if (length != 0) {
+    GRNXX_ERROR() << "invalid argument: ptr = " << ptr
+                  << ", length = " << length;
+    GRNXX_THROW();
+  }
+}
+
+String &String::operator=(const char *str) {
+  StringImpl *new_impl = StringImpl::default_instance();
+  if (str) {
+    const size_t length = std::strlen(str);
+    if (length != 0) {
+      new_impl = StringImpl::create(str, length);
+    }
+  }
+  impl_->decrement_reference_count();
+  impl_ = new_impl;
+  return *this;
+}
+
+std::ostream &operator<<(std::ostream &stream, const String &str) {
+  return stream.write(str.c_str(), str.length());
+}
+
+}  // namespace grnxx

  Added: lib/string.hpp (+218 -0) 100644
===================================================================
--- /dev/null
+++ lib/string.hpp    2012-11-28 16:36:40 +0900 (f09f14b)
@@ -0,0 +1,218 @@
+/*
+  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_STRING_HPP
+#define GRNXX_STRING_HPP
+
+#include "basic.hpp"
+#include "intrinsic.hpp"
+
+namespace grnxx {
+
+class StringImpl {
+ public:
+  static StringImpl *create(const char *ptr, size_t length);
+
+  // An instance is freed when its reference count becomes 0.
+  void increment_reference_count() {
+    if (this != default_instance()) {
+      atomic_fetch_and_add(1, &reference_count_);
+    }
+  }
+  void decrement_reference_count() {
+    // The default instance must not be deleted.
+    if (this != default_instance()) {
+      if (atomic_fetch_and_add(-1, &reference_count_) == 1) {
+        delete[] reinterpret_cast<char *>(this);
+      }
+    }
+  }
+
+  const char &operator[](size_t i) const {
+    return buf_[i];
+  }
+  size_t length() const {
+    return length_;
+  }
+  const char *c_str() const {
+    return buf_;
+  }
+
+  // A pointer to a persistent instance (an empty string) is returned.
+  static StringImpl *default_instance() {
+    static StringImpl empty_string;
+    return &empty_string;
+  }
+
+ private:
+  size_t length_;
+  volatile uint32_t reference_count_;
+  char buf_[1];
+
+  StringImpl() : length_(0), reference_count_(1), buf_() {
+    buf_[0] = '\0';
+  }
+
+  static size_t buf_offset() {
+    return static_cast<StringImpl *>(nullptr)->buf_
+        - static_cast<char *>(nullptr);
+  }
+};
+
+class String {
+ public:
+  String() : impl_(StringImpl::default_instance()) {}
+  String(const char *str);
+  String(const char *ptr, size_t length);
+  ~String() {
+    impl_->decrement_reference_count();
+  }
+
+  String(const String &str) : impl_(str.impl_) {
+    impl_->increment_reference_count();
+  }
+  String &operator=(const String &str) {
+    str.impl_->increment_reference_count();
+    impl_->decrement_reference_count();
+    impl_ = str.impl_;
+    return *this;
+  }
+
+  // Note: a moved instance must not be used.
+  String(String &&str) : impl_(str.impl_) {
+    str.impl_ = StringImpl::default_instance();
+  }
+  String &operator=(String &&str) {
+    impl_->decrement_reference_count();
+    impl_ = str.impl_;
+    str.impl_ = StringImpl::default_instance();
+    return *this;
+  }
+
+  String &operator=(const char *str);
+
+  GRNXX_EXPLICIT_CONVERSION operator bool() const {
+    return impl_->length() != 0;
+  }
+
+  bool contains(int byte) const {
+    for (size_t i = 0; i < length(); ++i) {
+      if ((*impl_)[i] == static_cast<char>(byte)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool starts_with(const char *str) const {
+    for (size_t i = 0; str[i] != '\0'; ++i) {
+      if ((i >= length()) || (str[i] != (*impl_)[i])) {
+        return false;
+      }
+    }
+    return true;
+  }
+  bool ends_with(const char *str) const {
+    size_t str_length = 0;
+    while (str[str_length] != '\0') {
+      if (str_length >= length()) {
+        return false;
+      }
+      ++str_length;
+    }
+    const char *impl_ptr = c_str() + length() - str_length;
+    for (size_t i = 0; i < str_length; ++i) {
+      if (str[i] != impl_ptr[i]) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  const char &operator[](size_t i) const {
+    return (*impl_)[i];
+  }
+  const char *c_str() const {
+    return impl_->c_str();
+  }
+  size_t length() const {
+    return impl_->length();
+  }
+
+  void swap(String &rhs) {
+    using std::swap;
+    swap(impl_, rhs.impl_);
+  }
+
+ private:
+  StringImpl *impl_;
+
+  // Copyable.
+};
+
+inline bool operator==(const String &lhs, const String &rhs) {
+  if (lhs.c_str() == rhs.c_str()) {
+    return true;
+  }
+  if (lhs.length() != rhs.length()) {
+    return false;
+  }
+  for (size_t i = 0; i < lhs.length(); ++i) {
+    if (lhs[i] != rhs[i]) {
+      return false;
+    }
+  }
+  return true;
+}
+
+inline bool operator==(const String &lhs, const char *rhs) {
+  if (!rhs) {
+    return false;
+  }
+  for (size_t i = 0; i < lhs.length(); ++i, ++rhs) {
+    if ((lhs[i] != *rhs) || (*rhs == '\0')) {
+      return false;
+    }
+  }
+  return *rhs == '\0';
+}
+
+inline bool operator==(const char *lhs, String &rhs) {
+  return rhs == lhs;
+}
+
+inline bool operator!=(const String &lhs, const String &rhs) {
+  return !(lhs == rhs);
+}
+
+inline bool operator!=(const String &lhs, const char *rhs) {
+  return !(lhs == rhs);
+}
+
+inline bool operator!=(const char *lhs, const String &rhs) {
+  return !(lhs == rhs);
+}
+
+inline void swap(String &lhs, String &rhs) {
+  lhs.swap(rhs);
+}
+
+std::ostream &operator<<(std::ostream &stream, const String &str);
+
+}  // namespace grnxx
+
+#endif  // GRNXX_STRING_HPP

  Added: lib/string_builder.cpp (+179 -0) 100644
===================================================================
--- /dev/null
+++ lib/string_builder.cpp    2012-11-28 16:36:40 +0900 (b6fdbe3)
@@ -0,0 +1,179 @@
+/*
+  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 "string_builder.hpp"
+
+#include <cmath>
+#include <ostream>
+
+namespace grnxx {
+
+StringBuilder::StringBuilder(size_t size, StringBuilderFlags flags)
+  : buf_((size != 0) ? (new (std::nothrow) char[size]) : nullptr),
+    begin_(buf_.get()),
+    end_(buf_ ? (begin_ + size - 1) : nullptr),
+    ptr_(begin_),
+    flags_(flags),
+    failed_(!buf_) {
+  if (buf_) {
+    *ptr_ = '\0';
+  }
+}
+
+bool StringBuilder::resize_buf(size_t size) {
+  if (size < STRING_BUILDER_BUF_SIZE_MIN) {
+    size = STRING_BUILDER_BUF_SIZE_MIN;
+  } else {
+    size = size_t(1) << (bit_scan_reverse(size - 1) + 1);
+  }
+
+  std::unique_ptr<char[]> new_buf(new (std::nothrow) char[size]);
+  if (!new_buf) {
+    return false;
+  }
+
+  const size_t length = ptr_ - begin_;
+  std::memcpy(new_buf.get(), begin_, length);
+  ptr_ = new_buf.get() + length;
+  begin_ = new_buf.get();
+  end_ = new_buf.get() + size - 1;
+  buf_ = std::move(new_buf);
+  return true;
+}
+
+StringBuilder &operator<<(StringBuilder &builder, long long value) {
+  if (!builder) {
+    return builder;
+  }
+
+  char buf[32];
+  char *ptr = buf;
+  char *left = ptr;
+  if (value >= 0) {
+    do {
+      *ptr++ = static_cast<char>('0' + (value % 10));
+      value /= 10;
+    } while (value != 0);
+  } else {
+    *ptr++ = '-';
+    ++left;
+
+    do {
+      // C++11 always rounds the result toward 0.
+      *ptr++ = static_cast<char>('0' - (value % 10));
+      value /= 10;
+    } while (value != 0);
+  }
+
+  char *right = ptr - 1;
+  while (left < right) {
+    using std::swap;
+    swap(*left++, *right--);
+  }
+
+  return builder.append(buf, ptr - buf);
+}
+StringBuilder &operator<<(StringBuilder &builder, unsigned long long value) {
+  if (!builder) {
+    return builder;
+  }
+
+  char buf[32];
+  char *ptr = buf;
+  do {
+    *ptr++ = static_cast<char>('0' + (value % 10));
+    value /= 10;
+  } while (value != 0);
+
+  char *left = buf;
+  char *right = ptr - 1;
+  while (left < right) {
+    using std::swap;
+    swap(*left++, *right--);
+  }
+
+  return builder.append(buf, ptr - buf);
+}
+
+StringBuilder &operator<<(StringBuilder &builder, float value) {
+  return builder << static_cast<double>(value);
+}
+
+StringBuilder &operator<<(StringBuilder &builder, double value) {
+  if (!builder) {
+    return builder;
+  }
+
+  switch (std::fpclassify(value)) {
+    case FP_NORMAL:
+    case FP_SUBNORMAL:
+    case FP_ZERO: {
+      break;
+    }
+    case FP_INFINITE: {
+      if (value > 0) {
+        return builder.append("inf", 3);
+      } else {
+        return builder.append("-inf", 4);
+      }
+    }
+    case FP_NAN:
+    default: {
+      return builder.append("nan", 3);
+    }
+  }
+
+  // The maximum value of double-precision floating point number (IEEE754)
+  // is 1.797693134862316E+308.
+  char buf[512];
+  int length = std::snprintf(buf, sizeof(buf), "%f", value);
+  if (length < 0) {
+    return builder.append("n/a", 3);
+  }
+  if (static_cast<size_t>(length) >= sizeof(buf)) {
+    length = sizeof(buf) - 1;
+  }
+  return builder.append(buf, length);
+}
+
+StringBuilder &operator<<(StringBuilder &builder, const void *value) {
+  if (!builder) {
+    return builder;
+  }
+  if (!value) {
+    return builder.append("nullptr", 7);
+  }
+
+  char buf[(sizeof(value) * 2) + 2];
+  buf[0] = '0';
+  buf[1] = 'x';
+
+  uintptr_t address = reinterpret_cast<uintptr_t>(value);
+  for (size_t i = 2; i < sizeof(buf); ++i) {
+    const uintptr_t digit = address >> ((sizeof(value) * 8) - 4);
+    buf[i] = static_cast<char>(
+        (digit < 10) ? ('0' + digit) : ('A' + digit - 10));
+    address <<= 4;
+  }
+  return builder.append(buf, sizeof(buf));
+}
+
+std::ostream &operator<<(std::ostream &stream, const StringBuilder &builder) {
+  return stream.write(builder.c_str(), builder.length());
+}
+
+}  // namespace grnxx

  Added: lib/string_builder.hpp (+297 -0) 100644
===================================================================
--- /dev/null
+++ lib/string_builder.hpp    2012-11-28 16:36:40 +0900 (8d7a6e8)
@@ -0,0 +1,297 @@
+/*
+  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_STRING_BUILDER_HPP
+#define GRNXX_STRING_BUILDER_HPP
+
+#include "basic.hpp"
+#include "flags_impl.hpp"
+#include "string.hpp"
+
+namespace grnxx {
+
+const size_t STRING_BUILDER_BUF_SIZE_MIN = 64;
+
+class StringBuilderFlagsIdentifier {};
+typedef FlagsImpl<StringBuilderFlagsIdentifier> StringBuilderFlags;
+
+const StringBuilderFlags STRING_BUILDER_AUTO_RESIZE =
+    StringBuilderFlags::define(0x01);
+
+class StringBuilder {
+ public:
+  explicit StringBuilder(StringBuilderFlags flags = StringBuilderFlags())
+    : buf_(),
+      begin_(nullptr),
+      end_(nullptr),
+      ptr_(nullptr),
+      flags_(flags),
+      failed_(false) {}
+
+  explicit StringBuilder(size_t size,
+                         StringBuilderFlags flags = StringBuilderFlags());
+
+  template <size_t T>
+  explicit StringBuilder(char (&buf)[T],
+                         StringBuilderFlags flags = StringBuilderFlags())
+    : buf_(),
+      begin_(buf),
+      end_(buf + T - 1),
+      ptr_(buf),
+      flags_(flags),
+      failed_(false) {
+    *ptr_ = '\0';
+  }
+
+  StringBuilder(char *buf, size_t size,
+                StringBuilderFlags flags = StringBuilderFlags())
+    : buf_(),
+      begin_(buf),
+      end_(buf + size - 1),
+      ptr_(buf),
+      flags_(flags),
+      failed_(false) {
+    *ptr_ = '\0';
+  }
+
+  ~StringBuilder() {}
+
+  StringBuilder(StringBuilder &&rhs)
+    : buf_(std::move(rhs.buf_)),
+      begin_(std::move(rhs.begin_)),
+      end_(std::move(rhs.end_)),
+      ptr_(std::move(rhs.ptr_)),
+      flags_(std::move(rhs.flags_)),
+      failed_(std::move(rhs.failed_)) {}
+
+  StringBuilder &operator=(StringBuilder &&rhs) {
+    buf_ = std::move(rhs.buf_);
+    begin_ = std::move(rhs.begin_);
+    end_ = std::move(rhs.end_);
+    ptr_ = std::move(rhs.ptr_);
+    flags_ = std::move(rhs.flags_);
+    failed_ = std::move(rhs.failed_);
+    return *this;
+  }
+
+  GRNXX_EXPLICIT_CONVERSION operator bool() const {
+    return !failed_;
+  }
+
+  // For one-liners.
+  StringBuilder &builder() {
+    return *this;
+  }
+
+  // Append a character.
+  StringBuilder &append(int byte) {
+    if (failed_) {
+      return *this;
+    }
+
+    if (ptr_ == end_) {
+      if ((~flags_ & STRING_BUILDER_AUTO_RESIZE) ||
+          !resize_buf(this->length() + 2)) {
+        failed_ = true;
+        return *this;
+      }
+    }
+
+    *ptr_ = static_cast<char>(byte);
+    *++ptr_ = '\0';
+    return *this;
+  }
+
+  // Append a sequence of the same character.
+  StringBuilder &append(int byte, size_t length) {
+    if (failed_ || (length == 0)) {
+      return *this;
+    }
+
+    const size_t size_left = end_ - ptr_;
+    if (length > size_left) {
+      if ((~flags_ & STRING_BUILDER_AUTO_RESIZE) ||
+          !resize_buf(this->length() + length + 1)) {
+        length = size_left;
+        failed_ = true;
+      }
+    }
+
+    std::memset(ptr_, byte, length);
+    ptr_ += length;
+    *ptr_ = '\0';
+    return *this;
+  }
+
+  // Append a sequence of length characters.
+  StringBuilder &append(const char *ptr, size_t length) {
+    if (failed_ || !ptr || (length == 0)) {
+      return *this;
+    }
+
+    const size_t size_left = end_ - ptr_;
+    if (length > size_left) {
+      if ((~flags_ & STRING_BUILDER_AUTO_RESIZE) ||
+          !resize_buf(this->length() + length + 1)) {
+        length = size_left;
+        failed_ = true;
+      }
+    }
+
+    std::memcpy(ptr_, ptr, length);
+    ptr_ += length;
+    *ptr_ = '\0';
+    return *this;
+  }
+
+  StringBuilder &resize(size_t length) {
+    const size_t size_left = end_ - ptr_;
+    if (length > size_left) {
+      if ((~flags_ & STRING_BUILDER_AUTO_RESIZE) ||
+          !resize_buf(length + 1)) {
+        length = size_left;
+        failed_ = true;
+      }
+    }
+    ptr_ = begin_ + length;
+    *ptr_ = '\0';
+    return *this;
+  }
+
+  String str() const {
+    return String(begin_, length());
+  }
+
+  const char &operator[](size_t i) const {
+    return begin_[i];
+  }
+  char &operator[](size_t i) {
+    return begin_[i];
+  }
+
+  const char *c_str() const {
+    return begin_ ? begin_ : "";
+  }
+  size_t length() const {
+    return ptr_ - begin_;
+  }
+
+  void swap(StringBuilder &rhs) {
+    using std::swap;
+    swap(buf_, rhs.buf_);
+    swap(begin_, rhs.begin_);
+    swap(end_, rhs.end_);
+    swap(ptr_, rhs.ptr_);
+    swap(flags_, rhs.flags_);
+    swap(failed_, rhs.failed_);
+  }
+
+ private:
+  std::unique_ptr<char[]> buf_;
+  char *begin_;
+  char *end_;
+  char *ptr_;
+  StringBuilderFlags flags_;
+  bool failed_;
+
+  // Resize buf_ to greater than or equal to size bytes.
+  bool resize_buf(size_t size);
+
+  StringBuilder(const StringBuilder &);
+  StringBuilder &operator=(const StringBuilder &);
+};
+
+inline void swap(StringBuilder &lhs, StringBuilder &rhs) {
+  lhs.swap(rhs);
+}
+
+// Characters.
+inline StringBuilder &operator<<(StringBuilder &builder, char value) {
+  return builder.append(value);
+}
+
+// Signed integers.
+StringBuilder &operator<<(StringBuilder &builder, long long value);
+
+inline StringBuilder &operator<<(StringBuilder &builder, signed char value) {
+  return builder << static_cast<long long>(value);
+}
+inline StringBuilder &operator<<(StringBuilder &builder, short value) {
+  return builder << static_cast<long long>(value);
+}
+inline StringBuilder &operator<<(StringBuilder &builder, int value) {
+  return builder << static_cast<long long>(value);
+}
+inline StringBuilder &operator<<(StringBuilder &builder, long value) {
+  return builder << static_cast<long long>(value);
+}
+
+// Unsigned integers.
+StringBuilder &operator<<(StringBuilder &builder, unsigned long long value);
+
+inline StringBuilder &operator<<(StringBuilder &builder, unsigned char value) {
+  return builder << static_cast<unsigned long long>(value);
+}
+inline StringBuilder &operator<<(StringBuilder &builder, unsigned short value) {
+  return builder << static_cast<unsigned long long>(value);
+}
+inline StringBuilder &operator<<(StringBuilder &builder, unsigned int value) {
+  return builder << static_cast<unsigned long long>(value);
+}
+inline StringBuilder &operator<<(StringBuilder &builder, unsigned long value) {
+  return builder << static_cast<unsigned long long>(value);
+}
+
+// Floating point numbers.
+StringBuilder &operator<<(StringBuilder &builder, float value);
+StringBuilder &operator<<(StringBuilder &builder, double value);
+
+// Boolean values (true/false).
+inline StringBuilder &operator<<(StringBuilder &builder, bool value) {
+  return value ? builder.append("true", 4) : builder.append("false", 5);
+}
+
+// Pointers.
+StringBuilder &operator<<(StringBuilder &builder, const void *value);
+
+// Zero-terminated strings.
+inline StringBuilder &operator<<(StringBuilder &builder, const char *value) {
+  if (!builder) {
+    return builder;
+  }
+  if (!value) {
+    return builder.append("nullptr", 7);
+  }
+  return builder.append(value, std::strlen(value));
+}
+
+// Strings.
+inline StringBuilder &operator<<(StringBuilder &builder, const String &value) {
+  return builder.append(value.c_str(), value.length());
+}
+
+// Exceptions.
+inline StringBuilder &operator<<(StringBuilder &builder,
+                                 const std::exception &exception) {
+  return builder << "{ what = " << exception.what() << " }";
+}
+
+std::ostream &operator<<(std::ostream &stream, const StringBuilder &builder);
+
+}  // namespace grnxx
+
+#endif  // GRNXX_STRING_BUILDER_HPP

  Added: lib/string_format.hpp (+131 -0) 100644
===================================================================
--- /dev/null
+++ lib/string_format.hpp    2012-11-28 16:36:40 +0900 (22d4efe)
@@ -0,0 +1,131 @@
+/*
+  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_STRING_FORMAT_HPP
+#define GRNXX_STRING_FORMAT_HPP
+
+#include "basic.hpp"
+#include "string_builder.hpp"
+
+namespace grnxx {
+
+enum StringFormatAlignmentAttribute {
+  STRING_FORMAT_ALIGNMENT_LEFT,
+  STRING_FORMAT_ALIGNMENT_RIGHT,
+  STRING_FORMAT_ALIGNMENT_CENTER
+};
+
+template <typename T>
+class StringFormatAlignment {
+ public:
+  StringFormatAlignment(const T &value, size_t width, int pad,
+                        StringFormatAlignmentAttribute attribute)
+    : value_(value), width_(width), pad_(pad), attribute_(attribute) {}
+
+  const T &value() const {
+    return value_;
+  }
+  size_t width() const {
+    return width_;
+  }
+  int pad() const {
+    return pad_;
+  }
+  StringFormatAlignmentAttribute attribute() const {
+    return attribute_;
+  }
+
+ private:
+  const T &value_;
+  const size_t width_;
+  const int pad_;
+  const StringFormatAlignmentAttribute attribute_;
+};
+
+class StringFormat {
+ public:
+  template <typename T>
+  static StringFormatAlignment<T> align_left(
+      const T &value, size_t width, int pad = ' ') {
+    return align<T>(value, width, pad, STRING_FORMAT_ALIGNMENT_LEFT);
+  }
+  template <typename T>
+  static StringFormatAlignment<T> align_right(
+      const T &value, size_t width, int pad = ' ') {
+    return align<T>(value, width, pad, STRING_FORMAT_ALIGNMENT_RIGHT);
+  }
+  template <typename T>
+  static StringFormatAlignment<T> align_center(
+      const T &value, size_t width, int pad = ' ') {
+    return align<T>(value, width, pad, STRING_FORMAT_ALIGNMENT_CENTER);
+  }
+
+  template <typename T>
+  static StringFormatAlignment<T> align(
+      const T &value, size_t width, int pad = ' ',
+      StringFormatAlignmentAttribute attribute = STRING_FORMAT_ALIGNMENT_LEFT) {
+    return StringFormatAlignment<T>(value, width, pad, attribute);
+  }
+
+ private:
+  StringFormat();
+  ~StringFormat();
+
+  StringFormat(const StringFormat &);
+  StringFormat &operator=(const StringFormat &);
+};
+
+template <typename T>
+StringBuilder &operator<<(StringBuilder &builder,
+                          const StringFormatAlignment<T> &alignment) {
+  char local_buf[STRING_BUILDER_BUF_SIZE_MIN];
+  const StringBuilderFlags local_flags =
+      (alignment.width() >= sizeof(local_buf)) ?
+          STRING_BUILDER_AUTO_RESIZE : StringBuilderFlags();
+
+  StringBuilder local_builder(local_buf, local_flags);
+  local_builder << alignment.value();
+  if (local_builder.length() >= alignment.width()) {
+    return builder.append(local_builder.c_str(), alignment.width());
+  }
+
+  const size_t unused_size = alignment.width() - local_builder.length();
+  switch (alignment.attribute()) {
+    case STRING_FORMAT_ALIGNMENT_LEFT: {
+      builder.append(local_builder.c_str(), local_builder.length());
+      builder.append(alignment.pad(), unused_size);
+      break;
+    }
+    case STRING_FORMAT_ALIGNMENT_RIGHT: {
+      builder.append(alignment.pad(), unused_size);
+      builder.append(local_builder.c_str(), local_builder.length());
+      break;
+    }
+    case STRING_FORMAT_ALIGNMENT_CENTER: {
+      const size_t offset = unused_size / 2;
+      builder.append(alignment.pad(), offset);
+      builder.append(local_builder.c_str(), local_builder.length());
+      builder.append(alignment.pad(), unused_size - offset);
+      break;
+    }
+  }
+  return builder;
+}
+
+}  // namespace grnxx
+
+#endif  // GRNXX_STRING_FORMAT_HPP

  Added: lib/thread.cpp (+85 -0) 100644
===================================================================
--- /dev/null
+++ lib/thread.cpp    2012-11-28 16:36:40 +0900 (b1a5412)
@@ -0,0 +1,85 @@
+/*
+  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 "thread.hpp"
+
+#ifdef GRNXX_WINDOWS
+# include <windows.h>
+#endif  // GRNXX_WINDOWS
+
+#ifdef GRNXX_HAS_SCHED_YIELD
+# include <sched.h>
+#endif  // GRNXX_HAS_SCHED_YIELD
+
+#ifdef GRNXX_HAS_NANOSLEEP
+# include <time.h>
+#endif  // GRNXX_HAS_NANOSLEEP
+
+namespace grnxx {
+
+bool Thread::switch_to_others() {
+#ifdef GRNXX_WINDOWS
+  return ::SwitchToThread() != 0;
+#elif defined(GRNXX_HAS_SCHED_YIELD)
+  return ::sched_yield() == 0;
+#else  // defined(GRNXX_HAS_SCHED_YIELD)
+  sleep(Duration(0));
+#endif  // defined(GRNXX_HAS_SCHED_YIELD)
+}
+
+void Thread::sleep(Duration duration) {
+#ifdef GRNXX_WINDOWS
+  if (duration.nanoseconds() < 0) {
+    ::Sleep(0);
+  } else {
+    const int64_t milliseconds = duration.nanoseconds() / 1000000;
+    if (milliseconds <
+        static_cast<int64_t>(std::numeric_limits<DWORD>::max())) {
+      ::Sleep(static_cast<DWORD>(milliseconds));
+    } else {
+      ::Sleep(std::numeric_limits<DWORD>::max());
+    }
+  }
+#elif defined(GRNXX_HAS_NANOSLEEP)
+  struct timespec request;
+  if (duration.nanoseconds() < 0) {
+    request.tv_sec = 0;
+    request.tv_nsec = 0;
+  } else {
+    const int64_t seconds = duration.nanoseconds() / 1000000000;
+    if (seconds < std::numeric_limits<time_t>::max()) {
+      request.tv_sec = static_cast<time_t>(seconds);
+    } else {
+      request.tv_sec = std::numeric_limits<time_t>::max();
+    }
+    duration %= Duration::seconds(1);
+    request.tv_nsec = static_cast<long>(duration.nanoseconds());
+  }
+  // Note that ::nanosleep() requires -lrt option.
+  ::nanosleep(&request, nullptr);
+#else  // defined(GRNXX_HAS_NANOSLEEP)
+  // Note that POSIX.1-2008 removes the specification of ::usleep().
+  const int64_t microseconds = duration.nanoseconds() / 1000;
+  if (microseconds < std::numeric_limits<useconds_t>::max()) {
+    ::usleep(static_cast<useconds_t>(microseconds));
+  } else {
+    ::usleep(std::numeric_limits<useconds_t>::max());
+  }
+#endif  // defined(GRNXX_HAS_NANOSLEEP)
+}
+
+}  // namespace grnxx

  Added: lib/thread.hpp (+38 -0) 100644
===================================================================
--- /dev/null
+++ lib/thread.hpp    2012-11-28 16:36:40 +0900 (b3a74ba)
@@ -0,0 +1,38 @@
+/*
+  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_THREAD_HPP
+#define GRNXX_THREAD_HPP
+
+#include "basic.hpp"
+#include "duration.hpp"
+
+namespace grnxx {
+
+class Thread {
+ public:
+  static bool switch_to_others();
+  static void sleep(Duration duration);
+
+ private:
+  Thread(const Thread &);
+  Thread &operator=(const Thread &);
+};
+
+}  // namespace grnxx
+
+#endif  // GRNXX_THREAD_HPP

  Added: lib/time.cpp (+122 -0) 100644
===================================================================
--- /dev/null
+++ lib/time.cpp    2012-11-28 16:36:40 +0900 (9b52c14)
@@ -0,0 +1,122 @@
+/*
+  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 "time.hpp"
+
+#ifdef GRNXX_WINDOWS
+# include <sys/types.h>
+# include <sys/timeb.h>
+#endif  // GRNXX_WINDOWS
+
+#ifndef GRNXX_HAS_CLOCK_GETTIME
+# include <sys/time.h>
+#endif  // GRNXX_HAS_CLOCK_GETTIME
+
+#include <cerrno>
+#include <ctime>
+#include <ostream>
+
+#include "error.hpp"
+#include "intrinsic.hpp"
+#include "logger.hpp"
+#include "string_format.hpp"
+
+namespace grnxx {
+
+Time Time::now() {
+#ifdef GRNXX_WINDOWS
+  struct _timeb current_time;
+# ifdef GRNXX_MSC
+  const errno_t error_code = ::_ftime_s(&current_time);
+  if (error_code != 0) {
+    // In practice, ::_ftime_s() must not fail.
+    return now_in_seconds();
+  }
+# else  // GRNXX_MSC
+  ::_ftime(&current_time);
+# endif  // GRNXX_MSC
+  return Time((current_time.time * int64_t(1000000000)) +
+              (current_time.millitm * 1000000));
+#else  // GRNXX_WINDOWS
+# if defined(GRNXX_HAS_CLOCK_GETTIME)
+  struct timespec current_time;
+  if (::clock_gettime(CLOCK_REALTIME, &current_time) != 0) {
+    // In practice, ::clock_gettime() must not fail.
+    return now_in_seconds();
+  }
+  return Time((current_time.tv_sec * int64_t(1000000000)) +
+              current_time.tv_nsec);
+# else  // defined(GRNXX_HAS_CLOCK_GETTIME)
+  // Note: POSIX.1-2008 marks gettimeofday() as obsolete.
+  struct timeval current_time;
+  if (::gettimeofday(&current_time, nullptr) != 0) {
+    // In practice, ::gettimeofday() must not fail.
+    return now_in_seconds();
+  }
+  return Time((current_time.tv_sec * int64_t(1000000000)) +
+              (current_time.tv_usec * 1000));
+# endif  // defined(GRNXX_HAS_CLOCK_GETTIME)
+#endif  // GRNXX_WINDOWS
+}
+
+Time Time::now_in_seconds() {
+  return Time(static_cast<int64_t>(std::time(nullptr)) * 1000000000);
+}
+
+StringBuilder &Time::write_to(StringBuilder &builder) const {
+  if (!builder) {
+    return builder;
+  }
+
+  const std::time_t posix_time =
+      static_cast<std::time_t>(nanoseconds_ / 1000000000);
+  struct tm broken_down_time;
+#ifdef GRNXX_MSC
+  if (::localtime_s(&posix_time, &broken_down_time) != 0) {
+    return builder << "0000-00-00 00:00:00.000000";
+  }
+#elif defined(GRNXX_HAS_LOCALTIME_R)
+  if (::localtime_r(&posix_time, &broken_down_time) == nullptr) {
+    return builder << "0000-00-00 00:00:00.000000";
+  }
+#else  // defined(GRNXX_HAS_LOCALTIME_R)
+  // TODO: should be thread-safe.
+  struct tm * const global_broken_down_time = ::localtime(&posix_time);
+  if (global_broken_down_time == nullptr) {
+    return builder << "0000-00-00 00:00:00.000000";
+  }
+  broken_down_time = *global_broken_down_time;
+#endif  // defined(GRNXX_HAS_LOCALTIME_R)
+
+  builder << (1900 + broken_down_time.tm_year) << '-'
+          << StringFormat::align_right(broken_down_time.tm_mon + 1, 2, '0') << '-'
+          << StringFormat::align_right(broken_down_time.tm_mday, 2, '0') << ' '
+          << StringFormat::align_right(broken_down_time.tm_hour, 2, '0') << ':'
+          << StringFormat::align_right(broken_down_time.tm_min, 2, '0') << ':'
+          << StringFormat::align_right(broken_down_time.tm_sec, 2, '0') << '.'
+          << StringFormat::align_right(nanoseconds_ / 1000 % 1000000, 6, '0');
+  return builder;
+}
+
+std::ostream &operator<<(std::ostream &stream, Time time) {
+  char buf[32];
+  StringBuilder builder(buf);
+  builder << time;
+  return stream.write(builder.c_str(), builder.length());
+}
+
+}  // namespace grnxx

  Added: lib/time.hpp (+109 -0) 100644
===================================================================
--- /dev/null
+++ lib/time.hpp    2012-11-28 16:36:40 +0900 (e2840ec)
@@ -0,0 +1,109 @@
+/*
+  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_TIME_HPP
+#define GRNXX_TIME_HPP
+
+#include "basic.hpp"
+#include "duration.hpp"
+
+namespace grnxx {
+
+class Time {
+ public:
+  Time() : nanoseconds_(INVALID_NANOSECONDS) {}
+  explicit Time(int64_t nanoseconds) : nanoseconds_(nanoseconds) {}
+
+  static Time now();
+  static Time now_in_seconds();
+
+  static Time invalid_time() {
+    return Time();
+  }
+
+  GRNXX_EXPLICIT_CONVERSION operator bool() const {
+    return nanoseconds_ != INVALID_NANOSECONDS;
+  }
+
+  int64_t nanoseconds() const {
+    return nanoseconds_;
+  }
+  void set_nanoseconds(int64_t nanoseconds) {
+    nanoseconds_ = nanoseconds;
+  }
+
+  StringBuilder &write_to(StringBuilder &builder) const;
+
+ private:
+  int64_t nanoseconds_;
+
+  static const int64_t INVALID_NANOSECONDS =
+      std::numeric_limits<int64_t>::min();
+
+  // Copyable.
+};
+
+inline Time &operator+=(Time &lhs, Duration rhs) {
+  lhs.set_nanoseconds(lhs.nanoseconds() + rhs.nanoseconds());
+  return lhs;
+}
+inline Time &operator-=(Time &lhs, Duration rhs) {
+  lhs.set_nanoseconds(lhs.nanoseconds() - rhs.nanoseconds());
+  return lhs;
+}
+
+inline Time operator+(Time lhs, Duration rhs) {
+  return Time(lhs.nanoseconds() + rhs.nanoseconds());
+}
+inline Time operator+(Duration lhs, Time rhs) {
+  return Time(lhs.nanoseconds() + rhs.nanoseconds());
+}
+inline Time operator-(Time lhs, Duration rhs) {
+  return Time(lhs.nanoseconds() - rhs.nanoseconds());
+}
+inline Duration operator-(Time lhs, Time rhs) {
+  return Duration(lhs.nanoseconds() - rhs.nanoseconds());
+}
+
+inline bool operator==(Time lhs, Time rhs) {
+  return lhs.nanoseconds() == rhs.nanoseconds();
+}
+inline bool operator!=(Time lhs, Time rhs) {
+  return lhs.nanoseconds() != rhs.nanoseconds();
+}
+inline bool operator<(Time lhs, Time rhs) {
+  return lhs.nanoseconds() < rhs.nanoseconds();
+}
+inline bool operator<=(Time lhs, Time rhs) {
+  return lhs.nanoseconds() <= rhs.nanoseconds();
+}
+inline bool operator>(Time lhs, Time rhs) {
+  return lhs.nanoseconds() > rhs.nanoseconds();
+}
+inline bool operator>=(Time lhs, Time rhs) {
+  return lhs.nanoseconds() >= rhs.nanoseconds();
+}
+
+inline StringBuilder &operator<<(StringBuilder &builder, Time time) {
+  return time.write_to(builder);
+}
+
+std::ostream &operator<<(std::ostream &stream, Time time);
+
+}  // namespace grnxx
+
+#endif  // GRNXX_TIME_HPP

  Added: test/Makefile.am (+101 -0) 100644
===================================================================
--- /dev/null
+++ test/Makefile.am    2012-11-28 16:36:40 +0900 (248074b)
@@ -0,0 +1,101 @@
+AM_CXXFLAGS = -I../lib
+
+TESTS =				\
+	test_backtrace		\
+	test_db_array		\
+	test_db_blob_vector	\
+	test_db_vector		\
+	test_duration		\
+	test_error		\
+	test_exception		\
+	test_features		\
+	test_grnxx		\
+	test_intrinsic		\
+	test_io_file		\
+	test_io_file_info	\
+	test_io_path		\
+	test_io_pool		\
+	test_io_view		\
+	test_logger		\
+	test_mutex		\
+	test_os			\
+	test_recycler		\
+	test_string		\
+	test_string_builder	\
+	test_string_format	\
+	test_thread		\
+	test_time
+
+check_PROGRAMS = $(TESTS)
+
+test_backtrace_SOURCES = test_backtrace.cpp
+test_backtrace_LDADD = ../lib/libgrnxx.la
+
+test_db_array_SOURCES = test_db_array.cpp
+test_db_array_LDADD = ../lib/libgrnxx.la
+
+test_db_blob_vector_SOURCES = test_db_blob_vector.cpp
+test_db_blob_vector_LDADD = ../lib/libgrnxx.la
+
+test_db_vector_SOURCES = test_db_vector.cpp
+test_db_vector_LDADD = ../lib/libgrnxx.la
+
+test_duration_SOURCES = test_duration.cpp
+test_duration_LDADD = ../lib/libgrnxx.la
+
+test_error_SOURCES = test_error.cpp
+test_error_LDADD = ../lib/libgrnxx.la
+
+test_exception_SOURCES = test_exception.cpp
+test_exception_LDADD = ../lib/libgrnxx.la
+
+test_features_SOURCES = test_features.cpp
+test_features_LDADD = ../lib/libgrnxx.la
+
+test_grnxx_SOURCES = test_grnxx.cpp
+test_grnxx_LDADD = ../lib/libgrnxx.la
+
+test_intrinsic_SOURCES = test_intrinsic.cpp
+test_intrinsic_LDADD = ../lib/libgrnxx.la
+
+test_io_file_SOURCES = test_io_file.cpp
+test_io_file_LDADD = ../lib/libgrnxx.la
+
+test_io_file_info_SOURCES = test_io_file_info.cpp
+test_io_file_info_LDADD = ../lib/libgrnxx.la
+
+test_io_path_SOURCES = test_io_path.cpp
+test_io_path_LDADD = ../lib/libgrnxx.la
+
+test_io_pool_SOURCES = test_io_pool.cpp
+test_io_pool_LDADD = ../lib/libgrnxx.la
+
+test_io_view_SOURCES = test_io_view.cpp
+test_io_view_LDADD = ../lib/libgrnxx.la
+
+test_logger_SOURCES = test_logger.cpp
+test_logger_LDADD = ../lib/libgrnxx.la
+
+test_mutex_SOURCES = test_mutex.cpp
+test_mutex_LDADD = ../lib/libgrnxx.la
+
+test_os_SOURCES = test_os.cpp
+test_os_LDADD = ../lib/libgrnxx.la
+
+test_recycler_SOURCES = test_recycler.cpp
+test_recycler_LDADD = ../lib/libgrnxx.la
+
+test_string_SOURCES = test_string.cpp
+test_string_LDADD = ../lib/libgrnxx.la
+
+test_string_builder_SOURCES = test_string_builder.cpp
+test_string_builder_LDADD = ../lib/libgrnxx.la
+
+test_string_format_SOURCES = test_string_format.cpp
+test_string_format_LDADD = ../lib/libgrnxx.la
+
+test_thread_SOURCES = test_thread.cpp
+test_thread_LDADD = ../lib/libgrnxx.la
+
+test_time_SOURCES = test_time.cpp
+test_time_LDADD = ../lib/libgrnxx.la

  Added: test/test_backtrace.cpp (+53 -0) 100644
===================================================================
--- /dev/null
+++ test/test_backtrace.cpp    2012-11-28 16:36:40 +0900 (f661699)
@@ -0,0 +1,53 @@
+/*
+  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 <cassert>
+
+#include "backtrace.hpp"
+#include "logger.hpp"
+
+void function_3rd() {
+  GRNXX_NOTICE() << __FUNCTION__ << "()";
+
+  std::vector<std::string> backtrace;
+  grnxx::Backtrace::pretty_backtrace(0, &backtrace);
+  for (std::size_t i = 0; i < backtrace.size(); ++i) {
+    GRNXX_NOTICE() << backtrace[i].c_str();
+  }
+}
+
+void function_2nd() {
+  GRNXX_NOTICE() << __FUNCTION__ << "()";
+
+  function_3rd();
+}
+
+void function_1st() {
+  GRNXX_NOTICE() << __FUNCTION__ << "()";
+
+  function_2nd();
+}
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  function_1st();
+
+  return 0;
+}

  Added: test/test_db_array.cpp (+110 -0) 100644
===================================================================
--- /dev/null
+++ test/test_db_array.cpp    2012-11-28 16:36:40 +0900 (fc5ded0)
@@ -0,0 +1,110 @@
+/*
+  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 <cstdlib>
+#include <random>
+#include <vector>
+
+#include "db/array.hpp"
+#include "logger.hpp"
+#include "time.hpp"
+
+void test_array_1() {
+  enum { VECTOR_SIZE = 1 << 24 };
+
+  grnxx::io::File::unlink_if_exists("temp.grn");
+  grnxx::io::File::unlink_if_exists("temp_000.grn");
+  grnxx::io::File::unlink_if_exists("temp_E000.grn");
+
+  std::mt19937 random;
+  std::vector<std::uint32_t> vector(VECTOR_SIZE);
+  for (std::size_t i = 0; i < vector.size(); ++i) {
+    vector[i] = random();
+  }
+
+  grnxx::io::Pool pool("temp.grn", grnxx::io::GRNXX_IO_CREATE);
+
+  grnxx::db::Array<std::uint32_t> array;
+  array.create(&pool, VECTOR_SIZE);
+
+  const std::uint32_t block_id = array.block_id();
+
+  GRNXX_NOTICE() << "array = " << array;
+
+  assert(array.size() == VECTOR_SIZE);
+
+  for (std::uint64_t i = 0; i < VECTOR_SIZE; ++i) {
+    array[i] = vector[i];
+  }
+
+  array.close();
+  array.open(&pool, block_id);
+
+  for (std::uint64_t i = 0; i < VECTOR_SIZE; ++i) {
+    assert(array[i] == vector[i]);
+  }
+
+  array.close();
+
+  grnxx::db::Array<std::uint32_t>::unlink(&pool, block_id);
+  assert(pool.get_block_info(block_id)->status() ==
+         grnxx::io::BLOCK_FROZEN);
+
+  grnxx::io::File::unlink_if_exists("temp.grn");
+  grnxx::io::File::unlink_if_exists("temp_000.grn");
+  grnxx::io::File::unlink_if_exists("temp_E000.grn");
+}
+
+void test_array_2() {
+  enum { VECTOR_SIZE = 1 << 24 };
+
+  grnxx::io::Pool pool("temp.grn", grnxx::io::GRNXX_IO_TEMPORARY);
+
+  grnxx::db::Array<std::uint8_t[3]> array;
+  array.create(&pool, VECTOR_SIZE);
+
+  GRNXX_NOTICE() << "array = " << array;
+
+  assert(array.size() == VECTOR_SIZE);
+
+  for (std::uint64_t i = 0; i < VECTOR_SIZE; ++i) {
+    array[i][0] = 'X';
+    array[i][1] = 'Y';
+    array[i][2] = 'Z';
+  }
+
+  for (std::uint64_t i = 0; i < VECTOR_SIZE; ++i) {
+    assert(array[i][0] == 'X');
+    assert(array[i][1] == 'Y');
+    assert(array[i][2] == 'Z');
+  }
+
+  array.close();
+}
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  test_array_1();
+  test_array_2();
+
+  return 0;
+}

  Added: test/test_db_blob_vector.cpp (+305 -0) 100644
===================================================================
--- /dev/null
+++ test/test_db_blob_vector.cpp    2012-11-28 16:36:40 +0900 (36a2f58)
@@ -0,0 +1,305 @@
+/*
+  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 <cassert>
+#include <cstdlib>
+#include <random>
+#include <vector>
+#include <unordered_map>
+
+#include "db/blob_vector.hpp"
+#include "logger.hpp"
+#include "time.hpp"
+
+void test_basics() {
+  grnxx::io::Pool::unlink_if_exists("temp.grn");
+
+  grnxx::io::Pool pool("temp.grn", grnxx::io::GRNXX_IO_CREATE);
+
+  grnxx::db::BlobVector vector;
+  vector.create(pool);
+
+  GRNXX_NOTICE() << "blob_vector = " << vector;
+
+  assert(vector.block_id() == 0);
+
+  grnxx::db::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, '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());
+
+  std::uint32_t block_id = vector.block_id();
+
+  vector.close();
+  pool = grnxx::io::Pool();
+
+  pool = grnxx::io::Pool("temp.grn", grnxx::io::GRNXX_IO_OPEN);
+  vector.open(pool, block_id);
+
+  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());
+
+  vector.close();
+  pool = grnxx::io::Pool();
+
+  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) {
+  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::Pool pool("temp.grn", grnxx::io::GRNXX_IO_TEMPORARY);
+
+  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_NOTICE() << "pool = " << pool;
+  GRNXX_NOTICE() << "blob_vector = " << vector;
+}
+
+void test_small_values() {
+  test_random_values(1 << 20,
+                     0,
+                     grnxx::db::BLOB_VECTOR_SMALL_VALUE_LENGTH_MAX);
+}
+
+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);
+}
+
+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);
+}
+
+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_random_updates(grnxx::Duration frozen_duration) {
+  std::mt19937 random;
+
+  std::unordered_map<std::uint64_t, std::string> map;
+
+  grnxx::io::PoolOptions options;
+  options.set_frozen_duration(frozen_duration);
+
+  grnxx::io::Pool pool("temp.grn", grnxx::io::GRNXX_IO_TEMPORARY, options);
+
+  grnxx::db::BlobVector vector;
+  vector.create(pool);
+
+  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);
+    }
+
+    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;
+  }
+
+  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_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("temp.grn", grnxx::io::GRNXX_IO_TEMPORARY);
+
+  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);
+  }
+}
+
+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_huge_values();
+
+  test_rewrite();
+  test_reuse();
+
+  test_random();
+
+  return 0;
+}

  Added: test/test_db_vector.cpp (+301 -0) 100644
===================================================================
--- /dev/null
+++ test/test_db_vector.cpp    2012-11-28 16:36:40 +0900 (6b635e0)
@@ -0,0 +1,301 @@
+/*
+  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 <cassert>
+#include <cstdlib>
+#include <random>
+#include <vector>
+
+#include "db/vector.hpp"
+#include "logger.hpp"
+#include "time.hpp"
+
+struct Point {
+  double x;
+  double y;
+};
+
+void test_basics() {
+  grnxx::io::Pool::unlink_if_exists("temp.grn");
+
+  grnxx::io::Pool pool("temp.grn", grnxx::io::GRNXX_IO_CREATE);
+
+  grnxx::db::Vector<std::uint32_t> vector;
+  vector.create(&pool);
+
+  assert(vector.value_size() == sizeof(std::uint32_t));
+  assert(vector.page_size() == grnxx::db::VECTOR_PAGE_SIZE_DEFAULT);
+  assert(vector.table_size() == grnxx::db::VECTOR_TABLE_SIZE_DEFAULT);
+  assert(vector.secondary_table_size() ==
+         grnxx::db::VECTOR_SECONDARY_TABLE_SIZE_DEFAULT);
+
+  GRNXX_NOTICE() << "vector = " << vector;
+
+  grnxx::db::Vector<std::uint32_t> vector2;
+
+  vector.swap(vector2);
+  vector2.swap(vector);
+
+  assert(vector.block_id() != grnxx::io::BLOCK_INVALID_ID);
+
+  vector[0] = 1;
+  vector[1000] = 10;
+  vector[1000000] = 100;
+  vector[1000000000] = 1000;
+  vector[1000000000000ULL] = 10000;
+  vector[vector.id_max()] = 100000;
+
+  assert(vector[0] == 1);
+  assert(vector[1000] == 10);
+  assert(vector[1000000] == 100);
+  assert(vector[1000000000] == 1000);
+  assert(vector[1000000000000ULL] == 10000);
+  assert(vector[vector.id_max()] == 100000);
+
+  const std::uint32_t block_id = vector.block_id();
+
+  vector.close();
+  pool = grnxx::io::Pool();
+
+  pool = grnxx::io::Pool("temp.grn", grnxx::io::GRNXX_IO_OPEN);
+
+  vector.open(&pool, block_id);
+
+  assert(vector[0] == 1);
+  assert(vector[1000] == 10);
+  assert(vector[1000000] == 100);
+  assert(vector[1000000000] == 1000);
+  assert(vector[1000000000000ULL] == 10000);
+  assert(vector[vector.id_max()] == 100000);
+
+  assert(grnxx::atomic_fetch_and_add(1, &vector[0]) == 1);
+  assert(vector[0] == 2);
+
+  assert(grnxx::atomic_fetch_and_add(10, &vector[0]) == 2);
+  assert(vector[0] == 12);
+
+  vector.create(&pool, 56789);
+
+  assert(vector[0] == 56789);
+  assert(vector[1000] == 56789);
+  assert(vector[1000000] == 56789);
+  assert(vector[1000000000] == 56789);
+  assert(vector[1000000000000ULL] == 56789);
+  assert(vector[vector.id_max()] == 56789);
+
+  assert(grnxx::atomic_compare_and_swap(
+      std::uint32_t(56789), std::uint32_t(98765), &vector[0]));
+  assert(!grnxx::atomic_compare_and_swap(
+      std::uint32_t(56789), std::uint32_t(98765), &vector[0]));
+  assert(grnxx::atomic_compare_and_swap(
+      std::uint32_t(98765), std::uint32_t(56789), &vector[0]));
+  assert(vector[0] == 56789);
+
+  vector.close();
+
+  grnxx::db::Vector<float> float_vector;
+  float_vector.create(&pool);
+
+  float_vector[0] = 1.0F;
+  assert(float_vector[0] == 1.0F);
+  float_vector[1 << 30] = 2.0F;
+  assert(float_vector[1 << 30] == 2.0F);
+
+  float_vector.close();
+
+  grnxx::db::Vector<double> double_vector;
+  double_vector.create(&pool);
+
+  double_vector[0] = 1.0;
+  assert(double_vector[0] == 1.0);
+  double_vector[1 << 30] = 2.0;
+  assert(double_vector[1 << 30] == 2.0);
+
+  double_vector.close();
+
+  grnxx::db::Vector<Point> point_vector;
+  point_vector.create(&pool);
+
+  point_vector[0].x = 123;
+  point_vector[0].y = 456;
+  assert(point_vector[0].x == 123);
+  assert(point_vector[0].y == 456);
+
+  point_vector[1 << 30].x = 987;
+  point_vector[1 << 30].y = 654;
+  assert(point_vector[1 << 30].x == 987);
+  assert(point_vector[1 << 30].y == 654);
+
+  point_vector.close();
+
+  pool = grnxx::io::Pool();
+  grnxx::io::Pool::unlink_if_exists("temp.grn");
+}
+
+template <typename T>
+void test_times() {
+  enum { VECTOR_SIZE = 1 << 20 };
+
+  grnxx::io::Pool pool("temp.grn", grnxx::io::GRNXX_IO_TEMPORARY);
+
+  grnxx::db::Vector<T> vector;
+  vector.create(&pool);
+
+  grnxx::Time start, end;
+
+  std::uint64_t total = 0;
+
+  start = grnxx::Time::now();
+  for (std::uint64_t id = 0; id < VECTOR_SIZE; ++id) {
+    vector[id] = T(0);
+  }
+  end = grnxx::Time::now();
+  grnxx::Duration set_1st_elapsed = (end - start) / VECTOR_SIZE;
+
+  start = grnxx::Time::now();
+  for (std::uint64_t id = 0; id < VECTOR_SIZE; ++id) {
+    vector[id] = T(1);
+  }
+  end = grnxx::Time::now();
+  grnxx::Duration set_2nd_elapsed = (end - start) / VECTOR_SIZE;
+
+  start = grnxx::Time::now();
+  for (std::uint64_t id = 0; id < VECTOR_SIZE; ++id) {
+    total += vector[id];
+  }
+  end = grnxx::Time::now();
+  grnxx::Duration get_elapsed = (end - start) / VECTOR_SIZE;
+
+
+  start = grnxx::Time::now();
+  for (std::uint64_t id = vector.id_max() - VECTOR_SIZE + 1;
+       id <= vector.id_max(); ++id) {
+    vector[id] = T(0);
+  }
+  end = grnxx::Time::now();
+  grnxx::Duration ex_set_1st_elapsed = (end - start) / VECTOR_SIZE;
+
+  start = grnxx::Time::now();
+  for (std::uint64_t id = vector.id_max() - VECTOR_SIZE + 1;
+       id <= vector.id_max(); ++id) {
+    vector[id] = T(1);
+  }
+  end = grnxx::Time::now();
+  grnxx::Duration ex_set_2nd_elapsed = (end - start) / VECTOR_SIZE;
+
+  start = grnxx::Time::now();
+  for (std::uint64_t id = vector.id_max() - VECTOR_SIZE + 1;
+       id <= vector.id_max(); ++id) {
+    total += vector[id];
+  }
+  end = grnxx::Time::now();
+  grnxx::Duration ex_get_elapsed = (end - start) / VECTOR_SIZE;
+
+
+  const std::uint64_t boundary = vector.page_size() * vector.table_size();
+  const std::uint64_t range = 1 << 16;
+  std::uint64_t id_begin = boundary - (range / 2);
+  std::uint64_t id_end = boundary + (range / 2);
+
+  for (uint64_t id = id_begin; id < id_end; ++id) {
+    vector[id] = T(0);
+  }
+
+  std::mt19937 engine;
+  std::vector<std::uint64_t> ids(VECTOR_SIZE);
+  for (int i = 0; i < VECTOR_SIZE; ++i) {
+    ids[i] = id_begin + (engine() % range);
+  }
+
+  start = grnxx::Time::now();
+  for (int i = 0; i < VECTOR_SIZE; ++i) {
+    vector[ids[i]] = T(0);
+  }
+  end = grnxx::Time::now();
+  grnxx::Duration boundary_set_1st_elapsed = (end - start) / VECTOR_SIZE;
+
+  start = grnxx::Time::now();
+  for (int i = 0; i < VECTOR_SIZE; ++i) {
+    vector[ids[i]] = T(1);
+  }
+  end = grnxx::Time::now();
+  grnxx::Duration boundary_set_2nd_elapsed = (end - start) / VECTOR_SIZE;
+
+  start = grnxx::Time::now();
+  for (int i = 0; i < VECTOR_SIZE; ++i) {
+    total += vector[ids[i]];
+  }
+  end = grnxx::Time::now();
+  grnxx::Duration boundary_get_elapsed = (end - start) / VECTOR_SIZE;
+
+  const std::uint32_t block_id = vector.block_id();
+  vector.close();
+
+
+  start = grnxx::Time::now();
+  grnxx::db::Vector<T>::unlink(&pool, block_id);
+  end = grnxx::Time::now();
+  grnxx::Duration unlink_elapsed = end - start;
+
+
+  vector.create(&pool, 0);
+
+  start = grnxx::Time::now();
+  for (std::uint64_t id = 0; id < VECTOR_SIZE; ++id) {
+    vector[id] = T(0);
+  }
+  end = grnxx::Time::now();
+  grnxx::Duration default_elapsed = (end - start) / VECTOR_SIZE;
+
+
+  GRNXX_NOTICE() << "elapsed [ns]: set = "
+                 << set_2nd_elapsed.nanoseconds() << " ("
+                 << set_1st_elapsed.nanoseconds() << ", "
+                 << default_elapsed.nanoseconds() << ')'
+                 << ", get = "
+                 << get_elapsed.nanoseconds()
+                 << ", ex. set = "
+                 << ex_set_2nd_elapsed.nanoseconds() << " ("
+                 << ex_set_1st_elapsed.nanoseconds() << ')'
+                 << ", ex. get = "
+                 << ex_get_elapsed.nanoseconds()
+                 << ", boundary set = "
+                 << boundary_set_2nd_elapsed.nanoseconds() << " ("
+                 << boundary_set_1st_elapsed.nanoseconds() << ')'
+                 << ", boundary get = "
+                 << boundary_get_elapsed.nanoseconds()
+                 << ", unlink = "
+                 << unlink_elapsed.nanoseconds()
+                 << ", total = "
+                 << total;
+}
+
+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_times<std::uint8_t>();
+  test_times<std::uint16_t>();
+  test_times<std::uint32_t>();
+  test_times<std::uint64_t>();
+  test_times<float>();
+  test_times<double>();
+
+  return 0;
+}

  Added: test/test_duration.cpp (+114 -0) 100644
===================================================================
--- /dev/null
+++ test/test_duration.cpp    2012-11-28 16:36:40 +0900 (8319a07)
@@ -0,0 +1,114 @@
+/*
+  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 <cassert>
+#include <sstream>
+
+#include "duration.hpp"
+#include "logger.hpp"
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  assert(grnxx::Duration().nanoseconds() == 0);
+  assert(grnxx::Duration(123).nanoseconds() == 123);
+
+  assert(grnxx::Duration::nanoseconds(123).nanoseconds() == 123);
+  assert(grnxx::Duration::seconds(1).nanoseconds() == 1000000000);
+  assert(grnxx::Duration::minutes(1).nanoseconds() == 1000000000LL * 60);
+  assert(grnxx::Duration::hours(1).nanoseconds() == 1000000000LL * 60 * 60);
+  assert(grnxx::Duration::days(1).nanoseconds() ==
+         1000000000LL * 60 * 60 * 24);
+  assert(grnxx::Duration::weeks(1).nanoseconds() ==
+         1000000000LL * 60 * 60 * 24 * 7);
+
+  GRNXX_NOTICE() << "nanosecond = " << grnxx::Duration::nanoseconds(1);
+  GRNXX_NOTICE() << "second = " << grnxx::Duration::seconds(1);
+  GRNXX_NOTICE() << "minute = " << grnxx::Duration::minutes(1);
+  GRNXX_NOTICE() << "hour = " << grnxx::Duration::hours(1);
+  GRNXX_NOTICE() << "day = " << grnxx::Duration::days(1);
+  GRNXX_NOTICE() << "week = " << grnxx::Duration::weeks(1);
+
+  const grnxx::Duration hour = grnxx::Duration::hours(1);
+
+  assert((hour + hour) == grnxx::Duration::hours(2));
+  assert((hour - hour) == grnxx::Duration::hours(0));
+  assert((hour * 3) == grnxx::Duration::hours(3));
+  assert((hour / 2) == grnxx::Duration::minutes(30));
+  assert((hour % grnxx::Duration::minutes(50)) ==
+         grnxx::Duration::minutes(10));
+
+  grnxx::Duration duration = grnxx::Duration::weeks(1);
+
+  assert((duration += grnxx::Duration::days(1)) ==
+         grnxx::Duration::days(8));
+  assert(duration == grnxx::Duration::days(8));
+
+  assert((duration -= grnxx::Duration::weeks(1)) ==
+         grnxx::Duration::days(1));
+  assert(duration == grnxx::Duration::days(1));
+
+  assert((duration *= 3) == grnxx::Duration::days(3));
+  assert(duration == grnxx::Duration::days(3));
+
+  assert((duration /= 24) == grnxx::Duration::hours(3));
+  assert(duration == grnxx::Duration::hours(3));
+
+  assert((duration %= grnxx::Duration::hours(3)) ==
+         grnxx::Duration::hours(0));
+  assert(duration == grnxx::Duration::hours(0));
+
+  assert(grnxx::Duration(123) == grnxx::Duration(123));
+  assert(grnxx::Duration(123) != grnxx::Duration(456));
+
+  assert(grnxx::Duration(123) < grnxx::Duration(456));
+  assert(grnxx::Duration(456) > grnxx::Duration(123));
+
+  assert(grnxx::Duration(123) <= grnxx::Duration(123));
+  assert(grnxx::Duration(123) <= grnxx::Duration(456));
+
+  assert(grnxx::Duration(456) >= grnxx::Duration(456));
+  assert(grnxx::Duration(456) >= grnxx::Duration(123));
+
+  std::stringstream stream;
+  stream << grnxx::Duration(123456789);
+  assert(stream.str() == "0.123456789");
+
+  stream.str("");
+  stream << grnxx::Duration::seconds(123);
+  assert(stream.str() == "123");
+
+  stream.str("");
+  stream << (grnxx::Duration::seconds(456) + grnxx::Duration(789));
+  assert(stream.str() == "456.000000789");
+
+  stream.str("");
+  stream << grnxx::Duration(-123456789);
+  assert(stream.str() == "-0.123456789");
+
+  stream.str("");
+  stream << grnxx::Duration::seconds(-123);
+  assert(stream.str() == "-123");
+
+  stream.str("");
+  stream << -(grnxx::Duration::seconds(456) + grnxx::Duration(789));
+  assert(stream.str() == "-456.000000789");
+
+  return 0;
+}

  Added: test/test_error.cpp (+32 -0) 100644
===================================================================
--- /dev/null
+++ test/test_error.cpp    2012-11-28 16:36:40 +0900 (b5ed3e2)
@@ -0,0 +1,32 @@
+/*
+  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 <cassert>
+#include <cerrno>
+
+#include "error.hpp"
+#include "logger.hpp"
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  GRNXX_ERROR() << "EINVAL: " << grnxx::Error(EINVAL);
+  GRNXX_ERROR() << "12345: " << grnxx::Error(12345);
+  return 0;
+}

  Added: test/test_exception.cpp (+44 -0) 100644
===================================================================
--- /dev/null
+++ test/test_exception.cpp    2012-11-28 16:36:40 +0900 (59d397d)
@@ -0,0 +1,44 @@
+/*
+  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 <cassert>
+
+#include "exception.hpp"
+#include "logger.hpp"
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  bool thrown = true;
+  bool catched = false;
+  try {
+    GRNXX_THROW();
+    thrown = false;
+  } catch (const grnxx::Exception &) {
+    catched = true;
+  }
+
+  GRNXX_NOTICE() << "thrown = " << thrown;
+  GRNXX_NOTICE() << "catched = " << catched;
+
+  assert(thrown);
+  assert(catched);
+
+  return 0;
+}

  Added: test/test_features.cpp (+22 -0) 100644
===================================================================
--- /dev/null
+++ test/test_features.cpp    2012-11-28 16:36:40 +0900 (a3e40a6)
@@ -0,0 +1,22 @@
+/*
+  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 "features.hpp"
+
+int main() {
+  return 0;
+}

  Added: test/test_grnxx.cpp (+31 -0) 100644
===================================================================
--- /dev/null
+++ test/test_grnxx.cpp    2012-11-28 16:36:40 +0900 (af8097f)
@@ -0,0 +1,31 @@
+/*
+  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 <cassert>
+
+#include "grnxx.hpp"
+#include "logger.hpp"
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  GRNXX_NOTICE() << "bugreport: " << grnxx::Grnxx::bugreport();
+  GRNXX_NOTICE() << "version: " << grnxx::Grnxx::version();
+  return 0;
+}

  Added: test/test_intrinsic.cpp (+177 -0) 100644
===================================================================
--- /dev/null
+++ test/test_intrinsic.cpp    2012-11-28 16:36:40 +0900 (1d6fc9d)
@@ -0,0 +1,177 @@
+/*
+  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 <cassert>
+
+#include "intrinsic.hpp"
+#include "logger.hpp"
+#include "time.hpp"
+
+void test_basics() {
+  assert(grnxx::bit_scan_reverse(std::uint8_t(100)) == 6);
+  assert(grnxx::bit_scan_reverse(std::uint16_t(0xFFF)) == 11);
+  assert(grnxx::bit_scan_reverse(std::uint32_t(1) << 30) == 30);
+  assert(grnxx::bit_scan_reverse(std::uint64_t(-1)) == 63);
+
+  assert(grnxx::bit_scan_forward(std::uint8_t(100)) == 2);
+  assert(grnxx::bit_scan_forward(std::uint16_t(0xFFF)) == 0);
+  assert(grnxx::bit_scan_forward(std::uint32_t(1) << 30) == 30);
+  assert(grnxx::bit_scan_forward(std::uint64_t(1) << 63) == 63);
+
+  volatile std::int32_t value_32 = 0;
+  assert(grnxx::atomic_fetch_and_add(5, &value_32) == 0);
+  assert(grnxx::atomic_fetch_and_add(-10, &value_32) == 5);
+  assert(grnxx::atomic_fetch_and_add(5, &value_32) == -5);
+
+  value_32 = 0;
+  assert(grnxx::atomic_fetch_and_or(std::int32_t(0x15), &value_32) == 0);
+  assert(grnxx::atomic_fetch_and_and(std::int32_t(0x10), &value_32) == 0x15);
+  assert(grnxx::atomic_fetch_and_xor(std::int32_t(0xFF), &value_32) == 0x10);
+  assert(value_32 == 0xEF);
+
+  volatile std::int64_t value_64 = 0;
+  assert(grnxx::atomic_fetch_and_add(std::int64_t(1) << 50, &value_64) == 0);
+  assert(grnxx::atomic_fetch_and_add(
+      std::int64_t(-1) << 51, &value_64) == std::int64_t(1) << 50);
+  assert(grnxx::atomic_fetch_and_add(
+      std::int64_t(1) << 50, &value_64) == std::int64_t(-1) << 50);
+
+  value_64 = 0;
+  assert(grnxx::atomic_fetch_and_or(std::int64_t(0x1515), &value_64) == 0);
+  assert(grnxx::atomic_fetch_and_and(
+      std::int64_t(0x130F), &value_64) == 0x1515);
+  assert(grnxx::atomic_fetch_and_xor(
+      std::int64_t(0x3327), &value_64) == 0x1105);
+  assert(value_64 == 0x2222);
+
+  std::uint64_t buf[16];
+  std::uint64_t *ptr = &buf[0];
+  assert(grnxx::atomic_fetch_and_add(1, &ptr) == &buf[0]);
+  assert(grnxx::atomic_fetch_and_add(2, &ptr) == &buf[1]);
+  assert(grnxx::atomic_fetch_and_add(3, &ptr) == &buf[3]);
+  assert(ptr == &buf[6]);
+
+  double value_float = 0.0;
+  assert(grnxx::atomic_fetch_and_add(1, &value_float) == 0.0);
+  assert(grnxx::atomic_fetch_and_add(2.0, &value_float) == 1.0);
+  assert(value_float == 3.0);
+
+  value_32 = 0;
+  assert(grnxx::atomic_compare_and_swap(
+      std::int32_t(0), std::int32_t(1), &value_32));
+  assert(grnxx::atomic_compare_and_swap(
+      std::int32_t(1), std::int32_t(2), &value_32));
+  assert(!grnxx::atomic_compare_and_swap(
+      std::int32_t(0), std::int32_t(1), &value_32));
+
+  value_64 = 0;
+  assert(grnxx::atomic_compare_and_swap(
+      std::int64_t(0), std::int64_t(10), &value_64));
+  assert(!grnxx::atomic_compare_and_swap(
+      std::int64_t(0), std::int64_t(20), &value_64));
+  assert(grnxx::atomic_compare_and_swap(
+      std::int64_t(10), std::int64_t(20), &value_64));
+
+  assert(grnxx::atomic_compare_and_swap(&buf[6], &buf[0], &ptr));
+  assert(ptr == &buf[0]);
+  assert(!grnxx::atomic_compare_and_swap(&buf[1], &buf[2], &ptr));
+
+  assert(grnxx::atomic_compare_and_swap(3.0, 0.0, &value_float));
+  assert(value_float == 0.0);
+  assert(!grnxx::atomic_compare_and_swap(1.0, 2.0, &value_float));
+}
+
+void test_times() {
+  enum { LOOP_COUNT = 1 << 20 };
+
+  grnxx::Time start, end;
+
+  start = grnxx::Time::now();
+  std::uint64_t total = 0;
+  for (std::uint32_t i = 1; i <= LOOP_COUNT; ++i) {
+    total += grnxx::bit_scan_reverse(i);
+  }
+  end = grnxx::Time::now();
+  GRNXX_NOTICE() << "bit_scan_reverse<32>: total = " << total
+                 << ", elapsed [ns] = "
+                 << (1.0 * (end - start).nanoseconds() / LOOP_COUNT);
+
+  start = grnxx::Time::now();
+  total = 0;
+  for (std::uint64_t i = 1; i <= LOOP_COUNT; ++i) {
+    total += grnxx::bit_scan_reverse(i << 20);
+  }
+  end = grnxx::Time::now();
+  GRNXX_NOTICE() << "bit_scan_reverse<64>: total = " << total
+                 << ", elapsed [ns] = "
+                 << (1.0 * (end - start).nanoseconds() / LOOP_COUNT);
+
+  start = grnxx::Time::now();
+  volatile std::uint32_t count_32 = 0;
+  for (std::uint32_t i = 0; i < LOOP_COUNT; ++i) {
+    assert(grnxx::atomic_fetch_and_add(1, &count_32) == i);
+  }
+  end = grnxx::Time::now();
+  assert(count_32 == LOOP_COUNT);
+  GRNXX_NOTICE() << "atomic_fetch_and_add<32>: total = " << count_32
+                 << ", elapsed [ns] = "
+                 << (1.0 * (end - start).nanoseconds() / LOOP_COUNT);
+
+  start = grnxx::Time::now();
+  volatile std::uint64_t count_64 = 0;
+  for (std::uint32_t i = 0; i < LOOP_COUNT; ++i) {
+    assert(grnxx::atomic_fetch_and_add(1, &count_64) == i);
+  }
+  end = grnxx::Time::now();
+  assert(count_64 == LOOP_COUNT);
+  GRNXX_NOTICE() << "atomic_fetch_and_add<64>: total = " << count_64
+                 << ", elapsed [ns] = "
+                 << (1.0 * (end - start).nanoseconds() / LOOP_COUNT);
+
+  start = grnxx::Time::now();
+  const std::int32_t a_32 = 0, b_32 = 1;
+  volatile std::int32_t value_32 = a_32;
+  for (std::uint32_t i = 0; i < (LOOP_COUNT / 2); ++i) {
+    assert(grnxx::atomic_compare_and_swap(a_32, b_32, &value_32));
+    assert(grnxx::atomic_compare_and_swap(b_32, a_32, &value_32));
+  }
+  end = grnxx::Time::now();
+  GRNXX_NOTICE() << "atomic_compare_and_swap<32>: elapsed [ns] = "
+                 << (1.0 * (end - start).nanoseconds() / LOOP_COUNT);
+
+  start = grnxx::Time::now();
+  const std::int64_t a_64 = 0, b_64 = 1;
+  volatile std::int64_t value_64 = a_64;
+  for (std::uint32_t i = 0; i < (LOOP_COUNT / 2); ++i) {
+    assert(grnxx::atomic_compare_and_swap(a_64, b_64, &value_64));
+    assert(grnxx::atomic_compare_and_swap(b_64, a_64, &value_64));
+  }
+  end = grnxx::Time::now();
+  GRNXX_NOTICE() << "atomic_compare_and_swap<64>: elapsed [ns] = "
+                 << (1.0 * (end - start).nanoseconds() / LOOP_COUNT);
+}
+
+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_times();
+
+  return 0;
+}

  Added: test/test_io_file.cpp (+242 -0) 100644
===================================================================
--- /dev/null
+++ test/test_io_file.cpp    2012-11-28 16:36:40 +0900 (11aebcd)
@@ -0,0 +1,242 @@
+/*
+  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 <cassert>
+
+#include "io/file.hpp"
+#include "logger.hpp"
+
+void test_create() {
+  const char FILE_PATH[] = "temp.grn";
+
+  grnxx::io::File::unlink_if_exists(FILE_PATH);
+
+  assert(!grnxx::io::File::exists(FILE_PATH));
+  assert(!grnxx::io::File::unlink_if_exists(FILE_PATH));
+
+  grnxx::io::File file(FILE_PATH, grnxx::io::GRNXX_IO_CREATE);
+
+  assert(file.path() == FILE_PATH);
+  assert(file.tell() == 0);
+  assert(file.size() == 0);
+
+  file = grnxx::io::File();
+
+  assert(grnxx::io::File::exists(FILE_PATH));
+  grnxx::io::File::unlink(FILE_PATH);
+
+  assert(!grnxx::io::File::exists(FILE_PATH));
+  assert(!grnxx::io::File::unlink_if_exists(FILE_PATH));
+}
+
+void test_open() {
+  const char FILE_PATH[] = "temp.grn";
+
+  grnxx::io::File::unlink_if_exists(FILE_PATH);
+  grnxx::io::File(FILE_PATH, grnxx::io::GRNXX_IO_CREATE);
+
+  grnxx::io::File file(FILE_PATH);
+
+  file = grnxx::io::File();
+  grnxx::io::File::unlink(FILE_PATH);
+}
+
+void test_create_or_open() {
+  const char FILE_PATH[] = "temp.grn";
+
+  grnxx::io::File::unlink_if_exists(FILE_PATH);
+
+  grnxx::io::File file(FILE_PATH, grnxx::io::GRNXX_IO_CREATE |
+                                  grnxx::io::GRNXX_IO_OPEN);
+
+  file = grnxx::io::File();
+  file = grnxx::io::File(FILE_PATH, grnxx::io::GRNXX_IO_CREATE |
+                         grnxx::io::GRNXX_IO_OPEN);
+
+  file = grnxx::io::File();
+  grnxx::io::File::unlink(FILE_PATH);
+}
+
+void test_write() {
+  const char FILE_PATH[] = "temp.grn";
+
+  grnxx::io::File::unlink_if_exists(FILE_PATH);
+  grnxx::io::File file(FILE_PATH, grnxx::io::GRNXX_IO_CREATE);
+
+  assert(file.write("0123456789", 10) == 10);
+  assert(file.tell() == 10);
+  assert(file.size() == 10);
+
+  file = grnxx::io::File();
+  grnxx::io::File::unlink(FILE_PATH);
+}
+
+void test_resize() {
+  const char FILE_PATH[] = "temp.grn";
+  const std::uint64_t FILE_SIZE = 1 << 20;
+
+  grnxx::io::File::unlink_if_exists(FILE_PATH);
+  grnxx::io::File file(FILE_PATH, grnxx::io::GRNXX_IO_CREATE);
+
+  file.resize(FILE_SIZE);
+  assert(file.tell() == FILE_SIZE);
+  assert(file.size() == FILE_SIZE);
+
+  file.resize(0);
+  assert(file.tell() == 0);
+  assert(file.size() == 0);
+
+  file = grnxx::io::File();
+  grnxx::io::File::unlink(FILE_PATH);
+}
+
+void test_seek() {
+  const char FILE_PATH[] = "temp.grn";
+  const std::uint64_t FILE_SIZE = 1 << 20;
+
+  grnxx::io::File::unlink_if_exists(FILE_PATH);
+  grnxx::io::File file(FILE_PATH, grnxx::io::GRNXX_IO_CREATE);
+
+  file.resize(FILE_SIZE);
+
+  assert(file.seek(0) == 0);
+  assert(file.tell() == 0);
+
+  assert(file.seek(FILE_SIZE / 2) == (FILE_SIZE / 2));
+  assert(file.tell() == (FILE_SIZE / 2));
+
+  assert(file.seek(FILE_SIZE / 4, SEEK_CUR) ==
+         ((FILE_SIZE / 2) + (FILE_SIZE / 4)));
+  assert(file.tell() == ((FILE_SIZE / 2) + (FILE_SIZE / 4)));
+
+  assert(file.seek(-(FILE_SIZE / 2), SEEK_END) == (FILE_SIZE / 2));
+  assert(file.tell() == (FILE_SIZE / 2));
+
+  file = grnxx::io::File();
+  grnxx::io::File::unlink(FILE_PATH);
+}
+
+void test_read() {
+  const char FILE_PATH[] = "temp.grn";
+
+  grnxx::io::File::unlink_if_exists(FILE_PATH);
+  grnxx::io::File file(FILE_PATH, grnxx::io::GRNXX_IO_CREATE);
+
+  file.write("0123456789", 10);
+  file.seek(0);
+
+  char buf[256];
+  assert(file.read(buf, sizeof(buf)) == 10);
+  assert(std::memcmp(buf, "0123456789", 10) == 0);
+  assert(file.tell() == 10);
+
+  file.seek(3);
+
+  assert(file.read(buf, 5) == 5);
+  assert(file.tell() == 8);
+  assert(std::memcmp(buf, "34567", 5) == 0);
+
+  file = grnxx::io::File();
+  grnxx::io::File::unlink(FILE_PATH);
+}
+
+void test_temporary() {
+  const char FILE_PATH[] = "temp.grn";
+
+  grnxx::io::File file(FILE_PATH, grnxx::io::GRNXX_IO_TEMPORARY);
+
+  assert(file.write("0123456789", 10) == 10);
+  assert(file.seek(0) == 0);
+
+  char buf[256];
+  assert(file.read(buf, sizeof(buf)) == 10);
+  assert(std::memcmp(buf, "0123456789", 10) == 0);
+
+  grnxx::String path = file.path();
+
+  file = grnxx::io::File();
+
+  assert(!grnxx::io::File::exists(path.c_str()));
+}
+
+void test_unlink_at_close() {
+  const char FILE_PATH[] = "temp.grn";
+
+  grnxx::io::File file(FILE_PATH, grnxx::io::GRNXX_IO_CREATE);
+
+  file.set_unlink_at_close(true);
+
+  assert(file.unlink_at_close());
+
+  file = grnxx::io::File();
+
+  assert(!grnxx::io::File::exists(FILE_PATH));
+}
+
+void test_lock() {
+  const char FILE_PATH[] = "temp.grn";
+
+  grnxx::io::File::unlink_if_exists(FILE_PATH);
+  grnxx::io::File file_1(FILE_PATH, grnxx::io::GRNXX_IO_CREATE);
+
+  assert(!file_1.unlock());
+  assert(file_1.try_lock(grnxx::io::GRNXX_IO_EXCLUSIVE_LOCK));
+  assert(!file_1.try_lock(grnxx::io::GRNXX_IO_SHARED_LOCK));
+  assert(file_1.unlock());
+
+  assert(file_1.try_lock(grnxx::io::GRNXX_IO_SHARED_LOCK));
+  assert(file_1.unlock());
+  assert(!file_1.unlock());
+
+  grnxx::io::File file_2(FILE_PATH, grnxx::io::GRNXX_IO_OPEN);
+
+  assert(file_1.try_lock(grnxx::io::GRNXX_IO_EXCLUSIVE_LOCK));
+  assert(!file_2.try_lock(grnxx::io::GRNXX_IO_SHARED_LOCK));
+  assert(!file_2.try_lock(grnxx::io::GRNXX_IO_EXCLUSIVE_LOCK));
+  assert(!file_2.unlock());
+  assert(file_1.unlock());
+
+  assert(file_1.try_lock(grnxx::io::GRNXX_IO_SHARED_LOCK));
+  assert(!file_2.try_lock(grnxx::io::GRNXX_IO_EXCLUSIVE_LOCK));
+  assert(file_2.try_lock(grnxx::io::GRNXX_IO_SHARED_LOCK));
+  assert(file_1.unlock());
+  assert(!file_1.try_lock(grnxx::io::GRNXX_IO_EXCLUSIVE_LOCK));
+  assert(file_2.unlock());
+
+  file_1 = grnxx::io::File();
+  file_2 = grnxx::io::File();
+  grnxx::io::File::unlink(FILE_PATH);
+}
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  test_create();
+  test_open();
+  test_create_or_open();
+  test_write();
+  test_resize();
+  test_seek();
+  test_read();
+  test_temporary();
+  test_unlink_at_close();
+  test_lock();
+
+  return 0;
+}

  Added: test/test_io_file_info.cpp (+123 -0) 100644
===================================================================
--- /dev/null
+++ test/test_io_file_info.cpp    2012-11-28 16:36:40 +0900 (b0ce2fe)
@@ -0,0 +1,123 @@
+/*
+  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 <cassert>
+
+#include "io/file.hpp"
+#include "io/file_info.hpp"
+#include "logger.hpp"
+
+void test_non_existent_file() {
+  const char FILE_PATH[] = "temp.grn";
+
+  grnxx::io::File::unlink_if_exists(FILE_PATH);
+
+  grnxx::io::FileInfo file_info(FILE_PATH);
+
+  GRNXX_NOTICE() << "file_info (invalid) = " << file_info;
+
+  assert(!file_info);
+  assert(!file_info.is_file());
+  assert(!file_info.is_directory());
+}
+
+void test_existent_file() {
+  const char FILE_PATH[] = "temp.grn";
+  const std::uint64_t FILE_SIZE = 12345;
+
+  grnxx::io::File::unlink_if_exists(FILE_PATH);
+  grnxx::io::File file(FILE_PATH, grnxx::io::GRNXX_IO_CREATE);
+  file.resize(FILE_SIZE);
+
+  grnxx::io::FileInfo file_info(FILE_PATH);
+
+  GRNXX_NOTICE() << "file_info (regular) = " << file_info;
+
+  assert(file_info);
+  assert(file_info.is_file());
+  assert(!file_info.is_directory());
+  assert(file_info.size() == FILE_SIZE);
+
+  assert(grnxx::io::FileInfo(file));
+
+  file = grnxx::io::File();
+  grnxx::io::File::unlink(FILE_PATH);
+}
+
+void test_non_existent_directory() {
+  const char DIRECTORY_PATH[] = "no_such_directory/";
+
+  grnxx::io::FileInfo file_info(DIRECTORY_PATH);
+
+  assert(!file_info);
+  assert(!file_info.is_file());
+  assert(!file_info.is_directory());
+}
+
+void test_existent_directory() {
+  const char DIRECTORY_PATH[] = "./";
+
+  grnxx::io::FileInfo file_info(DIRECTORY_PATH);
+
+  GRNXX_NOTICE() << "file_info (directory) = " << file_info;
+
+  assert(file_info);
+  assert(!file_info.is_file());
+  assert(file_info.is_directory());
+}
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  test_non_existent_file();
+  test_existent_file();
+  test_non_existent_directory();
+  test_existent_directory();
+
+  const char FILE_PATH[] = "temp.grn";
+
+  grnxx::io::File::unlink_if_exists(FILE_PATH);
+
+  grnxx::io::FileInfo file_info(FILE_PATH);
+
+  GRNXX_NOTICE() << "file_info = " << file_info;
+
+  assert(!file_info);
+
+  grnxx::io::File file("temp.dat", grnxx::io::GRNXX_IO_CREATE |
+                                   grnxx::io::GRNXX_IO_TEMPORARY);
+  file.resize(12345);
+
+  file_info = grnxx::io::FileInfo(file);
+
+  file_info = grnxx::io::FileInfo(file);
+
+  GRNXX_NOTICE() << "file_info = " << file_info;
+
+  assert(file_info);
+  assert(file_info.is_file());
+  assert(!file_info.is_directory());
+  assert(file_info.size() == 12345);
+
+  file = grnxx::io::File();
+
+  assert(!grnxx::io::File::exists("temp.dat"));
+
+  return 0;
+}

  Added: test/test_io_path.cpp (+54 -0) 100644
===================================================================
--- /dev/null
+++ test/test_io_path.cpp    2012-11-28 16:36:40 +0900 (c6d3024)
@@ -0,0 +1,54 @@
+/*
+  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 <cassert>
+#include <sstream>
+
+#include "io/path.hpp"
+#include "logger.hpp"
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  grnxx::String full_path = grnxx::io::Path::full_path(nullptr);
+  GRNXX_NOTICE() << "full_path = " << full_path;
+
+  full_path = grnxx::io::Path::full_path("temp.grn");
+  GRNXX_NOTICE() << "full_path = " << full_path;
+
+  assert(grnxx::io::Path::full_path("/") == "/");
+  assert(grnxx::io::Path::full_path("/.") == "/");
+  assert(grnxx::io::Path::full_path("/..") == "/");
+
+  assert(grnxx::io::Path::full_path("/usr/local/lib") == "/usr/local/lib");
+  assert(grnxx::io::Path::full_path("/usr/local/lib/") == "/usr/local/lib/");
+  assert(grnxx::io::Path::full_path("/usr/local/lib/.") == "/usr/local/lib");
+  assert(grnxx::io::Path::full_path("/usr/local/lib/./") == "/usr/local/lib/");
+
+  assert(grnxx::io::Path::full_path("/usr/local/lib/..") == "/usr/local");
+  assert(grnxx::io::Path::full_path("/usr/local/lib/../") == "/usr/local/");
+
+  grnxx::String unique_path = grnxx::io::Path::unique_path("temp.grn");
+  GRNXX_NOTICE() << "unique_path = " << unique_path;
+
+  unique_path = grnxx::io::Path::unique_path(nullptr);
+  GRNXX_NOTICE() << "unique_path = " << unique_path;
+
+  return 0;
+}

  Added: test/test_io_pool.cpp (+442 -0) 100644
===================================================================
--- /dev/null
+++ test/test_io_pool.cpp    2012-11-28 16:36:40 +0900 (35989fe)
@@ -0,0 +1,442 @@
+/*
+  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 <cassert>
+#include <random>
+#include <vector>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "io/pool.hpp"
+#include "logger.hpp"
+
+void test_constructor() {
+  grnxx::io::Pool::unlink_if_exists("temp.grn");
+
+  grnxx::io::Pool pool;
+  assert(!pool);
+
+  pool = grnxx::io::Pool("temp.grn", grnxx::io::GRNXX_IO_CREATE);
+  assert(pool);
+  assert(pool.flags() & grnxx::io::GRNXX_IO_CREATE);
+
+  pool = grnxx::io::Pool("temp.grn");
+  assert(pool);
+  assert(pool.flags() & grnxx::io::GRNXX_IO_OPEN);
+
+  pool = grnxx::io::Pool("temp.grn", grnxx::io::GRNXX_IO_ANONYMOUS);
+  assert(pool);
+  assert(pool.flags() & grnxx::io::GRNXX_IO_ANONYMOUS);
+
+  pool = grnxx::io::Pool("temp.grn", grnxx::io::GRNXX_IO_TEMPORARY);
+  assert(pool);
+  assert(pool.flags() & grnxx::io::GRNXX_IO_TEMPORARY);
+
+  grnxx::io::Pool::unlink_if_exists("temp.grn");
+}
+
+void test_compare() {
+  grnxx::io::Pool pool;
+  assert(pool == pool);
+
+  grnxx::io::Pool pool2("temp.grn", grnxx::io::GRNXX_IO_TEMPORARY);
+  assert(pool != pool2);
+  assert(pool2 == pool2);
+
+  grnxx::io::Pool pool3("temp.grn", grnxx::io::GRNXX_IO_TEMPORARY);
+  assert(pool != pool3);
+  assert(pool2 != pool3);
+  assert(pool3 == pool3);
+}
+
+void test_copy() {
+  grnxx::io::Pool pool("temp.grn", grnxx::io::GRNXX_IO_TEMPORARY);
+
+  grnxx::io::Pool pool2(pool);
+  assert(pool == pool2);
+
+  grnxx::io::Pool pool3;
+  pool3 = pool;
+  assert(pool == pool3);
+}
+
+void test_move() {
+  grnxx::io::Pool pool("temp.grn", grnxx::io::GRNXX_IO_TEMPORARY);
+  grnxx::io::Pool pool_copy(pool);
+
+  grnxx::io::Pool pool2(std::move(pool));
+  assert(pool2 == pool_copy);
+
+  grnxx::io::Pool pool3;
+  pool3 = std::move(pool2);
+  assert(pool3 == pool_copy);
+}
+
+void test_swap() {
+  grnxx::io::Pool pool("temp.grn", grnxx::io::GRNXX_IO_TEMPORARY);
+  grnxx::io::Pool pool2("temp.grn", grnxx::io::GRNXX_IO_TEMPORARY);
+
+  grnxx::io::Pool pool_copy(pool);
+  grnxx::io::Pool pool2_copy(pool2);
+
+  pool.swap(pool2);
+  assert(pool == pool2_copy);
+  assert(pool2 == pool_copy);
+
+  swap(pool, pool2);
+  assert(pool == pool_copy);
+  assert(pool2 == pool2_copy);
+}
+
+void test_exists() {
+  grnxx::io::Pool::unlink_if_exists("temp.grn");
+
+  assert(!grnxx::io::Pool::exists("temp.grn"));
+
+  grnxx::io::Pool("temp.grn", grnxx::io::GRNXX_IO_CREATE);
+
+  assert(grnxx::io::Pool::exists("temp.grn"));
+
+  grnxx::io::Pool::unlink("temp.grn");
+}
+
+void test_unlink() {
+  grnxx::io::Pool::unlink_if_exists("temp.grn");
+
+  grnxx::io::Pool("temp.grn", grnxx::io::GRNXX_IO_CREATE);
+
+  grnxx::io::Pool::unlink("temp.grn");
+}
+
+void test_unlink_if_exists() {
+  grnxx::io::Pool::unlink_if_exists("temp.grn");
+
+  grnxx::io::Pool("temp.grn", grnxx::io::GRNXX_IO_CREATE);
+
+  assert(grnxx::io::Pool::unlink_if_exists("temp.grn"));
+}
+
+void test_write_to() {
+  grnxx::io::Pool pool("temp.grn", grnxx::io::GRNXX_IO_TEMPORARY);
+
+  GRNXX_NOTICE() << "pool = " << pool;
+}
+
+void test_create_block() {
+  grnxx::io::Pool pool("temp.grn", grnxx::io::GRNXX_IO_ANONYMOUS);
+
+  // Create a minimum-size block.
+  const grnxx::io::BlockInfo *block_info = pool.create_block(0);
+  assert(block_info);
+  assert(block_info->id() == 0);
+  assert(block_info->status() == grnxx::io::BLOCK_ACTIVE);
+  assert(block_info->chunk_id() == 0);
+  assert(block_info->offset() == 0);
+  assert(block_info->size() == grnxx::io::BLOCK_UNIT_SIZE);
+
+  pool = grnxx::io::Pool("temp.grn", grnxx::io::GRNXX_IO_TEMPORARY);
+
+  // Create a maximum-size block.
+  block_info = pool.create_block(pool.options().max_block_chunk_size());
+  assert(block_info);
+  assert(block_info->id() == 0);
+  assert(block_info->status() == grnxx::io::BLOCK_ACTIVE);
+  assert(block_info->chunk_id() == 0);
+  assert(block_info->offset() == 0);
+  assert(block_info->size() == pool.options().max_block_chunk_size());
+
+  const int NUM_BLOCKS = 1 << 16;
+
+  std::mt19937 random;
+
+  pool = grnxx::io::Pool("temp.grn", grnxx::io::GRNXX_IO_TEMPORARY);
+
+  // Create small blocks.
+  const std::uint64_t SMALL_MAX_SIZE = std::uint64_t(1) << 16;
+  for (int i = 0; i < NUM_BLOCKS; ++i) {
+    pool.create_block(random() % SMALL_MAX_SIZE);
+  }
+
+  // Create medium blocks.
+  const std::uint64_t MEDIUM_MAX_SIZE = std::uint64_t(1) << 22;
+  for (int i = 0; i < NUM_BLOCKS; ++i) {
+    pool.create_block(random() % MEDIUM_MAX_SIZE);
+  }
+
+  // Create large blocks.
+  const std::uint64_t LARGE_MAX_SIZE = std::uint64_t(1) << 28;
+  for (int i = 0; i < NUM_BLOCKS; ++i) {
+    pool.create_block(random() % LARGE_MAX_SIZE);
+  }
+}
+
+void test_get_block_info() {
+  grnxx::io::Pool pool("temp.grn", grnxx::io::GRNXX_IO_ANONYMOUS);
+
+  const grnxx::io::BlockInfo *block_info;
+
+  block_info = pool.create_block(std::uint64_t(1) << 10);
+  assert(block_info == pool.get_block_info(block_info->id()));
+
+  block_info = pool.create_block(std::uint64_t(1) << 20);
+  assert(block_info == pool.get_block_info(block_info->id()));
+
+  block_info = pool.create_block(std::uint64_t(1) << 30);
+  assert(block_info == pool.get_block_info(block_info->id()));
+
+  block_info = pool.create_block(std::uint64_t(1) << 40);
+  assert(block_info == pool.get_block_info(block_info->id()));
+}
+
+void test_get_block_address() {
+  grnxx::io::Pool pool("temp.grn", grnxx::io::GRNXX_IO_ANONYMOUS);
+
+  const int NUM_BLOCKS = 1 << 10;
+  const std::uint32_t MAX_SIZE = 1 << 16;
+
+  std::mt19937 random;
+  std::unordered_map<const grnxx::io::BlockInfo *, int> map;
+
+  for (int i = 0; i < NUM_BLOCKS; ++i) {
+    // Create a block and fill it with a random alphabet.
+    const grnxx::io::BlockInfo *block_info =
+        pool.create_block(random() % MAX_SIZE);
+    void *block_address = pool.get_block_address(*block_info);
+
+    const int label = 'A' + (random() % 26);
+    std::memset(block_address, label, block_info->size());
+    map[block_info] = label;
+  }
+
+  for (auto it = map.begin(); it != map.end(); ++it) {
+    // Check the blocks are not broken.
+    const char *block_address = static_cast<char *>(
+        pool.get_block_address(it->first->id()));
+    for (std::uint64_t j = 0; j < it->first->size(); ++j) {
+      assert(block_address[j] == it->second);
+    }
+  }
+}
+
+void test_free_block() {
+  grnxx::io::Pool pool("temp.grn", grnxx::io::GRNXX_IO_ANONYMOUS);
+
+  const grnxx::io::BlockInfo *block_info = pool.create_block(0);
+  pool.free_block(block_info->id());
+  assert(block_info->status() == grnxx::io::BLOCK_FROZEN);
+
+  block_info = pool.create_block(1 << 20);
+  pool.free_block(*block_info);
+  assert(block_info->status() == grnxx::io::BLOCK_FROZEN);
+
+  const int NUM_BLOCKS = 1 << 16;
+
+  std::mt19937 random;
+  std::vector<const grnxx::io::BlockInfo *> block_infos;
+
+  pool = grnxx::io::Pool("temp.grn", grnxx::io::GRNXX_IO_TEMPORARY);
+
+  // Create small blocks.
+  const std::uint64_t SMALL_MAX_SIZE = std::uint64_t(1) << 16;
+  for (int i = 0; i < NUM_BLOCKS; ++i) {
+    block_infos.push_back(pool.create_block(random() % SMALL_MAX_SIZE));
+  }
+
+  // Create medium blocks.
+  const std::uint64_t MEDIUM_MAX_SIZE = std::uint64_t(1) << 22;
+  for (int i = 0; i < NUM_BLOCKS; ++i) {
+    block_infos.push_back(pool.create_block(random() % MEDIUM_MAX_SIZE));
+  }
+
+  // Create large blocks.
+  const std::uint64_t LARGE_MAX_SIZE = std::uint64_t(1) << 28;
+  for (int i = 0; i < NUM_BLOCKS; ++i) {
+    block_infos.push_back(pool.create_block(random() % LARGE_MAX_SIZE));
+  }
+
+  for (std::size_t i = 0; i < block_infos.size(); ++i) {
+    assert(block_infos[i]->status() == grnxx::io::BLOCK_ACTIVE);
+    pool.free_block(*block_infos[i]);
+    assert(block_infos[i]->status() == grnxx::io::BLOCK_FROZEN);
+  }
+}
+
+void test_unfreeze_block() {
+  // Enable immediate reuse of freed blocks.
+  grnxx::io::PoolOptions options;
+  options.set_frozen_duration(grnxx::Duration(0));
+
+  grnxx::io::Pool pool("temp.grn", grnxx::io::GRNXX_IO_TEMPORARY, options);
+  assert(pool.options().frozen_duration() == grnxx::Duration(0));
+
+  const grnxx::io::BlockInfo *block_info = pool.create_block(0);
+  pool.free_block(*block_info);
+
+  // The freed ID is not available yet.
+  block_info = pool.create_block(0);
+  assert(block_info->id() != 0);
+
+  const int OPERATION_COUNT = 1 << 16;
+
+  std::mt19937 random;
+  std::unordered_set<const grnxx::io::BlockInfo *> block_infos;
+
+  const std::uint64_t MAX_SIZE = std::uint64_t(1) << 32;
+  for (int i = 0; i < OPERATION_COUNT; ++i) {
+    if (!block_infos.empty() && ((random() % 2) == 0)) {
+      pool.free_block(**block_infos.begin());
+      block_infos.erase(block_infos.begin());
+    } else {
+      block_infos.insert(pool.create_block(random() % MAX_SIZE));
+    }
+  }
+
+  // The total_size may be greater than 100TB if the block reuse does not work.
+  GRNXX_NOTICE() << "total_size = " << pool.header().total_size();
+  assert(pool.header().total_size() < (std::uint64_t(1) << 42));
+}
+
+void test_random_queries() {
+  // Enable immediate reuse of freed blocks.
+  grnxx::io::PoolOptions options;
+  options.set_frozen_duration(grnxx::Duration(0));
+
+  grnxx::io::Pool pool("temp.grn", grnxx::io::GRNXX_IO_ANONYMOUS, options);
+
+  const int OPERATION_COUNT = 1 << 18;
+
+  std::mt19937 random;
+  std::unordered_set<std::uint32_t> id_set;
+
+  // Create and free blocks in random.
+  for (int i = 0; i < OPERATION_COUNT; ++i) {
+    const std::uint32_t value = random() & 256;
+    if (value < 16) {
+      // Free a block.
+      if (!id_set.empty()) {
+        pool.free_block(*id_set.begin());
+        id_set.erase(id_set.begin());
+      }
+    } else {
+      std::uint64_t size;
+      if (value < 32) {
+        // Create a small block.
+        const std::uint64_t SMALL_MAX_SIZE = std::uint64_t(1) << 16;
+        size = random() % SMALL_MAX_SIZE;
+      } else if (value < 248) {
+        // Create a medium block.
+        const std::uint64_t MEDIUM_MAX_SIZE = std::uint64_t(1) << 22;
+        size = random() % MEDIUM_MAX_SIZE;
+      } else {
+        // Create a large block.
+        const std::uint64_t LARGE_MAX_SIZE = std::uint64_t(1) << 28;
+        size = random() % LARGE_MAX_SIZE;
+      }
+      const grnxx::io::BlockInfo *block_info = pool.create_block(size);
+      id_set.insert(block_info->id());
+    }
+  }
+}
+
+void benchmark() {
+  const int OPERATION_COUNT = 1 << 16;
+
+  std::vector<const grnxx::io::BlockInfo *> block_infos;
+  block_infos.resize(OPERATION_COUNT);
+
+  grnxx::io::Pool pool("temp.grn", grnxx::io::GRNXX_IO_TEMPORARY);
+
+  // Measure the speed of create_block().
+  grnxx::Time start_time = grnxx::Time::now();
+  for (int i = 0; i < OPERATION_COUNT; ++i) {
+    block_infos[i] = pool.create_block(0);
+  }
+  grnxx::Duration elapsed = grnxx::Time::now() - start_time;
+  GRNXX_NOTICE() << "create_block: elapsed [ns] = "
+                 << (elapsed / OPERATION_COUNT).nanoseconds();
+
+  // Measure the speed of get_block_info().
+  start_time = grnxx::Time::now();
+  for (int i = 0; i < OPERATION_COUNT; ++i) {
+    pool.get_block_info(block_infos[i]->id());
+  }
+  elapsed = grnxx::Time::now() - start_time;
+  GRNXX_NOTICE() << "get_block_info: elapsed [ns] = "
+                 << (elapsed / OPERATION_COUNT).nanoseconds();
+
+  // Measure the speed of get_block_address().
+  start_time = grnxx::Time::now();
+  for (int i = 0; i < OPERATION_COUNT; ++i) {
+    pool.get_block_address(*block_infos[i]);
+  }
+  elapsed = grnxx::Time::now() - start_time;
+  GRNXX_NOTICE() << "get_block_address_by_info (1st): elapsed [ns] = "
+                 << (elapsed / OPERATION_COUNT).nanoseconds();
+
+  // Measure the speed of get_block_address() again.
+  start_time = grnxx::Time::now();
+  for (int i = 0; i < OPERATION_COUNT; ++i) {
+    pool.get_block_address(*block_infos[i]);
+  }
+  elapsed = grnxx::Time::now() - start_time;
+  GRNXX_NOTICE() << "get_block_address_by_info (2nd): elapsed [ns] = "
+                 << (elapsed / OPERATION_COUNT).nanoseconds();
+
+  // Measure the speed of get_block_address() again and again.
+  start_time = grnxx::Time::now();
+  for (int i = 0; i < OPERATION_COUNT; ++i) {
+    pool.get_block_address(block_infos[i]->id());
+  }
+  elapsed = grnxx::Time::now() - start_time;
+  GRNXX_NOTICE() << "get_block_address_by_id: elapsed [ns] = "
+                 << (elapsed / OPERATION_COUNT).nanoseconds();
+
+  // Measure the speed of free_block().
+  start_time = grnxx::Time::now();
+  for (int i = 0; i < OPERATION_COUNT; ++i) {
+    pool.free_block(block_infos[i]->id());
+  }
+  elapsed = grnxx::Time::now() - start_time;
+  GRNXX_NOTICE() << "free_block: elapsed [ns] = "
+                 << (elapsed / OPERATION_COUNT).nanoseconds();
+}
+
+int main() {
+  // Enables logging to the standard output.
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  test_constructor();
+  test_compare();
+  test_copy();
+  test_move();
+  test_swap();
+  test_exists();
+  test_unlink();
+  test_unlink_if_exists();
+  test_write_to();
+  test_create_block();
+  test_get_block_info();
+  test_get_block_address();
+  test_free_block();
+  test_unfreeze_block();
+  test_random_queries();
+  benchmark();
+
+  return 0;
+}

  Added: test/test_io_view.cpp (+109 -0) 100644
===================================================================
--- /dev/null
+++ test/test_io_view.cpp    2012-11-28 16:36:40 +0900 (e2379f7)
@@ -0,0 +1,109 @@
+/*
+  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 <cassert>
+
+#include "logger.hpp"
+#include "io/view.hpp"
+
+void test_anonymous_mmap() {
+  const std::uint64_t MMAP_SIZE = 1 << 20;
+
+  grnxx::io::View view;
+  assert(!view);
+
+  view = grnxx::io::View(grnxx::io::Flags(), MMAP_SIZE);
+
+  GRNXX_NOTICE() << "view = " << view;
+
+  assert(view);
+  assert(view.flags() == (grnxx::io::GRNXX_IO_ANONYMOUS |
+                          grnxx::io::GRNXX_IO_PRIVATE));
+  assert(view.address() != nullptr);
+  assert(view.offset() == 0);
+  assert(view.size() == MMAP_SIZE);
+
+  std::memset(view.address(), 0, view.size());
+}
+
+void test_file_backed_mmap() {
+  const char FILE_PATH[] = "temp.grn";
+  const std::uint64_t FILE_SIZE = 1 << 24;
+  const std::uint64_t MMAP_SIZE = 1 << 20;
+
+  grnxx::io::File file(FILE_PATH, grnxx::io::GRNXX_IO_TEMPORARY);
+
+  file.resize(FILE_SIZE);
+  assert(file.size() == FILE_SIZE);
+
+  // Create a memory mapping.
+  grnxx::io::View view(file, grnxx::io::GRNXX_IO_SHARED);
+
+  GRNXX_NOTICE() << "view = " << view;
+
+  assert(view);
+  assert(view.flags() == grnxx::io::GRNXX_IO_SHARED);
+  assert(view.address() != nullptr);
+  assert(view.offset() == 0);
+  assert(view.size() == FILE_SIZE);
+
+  std::memset(view.address(), 'x', view.size());
+
+  // Create a memory mapping.
+  view = grnxx::io::View(file, grnxx::io::GRNXX_IO_PRIVATE);
+
+  GRNXX_NOTICE() << "view = " << view;
+
+  assert(view);
+  assert(view.flags() == grnxx::io::GRNXX_IO_PRIVATE);
+  assert(view.address() != nullptr);
+  assert(view.offset() == 0);
+  assert(view.size() == FILE_SIZE);
+
+  for (std::uint64_t i = 0; i < FILE_SIZE; ++i) {
+    assert(static_cast<const char *>(view.address())[i] == 'x');
+  }
+  std::memset(view.address(), 'z', view.size());
+
+  // Create a memory mapping.
+  view = grnxx::io::View(file, grnxx::io::GRNXX_IO_SHARED |
+                               grnxx::io::GRNXX_IO_PRIVATE,
+                         FILE_SIZE / 2, MMAP_SIZE);
+
+  GRNXX_NOTICE() << "view = " << view;
+
+  assert(view);
+  assert(view.flags() == grnxx::io::GRNXX_IO_SHARED);
+  assert(view.address() != nullptr);
+  assert(view.offset() == (FILE_SIZE / 2));
+  assert(view.size() == MMAP_SIZE);
+
+  for (std::uint64_t i = 0; i < MMAP_SIZE; ++i) {
+    assert(static_cast<const char *>(view.address())[i] == 'x');
+  }
+}
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  test_anonymous_mmap();
+  test_file_backed_mmap();
+
+  return 0;
+}

  Added: test/test_logger.cpp (+75 -0) 100644
===================================================================
--- /dev/null
+++ test/test_logger.cpp    2012-11-28 16:36:40 +0900 (4789344)
@@ -0,0 +1,75 @@
+/*
+  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 <cassert>
+#include <cerrno>
+#include <cstdio>
+
+#include "error.hpp"
+#include "logger.hpp"
+
+//void f3() {
+//  std::cerr << "I'm f3()" << std::endl;
+//  GRNXX_LOG_BACKTRACE(5);
+//}
+
+//void f2() {
+//  std::cerr << "I'm f2()" << std::endl;
+//  f3();
+//  GRNXX_LOG_WITH_BACKTRACE(3, "This is a %s.", "pen");
+//}
+
+//void f1() {
+//  std::cerr << "I'm f1()" << std::endl;
+//  f2();
+//}
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  assert(grnxx::Logger::open("temp.log"));
+
+  GRNXX_LOGGER(grnxx::ERROR_LOGGER) << "ERROR_LOGGER";
+  GRNXX_LOGGER(grnxx::WARNING_LOGGER) << "WARNING_LOGGER";
+  GRNXX_LOGGER(grnxx::NOTICE_LOGGER) << "NOTICE_LOGGER";
+
+  GRNXX_ERROR() << "GRNXX_ERROR";
+  GRNXX_WARNING() << "GRNXX_WARNING";
+  GRNXX_NOTICE() << "GRNXX_NOTICE";
+
+  const char * const path = "no_such_directory/no_such_file";
+  assert(std::fopen(path, "rb") == NULL);
+
+  GRNXX_ERROR() << "failed to open file: <" << path << ">: 'fopen' "
+                << grnxx::Error(errno);
+
+  GRNXX_LOGGER(0) << "Level: 0";
+  GRNXX_LOGGER(1) << "Level: 1";
+  GRNXX_LOGGER(2) << "Level: 2";
+  GRNXX_LOGGER(3) << "Level: 3";
+  GRNXX_LOGGER(4) << "Level: 4";
+
+  GRNXX_NOTICE() << "This" << grnxx::Logger::new_line()
+                 << "is" << grnxx::Logger::new_line()
+                 << "a multi-line log.";
+
+  GRNXX_NOTICE() << "backtrace: " << grnxx::Logger::backtrace();
+
+  return 0;
+}

  Added: test/test_mutex.cpp (+82 -0) 100644
===================================================================
--- /dev/null
+++ test/test_mutex.cpp    2012-11-28 16:36:40 +0900 (d58f9c0)
@@ -0,0 +1,82 @@
+/*
+  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 <cassert>
+
+#include "lock.hpp"
+#include "logger.hpp"
+#include "mutex.hpp"
+#include "time.hpp"
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+
+  grnxx::Mutex mutex;
+  assert(mutex.value() == grnxx::Mutex::UNLOCKED);
+
+  GRNXX_NOTICE() << "mutex = " << mutex;
+
+  assert(mutex.try_lock());
+  assert(mutex.value() == grnxx::Mutex::LOCKED);
+
+  GRNXX_NOTICE() << "mutex = " << mutex;
+
+  assert(!mutex.try_lock());
+  assert(mutex.value() == grnxx::Mutex::LOCKED);
+
+  assert(mutex.unlock());
+  assert(mutex.value() == grnxx::Mutex::UNLOCKED);
+
+  assert(mutex.lock());
+  assert(mutex.value() == grnxx::Mutex::LOCKED);
+
+  mutex.clear();
+  assert(mutex.value() == grnxx::Mutex::UNLOCKED);
+
+
+  grnxx::Mutex::Object mutex_object = grnxx::Mutex::UNLOCKED;
+
+  assert(grnxx::Mutex::try_lock(&mutex_object));
+  assert(mutex_object == grnxx::Mutex::LOCKED);
+
+  assert(!grnxx::Mutex::try_lock(&mutex_object));
+  assert(mutex_object == grnxx::Mutex::LOCKED);
+
+  assert(grnxx::Mutex::unlock(&mutex_object));
+  assert(mutex_object == grnxx::Mutex::UNLOCKED);
+
+  assert(grnxx::Mutex::lock(&mutex_object));
+  assert(mutex_object == grnxx::Mutex::LOCKED);
+
+
+  enum { LOOP_COUNT = 1 << 20 };
+
+  const grnxx::Time start = grnxx::Time::now();
+  for (int i = 0; i < LOOP_COUNT; ++i) {
+    grnxx::Lock lock(&mutex);
+    assert(lock);
+  }
+  const grnxx::Time end = grnxx::Time::now();
+
+  GRNXX_NOTICE() << "grnxx::Lock: elapsed [ns] = "
+                 << ((end - start).nanoseconds() / LOOP_COUNT);
+
+  return 0;
+}

  Added: test/test_os.cpp (+36 -0) 100644
===================================================================
--- /dev/null
+++ test/test_os.cpp    2012-11-28 16:36:40 +0900 (1039d70)
@@ -0,0 +1,36 @@
+/*
+  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 <cassert>
+
+#include "os.hpp"
+#include "logger.hpp"
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  GRNXX_NOTICE() << "page_size = " << grnxx::OS::get_page_size();
+
+  GRNXX_NOTICE() << "getenv(PATH) = "
+                 << grnxx::OS::get_environment_variable("PATH");
+  GRNXX_NOTICE() << "getenv(NO_SUCH_NAME) = "
+                 << grnxx::OS::get_environment_variable("NO_SUCH_NAME");
+
+  return 0;
+}

  Added: test/test_recycler.cpp (+95 -0) 100644
===================================================================
--- /dev/null
+++ test/test_recycler.cpp    2012-11-28 16:36:40 +0900 (eb5feda)
@@ -0,0 +1,95 @@
+/*
+  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 <cassert>
+
+#include "logger.hpp"
+#include "recycler.hpp"
+
+void test() {
+  const grnxx::Duration FROZEN_DURATION = grnxx::Duration::minutes(10);
+
+  grnxx::Recycler recycler(FROZEN_DURATION);
+
+  assert(recycler.frozen_duration() == FROZEN_DURATION);
+
+  for (uint16_t i = 1; i < grnxx::RECYCLER_STAMP_COUNT_PER_UPDATE; ++i) {
+    assert(recycler.stamp() == 0);
+  }
+
+  for (uint16_t i = 0; i < grnxx::RECYCLER_STAMP_COUNT_PER_UPDATE; ++i) {
+    assert(recycler.stamp() == 1);
+  }
+
+  for (uint16_t i = 0; i < grnxx::RECYCLER_STAMP_COUNT_PER_UPDATE; ++i) {
+    assert(recycler.stamp() == 1);
+  }
+
+  GRNXX_NOTICE() << "recycler = " << recycler;
+}
+
+void benchmark(grnxx::Duration frozen_duration) {
+  const int STAMP_COUNT = 1 << 20;
+  const int CHECK_COUNT = 1 << 20;
+
+  grnxx::Recycler recycler(frozen_duration);
+  assert(recycler.frozen_duration() == frozen_duration);
+
+  double stamp_elapsed;
+  {
+    const grnxx::Time start = grnxx::Time::now();
+    for (int i = 0; i < STAMP_COUNT; ++i) {
+      recycler.stamp();
+    }
+    const grnxx::Time end = grnxx::Time::now();
+    stamp_elapsed = 1.0 * (end - start).nanoseconds() / STAMP_COUNT;
+  }
+
+  double check_elapsed;
+  {
+    const grnxx::Time start = grnxx::Time::now();
+    for (int i = 0; i < CHECK_COUNT; ++i) {
+      recycler.check(std::uint16_t(i));
+    }
+    const grnxx::Time end = grnxx::Time::now();
+    check_elapsed = 1.0 * (end - start).nanoseconds() / CHECK_COUNT;
+  }
+
+  GRNXX_NOTICE() << "frozen_duration = " << frozen_duration
+                 << ", stamp [ns] = " << stamp_elapsed
+                 << ", check [ns] = " << check_elapsed
+                 << ", stamp = " << recycler.stamp();
+}
+
+void benchmark() {
+  benchmark(grnxx::Duration::seconds(1));
+  benchmark(grnxx::Duration::milliseconds(100));
+  benchmark(grnxx::Duration::milliseconds(10));
+  benchmark(grnxx::Duration::milliseconds(1));
+  benchmark(grnxx::Duration::microseconds(1));
+}
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  test();
+  benchmark();
+
+  return 0;
+}

  Added: test/test_string.cpp (+179 -0) 100644
===================================================================
--- /dev/null
+++ test/test_string.cpp    2012-11-28 16:36:40 +0900 (09de202)
@@ -0,0 +1,179 @@
+/*
+  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 <cassert>
+
+#include "logger.hpp"
+#include "string.hpp"
+#include "time.hpp"
+
+void test_constructors() {
+  assert(!grnxx::String());
+
+  assert(!grnxx::String(nullptr));
+  assert(!grnxx::String(""));
+  assert(grnxx::String("ABC"));
+
+  assert(!grnxx::String(nullptr, 0));
+  assert(!grnxx::String("ABC", 0));
+  assert(grnxx::String("ABC", 3));
+
+  assert(grnxx::String() == "");
+
+  assert(grnxx::String(nullptr) == "");
+  assert(grnxx::String("") == "");
+  assert(grnxx::String("ABC") == "ABC");
+
+  assert(grnxx::String(nullptr, 0) == "");
+  assert(grnxx::String("ABC", 0) == "");
+  assert(grnxx::String("ABC", 1) == "A");
+  assert(grnxx::String("ABC", 2) == "AB");
+  assert(grnxx::String("ABC", 3) == "ABC");
+}
+
+void test_assignment_operators() {
+  grnxx::String str;
+
+  str = nullptr;
+  assert(!str);
+  assert(str == "");
+
+  str = "";
+  assert(!str);
+  assert(str == "");
+
+  str = "123";
+  assert(str);
+  assert(str == "123");
+
+  grnxx::String str2;
+
+  str2 = str;
+  assert(str2 == "123");
+  assert(str2 == str);
+}
+
+void test_comparison_operators() {
+  assert(grnxx::String("") == grnxx::String(""));
+  assert(grnxx::String("") != grnxx::String("X"));
+
+  assert(grnxx::String("ABC") != grnxx::String(""));
+  assert(grnxx::String("ABC") != grnxx::String("A"));
+  assert(grnxx::String("ABC") != grnxx::String("AB"));
+  assert(grnxx::String("ABC") == grnxx::String("ABC"));
+  assert(grnxx::String("ABC") != grnxx::String("ABCD"));
+}
+
+void test_contains() {
+  grnxx::String str = "BCD";
+
+  assert(!str.contains('A'));
+  assert(str.contains('B'));
+  assert(str.contains('C'));
+  assert(str.contains('D'));
+  assert(!str.contains('E'));
+
+  char buf[] = { 'X' ,'\0', 'Y' };
+  str = grnxx::String(buf, sizeof(buf));
+
+  assert(str.contains('X'));
+  assert(str.contains('\0'));
+  assert(str.contains('Y'));
+}
+
+void test_starts_with() {
+  grnxx::String str = "This is a pen.";
+
+  assert(str.starts_with(""));
+  assert(str.starts_with("T"));
+  assert(str.starts_with("This is"));
+  assert(str.starts_with("This is a pen."));
+
+  assert(!str.starts_with("XYZ"));
+  assert(!str.starts_with("This is a pen.+XYZ"));
+}
+
+void test_ends_with() {
+  grnxx::String str = "This is a pen.";
+
+  assert(str.ends_with(""));
+  assert(str.ends_with("."));
+  assert(str.ends_with("a pen."));
+  assert(str.ends_with("This is a pen."));
+
+  assert(!str.ends_with("XYZ"));
+  assert(!str.ends_with("XYZ+This is a pen."));
+}
+
+void test_swap() {
+  grnxx::String str = "ABC";
+  grnxx::String str2 = "XYZ";
+
+  str.swap(str2);
+
+  assert(str == "XYZ");
+  assert(str2 == "ABC");
+
+  using std::swap;
+  swap(str, str2);
+
+  assert(str == "ABC");
+  assert(str2 == "XYZ");
+}
+
+void benchmark() {
+  const int LOOP_COUNT = 1 << 16;
+
+  grnxx::String str, str2;
+  grnxx::Time start, end;
+
+  start = grnxx::Time::now();
+  for (int i = 0; i < LOOP_COUNT; ++i) {
+    str = grnxx::String("This is an apple.");
+  }
+  end = grnxx::Time::now();
+
+  GRNXX_NOTICE() << "string creation: elapsed [ns] = "
+                 << ((end - start).nanoseconds() / LOOP_COUNT);
+
+  start = grnxx::Time::now();
+  for (int i = 0; i < LOOP_COUNT; ++i) {
+    str2 = str;
+  }
+  end = grnxx::Time::now();
+
+  GRNXX_NOTICE() << "string copy: elapsed [ns] = "
+                 << ((end - start).nanoseconds() / LOOP_COUNT);
+}
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  test_constructors();
+  test_assignment_operators();
+  test_comparison_operators();
+  test_contains();
+  test_starts_with();
+  test_ends_with();
+  test_swap();
+
+  benchmark();
+
+  return 0;
+}

  Added: test/test_string_builder.cpp (+199 -0) 100644
===================================================================
--- /dev/null
+++ test/test_string_builder.cpp    2012-11-28 16:36:40 +0900 (0d57e06)
@@ -0,0 +1,199 @@
+/*
+  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 <cassert>
+
+#include "logger.hpp"
+#include "string_builder.hpp"
+#include "time.hpp"
+
+void test_basic_operations() {
+  grnxx::StringBuilder builder;
+
+  assert(builder);
+  assert(builder.str() == "");
+
+  assert(!builder.append('X'));
+  assert(builder.str() == "");
+
+  char buf[4];
+  builder = grnxx::StringBuilder(buf);
+
+  assert(builder);
+  assert(builder.str() == "");
+
+  assert(builder.append('0'));
+  assert(builder.append('1'));
+  assert(builder.append('2'));
+  assert(!builder.append('3'));
+  assert(builder.str() == "012");
+
+  builder = grnxx::StringBuilder(buf, 3);
+
+  assert(!builder.append("0123", 4));
+  assert(builder.str() == "01");
+
+  builder = grnxx::StringBuilder(grnxx::STRING_BUILDER_AUTO_RESIZE);
+
+  assert(builder.append('X', 3));
+  assert(builder.append('Y', 2));
+  assert(builder.append('Z', 1));
+  assert(builder.append('-', 0));
+  assert(builder.str() == "XXXYYZ");
+
+  assert(builder.resize(4).str() == "XXXY");
+  assert(builder.resize(1000).length() == 1000);
+
+  builder = grnxx::StringBuilder(grnxx::STRING_BUILDER_AUTO_RESIZE);
+
+  assert(builder);
+  assert(builder.str() == "");
+
+  const size_t STRING_LENGTH = 1 << 20;
+  for (size_t i = 0; i < STRING_LENGTH; ++i) {
+    assert(builder.append('X'));
+  }
+  assert(builder.str().length() == STRING_LENGTH);
+}
+
+void test_char() {
+  grnxx::StringBuilder builder(grnxx::STRING_BUILDER_AUTO_RESIZE);
+
+  builder << '0';
+  builder << '1';
+  builder << '2';
+  builder << '3';
+  assert(builder.str() == "0123");
+}
+
+void test_integer() {
+  grnxx::StringBuilder builder(grnxx::STRING_BUILDER_AUTO_RESIZE);
+
+  builder << 0;
+  assert(builder.str() == "0");
+
+  builder = grnxx::StringBuilder(grnxx::STRING_BUILDER_AUTO_RESIZE);
+
+  builder << 0U;
+  assert(builder.str() == "0");
+
+  builder = grnxx::StringBuilder(grnxx::STRING_BUILDER_AUTO_RESIZE);
+
+  builder << std::numeric_limits<std::int8_t>::min() << '/'
+          << std::numeric_limits<std::int8_t>::max() << ','
+          << std::numeric_limits<std::uint8_t>::min() << '/'
+          << std::numeric_limits<std::uint8_t>::max();
+  assert(builder.str() == "-128/127,0/255");
+
+  builder = grnxx::StringBuilder(grnxx::STRING_BUILDER_AUTO_RESIZE);
+
+  builder << std::numeric_limits<std::int16_t>::min() << '/'
+          << std::numeric_limits<std::int16_t>::max() << ','
+          << std::numeric_limits<std::uint16_t>::min() << '/'
+          << std::numeric_limits<std::uint16_t>::max();
+  assert(builder.str() == "-32768/32767,0/65535");
+
+  builder = grnxx::StringBuilder(grnxx::STRING_BUILDER_AUTO_RESIZE);
+
+  builder << std::numeric_limits<std::int32_t>::min() << '/'
+          << std::numeric_limits<std::int32_t>::max() << ','
+          << std::numeric_limits<std::uint32_t>::min() << '/'
+          << std::numeric_limits<std::uint32_t>::max();
+  assert(builder.str() == "-2147483648/2147483647,0/4294967295");
+
+  builder = grnxx::StringBuilder(grnxx::STRING_BUILDER_AUTO_RESIZE);
+
+  builder << std::numeric_limits<std::int64_t>::min() << '/'
+          << std::numeric_limits<std::int64_t>::max() << ','
+          << std::numeric_limits<std::uint64_t>::min() << '/'
+          << std::numeric_limits<std::uint64_t>::max();
+  assert(builder.str() ==
+         "-9223372036854775808/9223372036854775807,0/18446744073709551615");
+}
+
+void test_floating_point_number() {
+  grnxx::StringBuilder builder(grnxx::STRING_BUILDER_AUTO_RESIZE);
+
+  builder << 0.0;
+  assert(builder.str() == "0.000000");
+
+  builder = grnxx::StringBuilder(grnxx::STRING_BUILDER_AUTO_RESIZE);
+
+  builder << 16.5;
+  assert(builder.str() == "16.500000");
+
+  builder = grnxx::StringBuilder(grnxx::STRING_BUILDER_AUTO_RESIZE);
+
+  builder << 2.75F;
+  assert(builder.str() == "2.750000");
+
+  builder = grnxx::StringBuilder(grnxx::STRING_BUILDER_AUTO_RESIZE);
+
+  builder << (1.0 / 0.0) << '/' << (-1.0 / 0.0) << '/' << (0.0 / 0.0);
+  assert(builder.str() == "inf/-inf/nan");
+}
+
+void test_bool() {
+  grnxx::StringBuilder builder(grnxx::STRING_BUILDER_AUTO_RESIZE);
+
+  builder << true << '/' << false;
+  assert(builder.str() == "true/false");
+}
+
+void test_void_pointer() {
+  grnxx::StringBuilder builder(grnxx::STRING_BUILDER_AUTO_RESIZE);
+
+  builder << reinterpret_cast<void *>(0x13579BDF);
+  if (sizeof(void *) == 4) {
+    assert(builder.str() == "0x13579BDF");
+  } else {
+    assert(builder.str() == "0x0000000013579BDF");
+  }
+
+  builder = grnxx::StringBuilder(grnxx::STRING_BUILDER_AUTO_RESIZE);
+
+  builder << static_cast<void *>(nullptr);
+  assert(builder.str() == "nullptr");
+}
+
+void test_zero_terminated_string() {
+  grnxx::StringBuilder builder(grnxx::STRING_BUILDER_AUTO_RESIZE);
+
+  builder << "Hello, " << "world!";
+  assert(builder.str() == "Hello, world!");
+
+  builder = grnxx::StringBuilder(grnxx::STRING_BUILDER_AUTO_RESIZE);
+
+  builder << static_cast<char *>(nullptr);
+  assert(builder.str() == "nullptr");
+}
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  test_basic_operations();
+  test_char();
+  test_integer();
+  test_floating_point_number();
+  test_bool();
+  test_void_pointer();
+  test_zero_terminated_string();
+
+  return 0;
+}

  Added: test/test_string_format.cpp (+234 -0) 100644
===================================================================
--- /dev/null
+++ test/test_string_format.cpp    2012-11-28 16:36:40 +0900 (8224532)
@@ -0,0 +1,234 @@
+/*
+  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 <cassert>
+#include <iomanip>
+#include <iostream>
+
+#include "logger.hpp"
+#include "string_format.hpp"
+#include "time.hpp"
+
+void test_align() {
+  grnxx::StringBuilder builder;
+
+  char buf[8];
+  builder = grnxx::StringBuilder(buf);
+
+  assert(builder << grnxx::StringFormat::align(
+         "ABC", 6, '-', grnxx::STRING_FORMAT_ALIGNMENT_LEFT));
+  assert(builder.str() == "ABC---");
+
+  builder = grnxx::StringBuilder(buf);
+
+  assert(builder << grnxx::StringFormat::align(
+         "ABC", 6, '-', grnxx::STRING_FORMAT_ALIGNMENT_RIGHT));
+  assert(builder.str() == "---ABC");
+
+  builder = grnxx::StringBuilder(buf);
+
+  assert(builder << grnxx::StringFormat::align(
+         "ABC", 6, '-', grnxx::STRING_FORMAT_ALIGNMENT_CENTER));
+  assert(builder.str() == "-ABC--");
+}
+
+void test_align_left() {
+  grnxx::StringBuilder builder;
+
+  char buf[8];
+  builder = grnxx::StringBuilder(buf);
+
+  assert(builder << grnxx::StringFormat::align_left(123, 5));
+  assert(builder.str() == "123  ");
+
+  builder = grnxx::StringBuilder(buf);
+
+  assert(builder << grnxx::StringFormat::align_left(234, 5, 'X'));
+  assert(builder.str() == "234XX");
+
+  builder = grnxx::StringBuilder(buf);
+
+  assert(!(builder << grnxx::StringFormat::align_left(345, 10, 'x')));
+  assert(builder.str() == "345xxxx");
+}
+
+void test_align_right() {
+  grnxx::StringBuilder builder;
+
+  char buf[8];
+  builder = grnxx::StringBuilder(buf);
+
+  assert(builder << grnxx::StringFormat::align_right(456, 5));
+  assert(builder.str() == "  456");
+
+  builder = grnxx::StringBuilder(buf);
+
+  assert(builder << grnxx::StringFormat::align_right(567, 5, 'X'));
+  assert(builder.str() == "XX567");
+
+  builder = grnxx::StringBuilder(buf);
+
+  assert(!(builder << grnxx::StringFormat::align_right(678, 8, 'x')));
+  assert(builder.str() == "xxxxx67");
+}
+
+void test_align_center() {
+  grnxx::StringBuilder builder;
+
+  char buf[8];
+  builder = grnxx::StringBuilder(buf);
+
+  assert(builder << grnxx::StringFormat::align_center(789, 5));
+  assert(builder.str() == " 789 ");
+
+  builder = grnxx::StringBuilder(buf);
+
+  assert(builder << grnxx::StringFormat::align_center(890, 5, 'X'));
+  assert(builder.str() == "X890X");
+
+  builder = grnxx::StringBuilder(buf);
+
+  assert(!(builder << grnxx::StringFormat::align_center(901, 8, 'x')));
+  assert(builder.str() == "xx901xx");
+}
+
+class StreamBuffer : public std::streambuf {
+ public:
+  StreamBuffer(void *buf_ptr, std::size_t buf_size) : std::streambuf() {
+    setp(static_cast<char *>(buf_ptr),
+         static_cast<char *>(buf_ptr) + buf_size);
+  }
+
+  virtual int overflow(int) {
+    return EOF;
+  }
+};
+
+class Stream : public std::ostream {
+ public:
+  Stream(void *buf_ptr, std::size_t buf_size)
+    : std::ostream(), stream_buffer_(buf_ptr, buf_size) {
+    rdbuf(&stream_buffer_);
+  }
+
+ private:
+  StreamBuffer stream_buffer_;
+};
+
+void benchmark() {
+  static const std::uint32_t LOOP_COUNT = 1 << 16;
+
+  char buf[1024] = "";
+  grnxx::Time start, end;
+
+
+  start = grnxx::Time::now();
+  for (std::uint32_t i = 0; i < LOOP_COUNT; ++i) {
+    std::snprintf(buf, sizeof(buf), "%d", __LINE__);
+  }
+  end = grnxx::Time::now();
+  GRNXX_NOTICE() << "std::snprintf(int): elapsed [ns]: "
+                 << ((end - start).nanoseconds() / LOOP_COUNT);
+
+  start = grnxx::Time::now();
+  for (std::uint32_t i = 0; i < LOOP_COUNT; ++i) {
+    std::snprintf(buf, sizeof(buf), "%04d", __LINE__);
+  }
+  end = grnxx::Time::now();
+  GRNXX_NOTICE() << "std::snprintf(align_right): elapsed [ns]: "
+                 << ((end - start).nanoseconds() / LOOP_COUNT);
+
+  start = grnxx::Time::now();
+  for (std::uint32_t i = 0; i < LOOP_COUNT; ++i) {
+    std::snprintf(buf, sizeof(buf), "%s:%d: %s: In %s(): %s",
+                  __FILE__, __LINE__, "error", __func__, "failed");
+  }
+  end = grnxx::Time::now();
+  GRNXX_NOTICE() << "std::snprintf(complex): elapsed [ns]: "
+                 << ((end - start).nanoseconds() / LOOP_COUNT);
+
+
+  start = grnxx::Time::now();
+  for (std::uint32_t i = 0; i < LOOP_COUNT; ++i) {
+    Stream stream(buf, sizeof(buf));
+    stream << __LINE__;
+  }
+  end = grnxx::Time::now();
+  GRNXX_NOTICE() << "std::ostream(int): elapsed [ns]: "
+                 << ((end - start).nanoseconds() / LOOP_COUNT);
+
+  start = grnxx::Time::now();
+  for (std::uint32_t i = 0; i < LOOP_COUNT; ++i) {
+    Stream stream(buf, sizeof(buf));
+    stream << std::setw(4) << std::setfill('0') << __LINE__;
+  }
+  end = grnxx::Time::now();
+  GRNXX_NOTICE() << "std::ostream(align_right): elapsed [ns]: "
+                 << ((end - start).nanoseconds() / LOOP_COUNT);
+
+  start = grnxx::Time::now();
+  for (std::uint32_t i = 0; i < LOOP_COUNT; ++i) {
+    Stream stream(buf, sizeof(buf));
+    stream << __FILE__ << ':' << __LINE__ << ": " << "error" << ": In "
+           << __func__ << "(): " << "failed";
+  }
+  end = grnxx::Time::now();
+  GRNXX_NOTICE() << "std::ostream(complex): elapsed [ns]: "
+                 << ((end - start).nanoseconds() / LOOP_COUNT);
+
+
+  start = grnxx::Time::now();
+  for (std::uint32_t i = 0; i < LOOP_COUNT; ++i) {
+    grnxx::StringBuilder(buf).builder() << __LINE__;
+  }
+  end = grnxx::Time::now();
+  GRNXX_NOTICE() << "grnxx::StringBuilder(int): elapsed [ns]: "
+                 << ((end - start).nanoseconds() / LOOP_COUNT);
+
+  start = grnxx::Time::now();
+  for (std::uint32_t i = 0; i < LOOP_COUNT; ++i) {
+    grnxx::StringBuilder(buf).builder()
+        << grnxx::StringFormat::align_right(__LINE__, 4, '0');
+  }
+  end = grnxx::Time::now();
+  GRNXX_NOTICE() << "grnxx::StringBuilder(align_right): elapsed [ns]: "
+                 << ((end - start).nanoseconds() / LOOP_COUNT);
+
+  start = grnxx::Time::now();
+  for (std::uint32_t i = 0; i < LOOP_COUNT; ++i) {
+    grnxx::StringBuilder(buf).builder()
+        << __FILE__ << ':' << __LINE__ << ": "
+        << "error" << ": In " << __func__ << "(): " << "failed";
+  }
+  end = grnxx::Time::now();
+  GRNXX_NOTICE() << "grnxx::StringBuilder(complex): elapsed [ns]: "
+                 << ((end - start).nanoseconds() / LOOP_COUNT);
+}
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  test_align();
+  test_align_left();
+  test_align_right();
+  test_align_center();
+  benchmark();
+
+  return 0;
+}

  Added: test/test_thread.cpp (+57 -0) 100644
===================================================================
--- /dev/null
+++ test/test_thread.cpp    2012-11-28 16:36:40 +0900 (e899fb2)
@@ -0,0 +1,57 @@
+/*
+  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 <cassert>
+
+#include "logger.hpp"
+#include "thread.hpp"
+#include "time.hpp"
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  enum { LOOP_COUNT = 1000 };
+
+  grnxx::Time start = grnxx::Time::now();
+  for (int i = 0; i < LOOP_COUNT; ++i) {
+    grnxx::Thread::switch_to_others();
+  }
+  grnxx::Time end = grnxx::Time::now();
+
+  GRNXX_NOTICE() << "switch_to_others(): elapsed [ns]: "
+                 << ((end - start).nanoseconds() / LOOP_COUNT);
+
+  start = grnxx::Time::now();
+  for (int i = 0; i < LOOP_COUNT; ++i) {
+    grnxx::Thread::sleep(grnxx::Duration(0));
+  }
+  end = grnxx::Time::now();
+
+  GRNXX_NOTICE() << "sleep(0): elapsed [ns]: "
+                 << ((end - start).nanoseconds() / LOOP_COUNT);
+
+  start = grnxx::Time::now();
+  grnxx::Thread::sleep(grnxx::Duration::milliseconds(10));
+  end = grnxx::Time::now();
+
+  GRNXX_NOTICE() << "sleep(10ms): elapsed [ns] = "
+                 << (end - start).nanoseconds();
+
+  return 0;
+}

  Added: test/test_time.cpp (+61 -0) 100644
===================================================================
--- /dev/null
+++ test/test_time.cpp    2012-11-28 16:36:40 +0900 (ce52a36)
@@ -0,0 +1,61 @@
+/*
+  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 <cassert>
+
+#include "logger.hpp"
+#include "time.hpp"
+
+int main() {
+  grnxx::Logger::set_flags(grnxx::LOGGER_WITH_ALL |
+                           grnxx::LOGGER_ENABLE_COUT);
+  grnxx::Logger::set_max_level(grnxx::NOTICE_LOGGER);
+
+  grnxx::Time time;
+
+  assert(time == time.invalid_time());
+  assert(!time);
+
+  GRNXX_NOTICE() << "grnxx::Time::now: " << grnxx::Time::now();
+  GRNXX_NOTICE() << "grnxx::Time::now_in_seconds: "
+                 << grnxx::Time::now_in_seconds();
+
+  enum { LOOP_COUNT = 1 << 16 };
+
+  grnxx::Time start = grnxx::Time::now();
+  for (int i = 0; i < LOOP_COUNT; ++i) {
+    grnxx::Time::now();
+  }
+  grnxx::Time end = grnxx::Time::now();
+
+  grnxx::Duration elapsed = end - start;
+  GRNXX_NOTICE() << "grnxx::Time::now: average elapsed [ns] = "
+                 << (elapsed.nanoseconds() / LOOP_COUNT);
+
+  start = grnxx::Time::now();
+  for (int i = 0; i < LOOP_COUNT; ++i) {
+    grnxx::Time::now_in_seconds();
+  }
+  end = grnxx::Time::now();
+
+  elapsed = end - start;
+  GRNXX_NOTICE() << "grnxx::Time::now_in_seconds"
+                 << ": average elapsed [ns] = "
+                 << (elapsed.nanoseconds() / LOOP_COUNT);
+
+  return 0;
+}




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