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, ¤t_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, ¤t_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(¤t_time); + if (error_code != 0) { + // In practice, ::_ftime_s() must not fail. + return now_in_seconds(); + } +# else // GRNXX_MSC + ::_ftime(¤t_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, ¤t_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(¤t_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; +}