--- /dev/null
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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.
+\f
+ 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
+\f
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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!
+
+
--- /dev/null
+2008-04-22 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcbdb.c (tcbdbcmpdecimal, tcbdbcmpint32, tcbdbcmpint64): bugs of overflow were fixed.
+
+ - Release: 1.2.5
+
+2008-04-13 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcbdb.c (tcbdbopenimpl): comparison function checking was added.
+
+ * tcadb.c (tcadbopen): "capnum" option is now supported for B+ tree.
+
+ - Release: 1.2.4
+
+2008-04-07 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcutil.c (tcregexmatch, tcregexreplace, tcglobpat): new functions.
+
+ * tcbdb.c (tcbdbcmpfunc, tcbdbcmpop): new functions.
+
+ * tcbdb.c (tcbdboptimizeimpl): leaf size limitation is now implemented.
+
+ * tcbdb.c (tcbdbsetcapnum): new function.
+
+ - Release: 1.2.3
+
+2008-03-19 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcutil.c (tcmapnew2): "calloc" is now used.
+
+ * tcbdb.c (tcbdbputimpl): algorithm to divide large leaves was modified.
+
+ - Release: 1.2.2
+
+2008-03-13 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcutil.c (tclistelemcmp, tclistelemcmpci): bugs about multibyte ordering were fixed.
+
+ * tcutil.c (tclistsortex): new function.
+
+ * tcbdb.c (tcbdbsetecode): new function.
+
+ * tcadb.c (tcadbopen): "c", "t", "e", and "w" options were added.
+
+ - Release: 1.2.1
+
+2008-02-18 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcmdb.c (tcmdbfwmkeys, tcmdbfwmkeys2): new functions.
+
+ * tchdb.c (tchdbfwmkeys, tchdbfwmkeys2): new functions.
+
+ * tcbdb.c (tcbdbfwmkeys, tcbdbfwmkeys2): new functions instead of "tcbdbrange3".
+
+ * tcadb.c (tcadbfwmkeys, tcadbfwmkeys2): new functions.
+
+ * tcbdb.c (tcbdbrangeimpl): a bug related to mutex operation was fixed.
+
+ - The library version was bumped up for some binary packages.
+
+ - Release: 1.2.0
+
+2008-02-15 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcutil.c (tcbwtsortchrcount): time efficiency was improved.
+
+ - The library version was bumped up for some binary packages.
+
+ - Release: 1.1.15
+
+2008-02-05 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcatest.c (runwicked, procwicked): new functions.
+
+ - Release: 1.1.14
+
+2008-01-30 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcutil.c (tctime): measure unit was changed to in seconds.
+
+ * tchdb.c (tchdbcopy): shared lock is now used.
+
+ * tcadb.c (tcadbsync, tcadbcopy): new functions.
+
+ - Release: 1.1.13
+
+2008-01-23 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcbdb.c (tcbdbleafkill, tcbdbnodesubidx): new functions.
+
+ * tcbtest.c (runqueue, procqueue): new functions.
+
+ - Release: 1.1.12
+
+2008-01-20 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcutil.c (tcmapmsiz, tcmdbmsiz, tcadbsize): new functions.
+
+ * tcutil.c (tcglobalmutexshared): new function.
+
+ * tcutil.c (tcglobalmutexinit, tcglobalmutexdestroy): new functions.
+
+ * tcutil.c (tcreadfile): a bug related to the size assignment was fixed.
+
+ * tcadb.c (tcadbopen): "capsiz" parameter was added.
+
+ - Release: 1.1.11
+
+2008-01-17 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcutil.c (tclistshift, tclistshift2): memory alignment was improved.
+
+ * tcutil.c (tcvxstrprintf): a bug related to format of long long integer was fixed.
+
+ - Release: 1.1.10
+
+2008-01-10 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcutil.c (tcmdbvsiz, tcmdbvsiz2): new functions.
+
+ * tcutil.c (tcmdbiternext): a bug related to handling sparse map was fixed.
+
+ * tcadb.h, tcadb.c: new files.
+
+ * tcatest.c, tcadb.c: new files.
+
+ - Release: 1.1.9
+
+2008-01-03 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcutil.c (tcstrutftoucs, tcstrucstoutf, tcstrjoin): new function.
+
+ * tcutil.c (tcstrdist, tcstrdistutf): new function.
+
+ - Release: 1.1.8
+
+2007-12-28 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * myconf.h: some header includings were removed for environments not conforming to C99.
+
+ * tcutil.c (tctccalendar, tcdatestrwww, tcdatestrhttp): new functions.
+
+ * tcutil.c (tcmapaddint): new function.
+
+ * tcucodec.c (rundate, procdate): new functions.
+
+ - Release: 1.1.7
+
+2007-12-24 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tchdb.c (tcseekread, tcseekwrite): pread and pwrite were to be used.
+
+ - Release: 1.1.6
+
+2007-12-21 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcutil.c (tcmdbnew, tcmdbput, tcmdbget): concurrency was improved.
+
+ * tcutil.c (tcmapcutfront, tcmdbcutfront): new functions.
+
+ * tchdb.c (tchdbasyncimpl): large deferred buffer was to be flushed.
+
+ * tcumttest.c: new file.
+
+ - Release: 1.1.5
+
+2007-12-19 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcutil.c (tclrand, tcdrand, tcdrandnd): new functions.
+
+ * tchdb.c (tchdbcacheadjust): count checking was removed.
+
+ * tchmttest.c (myrandnd): new function.
+
+ * tcbmttest.c (myrandnd): new function.
+
+ - Release: 1.1.4
+
+2007-12-10 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcutil.c (tcglobalmutexlock): the type of the global mutex was changed to rwlock.
+
+ * tchdb.c (tchdbsetmutex, tchdbopenimpl): multiple reading file descriptors were added.
+
+ * tchmttest.c (runtypical, proctypical): reading ratio assignment was added.
+
+ * tcbmttest.c (runtypical, proctypical): reading ratio assignment was added.
+
+ - Release: 1.1.3
+
+2007-12-08 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcutil.c (tcmpoolput): mutex for operations was added.
+
+ - Release: 1.1.2
+
+2007-12-08 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcutil.c (tcberencode, tcberdecode): new functions.
+
+ * tcbdb.c (tcbdbleafload): speed was improved.
+
+ - Release: 1.1.1
+
+2007-12-07 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tchdb.c (tchdbsetcache): new function.
+
+ * tchmttest.c (procwrite, procread): random mode was added.
+
+ * tcbmttest.c (procwrite, procread): random mode was added.
+
+ - Release: 1.1.0
+
+2007-12-01 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tchmgr.c (procget): a bug related to the open omode was fixed.
+
+ * tchdb.c (tchdbreadrec): a bug related to overflow on 32-bit environment was fixed.
+
+ * tchdb.c (tchdbgetbucket): the type of the return value was changed.
+
+ * tcbdb.c (tcbdbcurfirst, tcbdbcurnext): cache adjustment was to be performed.
+
+ * tcbdb.c (tcbdbrange, tcbdbrange2, tcbdbrange3): new functions.
+
+ * tchmgr.c (proclist): "-m" option was added.
+
+ * tchmgr.c (procimporttsv): new function.
+
+ * tcbmgr.c (proclist): "-m", "-rb", and "-rp" options were added.
+
+ * tcbmgr.c (procimporttsv): new function.
+
+ - Release: 1.0.9
+
+2007-11-28 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tchmttest.c (threadtypical): a test case was added.
+
+ * tchdb.c (tchdbfbpmerge): a bug related to iterator was fixed.
+
+ * tcbdb.c (tcbdbgetimpl): a bug related to concurrent cache cleaning was fixed.
+
+ - Release: 1.0.8
+
+2007-11-20 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tchdb.c (tchdblockmethod): a bug related to R/W lock was fixed.
+
+ * tcbdb.c (tcbdblockmethod): a bug related to R/W lock was fixed.
+
+ * tchdb.c, tcbdb.c: locking functions were re-imlemented as macros.
+
+ * tchmttest.c, tcbmttest.c: test cases of typical operations were added.
+
+ * tokyocabinet.idl: methods handling list parameters were added.
+
+ - Release: 1.0.7
+
+2007-11-15 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcbdb.c (tcbdboptimize): the default behaviour of bnum was changed.
+
+ - Release: 1.0.6
+
+2007-11-10 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcutil.c (tcrealpath, tccopyfile): new functions.
+
+ * tchdb.c (tchdbcopy): new function.
+
+ * tcbdb.c (tcbdbcopy): new function.
+
+ - Release: 1.0.5
+
+2007-11-10 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcbdb.c (tcbdbtune): comparing functions were optimized with macros.
+
+ * tcbdb.c (tcbdbsetlsmax): new function.
+
+ * myconf.c, tcutil.c, tchdb.c, tcbdb.c: code cleanup and optimization.
+
+ - Release: 1.0.4
+
+2007-11-08 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcbdb.c (tcbdbleafsave, tcbdbleafload): warnings on 64-bit system were cleared.
+
+ * configure.in: 64-bit offset mode for 32-bit system was added.
+
+ * Makefile.in: Mac OS X is now supported.
+
+ - Release: 1.0.3
+
+2007-11-01 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcbdb.c (tcbdbdel): a bug that opened database was not closed was fixed.
+
+ * tcbdb.c (tcbdbcurout): a bug of deleting always the first value was fixed.
+
+ * tcbdb.c (tcbdbopenimpl): a potential bug of not initializing meta data was fixed.
+
+ * tcutil.h, tchdb.h, tcbdb.h: wrapped in C linkage block for C++.
+
+ * tokyocabinet.idl: definition of constants were added.
+
+ - commands for performance test were added.
+
+ - Release: 1.0.2
+
+2007-10-28 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcbdb.c (tchdboptimize): a bug related to custom comparison function was fixed.
+
+ * tcbdb.c (tcbdbleafsave): empty pages was to be removed for time space efficiency.
+
+ * tcbdb.c (tcbdbputdup3): new function.
+
+ * tchdb.c (tchdbvanish): new function.
+
+ * tcbdb.c (tcbdbvanish): new function.
+
+ * tokyocabinet.idl: new file.
+
+ - Release: 1.0.1
+
+2007-10-24 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tokyocabinet.pc.in: new file.
+
+ - document files were fulfilled.
+
+ - Release: 1.0.0
+
+2007-10-15 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tcbdb.c (tchdbtranbegin): locking mode was aolished.
+
+ * tcbdb.c (tcbdbsetcmpfunc): new function.
+
+ - Release: 0.4.1
+
+2007-10-11 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tchdb.c (tchdbopenimpl): database corruption was to be handled automatically.
+
+ * tcbdb.c, tcbtest.c, tcbmttest.c, tcbmgr.c: new files.
+
+ - Release: 0.4.0
+
+2007-09-09 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tchdb.c (tchdbsetmutex, tchdblockobj, tchdbunlockobj): new functions.
+
+ * tchmttest.c: new file.
+
+ - Release: 0.3.4
+
+2007-09-05 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tchdb.c (tchdbopen): TCBS compression mode is now supported.
+
+ - Release: 0.3.3
+
+2007-09-01 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tchdb.c (tcbsencode, tcbsdecode): new functions.
+
+ - Release: 0.3.2
+
+2007-08-25 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tchdb.c (tcpackencode, tcpackdecode): new functions.
+
+ * tchdb.c (tcbwtencode, tcbwtdecode): new functions.
+
+ - Release: 0.3.1
+
+2007-08-22 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tchdb.c (tchdbputasync, tchdbputasync2): new functions.
+
+ - Release: 0.3.0
+
+2007-08-18 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tchdb.c (tchdboptimize): a bug causing data corruption was fixed.
+
+ - Release: 0.2.8
+
+2007-08-15 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tchdb.c (tchdboptimize): new function.
+
+ - Release: 0.2.7
+
+2007-08-14 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tchdb.c (tchdbsavefbp, tchdbloadfbp): a bug related to 64-bit support was fixed.
+
+ * tchdb.c (tchdbreadrec, tchdbwriterec): a bug related to 64-bit support was fixed.
+
+ - Release: 0.2.6
+
+2007-08-13 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ * tchdb.c (tchdbsavefbp, tchdbloadfbp): new functions.
+
+ - Release: 0.2.5
+
+2007-08-12 Mikio Hirabayashi <mikio@users.sourceforge.net>
+
+ - The initial version.
+
+ - Release: 0.2.4
+
--- /dev/null
+# Makefile for Tokyo Cabinet
+
+
+
+#================================================================
+# Setting Variables
+#================================================================
+
+
+# Generic settings
+SHELL = @SHELL@
+
+# Package information
+PACKAGE = @PACKAGE_NAME@
+VERSION = @PACKAGE_VERSION@
+PACKAGEDIR = $(PACKAGE)-$(VERSION)
+PACKAGETGZ = $(PACKAGE)-$(VERSION).tar.gz
+LIBVER = @MYLIBVER@
+LIBREV = @MYLIBREV@
+FORMATVER = @MYFORMATVER@
+
+# Targets
+HEADERFILES = @MYHEADERFILES@
+LIBRARYFILES = @MYLIBRARYFILES@
+LIBOBJFILES = @MYLIBOBJFILES@
+COMMANDFILES = @MYCOMMANDFILES@
+MAN1FILES = @MYMAN1FILES@
+MAN3FILES = @MYMAN3FILES@
+DOCUMENTFILES = @MYDOCUMENTFILES@
+PCFILES = @MYPCFILES@
+
+# Install destinations
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+datarootdir = @datarootdir@
+INCLUDEDIR = @includedir@
+LIBDIR = @libdir@
+BINDIR = @bindir@
+LIBEXECDIR = @libexecdir@
+DATADIR = @datadir@/$(PACKAGE)
+MAN1DIR = @mandir@/man1
+MAN3DIR = @mandir@/man3
+PCDIR = @libdir@/pkgconfig
+DESTDIR =
+
+# Building configuration
+CC = @CC@
+CPPFLAGS = @MYCPPFLAGS@ \
+ -D_TC_PREFIX="\"$(prefix)\"" -D_TC_INCLUDEDIR="\"$(INCLUDEDIR)\"" \
+ -D_TC_LIBDIR="\"$(LIBDIR)\"" -D_TC_BINDIR="\"$(BINDIR)\"" -D_TC_LIBEXECDIR="\"$(LIBEXECDIR)\"" \
+ -D_TC_APPINC="\"-I$(INCLUDEDIR)\"" -D_TC_APPLIBS="\"-L$(LIBDIR) -ltokyocabinet @LIBS@\""
+CFLAGS = @MYCFLAGS@
+LDFLAGS = @MYLDFLAGS@
+LIBS = @LIBS@
+LDENV = LD_RUN_PATH=/lib:/usr/lib:$(LIBDIR):$(HOME)/lib:/usr/local/lib:@MYRUNPATH@:.
+RUNENV = @MYLDLIBPATHENV@=.:/lib:/usr/lib:$(LIBDIR):$(HOME)/lib:/usr/local/lib:@MYRUNPATH@
+POSTCMD = @MYPOSTCMD@
+NO_ECHO = @
+
+
+#================================================================
+# Suffix rules
+#================================================================
+
+
+.SUFFIXES :
+.SUFFIXES : .c .o
+
+.c.o :
+ @echo "Compiling $<"
+ $(NO_ECHO)$(CC) -c $(CPPFLAGS) $(CFLAGS) $<
+
+
+
+#================================================================
+# Actions
+#================================================================
+
+all: $(LIBRARYFILES)
+
+not-all : $(LIBRARYFILES) $(COMMANDFILES)
+ @$(POSTCMD)
+ @printf '\n'
+ @printf '#================================================================\n'
+ @printf '# Ready to install.\n'
+ @printf '#================================================================\n'
+
+
+clean :
+ rm -rf $(LIBRARYFILES) $(LIBOBJFILES) $(COMMANDFILES) \
+ *.o a.out check.in check.out gmon.out leak.log casket casket-* casket.* *~ hoge moge
+
+
+version :
+ vernum=`expr $(LIBVER)00 + $(LIBREV)` ; \
+ sed -e 's/_TC_VERSION.*/_TC_VERSION "$(VERSION)"/' \
+ -e "s/_TC_LIBVER.*/_TC_LIBVER $$vernum/" \
+ -e 's/_TC_FORMATVER.*/_TC_FORMATVER "$(FORMATVER)"/' tcutil.h > tcutil.h~
+ [ -f tcutil.h~ ] && mv -f tcutil.h~ tcutil.h
+
+untabify :
+ ls *.c *.h *.idl | while read name ; \
+ do \
+ sed -e 's/\t/ /g' -e 's/ *$$//' $$name > $$name~; \
+ [ -f $$name~ ] && mv -f $$name~ $$name ; \
+ done
+
+install:
+
+install-strip:
+
+uninstall:
+
+do-notinstall :
+ mkdir -p $(DESTDIR)$(INCLUDEDIR)
+ cp -Rf $(HEADERFILES) $(DESTDIR)$(INCLUDEDIR)
+ mkdir -p $(DESTDIR)$(LIBDIR)
+ cp -Rf $(LIBRARYFILES) $(DESTDIR)$(LIBDIR)
+ mkdir -p $(DESTDIR)$(BINDIR)
+ cp -Rf $(COMMANDFILES) $(DESTDIR)$(BINDIR)
+ mkdir -p $(DESTDIR)$(DATADIR)
+ cp -Rf $(DOCUMENTFILES) $(DESTDIR)$(DATADIR)
+ mkdir -p $(DESTDIR)$(MAN1DIR)
+ cd man && cp -Rf $(MAN1FILES) $(DESTDIR)$(MAN1DIR)
+ mkdir -p $(DESTDIR)$(MAN3DIR)
+ cd man && cp -Rf $(MAN3FILES) $(DESTDIR)$(MAN3DIR)
+ mkdir -p $(DESTDIR)$(PCDIR)
+ cp -Rf $(PCFILES) $(DESTDIR)$(PCDIR)
+ @printf '\n'
+ @printf '#================================================================\n'
+ @printf '# Thanks for using Tokyo Cabinet.\n'
+ @printf '#================================================================\n'
+
+
+do-notinstall-strip :
+ make DESTDIR=$(DESTDIR) install
+ cd $(DESTDIR)$(BINDIR) && strip $(MYCOMMANDS)
+
+
+do-notuninstall :
+ cd $(DESTDIR)$(INCLUDEDIR) && rm -f $(HEADERFILES)
+ cd $(DESTDIR)$(LIBDIR) && rm -f $(LIBRARYFILES)
+ cd $(DESTDIR)$(BINDIR) && rm -f $(COMMANDFILES)
+ cd $(DESTDIR)$(MAN1DIR) && rm -f $(MAN1FILES)
+ cd $(DESTDIR)$(MAN3DIR) && rm -f $(MAN3FILES)
+ rm -rf $(DESTDIR)$(DATADIR)
+ cd $(DESTDIR)$(PCDIR) && rm -f $(PCFILES)
+
+
+dist :
+ make version
+ make untabify
+ make distclean
+ cd .. && tar cvf - $(PACKAGEDIR) | gzip -c > $(PACKAGETGZ)
+ sync ; sync
+
+
+distclean : clean
+ rm -rf Makefile tokyocabinet.pc config.cache config.log config.status autom4te.cache
+
+
+check :
+ rm -rf casket*
+ $(RUNENV) $(RUNCMD) ./tcamgr version
+ $(RUNENV) $(RUNCMD) ./tcutest xstr 50000
+ $(RUNENV) $(RUNCMD) ./tcutest list 50000
+ $(RUNENV) $(RUNCMD) ./tcutest map 50000
+ $(RUNENV) $(RUNCMD) ./tcutest mdb 50000
+ $(RUNENV) $(RUNCMD) ./tcutest misc 500
+ $(RUNENV) $(RUNCMD) ./tcutest wicked 50000
+ $(RUNENV) $(RUNCMD) ./tcumttest combo 5 50000 500
+ $(RUNENV) $(RUNCMD) ./tcumttest combo -rnd 5 50000 500
+ $(RUNENV) $(RUNCMD) ./tchmttest typical casket 5 50000 5000
+ $(RUNENV) $(RUNCMD) ./tchmttest typical -rr 1000 casket 5 50000 5000
+ $(RUNENV) $(RUNCMD) ./tchmttest typical -rc 50000 -nc casket 5 50000 5000
+ $(RUNENV) $(RUNCMD) ./tcucodec url Makefile > check.in
+ $(RUNENV) $(RUNCMD) ./tcucodec url -d check.in > check.out
+ $(RUNENV) $(RUNCMD) ./tcucodec base Makefile > check.in
+ $(RUNENV) $(RUNCMD) ./tcucodec base -d check.in > check.out
+ $(RUNENV) $(RUNCMD) ./tcucodec quote Makefile > check.in
+ $(RUNENV) $(RUNCMD) ./tcucodec quote -d check.in > check.out
+ $(RUNENV) $(RUNCMD) ./tcucodec mime Makefile > check.in
+ $(RUNENV) $(RUNCMD) ./tcucodec mime -d check.in > check.out
+ $(RUNENV) $(RUNCMD) ./tcucodec pack -bwt Makefile > check.in
+ $(RUNENV) $(RUNCMD) ./tcucodec pack -d -bwt check.in > check.out
+ $(RUNENV) $(RUNCMD) ./tcucodec tcbs Makefile > check.in
+ $(RUNENV) $(RUNCMD) ./tcucodec tcbs -d check.in > check.out
+ $(RUNENV) $(RUNCMD) ./tcucodec zlib Makefile > check.in
+ $(RUNENV) $(RUNCMD) ./tcucodec zlib -d check.in > check.out
+ $(RUNENV) $(RUNCMD) ./tcucodec xml Makefile > check.in
+ $(RUNENV) $(RUNCMD) ./tcucodec xml -d check.in > check.out
+ $(RUNENV) $(RUNCMD) ./tcucodec ucs Makefile > check.in
+ $(RUNENV) $(RUNCMD) ./tcucodec ucs -d check.in > check.out
+ $(RUNENV) $(RUNCMD) ./tcucodec date -ds '1978-02-11T18:05:30+09:00' -rf > check.out
+ $(RUNENV) $(RUNCMD) ./tcucodec conf
+ $(RUNENV) $(RUNCMD) ./tchtest write casket 50000 5000 5 5
+ $(RUNENV) $(RUNCMD) ./tchtest read casket
+ $(RUNENV) $(RUNCMD) ./tchtest remove casket
+ $(RUNENV) $(RUNCMD) ./tchtest write -mt -tl -td -rc 50 casket 50000 5000 5 5
+ $(RUNENV) $(RUNCMD) ./tchtest read -mt -nb -rc 50 casket
+ $(RUNENV) $(RUNCMD) ./tchtest remove -mt -rc 50 casket
+ $(RUNENV) $(RUNCMD) ./tchtest write -as -tb -rc 50 casket 50000 50000 5 5
+ $(RUNENV) $(RUNCMD) ./tchtest read -nl -rc 50 casket
+ $(RUNENV) $(RUNCMD) ./tchtest remove -rc 50 casket
+ $(RUNENV) $(RUNCMD) ./tchtest rcat -pn 500 casket 50000 5000 5 5
+ $(RUNENV) $(RUNCMD) ./tchtest rcat -tl -td -pn 5000 casket 50000 500 5 15
+ $(RUNENV) $(RUNCMD) ./tchtest rcat -nl -pn 500 -rl casket 5000 500 5 5
+ $(RUNENV) $(RUNCMD) ./tchtest rcat -tb -pn 500 casket 5000 500 5 5
+ $(RUNENV) $(RUNCMD) ./tchmgr list -pv casket > check.out
+ $(RUNENV) $(RUNCMD) ./tchmgr list -pv -fm 1 -px casket > check.out
+ $(RUNENV) $(RUNCMD) ./tchtest misc casket 5000
+ $(RUNENV) $(RUNCMD) ./tchtest misc -tl -td casket 5000
+ $(RUNENV) $(RUNCMD) ./tchtest misc -mt -tb casket 500
+ $(RUNENV) $(RUNCMD) ./tchtest wicked casket 50000
+ $(RUNENV) $(RUNCMD) ./tchtest wicked -tl -td casket 50000
+ $(RUNENV) $(RUNCMD) ./tchtest wicked -mt -tb casket 5000
+ $(RUNENV) $(RUNCMD) ./tchmttest write -tl casket 5 5000 500 5
+ $(RUNENV) $(RUNCMD) ./tchmttest read casket 5
+ $(RUNENV) $(RUNCMD) ./tchmttest read -rnd casket 5
+ $(RUNENV) $(RUNCMD) ./tchmttest remove casket 5
+ $(RUNENV) $(RUNCMD) ./tchmttest wicked -nc casket 5 5000
+ $(RUNENV) $(RUNCMD) ./tchmttest wicked -tl -td casket 5 5000
+ $(RUNENV) $(RUNCMD) ./tchmttest wicked -tb casket 5 5000
+ $(RUNENV) $(RUNCMD) ./tchmttest typical casket 5 50000 5000
+ $(RUNENV) $(RUNCMD) ./tchmttest typical -rr 1000 casket 5 50000 5000
+ $(RUNENV) $(RUNCMD) ./tchmttest typical -tl -rc 50000 -nc casket 5 50000 5000
+ $(RUNENV) $(RUNCMD) ./tchmgr create casket 3 1 1
+ $(RUNENV) $(RUNCMD) ./tchmgr inform casket
+ $(RUNENV) $(RUNCMD) ./tchmgr put casket one first
+ $(RUNENV) $(RUNCMD) ./tchmgr put casket two second
+ $(RUNENV) $(RUNCMD) ./tchmgr put -dk casket three third
+ $(RUNENV) $(RUNCMD) ./tchmgr put -dc casket three third
+ $(RUNENV) $(RUNCMD) ./tchmgr put -dc casket three third
+ $(RUNENV) $(RUNCMD) ./tchmgr put -dc casket three third
+ $(RUNENV) $(RUNCMD) ./tchmgr put casket four fourth
+ $(RUNENV) $(RUNCMD) ./tchmgr put -dk casket five fifth
+ $(RUNENV) $(RUNCMD) ./tchmgr out casket one
+ $(RUNENV) $(RUNCMD) ./tchmgr out casket two
+ $(RUNENV) $(RUNCMD) ./tchmgr get casket three > check.out
+ $(RUNENV) $(RUNCMD) ./tchmgr get casket four > check.out
+ $(RUNENV) $(RUNCMD) ./tchmgr get casket five > check.out
+ $(RUNENV) $(RUNCMD) ./tchmgr list -pv casket > check.out
+ $(RUNENV) $(RUNCMD) ./tchmgr optimize casket
+ $(RUNENV) $(RUNCMD) ./tchmgr put -dc casket three third
+ $(RUNENV) $(RUNCMD) ./tchmgr get casket three > check.out
+ $(RUNENV) $(RUNCMD) ./tchmgr get casket four > check.out
+ $(RUNENV) $(RUNCMD) ./tchmgr get casket five > check.out
+ $(RUNENV) $(RUNCMD) ./tchmgr list -pv casket > check.out
+ $(RUNENV) $(RUNCMD) ./tcbtest write casket 50000 5 5 5000 5 5
+ $(RUNENV) $(RUNCMD) ./tcbtest read casket
+ $(RUNENV) $(RUNCMD) ./tcbtest remove casket
+ $(RUNENV) $(RUNCMD) ./tcbmgr list -rb 00001000 00002000 casket > check.out
+ $(RUNENV) $(RUNCMD) ./tcbmgr list -fm 000001 casket > check.out
+ $(RUNENV) $(RUNCMD) ./tcbtest write -mt -tl -td -ls 1024 casket 50000 5000 5000 5000 5 5
+ $(RUNENV) $(RUNCMD) ./tcbtest read -mt -nb casket
+ $(RUNENV) $(RUNCMD) ./tcbtest remove -mt casket
+ $(RUNENV) $(RUNCMD) ./tcbtest write -tb casket 50000 5 5 50000 5 5
+ $(RUNENV) $(RUNCMD) ./tcbtest read -nl casket
+ $(RUNENV) $(RUNCMD) ./tcbtest remove casket
+ $(RUNENV) $(RUNCMD) ./tcbtest rcat -lc 5 -nc 5 -pn 500 casket 50000 5 5 5000 5 5
+ $(RUNENV) $(RUNCMD) ./tcbtest rcat -tl -td -pn 5000 casket 50000 5 5 500 5 15
+ $(RUNENV) $(RUNCMD) ./tcbtest rcat -nl -pn 5000 -rl casket 15000 5 5 500 5 5
+ $(RUNENV) $(RUNCMD) ./tcbtest rcat -ca 1000 -tb -pn 5000 casket 15000 5 5 500 5 5
+ $(RUNENV) $(RUNCMD) ./tcbmgr list -pv casket > check.out
+ $(RUNENV) $(RUNCMD) ./tcbtest queue casket 15000 5 5
+ $(RUNENV) $(RUNCMD) ./tcbtest misc casket 5000
+ $(RUNENV) $(RUNCMD) ./tcbtest misc -tl -td casket 5000
+ $(RUNENV) $(RUNCMD) ./tcbtest misc -mt -tb casket 500
+ $(RUNENV) $(RUNCMD) ./tcbtest wicked casket 50000
+ $(RUNENV) $(RUNCMD) ./tcbtest wicked -tl -td casket 50000
+ $(RUNENV) $(RUNCMD) ./tcbtest wicked -mt -tb casket 5000
+ $(RUNENV) $(RUNCMD) ./tcbtest write -cd -lc 5 -nc 5 casket 5000 5 5 5 5 5
+ $(RUNENV) $(RUNCMD) ./tcbtest read -cd -lc 5 -nc 5 casket
+ $(RUNENV) $(RUNCMD) ./tcbtest remove -cd -lc 5 -nc 5 casket
+ $(RUNENV) $(RUNCMD) ./tcbmgr list -pv casket > check.out
+ $(RUNENV) $(RUNCMD) ./tcbtest write -ci -td -lc 5 -nc 5 casket 5000 5 5 5 5 5
+ $(RUNENV) $(RUNCMD) ./tcbtest read -ci -lc 5 -nc 5 casket
+ $(RUNENV) $(RUNCMD) ./tcbtest remove -ci -lc 5 -nc 5 casket
+ $(RUNENV) $(RUNCMD) ./tcbmgr list -pv casket > check.out
+ $(RUNENV) $(RUNCMD) ./tcbtest write -cj -tb -lc 5 -nc 5 casket 5000 5 5 5 5 5
+ $(RUNENV) $(RUNCMD) ./tcbtest read -cj -lc 5 -nc 5 casket
+ $(RUNENV) $(RUNCMD) ./tcbtest remove -cj -lc 5 -nc 5 casket
+ $(RUNENV) $(RUNCMD) ./tcbmgr list -pv casket > check.out
+ $(RUNENV) $(RUNCMD) ./tcbmttest write -tl casket 5 5000 5 5 500 5
+ $(RUNENV) $(RUNCMD) ./tcbmttest read casket 5
+ $(RUNENV) $(RUNCMD) ./tcbmttest read -rnd casket 5
+ $(RUNENV) $(RUNCMD) ./tcbmttest remove casket 5
+ $(RUNENV) $(RUNCMD) ./tcbmttest wicked -nc casket 5 5000
+ $(RUNENV) $(RUNCMD) ./tcbmttest wicked -tl -td casket 5 5000
+ $(RUNENV) $(RUNCMD) ./tchmttest wicked -tb casket 5 5000
+ $(RUNENV) $(RUNCMD) ./tcbmttest typical casket 5 50000 5 5
+ $(RUNENV) $(RUNCMD) ./tcbmttest typical -rr 1000 casket 5 50000 5 5
+ $(RUNENV) $(RUNCMD) ./tcbmttest typical -tl -nc casket 5 50000 5 5
+ $(RUNENV) $(RUNCMD) ./tcbmgr create casket 4 4 3 1 1
+ $(RUNENV) $(RUNCMD) ./tcbmgr inform casket
+ $(RUNENV) $(RUNCMD) ./tcbmgr put casket one first
+ $(RUNENV) $(RUNCMD) ./tcbmgr put casket two second
+ $(RUNENV) $(RUNCMD) ./tcbmgr put -dk casket three third
+ $(RUNENV) $(RUNCMD) ./tcbmgr put -dc casket three third
+ $(RUNENV) $(RUNCMD) ./tcbmgr put -dc casket three third
+ $(RUNENV) $(RUNCMD) ./tcbmgr put -dd casket three third
+ $(RUNENV) $(RUNCMD) ./tcbmgr put -dd casket three third
+ $(RUNENV) $(RUNCMD) ./tcbmgr put casket four fourth
+ $(RUNENV) $(RUNCMD) ./tcbmgr put -dk casket five fifth
+ $(RUNENV) $(RUNCMD) ./tcbmgr out casket one
+ $(RUNENV) $(RUNCMD) ./tcbmgr out casket two
+ $(RUNENV) $(RUNCMD) ./tcbmgr get casket three > check.out
+ $(RUNENV) $(RUNCMD) ./tcbmgr get casket four > check.out
+ $(RUNENV) $(RUNCMD) ./tcbmgr get casket five > check.out
+ $(RUNENV) $(RUNCMD) ./tcbmgr list -pv casket > check.out
+ $(RUNENV) $(RUNCMD) ./tcbmgr list -j three -pv casket > check.out
+ $(RUNENV) $(RUNCMD) ./tcbmgr optimize casket
+ $(RUNENV) $(RUNCMD) ./tcbmgr put -dc casket three third
+ $(RUNENV) $(RUNCMD) ./tcbmgr get casket three > check.out
+ $(RUNENV) $(RUNCMD) ./tcbmgr get casket four > check.out
+ $(RUNENV) $(RUNCMD) ./tcbmgr get casket five > check.out
+ $(RUNENV) $(RUNCMD) ./tcbmgr list -pv casket > check.out
+ $(RUNENV) $(RUNCMD) ./tcatest write 'casket.tch#mode=wct#bnum=5000' 50000
+ $(RUNENV) $(RUNCMD) ./tcatest read 'casket.tch#mode=r'
+ $(RUNENV) $(RUNCMD) ./tcatest remove 'casket.tch#mode=w'
+ $(RUNENV) $(RUNCMD) ./tcatest misc 'casket.tch#mode=wct#bnum=500#opts=ld' 5000
+ $(RUNENV) $(RUNCMD) ./tcatest wicked 'casket.tch#mode=wct' 5000
+ $(RUNENV) $(RUNCMD) ./tcatest write 'casket.tcb#mode=wct#lmemb=5#nmemb=5' 50000
+ $(RUNENV) $(RUNCMD) ./tcatest read 'casket.tcb#mode=r'
+ $(RUNENV) $(RUNCMD) ./tcatest remove 'casket.tcb#mode=w'
+ $(RUNENV) $(RUNCMD) ./tcatest misc 'casket.tcb#mode=wct#lmemb=5#nmemb=5#opts=ld' 5000
+ $(RUNENV) $(RUNCMD) ./tcatest wicked 'casket.tcb#mode=wct' 5000
+ $(RUNENV) $(RUNCMD) ./tcatest write '*#bnum=5000#cap=100' 50000
+ $(RUNENV) $(RUNCMD) ./tcatest misc '*' 5000
+ $(RUNENV) $(RUNCMD) ./tcatest wicked '*' 5000
+ $(RUNENV) $(RUNCMD) ./tcamgr create 'casket.tch#mode=wct#bnum=3'
+ $(RUNENV) $(RUNCMD) ./tcamgr inform 'casket.tch'
+ $(RUNENV) $(RUNCMD) ./tcamgr put casket.tch one first
+ $(RUNENV) $(RUNCMD) ./tcamgr put casket.tch two second
+ $(RUNENV) $(RUNCMD) ./tcamgr put -dk casket.tch three third
+ $(RUNENV) $(RUNCMD) ./tcamgr put -dc casket.tch three third
+ $(RUNENV) $(RUNCMD) ./tcamgr put -dc casket.tch three third
+ $(RUNENV) $(RUNCMD) ./tcamgr put -dc casket.tch three third
+ $(RUNENV) $(RUNCMD) ./tcamgr put casket.tch four fourth
+ $(RUNENV) $(RUNCMD) ./tcamgr put -dk casket.tch five fifth
+ $(RUNENV) $(RUNCMD) ./tcamgr out casket.tch one
+ $(RUNENV) $(RUNCMD) ./tcamgr out casket.tch two
+ $(RUNENV) $(RUNCMD) ./tcamgr get casket.tch three > check.out
+ $(RUNENV) $(RUNCMD) ./tcamgr get casket.tch four > check.out
+ $(RUNENV) $(RUNCMD) ./tcamgr get casket.tch five > check.out
+ $(RUNENV) $(RUNCMD) ./tcamgr list -pv -fm f casket.tch > check.out
+ $(RUNENV) $(RUNCMD) ./tcamgr put -dc casket.tch three third
+ $(RUNENV) $(RUNCMD) ./tcamgr get casket.tch three > check.out
+ $(RUNENV) $(RUNCMD) ./tcamgr get casket.tch four > check.out
+ $(RUNENV) $(RUNCMD) ./tcamgr get casket.tch five > check.out
+ $(RUNENV) $(RUNCMD) ./tcamgr list -pv casket.tch > check.out
+ rm -rf casket*
+ @printf '\n'
+ @printf '#================================================================\n'
+ @printf '# Checking completed.\n'
+ @printf '#================================================================\n'
+
+
+check-valgrind :
+ make RUNCMD="valgrind --tool=memcheck --log-fd=1" check | tee leak.log
+ grep ERROR leak.log
+ grep 'at exit' leak.log
+
+
+check-large :
+ rm -rf casket*
+ $(RUNENV) $(RUNCMD) ./tchmttest typical casket 3 1000000 5000000 13 8
+ $(RUNENV) $(RUNCMD) ./tchmttest typical -nc casket 3 1000000 5000000 13 8
+ $(RUNENV) $(RUNCMD) ./tcbmttest typical casket 3 500000 8 8 500000 16 8
+ $(RUNENV) $(RUNCMD) ./tcbmttest typical -nc casket 3 500000 8 8 500000 16 8
+ rm -rf casket*
+
+
+check-thread :
+ rm -rf casket*
+ $(RUNENV) $(RUNCMD) ./tcumttest typical 5 500000 500000
+ $(RUNENV) $(RUNCMD) ./tcumttest typical -nc -rr 1000 5 500000 500000
+ $(RUNENV) $(RUNCMD) ./tchmttest typical casket 5 500000 500000
+ $(RUNENV) $(RUNCMD) ./tchmttest typical -rc 500000 -nc -rr 1000 casket 5 500000 500000
+ $(RUNENV) $(RUNCMD) ./tcbmttest typical casket 5 100000 5 5
+ $(RUNENV) $(RUNCMD) ./tcbmttest typical -nc -rr 1000 casket 5 100000 5 5
+ rm -rf casket*
+
+
+check-forever :
+ while true ; \
+ do \
+ make check || break ; \
+ make check || break ; \
+ make check || break ; \
+ make check || break ; \
+ make check || break ; \
+ make check-thread || break ; \
+ done
+
+
+dicthdb :
+ rm -f casket
+ sed -e 's/.*$$/&\t&/' /usr/share/dict/words | \
+ ./tchmgr importtsv -sc casket
+
+
+dictbdb :
+ rm -f casket
+ sed -e 's/.*$$/&\t&/' /usr/share/dict/words | \
+ ./tcbmgr importtsv -sc casket
+
+
+.PHONY : all clean install check
+
+
+
+#================================================================
+# Building binaries
+#================================================================
+
+
+libtokyocabinet.a : $(LIBOBJFILES)
+ $(AR) $(ARFLAGS) $@ $(LIBOBJFILES)
+
+
+libtokyocabinet.so.$(LIBVER).$(LIBREV).0 : $(LIBOBJFILES)
+ $(CC) -shared -Wl,-soname,libtokyocabinet.so.$(LIBVER) -o $@ $(LIBOBJFILES) \
+ $(LDFLAGS) $(LIBS)
+
+
+libtokyocabinet.so.$(LIBVER) : libtokyocabinet.so.$(LIBVER).$(LIBREV).0
+ ln -f -s libtokyocabinet.so.$(LIBVER).$(LIBREV).0 $@
+
+
+libtokyocabinet.so : libtokyocabinet.so.$(LIBVER).$(LIBREV).0
+ ln -f -s libtokyocabinet.so.$(LIBVER).$(LIBREV).0 $@
+
+
+libtokyocabinet.$(LIBVER).$(LIBREV).0.dylib : $(LIBOBJFILES)
+ $(CC) -dynamiclib -o $@ \
+ -install_name $(LIBDIR)/libtokyocabinet.$(LIBVER).dylib \
+ -current_version $(LIBVER).$(LIBREV).0 -compatibility_version $(LIBVER) \
+ $(LIBOBJFILES) $(LDFLAGS) $(LIBS)
+
+
+libtokyocabinet.$(LIBVER).dylib : libtokyocabinet.$(LIBVER).$(LIBREV).0.dylib
+ ln -f -s libtokyocabinet.$(LIBVER).$(LIBREV).0.dylib $@
+
+
+libtokyocabinet.dylib : libtokyocabinet.$(LIBVER).$(LIBREV).0.dylib
+ ln -f -s libtokyocabinet.$(LIBVER).$(LIBREV).0.dylib $@
+
+
+tcutest : tcutest.o $(LIBRARYFILES)
+ $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tcumttest : tcumttest.o $(LIBRARYFILES)
+ $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tcucodec : tcucodec.o $(LIBRARYFILES)
+ $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tchtest : tchtest.o $(LIBRARYFILES)
+ $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tchmttest : tchmttest.o $(LIBRARYFILES)
+ $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tchmgr : tchmgr.o $(LIBRARYFILES)
+ $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tcbtest : tcbtest.o $(LIBRARYFILES)
+ $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tcbmttest : tcbmttest.o $(LIBRARYFILES)
+ $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tcbmgr : tcbmgr.o $(LIBRARYFILES)
+ $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tcatest : tcatest.o $(LIBRARYFILES)
+ $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+tcamgr : tcamgr.o $(LIBRARYFILES)
+ $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -ltokyocabinet $(LIBS)
+
+
+myconf.o : myconf.h
+
+tcutil.o : tcutil.h myconf.h
+
+tchdb.o : tchdb.h tcutil.h myconf.h
+
+tcbdb.o : tcbdb.h tchdb.h tcutil.h myconf.h
+
+tcutest.o tcucodec.o : tcutil.h myconf.h
+
+tchtest.o tchmttest.o tchmgr.o : tcutil.h tchdb.h myconf.h
+
+tcbtest.o tcbmttest.o tcbmgr.o : tcutil.h tchdb.h tcbdb.h myconf.h
+
+tcatest.o : tcutil.h tchdb.h tcbdb.h tcadb.h myconf.h
+
+
+
+# END OF FILE
--- /dev/null
+================================================================
+ Tokyo Cabinet: a modern implementation of DBM
+ Copyright (C) 2006-2008 Mikio Hirabayashi
+================================================================
+
+
+Please read the following documents with a WWW browser.
+How to install Tokyo Cabinet is explained in the specification.
+
+ README - this file
+ COPYING - license
+ ChangeLog - history of enhancement
+ THANKS - list of contributors
+ doc/index.html - index of documents
+
+
+Contents of the directory tree is below.
+
+ ./ - sources of Tokyo Cabinet
+ ./doc/ - manuals and specifications
+ ./man/ - manuals for nroff
+ ./example/ - sample code of tutorial
+ ./lab/ - for test and experiment
+ ./bros/ - for comparison with other database managers
+
+
+Tokyo Cabinet is released under the terms of the GNU Lesser General
+Public License. See the file `COPYING' for details.
+
+Tokyo Cabinet was written by Mikio Hirabayashi. You can contact the
+author by e-mail to `mikio@users.sourceforge.net'.
+
+
+Thanks.
+
+
+
+== END OF FILE ==
--- /dev/null
+================================================================
+ Thanks to all of the following for their valuable suggestions
+ or contributions.
+================================================================
+
+
+mixi Inc. and and its developers
+ - allowing me to develop and publish Tokyo Cabinet as business work
+
+
+
+== END OF FILE ==
--- /dev/null
+
+tcmdbfwmkeysを貝偏
+
+adbも実装
\ No newline at end of file
--- /dev/null
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59 for tokyocabinet 1.2.5.
+#
+# Copyright (C) 2003 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+ $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
+ as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
+ fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete. It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME='tokyocabinet'
+PACKAGE_TARNAME='tokyocabinet'
+PACKAGE_VERSION='1.2.5'
+PACKAGE_STRING='tokyocabinet 1.2.5'
+PACKAGE_BUGREPORT=''
+
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT MYLIBVER MYLIBREV MYFORMATVER MYHEADERFILES MYLIBRARYFILES MYLIBOBJFILES MYCOMMANDFILES MYMAN1FILES MYMAN3FILES MYDOCUMENTFILES MYPCFILES MYCFLAGS MYCPPFLAGS MYLDFLAGS MYRUNPATH MYLDLIBPATHENV MYPOSTCMD LIBOBJS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_option in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+ eval "enable_$ac_feature=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+ case $ac_option in
+ *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_$ac_feature='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case $ac_option in
+ *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_$ac_package='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package | sed 's/-/_/g'`
+ eval "with_$ac_package=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; }
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+ { (exit 1); exit 1; }; }
+ ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+ eval "$ac_envvar='$ac_optarg'"
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ { echo "$as_me: error: missing argument to $ac_option" >&2
+ { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+ eval ac_val=$`echo $ac_var`
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+ *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+ localstatedir libdir includedir oldincludedir infodir mandir
+do
+ eval ac_val=$`echo $ac_var`
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) ;;
+ *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used." >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$0" : 'X\(//\)[^/]' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+ { (exit 1); exit 1; }; }
+ else
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+ { (exit 1); exit 1; }; }
+ fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+ { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+ { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+ac_env_CC_set=${CC+set}
+ac_env_CC_value=$CC
+ac_cv_env_CC_set=${CC+set}
+ac_cv_env_CC_value=$CC
+ac_env_CFLAGS_set=${CFLAGS+set}
+ac_env_CFLAGS_value=$CFLAGS
+ac_cv_env_CFLAGS_set=${CFLAGS+set}
+ac_cv_env_CFLAGS_value=$CFLAGS
+ac_env_LDFLAGS_set=${LDFLAGS+set}
+ac_env_LDFLAGS_value=$LDFLAGS
+ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
+ac_cv_env_LDFLAGS_value=$LDFLAGS
+ac_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_env_CPPFLAGS_value=$CPPFLAGS
+ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_cv_env_CPPFLAGS_value=$CPPFLAGS
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures tokyocabinet 1.2.5 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+ cat <<_ACEOF
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --infodir=DIR info documentation [PREFIX/info]
+ --mandir=DIR man documentation [PREFIX/man]
+_ACEOF
+
+ cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+ case $ac_init_help in
+ short | recursive ) echo "Configuration of tokyocabinet 1.2.5:";;
+ esac
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --enable-debug build for debugging
+ --enable-devel build for development
+ --enable-profile build for profiling
+ --enable-off64 build with 64-bit file offset on 32-bit system
+ --enable-fastest build for fastest run
+ --enable-swab build for swapping byte-orders
+ --enable-uyield build for detecting race conditions
+ --disable-zlib build without ZLIB compression
+ --disable-pthread build without POSIX thread support
+ --disable-shared avoid to build shared libraries
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ CPPFLAGS C/C++ preprocessor flags, e.g. -I<include dir> if you have
+ headers in a nonstandard directory <include dir>
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ ac_popdir=`pwd`
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d $ac_dir || continue
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+ case "$ac_dir" in
+ .) ac_abs_builddir=`pwd`;;
+ [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+ *) ac_abs_builddir=`pwd`/"$ac_dir";;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+ case ${ac_top_builddir}. in
+ .) ac_abs_top_builddir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+ *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+ case $ac_srcdir in
+ .) ac_abs_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+ *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+ case $ac_top_srcdir in
+ .) ac_abs_top_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+ *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+ esac;;
+esac
+
+ cd $ac_dir
+ # Check for guested configure; otherwise get Cygnus style configure.
+ if test -f $ac_srcdir/configure.gnu; then
+ echo
+ $SHELL $ac_srcdir/configure.gnu --help=recursive
+ elif test -f $ac_srcdir/configure; then
+ echo
+ $SHELL $ac_srcdir/configure --help=recursive
+ elif test -f $ac_srcdir/configure.ac ||
+ test -f $ac_srcdir/configure.in; then
+ echo
+ $ac_configure --help
+ else
+ echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi
+ cd $ac_popdir
+ done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+ cat <<\_ACEOF
+tokyocabinet configure 1.2.5
+generated by GNU Autoconf 2.59
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by tokyocabinet $as_me 1.2.5, which was
+generated by GNU Autoconf 2.59. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo = `(hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+ ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+ 2)
+ ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+ # Get rid of the leading space.
+ ac_sep=" "
+ ;;
+ esac
+ done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+{
+ (set) 2>&1 |
+ case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ sed -n \
+ "s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+ ;;
+ *)
+ sed -n \
+ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+ ;;
+ esac;
+}
+ echo
+
+ cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=$`echo $ac_var`
+ echo "$ac_var='"'"'$ac_val'"'"'"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=$`echo $ac_var`
+ echo "$ac_var='"'"'$ac_val'"'"'"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+ echo
+ sed "/^$/d" confdefs.h | sort
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ echo "$as_me: caught signal $ac_signal"
+ echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core &&
+ rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+ ' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special
+ # files actually), so we avoid doing that.
+ if test -f "$cache_file"; then
+ { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . $cache_file;;
+ *) . ./$cache_file;;
+ esac
+ fi
+else
+ { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+ sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+ eval ac_new_val="\$ac_env_${ac_var}_value"
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ { echo "$as_me:$LINENO: former value: $ac_old_val" >&5
+echo "$as_me: former value: $ac_old_val" >&2;}
+ { echo "$as_me:$LINENO: current value: $ac_new_val" >&5
+echo "$as_me: current value: $ac_new_val" >&2;}
+ ac_cache_corrupted=:
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+ ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Package information
+MYLIBVER=3
+MYLIBREV=6
+MYFORMATVER="1.0"
+
+# Targets
+MYHEADERFILES="tcutil.h tchdb.h tcbdb.h tcadb.h"
+MYLIBRARYFILES="libtokyocabinet.a"
+MYLIBOBJFILES="tcutil.o tchdb.o tcbdb.o tcadb.o myconf.o"
+MYCOMMANDFILES="tcutest tcumttest tcucodec tchtest tchmttest tchmgr"
+MYCOMMANDFILES="$MYCOMMANDFILES tcbtest tcbmttest tcbmgr tcatest tcamgr"
+MYMAN1FILES="tcutest.1 tcumttest.1 tcucodec.1 tchtest.1 tchmttest.1 tchmgr.1"
+MYMAN1FILES="$MYMAN1FILES tcbtest.1 tcbmttest.1 tcbmgr.1 tcatest.1 tcamgr.1"
+MYMAN3FILES="tokyocabinet.3 tcutil.3 tcxstr.3 tclist.3 tcmap.3 tcmdb.3 tcmpool.3"
+MYMAN3FILES="$MYMAN3FILES tchdb.3 tcbdb.3 tcadb.3"
+MYDOCUMENTFILES="COPYING ChangeLog THANKS doc"
+MYPCFILES="tokyocabinet.pc"
+
+# Building flags
+MYCFLAGS="-std=c99 -Wall -fPIC -fsigned-char -O2"
+MYCPPFLAGS="-I. -I\$(INCLUDEDIR) -L$HOME/include -L/usr/local/include -DNDEBUG -D_GNU_SOURCE=1"
+MYLDFLAGS="-L. -L\$(LIBDIR) -L$HOME/lib -L/usr/local/lib"
+MYRUNPATH="\$(LIBDIR)"
+MYLDLIBPATHENV="LD_LIBRARY_PATH"
+MYPOSTCMD="true"
+
+# Building paths
+pathtmp="$PATH"
+PATH="$HOME/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin"
+PATH="$PATH:/usr/ccs/bin:/usr/ucb:/usr/xpg4/bin:/usr/xpg6/bin:$pathtmp"
+LIBRARY_PATH="$HOME/lib:/usr/local/lib:$LIBRARY_PATH"
+LD_LIBRARY_PATH="$HOME/lib:/usr/local/lib:$LD_LIBRARY_PATH"
+CPATH="$HOME/include:/usr/local/include:$CPATH"
+PKG_CONFIG_PATH="$HOME/lib/pkgconfig:/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH"
+export PATH LIBRARY_PATH LD_LIBRARY_PATH CPATH PKG_CONFIG_PATH
+
+
+
+#================================================================
+# Options
+#================================================================
+
+
+# Internal variables
+enables=""
+
+# Debug mode
+# Check whether --enable-debug or --disable-debug was given.
+if test "${enable_debug+set}" = set; then
+ enableval="$enable_debug"
+
+fi;
+if test "$enable_debug" = "yes"
+then
+ MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g"
+ MYCPPFLAGS="$MYCPPFLAGS -UNDEBUG"
+ MYLDFLAGS="$MYLDFLAGS -static"
+ enables="$enables (debug)"
+fi
+
+# Developping mode
+# Check whether --enable-devel or --disable-devel was given.
+if test "${enable_devel+set}" = set; then
+ enableval="$enable_devel"
+
+fi;
+if test "$enable_devel" = "yes"
+then
+ MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -O2 -pipe"
+ MYCPPFLAGS="$MYCPPFLAGS -UNDEBUG"
+ enables="$enables (devel)"
+fi
+
+# Profiling mode
+# Check whether --enable-profile or --disable-profile was given.
+if test "${enable_profile+set}" = set; then
+ enableval="$enable_profile"
+
+fi;
+if test "$enable_profile" = "yes"
+then
+ MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -pg -O2 -pipe"
+ enables="$enables (profile)"
+fi
+
+# 64-bit offset mode
+# Check whether --enable-off64 or --disable-off64 was given.
+if test "${enable_off64+set}" = set; then
+ enableval="$enable_off64"
+
+fi;
+if test "$enable_off64" = "yes"
+then
+ MYCPPFLAGS="$MYCPPFLAGS -D_FILE_OFFSET_BITS=64"
+ enables="$enables (off64)"
+fi
+
+# Fastest mode
+# Check whether --enable-fastest or --disable-fastest was given.
+if test "${enable_fastest+set}" = set; then
+ enableval="$enable_fastest"
+
+fi;
+if test "$enable_fastest" = "yes"
+then
+ MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -O3"
+ MYCFLAGS="$MYCFLAGS -fomit-frame-pointer -fforce-addr -minline-all-stringops"
+ MYCPPFLAGS="$MYCPPFLAGS -D_MYFASTEST"
+ enables="$enables (fastest)"
+fi
+
+# Swapping byte-orders mode
+# Check whether --enable-swab or --disable-swab was given.
+if test "${enable_swab+set}" = set; then
+ enableval="$enable_swab"
+
+fi;
+if test "$enable_swab" = "yes"
+then
+ MYCPPFLAGS="$MYCPPFLAGS -D_MYSWAB"
+ enables="$enables (swab)"
+fi
+
+# Micro yield mode
+# Check whether --enable-uyield or --disable-uyield was given.
+if test "${enable_uyield+set}" = set; then
+ enableval="$enable_uyield"
+
+fi;
+if test "$enable_uyield" = "yes"
+then
+ MYCPPFLAGS="$MYCPPFLAGS -D_MYMICROYIELD"
+ enables="$enables (uyield)"
+fi
+
+# Disable ZLIB compression
+# Check whether --enable-zlib or --disable-zlib was given.
+if test "${enable_zlib+set}" = set; then
+ enableval="$enable_zlib"
+
+fi;
+if test "$enable_zlib" = "no"
+then
+ MYCPPFLAGS="$MYCPPFLAGS -D_MYNOZLIB"
+ enables="$enables (no-zlib)"
+fi
+
+# Disable POSIX thread
+# Check whether --enable-pthread or --disable-pthread was given.
+if test "${enable_pthread+set}" = set; then
+ enableval="$enable_pthread"
+
+fi;
+if test "$enable_pthread" = "no"
+then
+ MYCPPFLAGS="$MYCPPFLAGS -D_MYNOPTHREAD"
+ enables="$enables (no-pthread)"
+fi
+
+# Disable shared object
+# Check whether --enable-shared or --disable-shared was given.
+if test "${enable_shared+set}" = set; then
+ enableval="$enable_shared"
+
+fi;
+if test "$enable_shared" = "no"
+then
+ enables="$enables (no-shared)"
+fi
+
+# Messages
+printf '#================================================================\n'
+printf '# Configuring Tokyo Cabinet version %s%s.\n' "$PACKAGE_VERSION" "$enables"
+printf '#================================================================\n'
+
+
+
+#================================================================
+# Checking Commands and Libraries
+#================================================================
+
+# C compiler
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ CC=$ac_ct_CC
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ CC=$ac_ct_CC
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$ac_ct_CC" && break
+done
+
+ CC=$ac_ct_CC
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+ "checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+ (eval $ac_compiler --version </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+ (eval $ac_compiler -v </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+ (eval $ac_compiler -V </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
+ (eval $ac_link_default) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # Find the output, starting from the most likely. This scheme is
+# not robust to junk in `.', hence go to wildcards (a.*) only as a last
+# resort.
+
+# Be careful to initialize this variable, since it used to be cached.
+# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
+ac_cv_exeext=
+# b.out is created by i960 compilers.
+for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
+ ;;
+ conftest.$ac_ext )
+ # This is the source file.
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ # FIXME: I believe we export ac_cv_exeext for Libtool,
+ # but it would be cool to find out if it's true. Does anybody
+ # maintain Libtool? --akim.
+ export ac_cv_exeext
+ break;;
+ * )
+ break;;
+ esac
+done
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+ { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6
+
+# Check the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+ if { ac_try='./$ac_file'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ fi
+fi
+echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6
+
+echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ export ac_cv_exeext
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
+if test "${ac_cv_objext+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_compiler_gnu=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+CFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_g+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_prog_cc_g=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cc_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
+echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_stdc+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_prog_cc_stdc=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std1 is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std1. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX -qlanglvl=ansi
+# Ultrix and OSF/1 -std1
+# HP-UX 10.20 and later -Ae
+# HP-UX older versions -Aa -D_HPUX_SOURCE
+# SVR4 -Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_prog_cc_stdc=$ac_arg
+break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext
+done
+rm -f conftest.$ac_ext conftest.$ac_objext
+CC=$ac_save_CC
+
+fi
+
+case "x$ac_cv_prog_cc_stdc" in
+ x|xno)
+ echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6 ;;
+ *)
+ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
+ CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+
+# Some people use a C++ compiler to compile C. Since we use `exit',
+# in C++ we need to declare it. In case someone uses the same compiler
+# for both compiling C and C++ we need to have the C++ compiler decide
+# the declaration of exit, since it's the most demanding environment.
+cat >conftest.$ac_ext <<_ACEOF
+#ifndef __cplusplus
+ choke me
+#endif
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ for ac_declaration in \
+ '' \
+ 'extern "C" void std::exit (int) throw (); using std::exit;' \
+ 'extern "C" void std::exit (int); using std::exit;' \
+ 'extern "C" void exit (int) throw ();' \
+ 'extern "C" void exit (int);' \
+ 'void exit (int);'
+do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+ echo '#ifdef __cplusplus' >>confdefs.h
+ echo $ac_declaration >>confdefs.h
+ echo '#endif' >>confdefs.h
+fi
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Byte order
+
+echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5
+echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6
+if test "${ac_cv_c_bigendian+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ # See if sys/param.h defines the BYTE_ORDER macro.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/param.h>
+
+int
+main ()
+{
+#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN
+ bogus endian macros
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ # It does; now see whether it defined to BIG_ENDIAN or not.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/param.h>
+
+int
+main ()
+{
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_c_bigendian=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_c_bigendian=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+# It does not; compile a test program.
+if test "$cross_compiling" = yes; then
+ # try to guess the endianness by grepping values into an object file
+ ac_cv_c_bigendian=unknown
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+short ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+short ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+void _ascii () { char *s = (char *) ascii_mm; s = (char *) ascii_ii; }
+short ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+short ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
+void _ebcdic () { char *s = (char *) ebcdic_mm; s = (char *) ebcdic_ii; }
+int
+main ()
+{
+ _ascii (); _ebcdic ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ if grep BIGenDianSyS conftest.$ac_objext >/dev/null ; then
+ ac_cv_c_bigendian=yes
+fi
+if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
+ if test "$ac_cv_c_bigendian" = unknown; then
+ ac_cv_c_bigendian=no
+ else
+ # finding both strings is unlikely to happen, but who knows?
+ ac_cv_c_bigendian=unknown
+ fi
+fi
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+int
+main ()
+{
+ /* Are we little or big endian? From Harbison&Steele. */
+ union
+ {
+ long l;
+ char c[sizeof (long)];
+ } u;
+ u.l = 1;
+ exit (u.c[sizeof (long) - 1] == 1);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_c_bigendian=no
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_c_bigendian=yes
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5
+echo "${ECHO_T}$ac_cv_c_bigendian" >&6
+case $ac_cv_c_bigendian in
+ yes)
+ MYCPPFLAGS="$MYCPPFLAGS -D_MYBIGEND" ;;
+ no)
+ ;;
+ *)
+ { { echo "$as_me:$LINENO: error: unknown endianness
+presetting ac_cv_c_bigendian=no (or yes) will help" >&5
+echo "$as_me: error: unknown endianness
+presetting ac_cv_c_bigendian=no (or yes) will help" >&2;}
+ { (exit 1); exit 1; }; } ;;
+esac
+
+
+# Underlying libraries
+
+echo "$as_me:$LINENO: checking for main in -lc" >&5
+echo $ECHO_N "checking for main in -lc... $ECHO_C" >&6
+if test "${ac_cv_lib_c_main+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lc $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+
+int
+main ()
+{
+main ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_c_main=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_c_main=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_c_main" >&5
+echo "${ECHO_T}$ac_cv_lib_c_main" >&6
+if test $ac_cv_lib_c_main = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBC 1
+_ACEOF
+
+ LIBS="-lc $LIBS"
+
+fi
+
+
+echo "$as_me:$LINENO: checking for main in -lm" >&5
+echo $ECHO_N "checking for main in -lm... $ECHO_C" >&6
+if test "${ac_cv_lib_m_main+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lm $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+
+int
+main ()
+{
+main ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_m_main=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_m_main=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_m_main" >&5
+echo "${ECHO_T}$ac_cv_lib_m_main" >&6
+if test $ac_cv_lib_m_main = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBM 1
+_ACEOF
+
+ LIBS="-lm $LIBS"
+
+fi
+
+if test "$enable_pthread" != "no"
+then
+
+echo "$as_me:$LINENO: checking for main in -lpthread" >&5
+echo $ECHO_N "checking for main in -lpthread... $ECHO_C" >&6
+if test "${ac_cv_lib_pthread_main+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpthread $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+
+int
+main ()
+{
+main ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_pthread_main=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_pthread_main=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_pthread_main" >&5
+echo "${ECHO_T}$ac_cv_lib_pthread_main" >&6
+if test $ac_cv_lib_pthread_main = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBPTHREAD 1
+_ACEOF
+
+ LIBS="-lpthread $LIBS"
+
+fi
+
+fi
+if test "$enable_zlib" != "no"
+then
+
+echo "$as_me:$LINENO: checking for main in -lz" >&5
+echo $ECHO_N "checking for main in -lz... $ECHO_C" >&6
+if test "${ac_cv_lib_z_main+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lz $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+
+int
+main ()
+{
+main ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag"
+ || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_z_main=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_z_main=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_z_main" >&5
+echo "${ECHO_T}$ac_cv_lib_z_main" >&6
+if test $ac_cv_lib_z_main = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBZ 1
+_ACEOF
+
+ LIBS="-lz $LIBS"
+
+fi
+
+fi
+
+# Shared libraries
+if test "$enable_shared" != "no" && test "$enable_profile" != "yes"
+then
+ if uname | grep Darwin >/dev/null
+ then
+ MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.$MYLIBVER.$MYLIBREV.0.dylib"
+ MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.$MYLIBVER.dylib"
+ MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.dylib"
+ MYLDLIBPATHENV="DYLD_LIBRARY_PATH"
+ else
+ MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.so.$MYLIBVER.$MYLIBREV.0"
+ MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.so.$MYLIBVER"
+ MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.so"
+ fi
+fi
+
+
+
+#================================================================
+# Generic Settings
+#================================================================
+
+# Export variables
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Targets
+ ac_config_files="$ac_config_files Makefile tokyocabinet.pc"
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+{
+ (set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n \
+ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+ ;;
+ esac;
+} |
+ sed '
+ t clear
+ : clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ : end' >>confcache
+if diff $cache_file confcache >/dev/null 2>&1; then :; else
+ if test -w $cache_file; then
+ test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+ cat confcache >$cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[ ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[ ]*$//;
+}'
+fi
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+#
+# If the first sed substitution is executed (which looks for macros that
+# take arguments), then we branch to the quote section. Otherwise,
+# look for a macro that doesn't take arguments.
+cat >confdef2opt.sed <<\_ACEOF
+t clear
+: clear
+s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\),-D\1=\2,g
+t quote
+s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\),-D\1=\2,g
+t quote
+d
+: quote
+s,[ `~#$^&*(){}\\|;'"<>?],\\&,g
+s,\[,\\&,g
+s,\],\\&,g
+s,\$,$$,g
+p
+_ACEOF
+# We use echo to avoid assuming a particular line-breaking character.
+# The extra dot is to prevent the shell from consuming trailing
+# line-breaks from the sub-command output. A line-break within
+# single-quotes doesn't work because, if this script is created in a
+# platform that uses two characters for line-breaks (e.g., DOS), tr
+# would break.
+ac_LF_and_DOT=`echo; echo .`
+DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'`
+rm -f confdef2opt.sed
+
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_i=`echo "$ac_i" |
+ sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+ # 2. Add them.
+ ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+ ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+ $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
+ as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
+ fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling. Logging --version etc. is OK.
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by tokyocabinet $as_me 1.2.5, which was
+generated by GNU Autoconf 2.59. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+ echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+ echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+ echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+ echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+
+Configuration files:
+$config_files
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+tokyocabinet config.status 1.2.5
+configured by $0, generated by GNU Autoconf 2.59,
+ with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value. By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=*)
+ ac_option=`expr "x$1" : 'x\([^=]*\)='`
+ ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ -*)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ *) # This is not an option, so the user has probably given explicit
+ # arguments.
+ ac_option=$1
+ ac_need_defaults=false;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --vers* | -V )
+ echo "$ac_cs_version"; exit 0 ;;
+ --he | --h)
+ # Conflict between --help and --header
+ { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; };;
+ --help | --hel | -h )
+ echo "$ac_cs_usage"; exit 0 ;;
+ --debug | --d* | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+ ac_need_defaults=false;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; } ;;
+
+ *) ac_config_targets="$ac_config_targets $1" ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+ echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+ exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+ case "$ac_config_target" in
+ # Handling of arguments.
+ "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "tokyocabinet.pc" ) CONFIG_FILES="$CONFIG_FILES tokyocabinet.pc" ;;
+ *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+ trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+ trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+ test -n "$tmp" && test -d "$tmp"
+} ||
+{
+ tmp=./confstat$$-$RANDOM
+ (umask 077 && mkdir $tmp)
+} ||
+{
+ echo "$me: cannot create a temporary directory in ." >&2
+ { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+ # Protect against being on the right side of a sed subst in config.status.
+ sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+ s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@CC@,$CC,;t t
+s,@CFLAGS@,$CFLAGS,;t t
+s,@LDFLAGS@,$LDFLAGS,;t t
+s,@CPPFLAGS@,$CPPFLAGS,;t t
+s,@ac_ct_CC@,$ac_ct_CC,;t t
+s,@EXEEXT@,$EXEEXT,;t t
+s,@OBJEXT@,$OBJEXT,;t t
+s,@MYLIBVER@,$MYLIBVER,;t t
+s,@MYLIBREV@,$MYLIBREV,;t t
+s,@MYFORMATVER@,$MYFORMATVER,;t t
+s,@MYHEADERFILES@,$MYHEADERFILES,;t t
+s,@MYLIBRARYFILES@,$MYLIBRARYFILES,;t t
+s,@MYLIBOBJFILES@,$MYLIBOBJFILES,;t t
+s,@MYCOMMANDFILES@,$MYCOMMANDFILES,;t t
+s,@MYMAN1FILES@,$MYMAN1FILES,;t t
+s,@MYMAN3FILES@,$MYMAN3FILES,;t t
+s,@MYDOCUMENTFILES@,$MYDOCUMENTFILES,;t t
+s,@MYPCFILES@,$MYPCFILES,;t t
+s,@MYCFLAGS@,$MYCFLAGS,;t t
+s,@MYCPPFLAGS@,$MYCPPFLAGS,;t t
+s,@MYLDFLAGS@,$MYLDFLAGS,;t t
+s,@MYRUNPATH@,$MYRUNPATH,;t t
+s,@MYLDLIBPATHENV@,$MYLDLIBPATHENV,;t t
+s,@MYPOSTCMD@,$MYPOSTCMD,;t t
+s,@LIBOBJS@,$LIBOBJS,;t t
+s,@LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+ cat >>$CONFIG_STATUS <<\_ACEOF
+ # Split the substitutions into bite-sized pieces for seds with
+ # small command number limits, like on Digital OSF/1 and HP-UX.
+ ac_max_sed_lines=48
+ ac_sed_frag=1 # Number of current file.
+ ac_beg=1 # First line for current file.
+ ac_end=$ac_max_sed_lines # Line after last line for current file.
+ ac_more_lines=:
+ ac_sed_cmds=
+ while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ else
+ sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ fi
+ if test ! -s $tmp/subs.frag; then
+ ac_more_lines=false
+ else
+ # The purpose of the label and of the branching condition is to
+ # speed up the sed processing (if there are no `@' at all, there
+ # is no need to browse any of the substitutions).
+ # These are the two extra sed commands mentioned above.
+ (echo ':t
+ /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+ fi
+ ac_sed_frag=`expr $ac_sed_frag + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_lines`
+ fi
+ done
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+ fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case $ac_file in
+ - | *:- | *:-:* ) # input from stdin
+ cat >$tmp/stdin
+ ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ * ) ac_file_in=$ac_file.in ;;
+ esac
+
+ # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+ ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ { if $as_mkdir_p; then
+ mkdir -p "$ac_dir"
+ else
+ as_dir="$ac_dir"
+ as_dirs=
+ while test ! -d "$as_dir"; do
+ as_dirs="$as_dir $as_dirs"
+ as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ done
+ test ! -n "$as_dirs" || mkdir $as_dirs
+ fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+ { (exit 1); exit 1; }; }; }
+
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+ case "$ac_dir" in
+ .) ac_abs_builddir=`pwd`;;
+ [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+ *) ac_abs_builddir=`pwd`/"$ac_dir";;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+ case ${ac_top_builddir}. in
+ .) ac_abs_top_builddir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+ *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+ case $ac_srcdir in
+ .) ac_abs_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+ *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+ case $ac_top_srcdir in
+ .) ac_abs_top_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+ *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+ esac;;
+esac
+
+
+
+ if test x"$ac_file" != x-; then
+ { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+ rm -f "$ac_file"
+ fi
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ if test x"$ac_file" = x-; then
+ configure_input=
+ else
+ configure_input="$ac_file. "
+ fi
+ configure_input=$configure_input"Generated from `echo $ac_file_in |
+ sed 's,.*/,,'` by configure."
+
+ # First look for the input files in the build tree, otherwise in the
+ # src tree.
+ ac_file_inputs=`IFS=:
+ for f in $ac_file_in; do
+ case $f in
+ -) echo $tmp/stdin ;;
+ [\\/$]*)
+ # Absolute (can't be DOS-style, as IFS=:)
+ test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ echo "$f";;
+ *) # Relative
+ if test -f "$f"; then
+ # Build tree
+ echo "$f"
+ elif test -f "$srcdir/$f"; then
+ # Source tree
+ echo "$srcdir/$f"
+ else
+ # /dev/null tree
+ { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ fi;;
+ esac
+ done` || { (exit 1); exit 1; }
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+ sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+ rm -f $tmp/stdin
+ if test x"$ac_file" != x-; then
+ mv $tmp/out $ac_file
+ else
+ cat $tmp/out
+ rm -f $tmp/out
+ fi
+
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || { (exit 1); exit 1; }
+fi
+
+
+# Messages
+printf '#================================================================\n'
+printf '# Ready to make.\n'
+printf '#================================================================\n'
+
+
+
+# END OF FILE
--- /dev/null
+# Source of configuration for Tokyo Cabinet
+
+
+
+#================================================================
+# Generic Settings
+#================================================================
+
+# Package name
+AC_INIT(tokyocabinet, 1.2.5)
+
+# Package information
+MYLIBVER=3
+MYLIBREV=6
+MYFORMATVER="1.0"
+
+# Targets
+MYHEADERFILES="tcutil.h tchdb.h tcbdb.h tcadb.h"
+MYLIBRARYFILES="libtokyocabinet.a"
+MYLIBOBJFILES="tcutil.o tchdb.o tcbdb.o tcadb.o myconf.o"
+MYCOMMANDFILES="tcutest tcumttest tcucodec tchtest tchmttest tchmgr"
+MYCOMMANDFILES="$MYCOMMANDFILES tcbtest tcbmttest tcbmgr tcatest tcamgr"
+MYMAN1FILES="tcutest.1 tcumttest.1 tcucodec.1 tchtest.1 tchmttest.1 tchmgr.1"
+MYMAN1FILES="$MYMAN1FILES tcbtest.1 tcbmttest.1 tcbmgr.1 tcatest.1 tcamgr.1"
+MYMAN3FILES="tokyocabinet.3 tcutil.3 tcxstr.3 tclist.3 tcmap.3 tcmdb.3 tcmpool.3"
+MYMAN3FILES="$MYMAN3FILES tchdb.3 tcbdb.3 tcadb.3"
+MYDOCUMENTFILES="COPYING ChangeLog THANKS doc"
+MYPCFILES="tokyocabinet.pc"
+
+# Building flags
+MYCFLAGS="-std=c99 -Wall -fPIC -fsigned-char -O2"
+MYCPPFLAGS="-I. -I\$(INCLUDEDIR) -L$HOME/include -L/usr/local/include -DNDEBUG -D_GNU_SOURCE=1"
+MYLDFLAGS="-L. -L\$(LIBDIR) -L$HOME/lib -L/usr/local/lib"
+MYRUNPATH="\$(LIBDIR)"
+MYLDLIBPATHENV="LD_LIBRARY_PATH"
+MYPOSTCMD="true"
+
+# Building paths
+pathtmp="$PATH"
+PATH="$HOME/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin"
+PATH="$PATH:/usr/ccs/bin:/usr/ucb:/usr/xpg4/bin:/usr/xpg6/bin:$pathtmp"
+LIBRARY_PATH="$HOME/lib:/usr/local/lib:$LIBRARY_PATH"
+LD_LIBRARY_PATH="$HOME/lib:/usr/local/lib:$LD_LIBRARY_PATH"
+CPATH="$HOME/include:/usr/local/include:$CPATH"
+PKG_CONFIG_PATH="$HOME/lib/pkgconfig:/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH"
+export PATH LIBRARY_PATH LD_LIBRARY_PATH CPATH PKG_CONFIG_PATH
+
+
+
+#================================================================
+# Options
+#================================================================
+
+
+# Internal variables
+enables=""
+
+# Debug mode
+AC_ARG_ENABLE(debug,
+ AC_HELP_STRING([--enable-debug], [build for debugging]))
+if test "$enable_debug" = "yes"
+then
+ MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g"
+ MYCPPFLAGS="$MYCPPFLAGS -UNDEBUG"
+ MYLDFLAGS="$MYLDFLAGS -static"
+ enables="$enables (debug)"
+fi
+
+# Developping mode
+AC_ARG_ENABLE(devel,
+ AC_HELP_STRING([--enable-devel], [build for development]))
+if test "$enable_devel" = "yes"
+then
+ MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -O2 -pipe"
+ MYCPPFLAGS="$MYCPPFLAGS -UNDEBUG"
+ enables="$enables (devel)"
+fi
+
+# Profiling mode
+AC_ARG_ENABLE(profile,
+ AC_HELP_STRING([--enable-profile], [build for profiling]))
+if test "$enable_profile" = "yes"
+then
+ MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -pg -O2 -pipe"
+ enables="$enables (profile)"
+fi
+
+# 64-bit offset mode
+AC_ARG_ENABLE(off64,
+ AC_HELP_STRING([--enable-off64], [build with 64-bit file offset on 32-bit system]))
+if test "$enable_off64" = "yes"
+then
+ MYCPPFLAGS="$MYCPPFLAGS -D_FILE_OFFSET_BITS=64"
+ enables="$enables (off64)"
+fi
+
+# Fastest mode
+AC_ARG_ENABLE(fastest,
+ AC_HELP_STRING([--enable-fastest], [build for fastest run]))
+if test "$enable_fastest" = "yes"
+then
+ MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -O3"
+ MYCFLAGS="$MYCFLAGS -fomit-frame-pointer -fforce-addr -minline-all-stringops"
+ MYCPPFLAGS="$MYCPPFLAGS -D_MYFASTEST"
+ enables="$enables (fastest)"
+fi
+
+# Swapping byte-orders mode
+AC_ARG_ENABLE(swab,
+ AC_HELP_STRING([--enable-swab], [build for swapping byte-orders]))
+if test "$enable_swab" = "yes"
+then
+ MYCPPFLAGS="$MYCPPFLAGS -D_MYSWAB"
+ enables="$enables (swab)"
+fi
+
+# Micro yield mode
+AC_ARG_ENABLE(uyield,
+ AC_HELP_STRING([--enable-uyield], [build for detecting race conditions]))
+if test "$enable_uyield" = "yes"
+then
+ MYCPPFLAGS="$MYCPPFLAGS -D_MYMICROYIELD"
+ enables="$enables (uyield)"
+fi
+
+# Disable ZLIB compression
+AC_ARG_ENABLE(zlib,
+ AC_HELP_STRING([--disable-zlib], [build without ZLIB compression]))
+if test "$enable_zlib" = "no"
+then
+ MYCPPFLAGS="$MYCPPFLAGS -D_MYNOZLIB"
+ enables="$enables (no-zlib)"
+fi
+
+# Disable POSIX thread
+AC_ARG_ENABLE(pthread,
+ AC_HELP_STRING([--disable-pthread], [build without POSIX thread support]))
+if test "$enable_pthread" = "no"
+then
+ MYCPPFLAGS="$MYCPPFLAGS -D_MYNOPTHREAD"
+ enables="$enables (no-pthread)"
+fi
+
+# Disable shared object
+AC_ARG_ENABLE(shared,
+ AC_HELP_STRING([--disable-shared], [avoid to build shared libraries]))
+if test "$enable_shared" = "no"
+then
+ enables="$enables (no-shared)"
+fi
+
+# Messages
+printf '#================================================================\n'
+printf '# Configuring Tokyo Cabinet version %s%s.\n' "$PACKAGE_VERSION" "$enables"
+printf '#================================================================\n'
+
+
+
+#================================================================
+# Checking Commands and Libraries
+#================================================================
+
+# C compiler
+AC_PROG_CC
+
+# Byte order
+AC_C_BIGENDIAN(MYCPPFLAGS="$MYCPPFLAGS -D_MYBIGEND")
+
+# Underlying libraries
+AC_CHECK_LIB(c, main)
+AC_CHECK_LIB(m, main)
+if test "$enable_pthread" != "no"
+then
+AC_CHECK_LIB(pthread, main)
+fi
+if test "$enable_zlib" != "no"
+then
+ AC_CHECK_LIB(z, main)
+fi
+
+# Shared libraries
+if test "$enable_shared" != "no" && test "$enable_profile" != "yes"
+then
+ if uname | grep Darwin >/dev/null
+ then
+ MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.$MYLIBVER.$MYLIBREV.0.dylib"
+ MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.$MYLIBVER.dylib"
+ MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.dylib"
+ MYLDLIBPATHENV="DYLD_LIBRARY_PATH"
+ else
+ MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.so.$MYLIBVER.$MYLIBREV.0"
+ MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.so.$MYLIBVER"
+ MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.so"
+ fi
+fi
+
+
+
+#================================================================
+# Generic Settings
+#================================================================
+
+# Export variables
+AC_SUBST(MYLIBVER)
+AC_SUBST(MYLIBREV)
+AC_SUBST(MYFORMATVER)
+AC_SUBST(MYHEADERFILES)
+AC_SUBST(MYLIBRARYFILES)
+AC_SUBST(MYLIBOBJFILES)
+AC_SUBST(MYCOMMANDFILES)
+AC_SUBST(MYMAN1FILES)
+AC_SUBST(MYMAN3FILES)
+AC_SUBST(MYDOCUMENTFILES)
+AC_SUBST(MYPCFILES)
+AC_SUBST(MYCFLAGS)
+AC_SUBST(MYCPPFLAGS)
+AC_SUBST(MYLDFLAGS)
+AC_SUBST(MYRUNPATH)
+AC_SUBST(MYLDLIBPATHENV)
+AC_SUBST(MYPOSTCMD)
+
+# Targets
+AC_OUTPUT(Makefile tokyocabinet.pc)
+
+# Messages
+printf '#================================================================\n'
+printf '# Ready to make.\n'
+printf '#================================================================\n'
+
+
+
+# END OF FILE
--- /dev/null
+/*************************************************************************************************
+ * System-dependent configurations of Tokyo Cabinet
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include "myconf.h"
+
+
+
+/*************************************************************************************************
+ * common settings
+ *************************************************************************************************/
+
+
+int _tc_dummyfunc(void){
+ return 0;
+}
+
+
+int _tc_dummyfuncv(int a, ...){
+ return 0;
+}
+
+
+
+/*************************************************************************************************
+ * for ZLIB
+ *************************************************************************************************/
+
+
+#if TCUSEZLIB
+
+
+#include <zlib.h>
+
+#define ZLIBBUFSIZ 8192
+
+
+static char *_tc_deflate_impl(const char *ptr, int size, int *sp, int mode);
+static char *_tc_inflate_impl(const char *ptr, int size, int *sp, int mode);
+static unsigned int _tc_getcrc_impl(const char *ptr, int size);
+
+
+char *(*_tc_deflate)(const char *, int, int *, int) = _tc_deflate_impl;
+char *(*_tc_inflate)(const char *, int, int *, int) = _tc_inflate_impl;
+unsigned int (*_tc_getcrc)(const char *, int) = _tc_getcrc_impl;
+
+
+static char *_tc_deflate_impl(const char *ptr, int size, int *sp, int mode){
+ assert(ptr && size >= 0 && sp);
+ z_stream zs;
+ char *buf, *swap;
+ unsigned char obuf[ZLIBBUFSIZ];
+ int rv, asiz, bsiz, osiz;
+ zs.zalloc = Z_NULL;
+ zs.zfree = Z_NULL;
+ zs.opaque = Z_NULL;
+ switch(mode){
+ case _TCZMRAW:
+ if(deflateInit2(&zs, 5, Z_DEFLATED, -15, 7, Z_DEFAULT_STRATEGY) != Z_OK)
+ return NULL;
+ break;
+ case _TCZMGZIP:
+ if(deflateInit2(&zs, 6, Z_DEFLATED, 15 + 16, 9, Z_DEFAULT_STRATEGY) != Z_OK)
+ return NULL;
+ break;
+ default:
+ if(deflateInit2(&zs, 6, Z_DEFLATED, 15, 8, Z_DEFAULT_STRATEGY) != Z_OK)
+ return NULL;
+ break;
+ }
+ asiz = size + 16;
+ if(asiz < ZLIBBUFSIZ) asiz = ZLIBBUFSIZ;
+ if(!(buf = malloc(asiz))){
+ deflateEnd(&zs);
+ return NULL;
+ }
+ bsiz = 0;
+ zs.next_in = (unsigned char *)ptr;
+ zs.avail_in = size;
+ zs.next_out = obuf;
+ zs.avail_out = ZLIBBUFSIZ;
+ while((rv = deflate(&zs, Z_FINISH)) == Z_OK){
+ osiz = ZLIBBUFSIZ - zs.avail_out;
+ if(bsiz + osiz > asiz){
+ asiz = asiz * 2 + osiz;
+ if(!(swap = realloc(buf, asiz))){
+ free(buf);
+ deflateEnd(&zs);
+ return NULL;
+ }
+ buf = swap;
+ }
+ memcpy(buf + bsiz, obuf, osiz);
+ bsiz += osiz;
+ zs.next_out = obuf;
+ zs.avail_out = ZLIBBUFSIZ;
+ }
+ if(rv != Z_STREAM_END){
+ free(buf);
+ deflateEnd(&zs);
+ return NULL;
+ }
+ osiz = ZLIBBUFSIZ - zs.avail_out;
+ if(bsiz + osiz + 1 > asiz){
+ asiz = asiz * 2 + osiz;
+ if(!(swap = realloc(buf, asiz))){
+ free(buf);
+ deflateEnd(&zs);
+ return NULL;
+ }
+ buf = swap;
+ }
+ memcpy(buf + bsiz, obuf, osiz);
+ bsiz += osiz;
+ buf[bsiz] = '\0';
+ if(mode == _TCZMRAW) bsiz++;
+ *sp = bsiz;
+ deflateEnd(&zs);
+ return buf;
+}
+
+
+static char *_tc_inflate_impl(const char *ptr, int size, int *sp, int mode){
+ assert(ptr && size >= 0 && sp);
+ z_stream zs;
+ char *buf, *swap;
+ unsigned char obuf[ZLIBBUFSIZ];
+ int rv, asiz, bsiz, osiz;
+ zs.zalloc = Z_NULL;
+ zs.zfree = Z_NULL;
+ zs.opaque = Z_NULL;
+ switch(mode){
+ case _TCZMRAW:
+ if(inflateInit2(&zs, -15) != Z_OK) return NULL;
+ break;
+ case _TCZMGZIP:
+ if(inflateInit2(&zs, 15 + 16) != Z_OK) return NULL;
+ break;
+ default:
+ if(inflateInit2(&zs, 15) != Z_OK) return NULL;
+ break;
+ }
+ asiz = size * 2 + 16;
+ if(asiz < ZLIBBUFSIZ) asiz = ZLIBBUFSIZ;
+ if(!(buf = malloc(asiz))){
+ inflateEnd(&zs);
+ return NULL;
+ }
+ bsiz = 0;
+ zs.next_in = (unsigned char *)ptr;
+ zs.avail_in = size;
+ zs.next_out = obuf;
+ zs.avail_out = ZLIBBUFSIZ;
+ while((rv = inflate(&zs, Z_NO_FLUSH)) == Z_OK){
+ osiz = ZLIBBUFSIZ - zs.avail_out;
+ if(bsiz + osiz >= asiz){
+ asiz = asiz * 2 + osiz;
+ if(!(swap = realloc(buf, asiz))){
+ free(buf);
+ inflateEnd(&zs);
+ return NULL;
+ }
+ buf = swap;
+ }
+ memcpy(buf + bsiz, obuf, osiz);
+ bsiz += osiz;
+ zs.next_out = obuf;
+ zs.avail_out = ZLIBBUFSIZ;
+ }
+ if(rv != Z_STREAM_END){
+ free(buf);
+ inflateEnd(&zs);
+ return NULL;
+ }
+ osiz = ZLIBBUFSIZ - zs.avail_out;
+ if(bsiz + osiz >= asiz){
+ asiz = asiz * 2 + osiz;
+ if(!(swap = realloc(buf, asiz))){
+ free(buf);
+ inflateEnd(&zs);
+ return NULL;
+ }
+ buf = swap;
+ }
+ memcpy(buf + bsiz, obuf, osiz);
+ bsiz += osiz;
+ buf[bsiz] = '\0';
+ *sp = bsiz;
+ inflateEnd(&zs);
+ return buf;
+}
+
+
+static unsigned int _tc_getcrc_impl(const char *ptr, int size){
+ assert(ptr && size >= 0);
+ int crc = crc32(0, Z_NULL, 0);
+ return crc32(crc, (unsigned char *)ptr, size);
+}
+
+
+#else
+
+
+char *(*_tc_deflate)(const char *, int, int *, int) = NULL;
+char *(*_tc_inflate)(const char *, int, int *, int) = NULL;
+unsigned int (*_tc_getcrc)(const char *, int) = NULL;
+
+
+#endif
+
+
+
+// END OF FILE
--- /dev/null
+/*************************************************************************************************
+ * System-dependent configurations of Tokyo Cabinet
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _MYCONF_H // duplication check
+#define _MYCONF_H
+
+
+
+/*************************************************************************************************
+ * system discrimination
+ *************************************************************************************************/
+
+
+#if defined(__linux__)
+
+#define _SYS_LINUX_
+#define ESTSYSNAME "Linux"
+
+#elif defined(__FreeBSD__)
+
+#define _SYS_FREEBSD_
+#define ESTSYSNAME "FreeBSD"
+
+#elif defined(__NetBSD__)
+
+#define _SYS_NETBSD_
+#define ESTSYSNAME "NetBSD"
+
+#elif defined(__OpenBSD__)
+
+#define _SYS_OPENBSD_
+#define ESTSYSNAME "OpenBSD"
+
+#elif defined(__sun__)
+
+#define _SYS_SUNOS_
+#define ESTSYSNAME "SunOS"
+
+#elif defined(__hpux)
+
+#define _SYS_HPUX_
+#define ESTSYSNAME "HP-UX"
+
+#elif defined(__osf)
+
+#define _SYS_TRU64_
+#define ESTSYSNAME "Tru64"
+
+#elif defined(_AIX)
+
+#define _SYS_AIX_
+#define ESTSYSNAME "AIX"
+
+#elif defined(__APPLE__) && defined(__MACH__)
+
+#define _SYS_MACOSX_
+#define ESTSYSNAME "Mac OS X"
+
+#elif defined(_MSC_VER)
+
+#define _SYS_MSVC_
+#define ESTSYSNAME "Windows (VC++)"
+
+#elif defined(_WIN32)
+
+#define _SYS_MINGW_
+#define ESTSYSNAME "Windows (MinGW)"
+
+#elif defined(__CYGWIN__)
+
+#define _SYS_CYGWIN_
+#define ESTSYSNAME "Windows (Cygwin)"
+
+#else
+
+#define _SYS_GENERIC_
+#define ESTSYSNAME "Generic"
+
+#endif
+
+
+
+/*************************************************************************************************
+ * common settings
+ *************************************************************************************************/
+
+
+#if defined(NDEBUG)
+#define TCDODEBUG(TC_expr) \
+ do { \
+ } while(false)
+#else
+#define TCDODEBUG(TC_expr) \
+ do { \
+ TC_expr; \
+ } while(false)
+#endif
+
+#define TCSWAB16(TC_num) \
+ ( \
+ ((TC_num & 0x00ffU) << 8) | \
+ ((TC_num & 0xff00U) >> 8) \
+ )
+
+#define TCSWAB32(TC_num) \
+ ( \
+ ((TC_num & 0x000000ffUL) << 24) | \
+ ((TC_num & 0x0000ff00UL) << 8) | \
+ ((TC_num & 0x00ff0000UL) >> 8) | \
+ ((TC_num & 0xff000000UL) >> 24) \
+ )
+
+#define TCSWAB64(TC_num) \
+ ( \
+ ((TC_num & 0x00000000000000ffULL) << 56) | \
+ ((TC_num & 0x000000000000ff00ULL) << 40) | \
+ ((TC_num & 0x0000000000ff0000ULL) << 24) | \
+ ((TC_num & 0x00000000ff000000ULL) << 8) | \
+ ((TC_num & 0x000000ff00000000ULL) >> 8) | \
+ ((TC_num & 0x0000ff0000000000ULL) >> 24) | \
+ ((TC_num & 0x00ff000000000000ULL) >> 40) | \
+ ((TC_num & 0xff00000000000000ULL) >> 56) \
+ )
+
+#if defined(_MYBIGEND) || defined(_MYSWAB)
+#define TCBIGEND 1
+#define TCHTOIS(TC_num) TCSWAB16(TC_num)
+#define TCHTOIL(TC_num) TCSWAB32(TC_num)
+#define TCHTOILL(TC_num) TCSWAB64(TC_num)
+#define TCITOHS(TC_num) TCSWAB16(TC_num)
+#define TCITOHL(TC_num) TCSWAB32(TC_num)
+#define TCITOHLL(TC_num) TCSWAB64(TC_num)
+#else
+#define TCBIGEND 0
+#define TCHTOIS(TC_num) (TC_num)
+#define TCHTOIL(TC_num) (TC_num)
+#define TCHTOILL(TC_num) (TC_num)
+#define TCITOHS(TC_num) (TC_num)
+#define TCITOHL(TC_num) (TC_num)
+#define TCITOHLL(TC_num) (TC_num)
+#endif
+
+#if defined(_MYNOZLIB)
+#define TCUSEZLIB 0
+#else
+#define TCUSEZLIB 1
+#endif
+
+#if defined(_MYNOPTHREAD)
+#define TCUSEPTHREAD 0
+#else
+#define TCUSEPTHREAD 1
+#endif
+
+#if defined(_MYMICROYIELD)
+#define TCMICROYIELD 1
+#else
+#define TCMICROYIELD 0
+#endif
+
+#define sizeof(a) ((int)sizeof(a))
+
+int _tc_dummyfunc(void);
+int _tc_dummyfuncv(int a, ...);
+
+
+
+/*************************************************************************************************
+ * general headers
+ *************************************************************************************************/
+
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <float.h>
+#include <limits.h>
+#include <locale.h>
+#include <math.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <time.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <regex.h>
+#include <glob.h>
+
+#if TCUSEPTHREAD
+#include <pthread.h>
+#endif
+
+
+
+/*************************************************************************************************
+ * notation of filesystems
+ *************************************************************************************************/
+
+
+#define MYPATHCHR '/'
+#define MYPATHSTR "/"
+#define MYEXTCHR '.'
+#define MYEXTSTR "."
+#define MYCDIRSTR "."
+#define MYPDIRSTR ".."
+
+
+
+/*************************************************************************************************
+ * for ZLIB
+ *************************************************************************************************/
+
+
+enum {
+ _TCZMZLIB,
+ _TCZMRAW,
+ _TCZMGZIP
+};
+
+
+extern char *(*_tc_deflate)(const char *, int, int *, int);
+
+extern char *(*_tc_inflate)(const char *, int, int *, int);
+
+extern unsigned int (*_tc_getcrc)(const char *, int);
+
+
+
+/*************************************************************************************************
+ * for POSIX thread disability
+ *************************************************************************************************/
+
+
+#if ! TCUSEPTHREAD
+
+#define pthread_once_t int
+#undef PTHREAD_ONCE_INIT
+#define PTHREAD_ONCE_INIT 0
+#define pthread_once(TC_a, TC_b) _tc_dummyfuncv((int)(TC_a), (TC_b))
+
+#define pthread_mutexattr_t int
+#undef PTHREAD_MUTEX_RECURSIVE
+#define PTHREAD_MUTEX_RECURSIVE 0
+#define pthread_mutexattr_init(TC_a) _tc_dummyfuncv((int)(TC_a))
+#define pthread_mutexattr_destroy(TC_a) _tc_dummyfuncv((int)(TC_a))
+#define pthread_mutexattr_settype(TC_a, TC_b) _tc_dummyfuncv((int)(TC_a), (TC_b))
+
+#define pthread_mutex_t int
+#undef PTHREAD_MUTEX_INITIALIZER
+#define PTHREAD_MUTEX_INITIALIZER 0
+#define pthread_mutex_init(TC_a, TC_b) _tc_dummyfuncv((int)(TC_a), (TC_b))
+#define pthread_mutex_destroy(TC_a) _tc_dummyfuncv((int)(TC_a))
+#define pthread_mutex_lock(TC_a) _tc_dummyfuncv((int)(TC_a))
+#define pthread_mutex_unlock(TC_a) _tc_dummyfuncv((int)(TC_a))
+
+#define pthread_rwlock_t int
+#undef PTHREAD_RWLOCK_INITIALIZER
+#define PTHREAD_RWLOCK_INITIALIZER 0
+#define pthread_rwlock_init(TC_a, TC_b) _tc_dummyfuncv((int)(TC_a), (TC_b))
+#define pthread_rwlock_destroy(TC_a) _tc_dummyfuncv((int)(TC_a))
+#define pthread_rwlock_rdlock(TC_a) _tc_dummyfuncv((int)(TC_a))
+#define pthread_rwlock_wrlock(TC_a) _tc_dummyfuncv((int)(TC_a))
+#define pthread_rwlock_unlock(TC_a) _tc_dummyfuncv((int)(TC_a))
+
+#define pthread_key_create(TC_a, TC_b) _tc_dummyfuncv((int)(TC_a), (TC_b))
+#define pthread_key_delete(TC_a) _tc_dummyfuncv((int)(TC_a))
+#define pthread_setspecific(TC_a, TC_b) _tc_dummyfuncv((int)(TC_a))
+#define pthread_getspecific(TC_a) _tc_dummyfuncv((int)(TC_a))
+
+#define pthread_create(TC_th, TC_attr, TC_func, TC_arg) \
+ (*(TC_th) = 0, (TC_func)(TC_arg), 0)
+#define pthread_join(TC_th, TC_rv) \
+ (*(TC_rv) = NULL, 0)
+#define pthread_detach(TC_th) 0
+
+#endif
+
+#if TCUSEPTHREAD && TCMICROYIELD
+#define TCTESTYIELD() \
+ do { \
+ static uint32_t cnt = 0; \
+ if(((++cnt) & (0x20 - 1)) == 0){ \
+ pthread_yield(); \
+ if(cnt > 0x1000) cnt = (uint32_t)time(NULL) % 0x1000; \
+ } \
+ } while(false)
+#undef assert
+#define assert(TC_expr) \
+ do { \
+ if(!(TC_expr)){ \
+ fprintf(stderr, "assertion failed: %s\n", #TC_expr); \
+ abort(); \
+ } \
+ TCTESTYIELD(); \
+ } while(false)
+#else
+#define TCTESTYIELD() \
+ do { \
+ } while(false);
+#endif
+
+
+
+/*************************************************************************************************
+ * utilities for implementation
+ *************************************************************************************************/
+
+
+#define TCNUMBUFSIZ 32 // size of a buffer for a number
+
+/* set a buffer for a variable length number */
+#define TCSETVNUMBUF(TC_len, TC_buf, TC_num) \
+ do { \
+ int _TC_num = (TC_num); \
+ if(_TC_num == 0){ \
+ ((signed char *)(TC_buf))[0] = 0; \
+ (TC_len) = 1; \
+ } else { \
+ (TC_len) = 0; \
+ while(_TC_num > 0){ \
+ int _TC_rem = _TC_num & 0x7f; \
+ _TC_num >>= 7; \
+ if(_TC_num > 0){ \
+ ((signed char *)(TC_buf))[(TC_len)] = -_TC_rem - 1; \
+ } else { \
+ ((signed char *)(TC_buf))[(TC_len)] = _TC_rem; \
+ } \
+ (TC_len)++; \
+ } \
+ } \
+ } while(false)
+
+/* set a buffer for a variable length number of 64-bit */
+#define TCSETVNUMBUF64(TC_len, TC_buf, TC_num) \
+ do { \
+ long long int _TC_num = (TC_num); \
+ if(_TC_num == 0){ \
+ ((signed char *)(TC_buf))[0] = 0; \
+ (TC_len) = 1; \
+ } else { \
+ (TC_len) = 0; \
+ while(_TC_num > 0){ \
+ int _TC_rem = _TC_num & 0x7f; \
+ _TC_num >>= 7; \
+ if(_TC_num > 0){ \
+ ((signed char *)(TC_buf))[(TC_len)] = -_TC_rem - 1; \
+ } else { \
+ ((signed char *)(TC_buf))[(TC_len)] = _TC_rem; \
+ } \
+ (TC_len)++; \
+ } \
+ } \
+ } while(false)
+
+/* read a variable length buffer */
+#define TCREADVNUMBUF(TC_buf, TC_num, TC_step) \
+ do { \
+ TC_num = 0; \
+ int _TC_base = 1; \
+ int _TC_i = 0; \
+ while(true){ \
+ if(((signed char *)(TC_buf))[_TC_i] >= 0){ \
+ TC_num += ((signed char *)(TC_buf))[_TC_i] * _TC_base; \
+ break; \
+ } \
+ TC_num += _TC_base * (((signed char *)(TC_buf))[_TC_i] + 1) * -1; \
+ _TC_base <<= 7; \
+ _TC_i++; \
+ } \
+ (TC_step) = _TC_i + 1; \
+ } while(false)
+
+/* read a variable length buffer */
+#define TCREADVNUMBUF64(TC_buf, TC_num, TC_step) \
+ do { \
+ TC_num = 0; \
+ long long int _TC_base = 1; \
+ int _TC_i = 0; \
+ while(true){ \
+ if(((signed char *)(TC_buf))[_TC_i] >= 0){ \
+ TC_num += ((signed char *)(TC_buf))[_TC_i] * _TC_base; \
+ break; \
+ } \
+ TC_num += _TC_base * (((signed char *)(TC_buf))[_TC_i] + 1) * -1; \
+ _TC_base <<= 7; \
+ _TC_i++; \
+ } \
+ (TC_step) = _TC_i + 1; \
+ } while(false)
+
+/* Compare keys of two records by lexical order. */
+#define TCCMPLEXICAL(TC_rv, TC_aptr, TC_asiz, TC_bptr, TC_bsiz) \
+ do { \
+ (TC_rv) = 0; \
+ int _TC_min = (TC_asiz) < (TC_bsiz) ? (TC_asiz) : (TC_bsiz); \
+ for(int _TC_i = 0; _TC_i < _TC_min; _TC_i++){ \
+ if(((unsigned char *)(TC_aptr))[_TC_i] != ((unsigned char *)(TC_bptr))[_TC_i]){ \
+ (TC_rv) = ((unsigned char *)(TC_aptr))[_TC_i] - ((unsigned char *)(TC_bptr))[_TC_i]; \
+ break; \
+ } \
+ } \
+ if((TC_rv) == 0) (TC_rv) = (TC_asiz) - (TC_bsiz); \
+ } while(false)
+
+
+
+#endif // duplication check
+
+
+// END OF FILE
--- /dev/null
+/*************************************************************************************************
+ * The abstract database API of Tokyo Cabinet
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include "tcutil.h"
+#include "tchdb.h"
+#include "tcbdb.h"
+#include "tcadb.h"
+#include "myconf.h"
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+/* Create an abstract database object. */
+TCADB *tcadbnew(void){
+ TCADB *adb;
+ TCMALLOC(adb, sizeof(*adb));
+ adb->name = NULL;
+ adb->mdb = NULL;
+ adb->hdb = NULL;
+ adb->bdb = NULL;
+ adb->capnum = -1;
+ adb->capsiz = -1;
+ adb->capcnt = 0;
+ adb->cur = NULL;
+ return adb;
+}
+
+
+/* Delete an abstract database object. */
+void tcadbdel(TCADB *adb){
+ assert(adb);
+ if(adb->name) tcadbclose(adb);
+ free(adb);
+}
+
+
+/* Open an abstract database. */
+bool tcadbopen(TCADB *adb, const char *name){
+ assert(adb && name);
+ TCLIST *elems = tcstrsplit(name, "#");
+ char *path = tclistshift2(elems);
+ if(!path){
+ tclistdel(elems);
+ return false;
+ }
+ int64_t bnum = -1;
+ int64_t capnum = -1;
+ int64_t capsiz = -1;
+ bool owmode = true;
+ bool ocmode = true;
+ bool otmode = false;
+ bool onlmode = false;
+ bool onbmode = false;
+ int8_t apow = -1;
+ int8_t fpow = -1;
+ bool tlmode = false;
+ bool tdmode = false;
+ int32_t rcnum = -1;
+ int32_t lmemb = -1;
+ int32_t nmemb = -1;
+ int32_t lcnum = -1;
+ int32_t ncnum = -1;
+ int ln = TCLISTNUM(elems);
+ for(int i = 0; i < ln; i++){
+ const char *elem = TCLISTVALPTR(elems, i);
+ char *pv = strchr(elem, '=');
+ if(!pv) continue;
+ *(pv++) = '\0';
+ if(!tcstricmp(elem, "bnum")){
+ bnum = strtoll(pv, NULL, 10);
+ } else if(!tcstricmp(elem, "capnum")){
+ capnum = strtoll(pv, NULL, 10);
+ } else if(!tcstricmp(elem, "capsiz")){
+ capsiz = strtoll(pv, NULL, 10);
+ } else if(!tcstricmp(elem, "mode")){
+ owmode = strchr(pv, 'w') || strchr(pv, 'W');
+ ocmode = strchr(pv, 'c') || strchr(pv, 'C');
+ otmode = strchr(pv, 't') || strchr(pv, 'T');
+ onlmode = strchr(pv, 'e') || strchr(pv, 'E');
+ onbmode = strchr(pv, 'f') || strchr(pv, 'F');
+ } else if(!tcstricmp(elem, "apow")){
+ apow = strtol(pv, NULL, 10);
+ } else if(!tcstricmp(elem, "fpow")){
+ fpow = strtol(pv, NULL, 10);
+ } else if(!tcstricmp(elem, "opts")){
+ if(strchr(pv, 'l') || strchr(pv, 'L')) tlmode = true;
+ if(strchr(pv, 'd') || strchr(pv, 'D')) tdmode = true;
+ } else if(!tcstricmp(elem, "rcnum")){
+ rcnum = strtol(pv, NULL, 10);
+ } else if(!tcstricmp(elem, "lmemb")){
+ lmemb = strtol(pv, NULL, 10);
+ } else if(!tcstricmp(elem, "nmemb")){
+ nmemb = strtol(pv, NULL, 10);
+ } else if(!tcstricmp(elem, "lcnum")){
+ lcnum = strtol(pv, NULL, 10);
+ } else if(!tcstricmp(elem, "ncnum")){
+ ncnum = strtol(pv, NULL, 10);
+ }
+ }
+ tclistdel(elems);
+ if(!tcstricmp(path, "*")){
+ adb->mdb = bnum > 0 ? tcmdbnew2(bnum) : tcmdbnew();
+ adb->capnum = capnum;
+ adb->capsiz = capsiz;
+ adb->capcnt = 0;
+ } else if(tcstribwm(path, ".tch")){
+ TCHDB *hdb = tchdbnew();
+ tchdbsetmutex(hdb);
+ int opts = 0;
+ if(tlmode) opts |= HDBTLARGE;
+ if(tdmode) opts |= HDBTDEFLATE;
+ tchdbtune(hdb, bnum, apow, fpow, opts);
+ tchdbsetcache(hdb, rcnum);
+ int omode = owmode ? HDBOWRITER : HDBOREADER;
+ if(ocmode) omode |= HDBOCREAT;
+ if(otmode) omode |= HDBOTRUNC;
+ if(onlmode) omode |= HDBONOLCK;
+ if(onbmode) omode |= HDBOLCKNB;
+ if(!tchdbopen(hdb, path, omode)){
+ tchdbdel(hdb);
+ free(path);
+ return false;
+ }
+ adb->hdb = hdb;
+ } else if(tcstribwm(path, ".tcb")){
+ TCBDB *bdb = tcbdbnew();
+ tcbdbsetmutex(bdb);
+ int opts = 0;
+ if(tlmode) opts |= BDBTLARGE;
+ if(tdmode) opts |= BDBTDEFLATE;
+ tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts);
+ tcbdbsetcache(bdb, lcnum, ncnum);
+ if(capnum > 0) tcbdbsetcapnum(bdb, capnum);
+ int omode = owmode ? BDBOWRITER : BDBOREADER;
+ if(ocmode) omode |= BDBOCREAT;
+ if(otmode) omode |= BDBOTRUNC;
+ if(onlmode) omode |= BDBONOLCK;
+ if(onbmode) omode |= BDBOLCKNB;
+ if(!tcbdbopen(bdb, path, omode)){
+ tcbdbdel(bdb);
+ free(path);
+ return false;
+ }
+ adb->bdb = bdb;
+ adb->cur = tcbdbcurnew(bdb);
+ } else {
+ free(path);
+ return false;
+ }
+ free(path);
+ adb->name = tcstrdup(name);
+ return true;
+}
+
+
+/* Close an abstract database object. */
+bool tcadbclose(TCADB *adb){
+ assert(adb);
+ int err = false;
+ if(!adb->name) return false;
+ if(adb->mdb){
+ tcmdbdel(adb->mdb);
+ adb->mdb = NULL;
+ } else if(adb->hdb){
+ if(!tchdbclose(adb->hdb)) err = true;
+ tchdbdel(adb->hdb);
+ adb->hdb = NULL;
+ } else if(adb->bdb){
+ tcbdbcurdel(adb->cur);
+ if(!tcbdbclose(adb->bdb)) err = true;
+ tcbdbdel(adb->bdb);
+ adb->bdb = NULL;
+ }
+ free(adb->name);
+ adb->name = NULL;
+ return !err;
+}
+
+
+/* Store a record into an abstract database object. */
+bool tcadbput(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+ assert(adb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ bool err = false;
+ if(adb->mdb){
+ tcmdbput(adb->mdb, kbuf, ksiz, vbuf, vsiz);
+ if(adb->capnum > 0 || adb->capsiz > 0){
+ adb->capcnt++;
+ if((adb->capcnt & 0xff) == 0){
+ if(adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum) tcmdbcutfront(adb->mdb, 0x100);
+ if(adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz) tcmdbcutfront(adb->mdb, 0x200);
+ }
+ }
+ } else if(adb->hdb){
+ if(!tchdbput(adb->hdb, kbuf, ksiz, vbuf, vsiz)) err = true;
+ } else if(adb->bdb){
+ if(!tcbdbput(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
+ } else {
+ err = true;
+ }
+ return !err;
+}
+
+
+/* Store a string record into an abstract object. */
+bool tcadbput2(TCADB *adb, const char *kstr, const char *vstr){
+ assert(adb && kstr && vstr);
+ return tcadbput(adb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Store a new record into an abstract database object. */
+bool tcadbputkeep(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+ assert(adb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ bool err = false;
+ if(adb->mdb){
+ if(tcmdbputkeep(adb->mdb, kbuf, ksiz, vbuf, vsiz)){
+ if(adb->capnum > 0 || adb->capsiz > 0){
+ adb->capcnt++;
+ if((adb->capcnt & 0xff) == 0){
+ if(adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum) tcmdbcutfront(adb->mdb, 0x100);
+ if(adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz) tcmdbcutfront(adb->mdb, 0x200);
+ }
+ }
+ } else {
+ err = true;
+ }
+ } else if(adb->hdb){
+ if(!tchdbputkeep(adb->hdb, kbuf, ksiz, vbuf, vsiz)) err = true;
+ } else if(adb->bdb){
+ if(!tcbdbputkeep(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
+ } else {
+ err = true;
+ }
+ return !err;
+}
+
+
+/* Store a new string record into an abstract database object. */
+bool tcadbputkeep2(TCADB *adb, const char *kstr, const char *vstr){
+ assert(adb && kstr && vstr);
+ return tcadbputkeep(adb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Concatenate a value at the end of the existing record in an abstract database object. */
+bool tcadbputcat(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+ assert(adb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ bool err = false;
+ if(adb->mdb){
+ tcmdbputcat(adb->mdb, kbuf, ksiz, vbuf, vsiz);
+ if(adb->capnum > 0 || adb->capsiz > 0){
+ adb->capcnt++;
+ if((adb->capcnt & 0xff) == 0){
+ if(adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum) tcmdbcutfront(adb->mdb, 0x100);
+ if(adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz) tcmdbcutfront(adb->mdb, 0x200);
+ }
+ }
+ } else if(adb->hdb){
+ if(!tchdbputcat(adb->hdb, kbuf, ksiz, vbuf, vsiz)) err = true;
+ } else if(adb->bdb){
+ if(!tcbdbputcat(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
+ } else {
+ err = true;
+ }
+ return !err;
+}
+
+
+/* Concatenate a string value at the end of the existing record in an abstract database object. */
+bool tcadbputcat2(TCADB *adb, const char *kstr, const char *vstr){
+ assert(adb && kstr && vstr);
+ return tcadbputcat(adb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Remove a record of an abstract database object. */
+bool tcadbout(TCADB *adb, const void *kbuf, int ksiz){
+ assert(adb && kbuf && ksiz >= 0);
+ bool err = false;
+ if(adb->mdb){
+ if(!tcmdbout(adb->mdb, kbuf, ksiz)) err = true;
+ } else if(adb->hdb){
+ if(!tchdbout(adb->hdb, kbuf, ksiz)) err = true;
+ } else if(adb->bdb){
+ if(!tcbdbout(adb->bdb, kbuf, ksiz)) err = true;
+ } else {
+ err = true;
+ }
+ return !err;
+}
+
+
+/* Remove a string record of an abstract database object. */
+bool tcadbout2(TCADB *adb, const char *kstr){
+ assert(adb && kstr);
+ return tcadbout(adb, kstr, strlen(kstr));
+}
+
+
+/* Retrieve a record in an abstract database object. */
+void *tcadbget(TCADB *adb, const void *kbuf, int ksiz, int *sp){
+ assert(adb && kbuf && ksiz >= 0 && sp);
+ char *rv;
+ if(adb->mdb){
+ rv = tcmdbget(adb->mdb, kbuf, ksiz, sp);
+ } else if(adb->hdb){
+ rv = tchdbget(adb->hdb, kbuf, ksiz, sp);
+ } else if(adb->bdb){
+ rv = tcbdbget(adb->bdb, kbuf, ksiz, sp);
+ } else {
+ rv = NULL;
+ }
+ return rv;
+}
+
+
+/* Retrieve a string record in an abstract database object. */
+char *tcadbget2(TCADB *adb, const char *kstr){
+ assert(adb && kstr);
+ int vsiz;
+ return tcadbget(adb, kstr, strlen(kstr), &vsiz);
+}
+
+
+/* Get the size of the value of a record in an abstract database object. */
+int tcadbvsiz(TCADB *adb, const void *kbuf, int ksiz){
+ assert(adb && kbuf && ksiz >= 0);
+ int rv;
+ if(adb->mdb){
+ rv = tcmdbvsiz(adb->mdb, kbuf, ksiz);
+ } else if(adb->hdb){
+ rv = tchdbvsiz(adb->hdb, kbuf, ksiz);
+ } else if(adb->bdb){
+ rv = tcbdbvsiz(adb->bdb, kbuf, ksiz);
+ } else {
+ rv = -1;
+ }
+ return rv;
+}
+
+
+/* Get the size of the value of a string record in an abstract database object. */
+int tcadbvsiz2(TCADB *adb, const char *kstr){
+ assert(adb && kstr);
+ return tcadbvsiz(adb, kstr, strlen(kstr));
+}
+
+
+/* Initialize the iterator of an abstract database object. */
+bool tcadbiterinit(TCADB *adb){
+ assert(adb);
+ bool err = false;
+ if(adb->mdb){
+ tcmdbiterinit(adb->mdb);
+ } else if(adb->hdb){
+ if(!tchdbiterinit(adb->hdb)) err = true;
+ } else if(adb->bdb){
+ if(!tcbdbcurfirst(adb->cur)){
+ int ecode = tcbdbecode(adb->bdb);
+ if(ecode != TCESUCCESS && ecode != TCEINVALID && ecode != TCEKEEP && ecode != TCENOREC)
+ err = true;
+ }
+ } else {
+ err = true;
+ }
+ return !err;
+}
+
+
+/* Get the next key of the iterator of an abstract database object. */
+void *tcadbiternext(TCADB *adb, int *sp){
+ assert(adb && sp);
+ char *rv;
+ if(adb->mdb){
+ rv = tcmdbiternext(adb->mdb, sp);
+ } else if(adb->hdb){
+ rv = tchdbiternext(adb->hdb, sp);
+ } else if(adb->bdb){
+ rv = tcbdbcurkey(adb->cur, sp);
+ tcbdbcurnext(adb->cur);
+ } else {
+ rv = NULL;
+ }
+ return rv;
+}
+
+
+/* Get the next key string of the iterator of an abstract database object. */
+char *tcadbiternext2(TCADB *adb){
+ assert(adb);
+ int vsiz;
+ return tcadbiternext(adb, &vsiz);
+}
+
+
+/* Get forward matching keys in an abstract database object. */
+TCLIST *tcadbfwmkeys(TCADB *adb, const void *pbuf, int psiz, int max){
+ assert(adb && pbuf && psiz >= 0);
+ TCLIST *rv;
+ if(adb->mdb){
+ rv = tcmdbfwmkeys(adb->mdb, pbuf, psiz, max);
+ } else if(adb->hdb){
+ rv = tchdbfwmkeys(adb->hdb, pbuf, psiz, max);
+ } else if(adb->bdb){
+ rv = tcbdbfwmkeys(adb->bdb, pbuf, psiz, max);
+ } else {
+ rv = tclistnew();
+ }
+ return rv;
+}
+
+
+/* Get forward matching string keys in an abstract database object. */
+TCLIST *tcadbfwmkeys2(TCADB *adb, const char *pstr, int max){
+ assert(adb && pstr);
+ return tcadbfwmkeys(adb, pstr, strlen(pstr), max);
+}
+
+
+/* Synchronize updated contents of an abstract database object with the file and the device. */
+bool tcadbsync(TCADB *adb){
+ assert(adb);
+ bool err = false;
+ if(adb->mdb){
+ err = true;
+ } else if(adb->hdb){
+ if(!tchdbsync(adb->hdb)) err = true;
+ } else if(adb->bdb){
+ if(!tcbdbsync(adb->bdb)) err = true;
+ } else {
+ err = true;
+ }
+ return !err;
+}
+
+
+/* Remove all records of an abstract database object. */
+bool tcadbvanish(TCADB *adb){
+ assert(adb);
+ bool err = false;
+ if(adb->mdb){
+ tcmdbvanish(adb->mdb);
+ } else if(adb->hdb){
+ if(!tchdbvanish(adb->hdb)) err = true;
+ } else if(adb->bdb){
+ if(!tcbdbvanish(adb->bdb)) err = true;
+ } else {
+ err = true;
+ }
+ return !err;
+}
+
+
+/* Copy the database file of an abstract database object. */
+bool tcadbcopy(TCADB *adb, const char *path){
+ assert(adb && path);
+ bool err = false;
+ if(adb->mdb){
+ err = true;
+ } else if(adb->hdb){
+ if(!tchdbcopy(adb->hdb, path)) err = true;
+ } else if(adb->bdb){
+ if(!tcbdbcopy(adb->bdb, path)) err = true;
+ } else {
+ err = true;
+ }
+ return !err;
+}
+
+
+/* Get the number of records of an abstract database object. */
+uint64_t tcadbrnum(TCADB *adb){
+ assert(adb);
+ uint64_t rv;
+ if(adb->mdb){
+ rv = tcmdbrnum(adb->mdb);
+ } else if(adb->hdb){
+ rv = tchdbrnum(adb->hdb);
+ } else if(adb->bdb){
+ rv = tcbdbrnum(adb->bdb);
+ } else {
+ rv = 0;
+ }
+ return rv;
+}
+
+
+/* Get the size of the database of an abstract database object. */
+uint64_t tcadbsize(TCADB *adb){
+ assert(adb);
+ uint64_t rv;
+ if(adb->mdb){
+ rv = tcmdbmsiz(adb->mdb);
+ } else if(adb->hdb){
+ rv = tchdbfsiz(adb->hdb);
+ } else if(adb->bdb){
+ rv = tcbdbfsiz(adb->bdb);
+ } else {
+ rv = 0;
+ }
+ return rv;
+}
+
+
+
+// END OF FILE
--- /dev/null
+/*************************************************************************************************
+ * The abstract database API of Tokyo Cabinet
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _TCADB_H /* duplication check */
+#define _TCADB_H
+
+#if defined(__cplusplus)
+#define __TCADB_CLINKAGEBEGIN extern "C" {
+#define __TCADB_CLINKAGEEND }
+#else
+#define __TCADB_CLINKAGEBEGIN
+#define __TCADB_CLINKAGEEND
+#endif
+__TCADB_CLINKAGEBEGIN
+
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <time.h>
+#include <tcutil.h>
+#include <tchdb.h>
+#include <tcbdb.h>
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+typedef struct { /* type of structure for an abstract database */
+ char *name; /* name of the database */
+ TCMDB *mdb; /* on-memory database object */
+ TCHDB *hdb; /* hash database object */
+ TCBDB *bdb; /* B+ tree database object */
+ int64_t capnum; /* capacity number of records */
+ int64_t capsiz; /* capacity size of using memory */
+ uint32_t capcnt; /* count for capacity check */
+ BDBCUR *cur; /* cursor of B+ tree */
+} TCADB;
+
+
+/* Create an abstract database object.
+ The return value is the new abstract database object. */
+TCADB *tcadbnew(void);
+
+
+/* Delete an abstract database object.
+ `adb' specifies the abstract database object. */
+void tcadbdel(TCADB *adb);
+
+
+/* Open an abstract database.
+ `adb' specifies the abstract database object.
+ `name' specifies the name of the database. If it is "*", the database will be an on-memory
+ database. If its suffix is ".tch", the database will be a hash database. If its suffix is
+ ".tcb", the database will be a B+ tree database. Otherwise, this function fails. Tuning
+ parameters can trail the name, separated by "#". Each parameter is composed of the name and
+ the number, separated by "=". On-memory database supports "bnum", "capnum", and "capsiz".
+ Hash database supports "mode", "bnum", "apow", "fpow", "opts", and "rcnum". B+ tree database
+ supports "mode", "lmemb", "nmemb", "bnum", "apow", "fpow", "opts", "lcnum", and "ncnum".
+ "capnum" specifies the capacity number of records. "capsiz" specifies the capacity size of
+ using memory. Records spilled the capacity are removed by the storing order. "mode" can
+ contain "w" of writer, "r" of reader, "c" of creating, "t" of truncating, "e" of no locking,
+ and "f" of non-blocking lock. The default mode is relevant to "wc". "opts" can contains "l"
+ of large option, "d" of Deflate option, and "b" of TCBS option. For example,
+ "casket.tch#bnum=1000000#opts=ld" means that the name of the database file is "casket.tch",
+ and the bucket number is 1000000, and the options are large and Deflate.
+ If successful, the return value is true, else, it is false. */
+bool tcadbopen(TCADB *adb, const char *name);
+
+
+/* Close an abstract database object.
+ `adb' specifies the abstract database object.
+ If successful, the return value is true, else, it is false.
+ Update of a database is assured to be written when the database is closed. If a writer opens
+ a database but does not close it appropriately, the database will be broken. */
+bool tcadbclose(TCADB *adb);
+
+
+/* Store a record into an abstract database object.
+ `adb' specifies the abstract database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, it is overwritten. */
+bool tcadbput(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a string record into an abstract object.
+ `adb' specifies the abstract database object.
+ `kstr' specifies the string of the key.
+ `vstr' specifies the string of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, it is overwritten. */
+bool tcadbput2(TCADB *adb, const char *kstr, const char *vstr);
+
+
+/* Store a new record into an abstract database object.
+ `adb' specifies the abstract database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, this function has no effect. */
+bool tcadbputkeep(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a new string record into an abstract database object.
+ `adb' specifies the abstract database object.
+ `kstr' specifies the string of the key.
+ `vstr' specifies the string of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, this function has no effect. */
+bool tcadbputkeep2(TCADB *adb, const char *kstr, const char *vstr);
+
+
+/* Concatenate a value at the end of the existing record in an abstract database object.
+ `adb' specifies the abstract database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If successful, the return value is true, else, it is false.
+ If there is no corresponding record, a new record is created. */
+bool tcadbputcat(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Concatenate a string value at the end of the existing record in an abstract database object.
+ `adb' specifies the abstract database object.
+ `kstr' specifies the string of the key.
+ `vstr' specifies the string of the value.
+ If successful, the return value is true, else, it is false.
+ If there is no corresponding record, a new record is created. */
+bool tcadbputcat2(TCADB *adb, const char *kstr, const char *vstr);
+
+
+/* Remove a record of an abstract database object.
+ `adb' specifies the abstract database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is true, else, it is false. */
+bool tcadbout(TCADB *adb, const void *kbuf, int ksiz);
+
+
+/* Remove a string record of an abstract database object.
+ `adb' specifies the abstract database object.
+ `kstr' specifies the string of the key.
+ If successful, the return value is true, else, it is false. */
+bool tcadbout2(TCADB *adb, const char *kstr);
+
+
+/* Retrieve a record in an abstract database object.
+ `adb' specifies the abstract database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the value of the corresponding
+ record. `NULL' is returned if no record corresponds.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when
+ it is no longer in use. */
+void *tcadbget(TCADB *adb, const void *kbuf, int ksiz, int *sp);
+
+
+/* Retrieve a string record in an abstract database object.
+ `adb' specifies the abstract database object.
+ `kstr' specifies the string of the key.
+ If successful, the return value is the string of the value of the corresponding record.
+ `NULL' is returned if no record corresponds.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. */
+char *tcadbget2(TCADB *adb, const char *kstr);
+
+
+/* Get the size of the value of a record in an abstract database object.
+ `adb' specifies the abstract database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is the size of the value of the corresponding record, else,
+ it is -1. */
+int tcadbvsiz(TCADB *adb, const void *kbuf, int ksiz);
+
+
+/* Get the size of the value of a string record in an abstract database object.
+ `adb' specifies the abstract database object.
+ `kstr' specifies the string of the key.
+ If successful, the return value is the size of the value of the corresponding record, else,
+ it is -1. */
+int tcadbvsiz2(TCADB *adb, const char *kstr);
+
+
+/* Initialize the iterator of an abstract database object.
+ `adb' specifies the abstract database object.
+ If successful, the return value is true, else, it is false.
+ The iterator is used in order to access the key of every record stored in a database. */
+bool tcadbiterinit(TCADB *adb);
+
+
+/* Get the next key of the iterator of an abstract database object.
+ `adb' specifies the abstract database object.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the next key, else, it is
+ `NULL'. `NULL' is returned when no record is to be get out of the iterator.
+ Because an additional zero code is appended at the end of the region of the return value, the
+ return value can be treated as a character string. Because the region of the return value is
+ allocated with the `malloc' call, it should be released with the `free' call when it is no
+ longer in use. It is possible to access every record by iteration of calling this function.
+ It is allowed to update or remove records whose keys are fetched while the iteration.
+ However, it is not assured if updating the database is occurred while the iteration. Besides,
+ the order of this traversal access method is arbitrary, so it is not assured that the order of
+ storing matches the one of the traversal access. */
+void *tcadbiternext(TCADB *adb, int *sp);
+
+
+/* Get the next key string of the iterator of an abstract database object.
+ `adb' specifies the abstract database object.
+ If successful, the return value is the string of the next key, else, it is `NULL'. `NULL' is
+ returned when no record is to be get out of the iterator.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. It is possible to access every
+ record by iteration of calling this function. However, it is not assured if updating the
+ database is occurred while the iteration. Besides, the order of this traversal access method
+ is arbitrary, so it is not assured that the order of storing matches the one of the traversal
+ access. */
+char *tcadbiternext2(TCADB *adb);
+
+
+/* Get forward matching keys in an abstract database object.
+ `adb' specifies the abstract database object.
+ `pbuf' specifies the pointer to the region of the prefix.
+ `psiz' specifies the size of the region of the prefix.
+ `max' specifies the maximum number of keys to be fetched. If it is negative, no limit is
+ specified.
+ The return value is a list object of the corresponding keys. This function does never fail
+ and return an empty list even if no key corresponds.
+ Because the object of the return value is created with the function `tclistnew', it should be
+ deleted with the function `tclistdel' when it is no longer in use. Note that this function
+ may be very slow because every key in the database is scanned. */
+TCLIST *tcadbfwmkeys(TCADB *adb, const void *pbuf, int psiz, int max);
+
+
+/* Get forward matching string keys in an abstract database object.
+ `adb' specifies the abstract database object.
+ `pstr' specifies the string of the prefix.
+ `max' specifies the maximum number of keys to be fetched. If it is negative, no limit is
+ specified.
+ The return value is a list object of the corresponding keys. This function does never fail
+ and return an empty list even if no key corresponds.
+ Because the object of the return value is created with the function `tclistnew', it should be
+ deleted with the function `tclistdel' when it is no longer in use. Note that this function
+ may be very slow because every key in the database is scanned. */
+TCLIST *tcadbfwmkeys2(TCADB *adb, const char *pstr, int max);
+
+
+/* Synchronize updated contents of an abstract database object with the file and the device.
+ `adb' specifies the abstract database object.
+ If successful, the return value is true, else, it is false.
+ This function fails and has no effect for on-memory database. */
+bool tcadbsync(TCADB *adb);
+
+
+/* Remove all records of an abstract database object.
+ `adb' specifies the abstract database object.
+ If successful, the return value is true, else, it is false. */
+bool tcadbvanish(TCADB *adb);
+
+
+/* Copy the database file of an abstract database object.
+ `adb' specifies the abstract database object.
+ `path' specifies the path of the destination file. If it begins with `@', the trailing
+ substring is executed as a command line.
+ If successful, the return value is true, else, it is false. False is returned if the executed
+ command returns non-zero code.
+ The database file is assured to be kept synchronized and not modified while the copying or
+ executing operation is in progress. So, this function is useful to create a backup file of
+ the database file. This function fails and has no effect for on-memory database. */
+bool tcadbcopy(TCADB *adb, const char *path);
+
+
+/* Get the number of records of an abstract database object.
+ `adb' specifies the abstract database object.
+ The return value is the number of records or 0 if the object does not connect to any database
+ instance. */
+uint64_t tcadbrnum(TCADB *adb);
+
+
+/* Get the size of the database of an abstract database object.
+ `adb' specifies the abstract database object.
+ The return value is the size of the database or 0 if the object does not connect to any
+ database instance. */
+uint64_t tcadbsize(TCADB *adb);
+
+
+
+__TCADB_CLINKAGEEND
+#endif /* duplication check */
+
+
+/* END OF FILE */
--- /dev/null
+/*************************************************************************************************
+ * The command line utility of the abstract database API
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include <tcadb.h>
+#include "myconf.h"
+
+
+/* global variables */
+const char *g_progname; // program name
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void printerr(TCADB *adb);
+static int printdata(const char *ptr, int size, bool px);
+static char *hextoobj(const char *str, int *sp);
+static int runcreate(int argc, char **argv);
+static int runinform(int argc, char **argv);
+static int runput(int argc, char **argv);
+static int runout(int argc, char **argv);
+static int runget(int argc, char **argv);
+static int runlist(int argc, char **argv);
+static int runversion(int argc, char **argv);
+static int proccreate(const char *name);
+static int procinform(const char *name);
+static int procput(const char *name, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+ int dmode);
+static int procout(const char *name, const char *kbuf, int ksiz);
+static int procget(const char *name, const char *kbuf, int ksiz, bool px, bool pz);
+static int proclist(const char *name, int max, bool pv, bool px, const char *fmstr);
+static int procversion(void);
+
+
+/* main routine */
+int main(int argc, char **argv){
+ g_progname = argv[0];
+ if(argc < 2) usage();
+ int rv = 0;
+ if(!strcmp(argv[1], "create")){
+ rv = runcreate(argc, argv);
+ } else if(!strcmp(argv[1], "inform")){
+ rv = runinform(argc, argv);
+ } else if(!strcmp(argv[1], "put")){
+ rv = runput(argc, argv);
+ } else if(!strcmp(argv[1], "out")){
+ rv = runout(argc, argv);
+ } else if(!strcmp(argv[1], "get")){
+ rv = runget(argc, argv);
+ } else if(!strcmp(argv[1], "list")){
+ rv = runlist(argc, argv);
+ } else if(!strcmp(argv[1], "version") || !strcmp(argv[1], "--version")){
+ rv = runversion(argc, argv);
+ } else {
+ usage();
+ }
+ return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+ fprintf(stderr, "%s: the command line utility of the abstract database API\n", g_progname);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, " %s create name\n", g_progname);
+ fprintf(stderr, " %s inform name\n", g_progname);
+ fprintf(stderr, " %s put [-sx] [-dk|-dc] name key value\n", g_progname);
+ fprintf(stderr, " %s out [-sx] name key\n", g_progname);
+ fprintf(stderr, " %s get [-sx] [-px] [-pz] name key\n", g_progname);
+ fprintf(stderr, " %s list [-m num] [-pv] [-px] [-fm str] name\n", g_progname);
+ fprintf(stderr, " %s version\n", g_progname);
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+
+/* print error information */
+static void printerr(TCADB *adb){
+ fprintf(stderr, "%s: error\n", g_progname);
+}
+
+
+/* print record data */
+static int printdata(const char *ptr, int size, bool px){
+ int len = 0;
+ while(size-- > 0){
+ if(px){
+ if(len > 0) putchar(' ');
+ len += printf("%02X", *(unsigned char *)ptr);
+ } else {
+ putchar(*ptr);
+ len++;
+ }
+ ptr++;
+ }
+ return len;
+}
+
+
+/* create a binary object from a hexadecimal string */
+static char *hextoobj(const char *str, int *sp){
+ int len = strlen(str);
+ char *buf = tcmalloc(len + 1);
+ int j = 0;
+ for(int i = 0; i < len; i += 2){
+ while(strchr(" \n\r\t\f\v", str[i])){
+ i++;
+ }
+ char mbuf[3];
+ if((mbuf[0] = str[i]) == '\0') break;
+ if((mbuf[1] = str[i+1]) == '\0') break;
+ mbuf[2] = '\0';
+ buf[j++] = (char)strtol(mbuf, NULL, 16);
+ }
+ buf[j] = '\0';
+ *sp = j;
+ return buf;
+}
+
+
+/* parse arguments of create command */
+static int runcreate(int argc, char **argv){
+ char *name = NULL;
+ for(int i = 2; i < argc; i++){
+ if(!name && argv[i][0] == '-'){
+ usage();
+ } else if(!name){
+ name = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!name) usage();
+ int rv = proccreate(name);
+ return rv;
+}
+
+
+/* parse arguments of inform command */
+static int runinform(int argc, char **argv){
+ char *name = NULL;
+ for(int i = 2; i < argc; i++){
+ if(!name && argv[i][0] == '-'){
+ usage();
+ } else if(!name){
+ name = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!name) usage();
+ name = tcsprintf("%s#mode=r", name);
+ int rv = procinform(name);
+ free(name);
+ return rv;
+}
+
+
+/* parse arguments of put command */
+static int runput(int argc, char **argv){
+ char *name = NULL;
+ char *key = NULL;
+ char *value = NULL;
+ int dmode = 0;
+ bool sx = false;
+ for(int i = 2; i < argc; i++){
+ if(!name && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-dk")){
+ dmode = -1;
+ } else if(!strcmp(argv[i], "-dc")){
+ dmode = 1;
+ } else if(!strcmp(argv[i], "-sx")){
+ sx = true;
+ } else {
+ usage();
+ }
+ } else if(!name){
+ name = argv[i];
+ } else if(!key){
+ key = argv[i];
+ } else if(!value){
+ value = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!name || !key || !value) usage();
+ int ksiz, vsiz;
+ char *kbuf, *vbuf;
+ if(sx){
+ kbuf = hextoobj(key, &ksiz);
+ vbuf = hextoobj(value, &vsiz);
+ } else {
+ ksiz = strlen(key);
+ kbuf = tcmemdup(key, ksiz);
+ vsiz = strlen(value);
+ vbuf = tcmemdup(value, vsiz);
+ }
+ int rv = procput(name, kbuf, ksiz, vbuf, vsiz, dmode);
+ free(vbuf);
+ free(kbuf);
+ return rv;
+}
+
+
+/* parse arguments of out command */
+static int runout(int argc, char **argv){
+ char *name = NULL;
+ char *key = NULL;
+ bool sx = false;
+ for(int i = 2; i < argc; i++){
+ if(!name && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-sx")){
+ sx = true;
+ } else {
+ usage();
+ }
+ } else if(!name){
+ name = argv[i];
+ } else if(!key){
+ key = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!name || !key) usage();
+ int ksiz;
+ char *kbuf;
+ if(sx){
+ kbuf = hextoobj(key, &ksiz);
+ } else {
+ ksiz = strlen(key);
+ kbuf = tcmemdup(key, ksiz);
+ }
+ int rv = procout(name, kbuf, ksiz);
+ free(kbuf);
+ return rv;
+}
+
+
+/* parse arguments of get command */
+static int runget(int argc, char **argv){
+ char *name = NULL;
+ char *key = NULL;
+ bool sx = false;
+ bool px = false;
+ bool pz = false;
+ for(int i = 2; i < argc; i++){
+ if(!name && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-sx")){
+ sx = true;
+ } else if(!strcmp(argv[i], "-px")){
+ px = true;
+ } else if(!strcmp(argv[i], "-pz")){
+ pz = true;
+ } else {
+ usage();
+ }
+ } else if(!name){
+ name = argv[i];
+ } else if(!key){
+ key = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!name || !key) usage();
+ int ksiz;
+ char *kbuf;
+ if(sx){
+ kbuf = hextoobj(key, &ksiz);
+ } else {
+ ksiz = strlen(key);
+ kbuf = tcmemdup(key, ksiz);
+ }
+ name = tcsprintf("%s#mode=r", name);
+ int rv = procget(name, kbuf, ksiz, px, pz);
+ free(name);
+ free(kbuf);
+ return rv;
+}
+
+
+/* parse arguments of list command */
+static int runlist(int argc, char **argv){
+ char *name = NULL;
+ int max = -1;
+ bool pv = false;
+ bool px = false;
+ char *fmstr = NULL;
+ for(int i = 2; i < argc; i++){
+ if(!name && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-m")){
+ if(++i >= argc) usage();
+ max = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-pv")){
+ pv = true;
+ } else if(!strcmp(argv[i], "-px")){
+ px = true;
+ } else if(!strcmp(argv[i], "-fm")){
+ if(++i >= argc) usage();
+ fmstr = argv[i];
+ } else {
+ usage();
+ }
+ } else if(!name){
+ name = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!name) usage();
+ name = tcsprintf("%s#mode=r", name);
+ int rv = proclist(name, max, pv, px, fmstr);
+ free(name);
+ return rv;
+}
+
+
+/* parse arguments of version command */
+static int runversion(int argc, char **argv){
+ int rv = procversion();
+ return rv;
+}
+
+
+/* perform create command */
+static int proccreate(const char *name){
+ TCADB *adb = tcadbnew();
+ if(!tcadbopen(adb, name)){
+ printerr(adb);
+ tcadbdel(adb);
+ return 1;
+ }
+ bool err = false;
+ if(!tcadbclose(adb)){
+ printerr(adb);
+ err = true;
+ }
+ tcadbdel(adb);
+ return err ? 1 : 0;
+}
+
+
+/* perform inform command */
+static int procinform(const char *name){
+ TCADB *adb = tcadbnew();
+ if(!tcadbopen(adb, name)){
+ printerr(adb);
+ tcadbdel(adb);
+ return 1;
+ }
+ bool err = false;
+ printf("record number: %llu\n", (unsigned long long)tcadbrnum(adb));
+ printf("size: %llu\n", (unsigned long long)tcadbsize(adb));
+ if(!tcadbclose(adb)){
+ printerr(adb);
+ err = true;
+ }
+ tcadbdel(adb);
+ return err ? 1 : 0;
+}
+
+
+/* perform put command */
+static int procput(const char *name, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+ int dmode){
+ TCADB *adb = tcadbnew();
+ if(!tcadbopen(adb, name)){
+ printerr(adb);
+ tcadbdel(adb);
+ return 1;
+ }
+ bool err = false;
+ switch(dmode){
+ case -1:
+ if(!tcadbputkeep(adb, kbuf, ksiz, vbuf, vsiz)){
+ printerr(adb);
+ err = true;
+ }
+ break;
+ case 1:
+ if(!tcadbputcat(adb, kbuf, ksiz, vbuf, vsiz)){
+ printerr(adb);
+ err = true;
+ }
+ break;
+ default:
+ if(!tcadbput(adb, kbuf, ksiz, vbuf, vsiz)){
+ printerr(adb);
+ err = true;
+ }
+ break;
+ }
+ if(!tcadbclose(adb)){
+ if(!err) printerr(adb);
+ err = true;
+ }
+ tcadbdel(adb);
+ return err ? 1 : 0;
+}
+
+
+/* perform out command */
+static int procout(const char *name, const char *kbuf, int ksiz){
+ TCADB *adb = tcadbnew();
+ if(!tcadbopen(adb, name)){
+ printerr(adb);
+ tcadbdel(adb);
+ return 1;
+ }
+ bool err = false;
+ if(!tcadbout(adb, kbuf, ksiz)){
+ printerr(adb);
+ err = true;
+ }
+ if(!tcadbclose(adb)){
+ if(!err) printerr(adb);
+ err = true;
+ }
+ tcadbdel(adb);
+ return err ? 1 : 0;
+}
+
+
+/* perform get command */
+static int procget(const char *name, const char *kbuf, int ksiz, bool px, bool pz){
+ TCADB *adb = tcadbnew();
+ if(!tcadbopen(adb, name)){
+ printerr(adb);
+ tcadbdel(adb);
+ return 1;
+ }
+ bool err = false;
+ int vsiz;
+ char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz);
+ if(vbuf){
+ printdata(vbuf, vsiz, px);
+ if(!pz) putchar('\n');
+ free(vbuf);
+ } else {
+ printerr(adb);
+ err = true;
+ }
+ if(!tcadbclose(adb)){
+ if(!err) printerr(adb);
+ err = true;
+ }
+ tcadbdel(adb);
+ return err ? 1 : 0;
+}
+
+
+/* perform list command */
+static int proclist(const char *name, int max, bool pv, bool px, const char *fmstr){
+ TCADB *adb = tcadbnew();
+ if(!tcadbopen(adb, name)){
+ printerr(adb);
+ tcadbdel(adb);
+ return 1;
+ }
+ bool err = false;
+ if(fmstr){
+ TCLIST *keys = tcadbfwmkeys2(adb, fmstr, max);
+ for(int i = 0; i < tclistnum(keys); i++){
+ int ksiz;
+ const char *kbuf = tclistval(keys, i, &ksiz);
+ printdata(kbuf, ksiz, px);
+ if(pv){
+ int vsiz;
+ char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz);
+ if(vbuf){
+ putchar('\t');
+ printdata(vbuf, vsiz, px);
+ free(vbuf);
+ }
+ }
+ putchar('\n');
+ }
+ tclistdel(keys);
+ } else {
+ if(!tcadbiterinit(adb)){
+ printerr(adb);
+ err = true;
+ }
+ int ksiz;
+ char *kbuf;
+ int cnt = 0;
+ while((kbuf = tcadbiternext(adb, &ksiz)) != NULL){
+ printdata(kbuf, ksiz, px);
+ if(pv){
+ int vsiz;
+ char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz);
+ if(vbuf){
+ putchar('\t');
+ printdata(vbuf, vsiz, px);
+ free(vbuf);
+ }
+ }
+ putchar('\n');
+ free(kbuf);
+ if(max >= 0 && ++cnt >= max) break;
+ }
+ }
+ if(!tcadbclose(adb)){
+ if(!err) printerr(adb);
+ err = true;
+ }
+ tcadbdel(adb);
+ return err ? 1 : 0;
+}
+
+
+/* perform version command */
+static int procversion(void){
+ printf("Tokyo Cabinet version %s (%d:%s)\n", tcversion, _TC_LIBVER, _TC_FORMATVER);
+ printf("Copyright (C) 2006-2008 Mikio Hirabayashi\n");
+ return 0;
+}
+
+
+
+// END OF FILE
--- /dev/null
+/*************************************************************************************************
+ * The test cases of the abstract database API
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include <tcadb.h>
+#include "myconf.h"
+
+#define RECBUFSIZ 32 // buffer for records
+
+
+/* global variables */
+const char *g_progname; // program name
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void iprintf(const char *format, ...);
+static void eprint(TCADB *adb, const char *func);
+static int myrand(int range);
+static int runwrite(int argc, char **argv);
+static int runread(int argc, char **argv);
+static int runremove(int argc, char **argv);
+static int runrcat(int argc, char **argv);
+static int runmisc(int argc, char **argv);
+static int runwicked(int argc, char **argv);
+static int procwrite(const char *name, int rnum);
+static int procread(const char *name);
+static int procremove(const char *name);
+static int procrcat(const char *name, int rnum);
+static int procmisc(const char *name, int rnum);
+static int procwicked(const char *name, int rnum);
+
+
+/* main routine */
+int main(int argc, char **argv){
+ g_progname = argv[0];
+ srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+ if(argc < 2) usage();
+ int rv = 0;
+ if(!strcmp(argv[1], "write")){
+ rv = runwrite(argc, argv);
+ } else if(!strcmp(argv[1], "read")){
+ rv = runread(argc, argv);
+ } else if(!strcmp(argv[1], "remove")){
+ rv = runremove(argc, argv);
+ } else if(!strcmp(argv[1], "rcat")){
+ rv = runrcat(argc, argv);
+ } else if(!strcmp(argv[1], "misc")){
+ rv = runmisc(argc, argv);
+ } else if(!strcmp(argv[1], "wicked")){
+ rv = runwicked(argc, argv);
+ } else {
+ usage();
+ }
+ return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+ fprintf(stderr, "%s: test cases of the abstract database API of Tokyo Cabinet\n", g_progname);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, " %s write name rnum\n", g_progname);
+ fprintf(stderr, " %s read name\n", g_progname);
+ fprintf(stderr, " %s remove name\n", g_progname);
+ fprintf(stderr, " %s rcat name rnum\n", g_progname);
+ fprintf(stderr, " %s misc name rnum\n", g_progname);
+ fprintf(stderr, " %s wicked name rnum\n", g_progname);
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+
+/* print formatted information string and flush the buffer */
+static void iprintf(const char *format, ...){
+ va_list ap;
+ va_start(ap, format);
+ vprintf(format, ap);
+ fflush(stdout);
+ va_end(ap);
+}
+
+
+/* print error message of abstract database */
+static void eprint(TCADB *adb, const char *func){
+ fprintf(stderr, "%s: %s: error\n", g_progname, func);
+}
+
+
+/* get a random number */
+static int myrand(int range){
+ return (int)((double)range * rand() / (RAND_MAX + 1.0));
+}
+
+
+/* parse arguments of write command */
+static int runwrite(int argc, char **argv){
+ char *name = NULL;
+ char *rstr = NULL;
+ for(int i = 2; i < argc; i++){
+ if(!name && argv[i][0] == '-'){
+ usage();
+ } else if(!name){
+ name = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!name || !rstr) usage();
+ int rnum = atoi(rstr);
+ if(rnum < 1) usage();
+ int rv = procwrite(name, rnum);
+ return rv;
+}
+
+
+/* parse arguments of read command */
+static int runread(int argc, char **argv){
+ char *name = NULL;
+ for(int i = 2; i < argc; i++){
+ if(!name && argv[i][0] == '-'){
+ usage();
+ } else if(!name){
+ name = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!name) usage();
+ int rv = procread(name);
+ return rv;
+}
+
+
+/* parse arguments of remove command */
+static int runremove(int argc, char **argv){
+ char *name = NULL;
+ for(int i = 2; i < argc; i++){
+ if(!name && argv[i][0] == '-'){
+ usage();
+ } else if(!name){
+ name = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!name) usage();
+ int rv = procremove(name);
+ return rv;
+}
+
+
+/* parse arguments of rcat command */
+static int runrcat(int argc, char **argv){
+ char *name = NULL;
+ char *rstr = NULL;
+ for(int i = 2; i < argc; i++){
+ if(!name && argv[i][0] == '-'){
+ usage();
+ } else if(!name){
+ name = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!name || !rstr) usage();
+ int rnum = atoi(rstr);
+ if(rnum < 1) usage();
+ int rv = procrcat(name, rnum);
+ return rv;
+}
+
+
+/* parse arguments of misc command */
+static int runmisc(int argc, char **argv){
+ char *name = NULL;
+ char *rstr = NULL;
+ for(int i = 2; i < argc; i++){
+ if(!name && argv[i][0] == '-'){
+ usage();
+ } else if(!name){
+ name = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!name || !rstr) usage();
+ int rnum = atoi(rstr);
+ if(rnum < 1) usage();
+ int rv = procmisc(name, rnum);
+ return rv;
+}
+
+
+/* parse arguments of wicked command */
+static int runwicked(int argc, char **argv){
+ char *name = NULL;
+ char *rstr = NULL;
+ for(int i = 2; i < argc; i++){
+ if(!name && argv[i][0] == '-'){
+ usage();
+ } else if(!name){
+ name = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!name || !rstr) usage();
+ int rnum = atoi(rstr);
+ if(rnum < 1) usage();
+ int rv = procwicked(name, rnum);
+ return rv;
+}
+
+
+/* perform write command */
+static int procwrite(const char *name, int rnum){
+ iprintf("<Writing Test>\n name=%s rnum=%d\n\n", name, rnum);
+ bool err = false;
+ double stime = tctime();
+ TCADB *adb = tcadbnew();
+ if(!tcadbopen(adb, name)){
+ eprint(adb, "tcadbopen");
+ err = true;
+ }
+ for(int i = 1; i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len = sprintf(buf, "%08d", i);
+ if(!tcadbput(adb, buf, len, buf, len)){
+ eprint(adb, "tcadbput");
+ err = true;
+ break;
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb));
+ iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb));
+ if(!tcadbclose(adb)){
+ eprint(adb, "tcadbclose");
+ err = true;
+ }
+ tcadbdel(adb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform read command */
+static int procread(const char *name){
+ iprintf("<Reading Test>\n name=%s\n\n", name);
+ bool err = false;
+ double stime = tctime();
+ TCADB *adb = tcadbnew();
+ if(!tcadbopen(adb, name)){
+ eprint(adb, "tcadbopen");
+ err = true;
+ }
+ int rnum = tcadbrnum(adb);
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%08d", i);
+ int vsiz;
+ char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz);
+ if(!vbuf){
+ eprint(adb, "tcadbget");
+ err = true;
+ break;
+ }
+ free(vbuf);
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb));
+ iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb));
+ if(!tcadbclose(adb)){
+ eprint(adb, "tcadbclose");
+ err = true;
+ }
+ tcadbdel(adb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform remove command */
+static int procremove(const char *name){
+ iprintf("<Removing Test>\n name=%s\n\n", name);
+ bool err = false;
+ double stime = tctime();
+ TCADB *adb = tcadbnew();
+ if(!tcadbopen(adb, name)){
+ eprint(adb, "tcadbopen");
+ err = true;
+ }
+ int rnum = tcadbrnum(adb);
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%08d", i);
+ if(!tcadbout(adb, kbuf, ksiz)){
+ eprint(adb, "tcadbout");
+ err = true;
+ break;
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb));
+ iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb));
+ if(!tcadbclose(adb)){
+ eprint(adb, "tcadbclose");
+ err = true;
+ }
+ tcadbdel(adb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform rcat command */
+static int procrcat(const char *name, int rnum){
+ iprintf("<Random Concatenating Test>\n name=%s rnum=%d\n\n", name, rnum);
+ int pnum = rnum / 5 + 1;
+ bool err = false;
+ double stime = tctime();
+ TCADB *adb = tcadbnew();
+ if(!tcadbopen(adb, name)){
+ eprint(adb, "tcadbopen");
+ err = true;
+ }
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", myrand(pnum));
+ if(!tcadbputcat(adb, kbuf, ksiz, kbuf, ksiz)){
+ eprint(adb, "tcadbputcat");
+ err = true;
+ break;
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb));
+ iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb));
+ if(!tcadbclose(adb)){
+ eprint(adb, "tcadbclose");
+ err = true;
+ }
+ tcadbdel(adb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform misc command */
+static int procmisc(const char *name, int rnum){
+ iprintf("<Random Concatenating Test>\n name=%s rnum=%d\n\n", name, rnum);
+ bool err = false;
+ double stime = tctime();
+ TCADB *adb = tcadbnew();
+ if(!tcadbopen(adb, name)){
+ eprint(adb, "tcadbopen");
+ err = true;
+ }
+ iprintf("writing:\n");
+ for(int i = 1; i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len = sprintf(buf, "%08d", i);
+ if(!tcadbputkeep(adb, buf, len, buf, len)){
+ eprint(adb, "tcadbputkeep");
+ err = true;
+ break;
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("reading:\n");
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%08d", i);
+ int vsiz;
+ char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz);
+ if(!vbuf){
+ eprint(adb, "tcadbget");
+ err = true;
+ break;
+ } else if(vsiz != ksiz || memcmp(vbuf, kbuf, vsiz)){
+ eprint(adb, "(validation)");
+ err = true;
+ free(vbuf);
+ break;
+ }
+ free(vbuf);
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ if(tcadbrnum(adb) != rnum){
+ eprint(adb, "(validation)");
+ err = true;
+ }
+ iprintf("random writing:\n");
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+ char vbuf[RECBUFSIZ];
+ int vsiz = myrand(RECBUFSIZ);
+ memset(vbuf, '*', vsiz);
+ if(!tcadbput(adb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(adb, "tcadbput");
+ err = true;
+ break;
+ }
+ int rsiz;
+ char *rbuf = tcadbget(adb, kbuf, ksiz, &rsiz);
+ if(!rbuf){
+ eprint(adb, "tcadbget");
+ err = true;
+ break;
+ }
+ if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+ eprint(adb, "(validation)");
+ err = true;
+ free(rbuf);
+ break;
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ free(rbuf);
+ }
+ iprintf("word writing:\n");
+ const char *words[] = {
+ "a", "A", "bb", "BB", "ccc", "CCC", "dddd", "DDDD", "eeeee", "EEEEEE",
+ "mikio", "hirabayashi", "tokyo", "cabinet", "hyper", "estraier", "19780211", "birth day",
+ "one", "first", "two", "second", "three", "third", "four", "fourth", "five", "fifth",
+ "_[1]_", "uno", "_[2]_", "dos", "_[3]_", "tres", "_[4]_", "cuatro", "_[5]_", "cinco",
+ "[\xe5\xb9\xb3\xe6\x9e\x97\xe5\xb9\xb9\xe9\x9b\x84]", "[\xe9\xa6\xac\xe9\xb9\xbf]", NULL
+ };
+ for(int i = 0; words[i] != NULL; i += 2){
+ const char *kbuf = words[i];
+ int ksiz = strlen(kbuf);
+ const char *vbuf = words[i+1];
+ int vsiz = strlen(vbuf);
+ if(!tcadbputkeep(adb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(adb, "tcadbputkeep");
+ err = true;
+ break;
+ }
+ if(rnum > 250) putchar('.');
+ }
+ if(rnum > 250) iprintf(" (%08d)\n", sizeof(words) / sizeof(*words));
+ iprintf("random erasing:\n");
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+ tcadbout(adb, kbuf, ksiz);
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("writing:\n");
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "[%d]", i);
+ char vbuf[RECBUFSIZ];
+ int vsiz = i % RECBUFSIZ;
+ memset(vbuf, '*', vsiz);
+ if(!tcadbputkeep(adb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(adb, "tcadbputkeep");
+ err = true;
+ break;
+ }
+ if(vsiz < 1){
+ char tbuf[PATH_MAX];
+ for(int j = 0; j < PATH_MAX; j++){
+ tbuf[j] = myrand(0x100);
+ }
+ if(!tcadbput(adb, kbuf, ksiz, tbuf, PATH_MAX)){
+ eprint(adb, "tcadbput");
+ err = true;
+ break;
+ }
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("erasing:\n");
+ for(int i = 1; i <= rnum; i++){
+ if(i % 2 == 1){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "[%d]", i);
+ if(!tcadbout(adb, kbuf, ksiz)){
+ eprint(adb, "tcadbout");
+ err = true;
+ break;
+ }
+ tcadbout(adb, kbuf, ksiz);
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("iterator checking:\n");
+ if(!tcadbiterinit(adb)){
+ eprint(adb, "tcadbiterinit");
+ err = true;
+ }
+ char *kbuf;
+ int ksiz;
+ int inum = 0;
+ for(int i = 1; (kbuf = tcadbiternext(adb, &ksiz)) != NULL; i++, inum++){
+ int vsiz;
+ char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz);
+ if(!vbuf){
+ eprint(adb, "tcadbget");
+ err = true;
+ free(kbuf);
+ break;
+ }
+ free(vbuf);
+ free(kbuf);
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ if(rnum > 250) iprintf(" (%08d)\n", inum);
+ if(inum != tcadbrnum(adb)){
+ eprint(adb, "(validation)");
+ err = true;
+ }
+ if(*name != '*' && !tcadbsync(adb)){
+ eprint(adb, "tcadbsync");
+ err = true;
+ }
+ if(!tcadbvanish(adb)){
+ eprint(adb, "tcadbsync");
+ err = true;
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb));
+ iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb));
+ if(!tcadbclose(adb)){
+ eprint(adb, "tcadbclose");
+ err = true;
+ }
+ tcadbdel(adb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform wicked command */
+static int procwicked(const char *name, int rnum){
+ iprintf("<Wicked Writing Test>\n name=%s rnum=%d\n\n", name, rnum);
+ bool err = false;
+ double stime = tctime();
+ TCADB *adb = tcadbnew();
+ if(!tcadbopen(adb, name)){
+ eprint(adb, "tcadbopen");
+ err = true;
+ }
+ TCMAP *map = tcmapnew2(rnum / 5);
+ for(int i = 1; i <= rnum && !err; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+ char vbuf[RECBUFSIZ];
+ int vsiz = myrand(RECBUFSIZ);
+ memset(vbuf, '*', vsiz);
+ vbuf[vsiz] = '\0';
+ char *rbuf;
+ switch(myrand(16)){
+ case 0:
+ putchar('0');
+ if(!tcadbput(adb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(adb, "tcadbput");
+ err = true;
+ }
+ tcmapput(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 1:
+ putchar('1');
+ if(!tcadbput2(adb, kbuf, vbuf)){
+ eprint(adb, "tcadbput2");
+ err = true;
+ }
+ tcmapput2(map, kbuf, vbuf);
+ break;
+ case 2:
+ putchar('2');
+ tcadbputkeep(adb, kbuf, ksiz, vbuf, vsiz);
+ tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 3:
+ putchar('3');
+ tcadbputkeep2(adb, kbuf, vbuf);
+ tcmapputkeep2(map, kbuf, vbuf);
+ break;
+ case 4:
+ putchar('4');
+ if(!tcadbputcat(adb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(adb, "tcadbputcat");
+ err = true;
+ }
+ tcmapputcat(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 5:
+ putchar('5');
+ if(!tcadbputcat2(adb, kbuf, vbuf)){
+ eprint(adb, "tcadbputcat2");
+ err = true;
+ }
+ tcmapputcat2(map, kbuf, vbuf);
+ break;
+ case 6:
+ putchar('6');
+ if(myrand(10) == 0){
+ tcadbout(adb, kbuf, ksiz);
+ tcmapout(map, kbuf, ksiz);
+ }
+ break;
+ case 7:
+ putchar('7');
+ if(myrand(10) == 0){
+ tcadbout2(adb, kbuf);
+ tcmapout2(map, kbuf);
+ }
+ break;
+ case 8:
+ putchar('8');
+ if((rbuf = tcadbget(adb, kbuf, ksiz, &vsiz)) != NULL) free(rbuf);
+ break;
+ case 9:
+ putchar('9');
+ if((rbuf = tcadbget2(adb, kbuf)) != NULL) free(rbuf);
+ break;
+ case 10:
+ putchar('A');
+ tcadbvsiz(adb, kbuf, ksiz);
+ break;
+ case 11:
+ putchar('B');
+ tcadbvsiz2(adb, kbuf);
+ break;
+ case 12:
+ putchar('E');
+ if(myrand(rnum / 50) == 0){
+ if(!tcadbiterinit(adb)){
+ eprint(adb, "tcadbiterinit");
+ err = true;
+ }
+ }
+ for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){
+ int iksiz;
+ char *ikbuf = tcadbiternext(adb, &iksiz);
+ if(ikbuf) free(ikbuf);
+ }
+ break;
+ default:
+ putchar('@');
+ if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+ break;
+ }
+ if(i % 50 == 0) iprintf(" (%08d)\n", i);
+ }
+ if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
+ tcadbsync(adb);
+ if(tcadbrnum(adb) != tcmaprnum(map)){
+ eprint(adb, "(validation)");
+ err = true;
+ }
+ for(int i = 1; i <= rnum && !err; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", i - 1);
+ int vsiz;
+ const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz);
+ int rsiz;
+ char *rbuf = tcadbget(adb, kbuf, ksiz, &rsiz);
+ if(vbuf){
+ putchar('.');
+ if(!rbuf){
+ eprint(adb, "tcadbget");
+ err = true;
+ } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+ eprint(adb, "(validation)");
+ err = true;
+ }
+ } else {
+ putchar('*');
+ if(rbuf){
+ eprint(adb, "(validation)");
+ err = true;
+ }
+ }
+ free(rbuf);
+ if(i % 50 == 0) iprintf(" (%08d)\n", i);
+ }
+ if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
+ tcmapiterinit(map);
+ int ksiz;
+ const char *kbuf;
+ for(int i = 1; (kbuf = tcmapiternext(map, &ksiz)) != NULL; i++){
+ putchar('+');
+ int vsiz;
+ const char *vbuf = tcmapiterval(kbuf, &vsiz);
+ int rsiz;
+ char *rbuf = tcadbget(adb, kbuf, ksiz, &rsiz);
+ if(!rbuf){
+ eprint(adb, "tcadbget");
+ err = true;
+ } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+ eprint(adb, "(validation)");
+ err = true;
+ }
+ free(rbuf);
+ if(!tcadbout(adb, kbuf, ksiz)){
+ eprint(adb, "tcadbout");
+ err = true;
+ }
+ if(i % 50 == 0) iprintf(" (%08d)\n", i);
+ }
+ int mrnum = tcmaprnum(map);
+ if(mrnum % 50 > 0) iprintf(" (%08d)\n", mrnum);
+ if(tcadbrnum(adb) != 0){
+ eprint(adb, "(validation)");
+ err = true;
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb));
+ iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb));
+ tcmapdel(map);
+ if(!tcadbclose(adb)){
+ eprint(adb, "tcadbclose");
+ err = true;
+ }
+ tcadbdel(adb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+
+// END OF FILE
--- /dev/null
+/*************************************************************************************************
+ * The B+ tree database API of Tokyo Cabinet
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include "tcutil.h"
+#include "tchdb.h"
+#include "tcbdb.h"
+#include "myconf.h"
+
+#define BDBFILEMODE 00644 // permission of created files
+#define BDBOPAQUESIZ 64 // size of using opaque field
+#define BDBPAGEBUFSIZ 32768 // size of a buffer to read each page
+#define BDBNODEIDBASE ((1LL<<48)+1) // base number of node ID
+#define BDBLEVELMAX 64 // max level of B+ tree
+#define BDBCACHEOUT 8 // number of pages in a process of cacheout
+
+#define BDBDEFLMEMB 128 // default number of members in each leaf
+#define BDBMINLMEMB 4 // minimum number of members in each leaf
+#define BDBDEFNMEMB 256 // default number of members in each node
+#define BDBMINNMEMB 4 // minimum number of members in each node
+#define BDBDEFBNUM 16381 // default bucket number
+#define BDBDEFAPOW 8 // default alignment power
+#define BDBDEFFPOW 10 // default free block pool power
+#define BDBDEFLCNUM 1024 // default number of leaf cache
+#define BDBDEFNCNUM 512 // default number of node cache
+
+typedef struct { // type of structure for a record
+ char *kbuf; // pointer to the key region
+ int ksiz; // size of the key region
+ char *vbuf; // pointer to the value region
+ int vsiz; // size of the value region
+ TCLIST *rest; // list of value objects
+} BDBREC;
+
+typedef struct { // type of structure for a leaf page
+ uint64_t id; // ID number of the leaf
+ TCLIST *recs; // list of records
+ uint64_t prev; // ID number of the previous leaf
+ uint64_t next; // ID number of the next leaf
+ bool dirty; // whether to be written back
+ bool dead; // whether to be removed
+} BDBLEAF;
+
+typedef struct { // type of structure for a page index
+ uint64_t pid; // ID number of the referring page
+ char *kbuf; // pointer to the key region
+ int ksiz; // size of the key region
+} BDBIDX;
+
+typedef struct { // type of structure for a node page
+ uint64_t id; // ID number of the node
+ uint64_t heir; // ID of the child before the first index
+ TCLIST *idxs; // list of indexes
+ bool dirty; // whether to be written back
+} BDBNODE;
+
+enum { // enumeration for duplication behavior
+ BDBPDOVER, // overwrite an existing value
+ BDBPDKEEP, // keep the existing value
+ BDBPDCAT, // concatenate values
+ BDBPDDUP, // allow duplication of keys
+ BDBPDDUPB, // allow backward duplication
+};
+
+
+/* private macros */
+#define BDBLOCKMETHOD(TC_bdb, TC_wr) \
+ ((TC_bdb)->mmtx ? tcbdblockmethod((TC_bdb), (TC_wr)) : true)
+#define BDBUNLOCKMETHOD(TC_bdb) \
+ ((TC_bdb)->mmtx ? tcbdbunlockmethod(TC_bdb) : true)
+#define BDBLOCKCACHE(TC_bdb) \
+ ((TC_bdb)->mmtx ? tcbdblockcache(TC_bdb) : true)
+#define BDBUNLOCKCACHE(TC_bdb) \
+ ((TC_bdb)->mmtx ? tcbdbunlockcache(TC_bdb) : true)
+#define BDBLOCKTRAN(TC_bdb) \
+ ((TC_bdb)->mmtx ? tcbdblocktran(TC_bdb) : true)
+#define BDBUNLOCKTRAN(TC_bdb) \
+ ((TC_bdb)->mmtx ? tcbdbunlocktran(TC_bdb) : true)
+
+
+/* private function prototypes */
+static void tcbdbclear(TCBDB *bdb);
+static void tcdumpmeta(TCBDB *bdb);
+static void tcloadmeta(TCBDB *bdb);
+static BDBLEAF *tcbdbleafnew(TCBDB *bdb, uint64_t prev, uint64_t next);
+static bool tcbdbleafcacheout(TCBDB *bdb, BDBLEAF *leaf);
+static bool tcbdbleafsave(TCBDB *bdb, BDBLEAF *leaf);
+static BDBLEAF *tcbdbleafload(TCBDB *bdb, uint64_t id);
+static BDBLEAF *tcbdbgethistleaf(TCBDB *bdb, const char *kbuf, int ksiz);
+static bool tcbdbleafaddrec(TCBDB *bdb, BDBLEAF *leaf, int dmode,
+ const char *kbuf, int ksiz, const char *vbuf, int vsiz);
+static int tcbdbleafdatasize(BDBLEAF *leaf);
+static BDBLEAF *tcbdbleafdivide(TCBDB *bdb, BDBLEAF *leaf);
+static bool tcbdbleafkill(TCBDB *bdb, BDBLEAF *leaf);
+static BDBNODE *tcbdbnodenew(TCBDB *bdb, uint64_t heir);
+static bool tcbdbnodecacheout(TCBDB *bdb, BDBNODE *node);
+static bool tcbdbnodesave(TCBDB *bdb, BDBNODE *node);
+static BDBNODE *tcbdbnodeload(TCBDB *bdb, uint64_t id);
+static void tcbdbnodeaddidx(TCBDB *bdb, BDBNODE *node, bool order, uint64_t pid,
+ const char *kbuf, int ksiz);
+static bool tcbdbnodesubidx(TCBDB *bdb, BDBNODE *node, uint64_t pid);
+static uint64_t tcbdbsearchleaf(TCBDB *bdb, const char *kbuf, int ksiz);
+static BDBREC *tcbdbsearchrec(TCBDB *bdb, BDBLEAF *leaf, const char *kbuf, int ksiz, int *ip);
+static bool tcbdbcacheadjust(TCBDB *bdb);
+static void tcbdbcachepurge(TCBDB *bdb);
+static bool tcbdbopenimpl(TCBDB *bdb, const char *path, int omode);
+static bool tcbdbcloseimpl(TCBDB *bdb);
+static bool tcbdbputimpl(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz,
+ int dmode);
+static bool tcbdboutimpl(TCBDB *bdb, const char *kbuf, int ksiz);
+static bool tcbdboutlist(TCBDB *bdb, const char *kbuf, int ksiz);
+static const char *tcbdbgetimpl(TCBDB *bdb, const char *kbuf, int ksiz, int *sp);
+static int tcbdbgetnum(TCBDB *bdb, const char *kbuf, int ksiz);
+static TCLIST *tcbdbgetlist(TCBDB *bdb, const char *kbuf, int ksiz);
+static bool tcbdbrangeimpl(TCBDB *bdb, const char *bkbuf, int bksiz, bool binc,
+ const char *ekbuf, int eksiz, bool einc, int max, TCLIST *keys);
+static bool tcbdbrangefwm(TCBDB *bdb, const char *pbuf, int psiz, int max, TCLIST *keys);
+static bool tcbdboptimizeimpl(TCBDB *bdb, int32_t lmemb, int32_t nmemb,
+ int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+static bool tcbdbvanishimpl(TCBDB *bdb);
+static bool tcbdblockmethod(TCBDB *bdb, bool wr);
+static bool tcbdbunlockmethod(TCBDB *bdb);
+static bool tcbdblockcache(TCBDB *bdb);
+static bool tcbdbunlockcache(TCBDB *bdb);
+static bool tcbdblocktran(TCBDB *bdb);
+static bool tcbdbunlocktran(TCBDB *bdb);
+static bool tcbdbcurfirstimpl(BDBCUR *cur);
+static bool tcbdbcurlastimpl(BDBCUR *cur);
+static bool tcbdbcurjumpimpl(BDBCUR *cur, const char *kbuf, int ksiz, bool forward);
+static bool tcbdbcuradjust(BDBCUR *cur, bool forward);
+static bool tcbdbcurprevimpl(BDBCUR *cur);
+static bool tcbdbcurnextimpl(BDBCUR *cur);
+static bool tcbdbcurputimpl(BDBCUR *cur, const char *vbuf, int vsiz, int mode);
+static bool tcbdbcuroutimpl(BDBCUR *cur);
+static bool tcbdbcurrecimpl(BDBCUR *cur, const char **kbp, int *ksp, const char **vbp, int *vsp);
+
+
+/* debugging function prototypes */
+void tcbdbprintmeta(TCBDB *bdb);
+void tcbdbprintleaf(TCBDB *bdb, BDBLEAF *leaf);
+void tcbdbprintnode(TCBDB *bdb, BDBNODE *node);
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+/* Get the message string corresponding to an error code. */
+const char *tcbdberrmsg(int ecode){
+ return tchdberrmsg(ecode);
+}
+
+
+/* Create a B+ tree database object. */
+TCBDB *tcbdbnew(void){
+ TCBDB *bdb;
+ TCMALLOC(bdb, sizeof(*bdb));
+ tcbdbclear(bdb);
+ bdb->hdb = tchdbnew();
+ TCMALLOC(bdb->hist, sizeof(*bdb->hist) * BDBLEVELMAX);
+ return bdb;
+}
+
+
+/* Delete a B+ tree database object. */
+void tcbdbdel(TCBDB *bdb){
+ assert(bdb);
+ if(bdb->open) tcbdbclose(bdb);
+ free(bdb->hist);
+ tchdbdel(bdb->hdb);
+ if(bdb->mmtx){
+ pthread_mutex_destroy(bdb->tmtx);
+ pthread_mutex_destroy(bdb->cmtx);
+ pthread_rwlock_destroy(bdb->mmtx);
+ free(bdb->tmtx);
+ free(bdb->cmtx);
+ free(bdb->mmtx);
+ }
+ free(bdb);
+}
+
+
+/* Get the last happened error code of a B+ tree database object. */
+int tcbdbecode(TCBDB *bdb){
+ assert(bdb);
+ return tchdbecode(bdb->hdb);
+}
+
+
+/* Set mutual exclusion control of a B+ tree database object for threading. */
+bool tcbdbsetmutex(TCBDB *bdb){
+ assert(bdb);
+ if(!TCUSEPTHREAD) return true;
+ if(!tcglobalmutexlock()) return false;
+ if(bdb->mmtx || bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ tcglobalmutexunlock();
+ return false;
+ }
+ TCMALLOC(bdb->mmtx, sizeof(pthread_rwlock_t));
+ TCMALLOC(bdb->cmtx, sizeof(pthread_mutex_t));
+ TCMALLOC(bdb->tmtx, sizeof(pthread_mutex_t));
+ if(pthread_rwlock_init(bdb->mmtx, NULL) != 0 || pthread_mutex_init(bdb->cmtx, NULL) != 0 ||
+ pthread_mutex_init(bdb->tmtx, NULL) != 0){
+ free(bdb->tmtx);
+ free(bdb->cmtx);
+ free(bdb->mmtx);
+ bdb->tmtx = NULL;
+ bdb->cmtx = NULL;
+ bdb->mmtx = NULL;
+ tcglobalmutexunlock();
+ return false;
+ }
+ tcglobalmutexunlock();
+ return tchdbsetmutex(bdb->hdb);
+}
+
+
+/* Set the custom comparison function of a B+ tree database object. */
+bool tcbdbsetcmpfunc(TCBDB *bdb, BDBCMP cmp, void *cmpop){
+ assert(bdb && cmp);
+ if(bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ bdb->cmp = cmp;
+ bdb->cmpop = cmpop;
+ return true;
+}
+
+
+/* Set the tuning parameters of a B+ tree database object. */
+bool tcbdbtune(TCBDB *bdb, int32_t lmemb, int32_t nmemb,
+ int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){
+ assert(bdb);
+ if(bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ bdb->lmemb = (lmemb > 0) ? tclmax(lmemb, BDBMINLMEMB) : BDBDEFLMEMB;
+ bdb->nmemb = (nmemb > 0) ? tclmax(nmemb, BDBMINNMEMB) : BDBDEFNMEMB;
+ bdb->opts = opts;
+ uint8_t hopts = 0;
+ if(opts & BDBTLARGE) hopts |= HDBTLARGE;
+ if(opts & BDBTDEFLATE) hopts |= HDBTDEFLATE;
+ if(opts & BDBTTCBS) hopts |= HDBTTCBS;
+ bnum = (bnum > 0) ? bnum : BDBDEFBNUM;
+ apow = (apow >= 0) ? apow : BDBDEFAPOW;
+ fpow = (fpow >= 0) ? fpow : BDBDEFFPOW;
+ return tchdbtune(bdb->hdb, bnum, apow, fpow, hopts);
+}
+
+
+/* Set the caching parameters of a B+ tree database object. */
+bool tcbdbsetcache(TCBDB *bdb, int32_t lcnum, int32_t ncnum){
+ assert(bdb);
+ if(bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ if(lcnum > 0) bdb->lcnum = tclmax(lcnum, BDBLEVELMAX);
+ if(ncnum > 0) bdb->ncnum = tclmax(ncnum, BDBLEVELMAX);
+ return true;
+}
+
+
+/* Open a database file and connect a B+ tree database object. */
+bool tcbdbopen(TCBDB *bdb, const char *path, int omode){
+ assert(bdb && path);
+ if(!BDBLOCKMETHOD(bdb, true)) return false;
+ if(bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdbopenimpl(bdb, path, omode);
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Close a B+ tree database object. */
+bool tcbdbclose(TCBDB *bdb){
+ assert(bdb);
+ if(!BDBLOCKMETHOD(bdb, true)) return false;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdbcloseimpl(bdb);
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Store a record into a B+ tree database object. */
+bool tcbdbput(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+ assert(bdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ if(!BDBLOCKMETHOD(bdb, true)) return false;
+ if(!bdb->open || !bdb->wmode){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdbputimpl(bdb, kbuf, ksiz, vbuf, vsiz, BDBPDOVER);
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Store a string record into a B+ tree database object. */
+bool tcbdbput2(TCBDB *bdb, const char *kstr, const char *vstr){
+ assert(bdb && kstr && vstr);
+ return tcbdbput(bdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Store a new record into a B+ tree database object. */
+bool tcbdbputkeep(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+ assert(bdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ if(!BDBLOCKMETHOD(bdb, true)) return false;
+ if(!bdb->open || !bdb->wmode){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdbputimpl(bdb, kbuf, ksiz, vbuf, vsiz, BDBPDKEEP);
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Store a new string record into a B+ tree database object. */
+bool tcbdbputkeep2(TCBDB *bdb, const char *kstr, const char *vstr){
+ assert(bdb && kstr && vstr);
+ return tcbdbputkeep(bdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Concatenate a value at the end of the existing record in a B+ tree database object. */
+bool tcbdbputcat(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+ assert(bdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ if(!BDBLOCKMETHOD(bdb, true)) return false;
+ if(!bdb->open || !bdb->wmode){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdbputimpl(bdb, kbuf, ksiz, vbuf, vsiz, BDBPDCAT);
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Concatenate a string value at the end of the existing record in a B+ tree database object. */
+bool tcbdbputcat2(TCBDB *bdb, const char *kstr, const char *vstr){
+ assert(bdb && kstr && vstr);
+ return tcbdbputcat(bdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Store a record into a B+ tree database object with allowing duplication of keys. */
+bool tcbdbputdup(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+ assert(bdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ if(!BDBLOCKMETHOD(bdb, true)) return false;
+ if(!bdb->open || !bdb->wmode){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdbputimpl(bdb, kbuf, ksiz, vbuf, vsiz, BDBPDDUP);
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Store a string record into a B+ tree database object with allowing duplication of keys. */
+bool tcbdbputdup2(TCBDB *bdb, const char *kstr, const char *vstr){
+ assert(bdb && kstr && vstr);
+ return tcbdbputdup(bdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Store records into a B+ tree database object with allowing duplication of keys. */
+bool tcbdbputdup3(TCBDB *bdb, const void *kbuf, int ksiz, const TCLIST *vals){
+ assert(bdb && kbuf && ksiz >= 0 && vals);
+ if(!BDBLOCKMETHOD(bdb, true)) return false;
+ if(!bdb->open || !bdb->wmode){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool err = false;
+ int ln = TCLISTNUM(vals);
+ for(int i = 0; i < ln; i++){
+ int vsiz;
+ const char *vbuf = tclistval(vals, i, &vsiz);
+ if(!tcbdbputimpl(bdb, kbuf, ksiz, vbuf, vsiz, BDBPDDUP)) err = true;
+ }
+ BDBUNLOCKMETHOD(bdb);
+ return !err;
+}
+
+
+/* Remove a record of a B+ tree database object. */
+bool tcbdbout(TCBDB *bdb, const void *kbuf, int ksiz){
+ assert(bdb && kbuf && ksiz >= 0);
+ if(!BDBLOCKMETHOD(bdb, true)) return false;
+ if(!bdb->open || !bdb->wmode){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdboutimpl(bdb, kbuf, ksiz);
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Remove a string record of a B+ tree database object. */
+bool tcbdbout2(TCBDB *bdb, const char *kstr){
+ assert(bdb && kstr);
+ return tcbdbout(bdb, kstr, strlen(kstr));
+}
+
+
+/* Remove records of a B+ tree database object. */
+bool tcbdbout3(TCBDB *bdb, const void *kbuf, int ksiz){
+ assert(bdb && kbuf && ksiz >= 0);
+ if(!BDBLOCKMETHOD(bdb, true)) return false;
+ if(!bdb->open || !bdb->wmode){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdboutlist(bdb, kbuf, ksiz);
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Retrieve a record in a B+ tree database object. */
+void *tcbdbget(TCBDB *bdb, const void *kbuf, int ksiz, int *sp){
+ assert(bdb && kbuf && ksiz >= 0 && sp);
+ if(!BDBLOCKMETHOD(bdb, false)) return NULL;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return NULL;
+ }
+ const char *vbuf = tcbdbgetimpl(bdb, kbuf, ksiz, sp);
+ char *rv;
+ if(vbuf){
+ TCMEMDUP(rv, vbuf, *sp);
+ } else {
+ rv = NULL;
+ }
+ bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+ BDBUNLOCKMETHOD(bdb);
+ if(adj && BDBLOCKMETHOD(bdb, true)){
+ if(!bdb->tran && !tcbdbcacheadjust(bdb)){
+ free(rv);
+ rv = NULL;
+ }
+ BDBUNLOCKMETHOD(bdb);
+ }
+ return rv;
+}
+
+
+/* Retrieve a string record in a B+ tree database object. */
+char *tcbdbget2(TCBDB *bdb, const char *kstr){
+ assert(bdb && kstr);
+ int vsiz;
+ return tcbdbget(bdb, kstr, strlen(kstr), &vsiz);
+}
+
+
+/* Retrieve a record in a B+ tree database object and write the value into a buffer. */
+const void *tcbdbget3(TCBDB *bdb, const void *kbuf, int ksiz, int *sp){
+ assert(bdb && kbuf && ksiz >= 0 && sp);
+ if(!BDBLOCKMETHOD(bdb, false)) return NULL;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return NULL;
+ }
+ const char *rv = tcbdbgetimpl(bdb, kbuf, ksiz, sp);
+ bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+ BDBUNLOCKMETHOD(bdb);
+ if(adj && BDBLOCKMETHOD(bdb, true)){
+ if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = NULL;
+ BDBUNLOCKMETHOD(bdb);
+ }
+ return rv;
+}
+
+
+/* Retrieve records in a B+ tree database object. */
+TCLIST *tcbdbget4(TCBDB *bdb, const void *kbuf, int ksiz){
+ assert(bdb && kbuf && ksiz >= 0);
+ if(!BDBLOCKMETHOD(bdb, false)) return NULL;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return NULL;
+ }
+ TCLIST *rv = tcbdbgetlist(bdb, kbuf, ksiz);
+ bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+ BDBUNLOCKMETHOD(bdb);
+ if(adj && BDBLOCKMETHOD(bdb, true)){
+ if(!bdb->tran && !tcbdbcacheadjust(bdb)){
+ if(rv) tclistdel(rv);
+ rv = NULL;
+ }
+ BDBUNLOCKMETHOD(bdb);
+ }
+ return rv;
+}
+
+
+/* Get the number of records corresponding a key in a B+ tree database object. */
+int tcbdbvnum(TCBDB *bdb, const void *kbuf, int ksiz){
+ assert(bdb && kbuf && ksiz >= 0);
+ if(!BDBLOCKMETHOD(bdb, false)) return 0;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return 0;
+ }
+ int rv = tcbdbgetnum(bdb, kbuf, ksiz);
+ bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+ BDBUNLOCKMETHOD(bdb);
+ if(adj && BDBLOCKMETHOD(bdb, true)){
+ if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = 0;
+ BDBUNLOCKMETHOD(bdb);
+ }
+ return rv;
+}
+
+
+/* Get the number of records corresponding a string key in a B+ tree database object. */
+int tcbdbvnum2(TCBDB *bdb, const char *kstr){
+ assert(bdb && kstr);
+ return tcbdbvnum(bdb, kstr, strlen(kstr));
+}
+
+
+/* Get the size of the value of a record in a B+ tree database object. */
+int tcbdbvsiz(TCBDB *bdb, const void *kbuf, int ksiz){
+ assert(bdb && kbuf && ksiz >= 0);
+ int vsiz;
+ if(!tcbdbget3(bdb, kbuf, ksiz, &vsiz)) return -1;
+ return vsiz;
+}
+
+
+/* Get the size of the value of a string record in a B+ tree database object. */
+int tcbdbvsiz2(TCBDB *bdb, const char *kstr){
+ assert(bdb && kstr);
+ return tcbdbvsiz(bdb, kstr, strlen(kstr));
+}
+
+
+/* Get keys of ranged records in a B+ tree database object. */
+TCLIST *tcbdbrange(TCBDB *bdb, const void *bkbuf, int bksiz, bool binc,
+ const void *ekbuf, int eksiz, bool einc, int max){
+ assert(bdb);
+ TCLIST *keys = tclistnew();
+ if(!BDBLOCKMETHOD(bdb, false)) return keys;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return keys;
+ }
+ tcbdbrangeimpl(bdb, bkbuf, bksiz, binc, ekbuf, eksiz, einc, max, keys);
+ bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+ BDBUNLOCKMETHOD(bdb);
+ if(adj && BDBLOCKMETHOD(bdb, true)){
+ tcbdbcacheadjust(bdb);
+ BDBUNLOCKMETHOD(bdb);
+ }
+ return keys;
+}
+
+
+/* Get string keys of ranged records in a B+ tree database object. */
+TCLIST *tcbdbrange2(TCBDB *bdb, const char *bkstr, bool binc,
+ const char *ekstr, bool einc, int max){
+ return tcbdbrange(bdb, bkstr, bkstr ? strlen(bkstr) : 0, binc,
+ ekstr, ekstr ? strlen(ekstr) : 0, einc, max);
+}
+
+
+/* Get forward matching keys in a B+ tree database object. */
+TCLIST *tcbdbfwmkeys(TCBDB *bdb, const void *pbuf, int psiz, int max){
+ assert(bdb && pbuf && psiz >= 0);
+ TCLIST *keys = tclistnew();
+ if(!BDBLOCKMETHOD(bdb, false)) return keys;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return keys;
+ }
+ tcbdbrangefwm(bdb, pbuf, psiz, max, keys);
+ bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+ BDBUNLOCKMETHOD(bdb);
+ if(adj && BDBLOCKMETHOD(bdb, true)){
+ tcbdbcacheadjust(bdb);
+ BDBUNLOCKMETHOD(bdb);
+ }
+ return keys;
+}
+
+
+/* Get forward matching string keys in a B+ tree database object. */
+TCLIST *tcbdbfwmkeys2(TCBDB *bdb, const char *pstr, int max){
+ assert(bdb && pstr);
+ return tcbdbfwmkeys(bdb, pstr, strlen(pstr), max);
+}
+
+
+
+/* Synchronize updated contents of a B+ tree database object with the file and the device. */
+bool tcbdbsync(TCBDB *bdb){
+ assert(bdb);
+ if(!BDBLOCKMETHOD(bdb, true)) return false;
+ if(!bdb->open || !bdb->wmode || bdb->tran){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdbmemsync(bdb, true);
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Optimize the file of a B+ tree database object. */
+bool tcbdboptimize(TCBDB *bdb, int32_t lmemb, int32_t nmemb,
+ int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){
+ assert(bdb);
+ if(!BDBLOCKMETHOD(bdb, true)) return false;
+ if(!bdb->open || !bdb->wmode || bdb->tran){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdboptimizeimpl(bdb, lmemb, nmemb, bnum, apow, fpow, opts);
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Remove all records of a B+ tree database object. */
+bool tcbdbvanish(TCBDB *bdb){
+ assert(bdb);
+ if(!BDBLOCKMETHOD(bdb, true)) return false;
+ if(!bdb->open || !bdb->wmode || bdb->tran){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdbvanishimpl(bdb);
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Copy the database file of a B+ tree database object. */
+bool tcbdbcopy(TCBDB *bdb, const char *path){
+ assert(bdb && path);
+ if(!BDBLOCKMETHOD(bdb, true)) return false;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ TCLIST *lids = tclistnew();
+ TCLIST *nids = tclistnew();
+ const char *vbuf;
+ int vsiz;
+ TCMAP *leafc = bdb->leafc;
+ tcmapiterinit(leafc);
+ while((vbuf = tcmapiternext(leafc, &vsiz)) != NULL){
+ tclistpush(lids, vbuf, vsiz);
+ }
+ TCMAP *nodec = bdb->nodec;
+ tcmapiterinit(nodec);
+ while((vbuf = tcmapiternext(nodec, &vsiz)) != NULL){
+ tclistpush(nids, vbuf, vsiz);
+ }
+ BDBUNLOCKMETHOD(bdb);
+ bool err = false;
+ int ln = TCLISTNUM(lids);
+ for(int i = 0; i < ln; i++){
+ vbuf = TCLISTVALPTR(lids, i);
+ if(BDBLOCKMETHOD(bdb, true)){
+ if(bdb->open){
+ int rsiz;
+ BDBLEAF *leaf = (BDBLEAF *)tcmapget(bdb->leafc, vbuf, sizeof(leaf->id), &rsiz);
+ if(leaf && leaf->dirty && !tcbdbleafsave(bdb, leaf)) err = true;
+ } else {
+ err = true;
+ }
+ BDBUNLOCKMETHOD(bdb);
+ } else {
+ err = true;
+ }
+ }
+ ln = TCLISTNUM(nids);
+ for(int i = 0; i < ln; i++){
+ vbuf = TCLISTVALPTR(nids, i);
+ if(BDBLOCKMETHOD(bdb, true)){
+ if(bdb->open){
+ int rsiz;
+ BDBNODE *node = (BDBNODE *)tcmapget(bdb->nodec, vbuf, sizeof(node->id), &rsiz);
+ if(node && node->dirty && !tcbdbnodesave(bdb, node)) err = true;
+ } else {
+ err = true;
+ }
+ BDBUNLOCKMETHOD(bdb);
+ } else {
+ err = true;
+ }
+ }
+ tclistdel(nids);
+ tclistdel(lids);
+ if(!tcbdbtranbegin(bdb)) err = true;
+ if(BDBLOCKMETHOD(bdb, false)){
+ if(!tchdbcopy(bdb->hdb, path)) err = true;
+ BDBUNLOCKMETHOD(bdb);
+ } else {
+ err = true;
+ }
+ if(!tcbdbtrancommit(bdb)) err = true;
+ return !err;
+}
+
+
+/* Begin the transaction of a B+ tree database object. */
+bool tcbdbtranbegin(TCBDB *bdb){
+ assert(bdb);
+ if(!BDBLOCKMETHOD(bdb, true)) return false;
+ if(!bdb->open || !bdb->wmode || bdb->tran){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ if(!tcbdbmemsync(bdb, false)){
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ if(!BDBLOCKTRAN(bdb)){
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bdb->tran = true;
+ TCMEMDUP(bdb->rbopaque, bdb->opaque, BDBOPAQUESIZ);
+ BDBUNLOCKMETHOD(bdb);
+ return true;
+}
+
+
+/* Commit the transaction of a B+ tree database object. */
+bool tcbdbtrancommit(TCBDB *bdb){
+ assert(bdb);
+ if(!BDBLOCKMETHOD(bdb, true)) return false;
+ if(!bdb->open || !bdb->wmode || !bdb->tran){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ free(bdb->rbopaque);
+ bdb->tran = false;
+ bdb->rbopaque = NULL;
+ bool err = false;
+ if(!tcbdbmemsync(bdb, false)) err = true;
+ BDBUNLOCKTRAN(bdb);
+ BDBUNLOCKMETHOD(bdb);
+ return !err;
+}
+
+
+/* Abort the transaction of a B+ tree database object. */
+bool tcbdbtranabort(TCBDB *bdb){
+ assert(bdb);
+ if(!BDBLOCKMETHOD(bdb, true)) return false;
+ if(!bdb->open || !bdb->wmode || !bdb->tran){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ tcbdbcachepurge(bdb);
+ memcpy(bdb->opaque, bdb->rbopaque, BDBOPAQUESIZ);
+ tcloadmeta(bdb);
+ free(bdb->rbopaque);
+ bdb->tran = false;
+ bdb->rbopaque = NULL;
+ BDBUNLOCKTRAN(bdb);
+ BDBUNLOCKMETHOD(bdb);
+ return true;
+}
+
+
+/* Get the file path of a B+ tree database object. */
+const char *tcbdbpath(TCBDB *bdb){
+ assert(bdb);
+ if(!BDBLOCKMETHOD(bdb, false)) return NULL;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return NULL;
+ }
+ const char *rv = tchdbpath(bdb->hdb);
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Get the number of records of a B+ tree database object. */
+uint64_t tcbdbrnum(TCBDB *bdb){
+ assert(bdb);
+ if(!BDBLOCKMETHOD(bdb, false)) return 0;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return 0;
+ }
+ uint64_t rv = bdb->rnum;
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Get the size of the database file of a B+ tree database object. */
+uint64_t tcbdbfsiz(TCBDB *bdb){
+ assert(bdb);
+ if(!BDBLOCKMETHOD(bdb, false)) return 0;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return 0;
+ }
+ uint64_t rv = tchdbfsiz(bdb->hdb);
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Create a cursor object. */
+BDBCUR *tcbdbcurnew(TCBDB *bdb){
+ assert(bdb);
+ BDBCUR *cur;
+ TCMALLOC(cur, sizeof(*cur));
+ cur->bdb = bdb;
+ cur->id = 0;
+ cur->kidx = 0;
+ cur->vidx = 0;
+ return cur;
+}
+
+
+/* Delete a cursor object. */
+void tcbdbcurdel(BDBCUR *cur){
+ assert(cur);
+ free(cur);
+}
+
+
+/* Move a cursor object to the first record. */
+bool tcbdbcurfirst(BDBCUR *cur){
+ assert(cur);
+ TCBDB *bdb = cur->bdb;
+ if(!BDBLOCKMETHOD(bdb, false)) return false;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdbcurfirstimpl(cur);
+ bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+ BDBUNLOCKMETHOD(bdb);
+ if(adj && BDBLOCKMETHOD(bdb, true)){
+ if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = false;
+ BDBUNLOCKMETHOD(bdb);
+ }
+ return rv;
+}
+
+
+/* Move a cursor object to the last record. */
+bool tcbdbcurlast(BDBCUR *cur){
+ assert(cur);
+ TCBDB *bdb = cur->bdb;
+ if(!BDBLOCKMETHOD(bdb, false)) return false;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdbcurlastimpl(cur);
+ bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+ BDBUNLOCKMETHOD(bdb);
+ if(adj && BDBLOCKMETHOD(bdb, true)){
+ if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = false;
+ BDBUNLOCKMETHOD(bdb);
+ }
+ return rv;
+}
+
+
+/* Move a cursor object to the front of records corresponding a key. */
+bool tcbdbcurjump(BDBCUR *cur, const void *kbuf, int ksiz){
+ assert(cur && kbuf && ksiz >= 0);
+ TCBDB *bdb = cur->bdb;
+ if(!BDBLOCKMETHOD(bdb, false)) return false;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdbcurjumpimpl(cur, kbuf, ksiz, true);
+ bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+ BDBUNLOCKMETHOD(bdb);
+ if(adj && BDBLOCKMETHOD(bdb, true)){
+ if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = false;
+ BDBUNLOCKMETHOD(bdb);
+ }
+ return rv;
+}
+
+
+/* Move a cursor object to the front of records corresponding a key string. */
+bool tcbdbcurjump2(BDBCUR *cur, const char *kstr){
+ assert(cur && kstr);
+ return tcbdbcurjump(cur, kstr, strlen(kstr));
+}
+
+
+/* Move a cursor object to the previous record. */
+bool tcbdbcurprev(BDBCUR *cur){
+ assert(cur);
+ TCBDB *bdb = cur->bdb;
+ if(!BDBLOCKMETHOD(bdb, false)) return false;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ if(cur->id < 1){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdbcurprevimpl(cur);
+ bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+ BDBUNLOCKMETHOD(bdb);
+ if(adj && BDBLOCKMETHOD(bdb, true)){
+ if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = false;
+ BDBUNLOCKMETHOD(bdb);
+ }
+ return rv;
+}
+
+
+/* Move a cursor object to the next record. */
+bool tcbdbcurnext(BDBCUR *cur){
+ assert(cur);
+ TCBDB *bdb = cur->bdb;
+ if(!BDBLOCKMETHOD(bdb, false)) return false;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ if(cur->id < 1){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdbcurnextimpl(cur);
+ bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum;
+ BDBUNLOCKMETHOD(bdb);
+ if(adj && BDBLOCKMETHOD(bdb, true)){
+ if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = false;
+ BDBUNLOCKMETHOD(bdb);
+ }
+ return rv;
+}
+
+
+/* Insert a record around a cursor object. */
+bool tcbdbcurput(BDBCUR *cur, const void *vbuf, int vsiz, int cpmode){
+ assert(cur && vbuf && vsiz >= 0);
+ TCBDB *bdb = cur->bdb;
+ if(!BDBLOCKMETHOD(bdb, true)) return false;
+ if(!bdb->open || !bdb->wmode){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ if(cur->id < 1){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdbcurputimpl(cur, vbuf, vsiz, cpmode);
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Insert a string record around a cursor object. */
+bool tcbdbcurput2(BDBCUR *cur, const char *vstr, int cpmode){
+ assert(cur && vstr);
+ return tcbdbcurput(cur, vstr, strlen(vstr), cpmode);
+}
+
+
+/* Delete the record where a cursor object is. */
+bool tcbdbcurout(BDBCUR *cur){
+ assert(cur);
+ TCBDB *bdb = cur->bdb;
+ if(!BDBLOCKMETHOD(bdb, true)) return false;
+ if(!bdb->open || !bdb->wmode){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ if(cur->id < 1){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdbcuroutimpl(cur);
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Get the key of the record where the cursor object is. */
+char *tcbdbcurkey(BDBCUR *cur, int *sp){
+ assert(cur && sp);
+ TCBDB *bdb = cur->bdb;
+ if(!BDBLOCKMETHOD(bdb, false)) return false;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ if(cur->id < 1){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ const char *kbuf, *vbuf;
+ int ksiz, vsiz;
+ char *rv;
+ if(tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){
+ TCMEMDUP(rv, kbuf, ksiz);
+ *sp = ksiz;
+ } else {
+ rv = NULL;
+ }
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Get the key string of the record where the cursor object is. */
+char *tcbdbcurkey2(BDBCUR *cur){
+ assert(cur);
+ int ksiz;
+ return tcbdbcurkey(cur, &ksiz);
+}
+
+
+/* Get the key of the record where the cursor object is, as a volatile buffer. */
+const char *tcbdbcurkey3(BDBCUR *cur, int *sp){
+ assert(cur && sp);
+ TCBDB *bdb = cur->bdb;
+ if(!BDBLOCKMETHOD(bdb, false)) return false;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ if(cur->id < 1){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ const char *kbuf, *vbuf;
+ int ksiz, vsiz;
+ const char *rv;
+ if(tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){
+ rv = kbuf;
+ *sp = ksiz;
+ } else {
+ rv = NULL;
+ }
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Get the value of the record where the cursor object is. */
+char *tcbdbcurval(BDBCUR *cur, int *sp){
+ assert(cur && sp);
+ TCBDB *bdb = cur->bdb;
+ if(!BDBLOCKMETHOD(bdb, false)) return false;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ if(cur->id < 1){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ const char *kbuf, *vbuf;
+ int ksiz, vsiz;
+ char *rv;
+ if(tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){
+ TCMEMDUP(rv, vbuf, vsiz);
+ *sp = vsiz;
+ } else {
+ rv = NULL;
+ }
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Get the value string of the record where the cursor object is. */
+char *tcbdbcurval2(BDBCUR *cur){
+ assert(cur);
+ int vsiz;
+ return tcbdbcurval(cur, &vsiz);
+}
+
+
+/* Get the value of the record where the cursor object is, as a volatile buffer. */
+const char *tcbdbcurval3(BDBCUR *cur, int *sp){
+ assert(cur && sp);
+ TCBDB *bdb = cur->bdb;
+ if(!BDBLOCKMETHOD(bdb, false)) return false;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ if(cur->id < 1){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ const char *kbuf, *vbuf;
+ int ksiz, vsiz;
+ const char *rv;
+ if(tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){
+ rv = vbuf;
+ *sp = vsiz;
+ } else {
+ rv = NULL;
+ }
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Get the key and the value of the record where the cursor object is. */
+bool tcbdbcurrec(BDBCUR *cur, TCXSTR *kxstr, TCXSTR *vxstr){
+ assert(cur && kxstr && vxstr);
+ TCBDB *bdb = cur->bdb;
+ if(!BDBLOCKMETHOD(bdb, false)) return false;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ if(cur->id < 1){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ const char *kbuf, *vbuf;
+ int ksiz, vsiz;
+ bool rv;
+ if(tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){
+ tcxstrclear(kxstr);
+ TCXSTRCAT(kxstr, kbuf, ksiz);
+ tcxstrclear(vxstr);
+ TCXSTRCAT(vxstr, vbuf, vsiz);
+ rv = true;
+ } else {
+ rv = false;
+ }
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Set the error code of a B+ tree database object. */
+void tcbdbsetecode(TCBDB *bdb, int ecode, const char *filename, int line, const char *func){
+ assert(bdb && filename && line >= 1 && func);
+ tchdbsetecode(bdb->hdb, ecode, filename, line, func);
+}
+
+
+/* Set the file descriptor for debugging output. */
+void tcbdbsetdbgfd(TCBDB *bdb, int fd){
+ assert(bdb && fd >= 0);
+ tchdbsetdbgfd(bdb->hdb, fd);
+}
+
+
+/* Get the file descriptor for debugging output. */
+int tcbdbdbgfd(TCBDB *bdb){
+ assert(bdb);
+ return tchdbdbgfd(bdb->hdb);
+}
+
+
+/* Synchronize updating contents on memory. */
+bool tcbdbmemsync(TCBDB *bdb, bool phys){
+ assert(bdb);
+ if(!bdb->open || !bdb->wmode){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ bool err = false;
+ bool clk = BDBLOCKCACHE(bdb);
+ const char *vbuf;
+ int vsiz;
+ TCMAP *leafc = bdb->leafc;
+ tcmapiterinit(leafc);
+ while((vbuf = tcmapiternext(leafc, &vsiz)) != NULL){
+ int rsiz;
+ BDBLEAF *leaf = (BDBLEAF *)tcmapiterval(vbuf, &rsiz);
+ if(leaf->dirty && !tcbdbleafsave(bdb, leaf)) err = true;
+ }
+ TCMAP *nodec = bdb->nodec;
+ tcmapiterinit(nodec);
+ while((vbuf = tcmapiternext(nodec, &vsiz)) != NULL){
+ int rsiz;
+ BDBNODE *node = (BDBNODE *)tcmapiterval(vbuf, &rsiz);
+ if(node->dirty && !tcbdbnodesave(bdb, node)) err = true;
+ }
+ if(clk) BDBUNLOCKCACHE(bdb);
+ tcdumpmeta(bdb);
+ if(!tchdbmemsync(bdb->hdb, phys)) err = true;
+ return !err;
+}
+
+
+/* Get the comparison function of a B+ tree database object. */
+BDBCMP tcbdbcmpfunc(TCBDB *bdb){
+ assert(bdb);
+ return bdb->cmp;
+}
+
+
+/* Get the opaque object for the comparison function of a B+ tree database object. */
+void *tcbdbcmpop(TCBDB *bdb){
+ assert(bdb);
+ return bdb->cmpop;
+}
+
+
+/* Get the maximum number of cached leaf nodes of a B+ tree database object. */
+uint32_t tcbdblmemb(TCBDB *bdb){
+ assert(bdb);
+ return bdb->lmemb;
+}
+
+
+/* Get the maximum number of cached non-leaf nodes of a B+ tree database object. */
+uint32_t tcbdbnmemb(TCBDB *bdb){
+ assert(bdb);
+ return bdb->nmemb;
+}
+
+
+/* Get the number of the leaf nodes of B+ tree database object. */
+uint64_t tcbdblnum(TCBDB *bdb){
+ assert(bdb);
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return bdb->lnum;
+}
+
+
+/* Get the number of the non-leaf nodes of B+ tree database object. */
+uint64_t tcbdbnnum(TCBDB *bdb){
+ assert(bdb);
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return bdb->nnum;
+}
+
+
+/* Get the number of elements of the bucket array of a B+ tree database object. */
+uint64_t tcbdbbnum(TCBDB *bdb){
+ assert(bdb);
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return tchdbbnum(bdb->hdb);
+}
+
+
+/* Get the record alignment of a B+ tree database object. */
+uint32_t tcbdbalign(TCBDB *bdb){
+ assert(bdb);
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return tchdbalign(bdb->hdb);
+}
+
+
+/* Get the maximum number of the free block pool of a B+ tree database object. */
+uint32_t tcbdbfbpmax(TCBDB *bdb){
+ assert(bdb);
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return tchdbfbpmax(bdb->hdb);
+}
+
+
+/* Get the inode number of the database file of a B+ tree database object. */
+uint64_t tcbdbinode(TCBDB *bdb){
+ assert(bdb);
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return tchdbinode(bdb->hdb);
+}
+
+
+/* Get the modification time of the database file of a B+ tree database object. */
+time_t tcbdbmtime(TCBDB *bdb){
+ assert(bdb);
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return tchdbmtime(bdb->hdb);
+}
+
+
+/* Get the additional flags of a B+ tree database object. */
+uint8_t tcbdbflags(TCBDB *bdb){
+ assert(bdb);
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return tchdbflags(bdb->hdb);
+}
+
+
+/* Get the options of a B+ tree database object. */
+uint8_t tcbdbopts(TCBDB *bdb){
+ assert(bdb);
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return bdb->opts;
+}
+
+
+/* Get the pointer to the opaque field of a B+ tree database object. */
+char *tcbdbopaque(TCBDB *bdb){
+ assert(bdb);
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return tchdbopaque(bdb->hdb) + BDBOPAQUESIZ;
+}
+
+
+/* Get the number of used elements of the bucket array of a B+ tree database object. */
+uint64_t tcbdbbnumused(TCBDB *bdb){
+ assert(bdb);
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return tchdbbnumused(bdb->hdb);
+}
+
+
+/* Set the maximum size of each leaf node. */
+bool tcbdbsetlsmax(TCBDB *bdb, uint32_t lsmax){
+ assert(bdb);
+ if(bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ bdb->lsmax = lsmax;
+ return true;
+}
+
+
+/* Set the capacity number of records. */
+bool tcbdbsetcapnum(TCBDB *bdb, uint64_t capnum){
+ assert(bdb);
+ if(bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ bdb->capnum = capnum;
+ return true;
+}
+
+
+/* Store a new record into a B+ tree database object with backward duplication. */
+bool tcbdbputdupback(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+ assert(bdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ if(!BDBLOCKMETHOD(bdb, true)) return false;
+ if(!bdb->open || !bdb->wmode){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdbputimpl(bdb, kbuf, ksiz, vbuf, vsiz, BDBPDDUPB);
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Store a new string record into a B+ tree database object with backward duplication. */
+bool tcbdbputdupback2(TCBDB *bdb, const char *kstr, const char *vstr){
+ assert(bdb && kstr && vstr);
+ return tcbdbputdupback(bdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Move a cursor object to the rear of records corresponding a key. */
+bool tcbdbcurjumpback(BDBCUR *cur, const void *kbuf, int ksiz){
+ assert(cur && kbuf && ksiz >= 0);
+ TCBDB *bdb = cur->bdb;
+ if(!BDBLOCKMETHOD(bdb, false)) return false;
+ if(!bdb->open){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ BDBUNLOCKMETHOD(bdb);
+ return false;
+ }
+ bool rv = tcbdbcurjumpimpl(cur, kbuf, ksiz, false);
+ BDBUNLOCKMETHOD(bdb);
+ return rv;
+}
+
+
+/* Move a cursor object to the rear of records corresponding a key string. */
+bool tcbdbcurjumpback2(BDBCUR *cur, const char *kstr){
+ assert(cur && kstr);
+ return tcbdbcurjumpback(cur, kstr, strlen(kstr));
+}
+
+
+/* Compare keys of two records by lexical order. */
+int tcbdbcmplexical(const char *aptr, int asiz, const char *bptr, int bsiz, void *op){
+ assert(aptr && asiz >= 0 && bptr && bsiz >= 0);
+ int rv;
+ TCCMPLEXICAL(rv, aptr, asiz, bptr, bsiz);
+ return rv;
+}
+
+
+/* Compare two keys as decimal strings of real numbers. */
+int tcbdbcmpdecimal(const char *aptr, int asiz, const char *bptr, int bsiz, void *op){
+ assert(aptr && asiz >= 0 && bptr && bsiz >= 0);
+ int sign;
+ int64_t anum = 0;
+ sign = 1;
+ if(asiz > 0 && *aptr == '-'){
+ aptr++;
+ asiz--;
+ sign = -1;
+ }
+ for(int i = 0; i < asiz; i++){
+ int c = aptr[i];
+ if(c < '0' || c > '9') continue;
+ anum = anum * 10 + c - '0';
+ }
+ anum *= sign;
+ int64_t bnum = 0;
+ sign = 1;
+ if(bsiz > 0 && *bptr == '-'){
+ bptr++;
+ bsiz--;
+ sign = -1;
+ }
+ for(int i = 0; i < bsiz; i++){
+ int c = bptr[i];
+ if(c < '0' || c > '9') continue;
+ bnum = bnum * 10 + c - '0';
+ }
+ bnum *= sign;
+ return (anum < bnum) ? -1 : anum > bnum;
+}
+
+
+/* Compare two keys as 32-bit integers in the native byte order. */
+int tcbdbcmpint32(const char *aptr, int asiz, const char *bptr, int bsiz, void *op){
+ assert(aptr && bptr);
+ int32_t anum, bnum;
+ if(asiz == sizeof(int32_t)){
+ memcpy(&anum, aptr, sizeof(int32_t));
+ } else if(asiz < sizeof(int32_t)){
+ memset(&anum, 0, sizeof(int32_t));
+ memcpy(&anum, aptr, asiz);
+ } else {
+ memcpy(&anum, aptr, sizeof(int32_t));
+ }
+ if(bsiz == sizeof(int32_t)){
+ memcpy(&bnum, bptr, sizeof(int32_t));
+ } else if(bsiz < sizeof(int32_t)){
+ memset(&bnum, 0, sizeof(int32_t));
+ memcpy(&bnum, bptr, bsiz);
+ } else {
+ memcpy(&bnum, bptr, sizeof(int32_t));
+ }
+ return (anum < bnum) ? -1 : anum > bnum;
+}
+
+
+/* Compare two keys as 64-bit integers in the native byte order. */
+int tcbdbcmpint64(const char *aptr, int asiz, const char *bptr, int bsiz, void *op){
+ assert(aptr && bptr);
+ int64_t anum, bnum;
+ if(asiz == sizeof(int64_t)){
+ memcpy(&anum, aptr, sizeof(int64_t));
+ } else if(asiz < sizeof(int64_t)){
+ memset(&anum, 0, sizeof(int64_t));
+ memcpy(&anum, aptr, asiz);
+ } else {
+ memcpy(&anum, aptr, sizeof(int64_t));
+ }
+ if(bsiz == sizeof(int64_t)){
+ memcpy(&bnum, bptr, sizeof(int64_t));
+ } else if(bsiz < sizeof(int64_t)){
+ memset(&bnum, 0, sizeof(int64_t));
+ memcpy(&bnum, bptr, bsiz);
+ } else {
+ memcpy(&bnum, bptr, sizeof(int64_t));
+ }
+ return (anum < bnum) ? -1 : anum > bnum;
+}
+
+
+
+/*************************************************************************************************
+ * private features
+ *************************************************************************************************/
+
+
+/* Clear all members.
+ `bdb' specifies the B+ tree database object. */
+static void tcbdbclear(TCBDB *bdb){
+ assert(bdb);
+ bdb->mmtx = NULL;
+ bdb->cmtx = NULL;
+ bdb->tmtx = NULL;
+ bdb->hdb = NULL;
+ bdb->opaque = NULL;
+ bdb->open = false;
+ bdb->wmode = false;
+ bdb->lmemb = BDBDEFLMEMB;
+ bdb->nmemb = BDBDEFNMEMB;
+ bdb->opts = 0;
+ bdb->root = 0;
+ bdb->first = 0;
+ bdb->last = 0;
+ bdb->lnum = 0;
+ bdb->nnum = 0;
+ bdb->rnum = 0;
+ bdb->leafc = NULL;
+ bdb->nodec = NULL;
+ bdb->cmp = NULL;
+ bdb->cmpop = NULL;
+ bdb->lcnum = BDBDEFLCNUM;
+ bdb->ncnum = BDBDEFNCNUM;
+ bdb->lsmax = 0;
+ bdb->lschk = 0;
+ bdb->capnum = 0;
+ bdb->hist = NULL;
+ bdb->hnum = 0;
+ bdb->hleaf = 0;
+ bdb->lleaf = 0;
+ bdb->tran = false;
+ bdb->rbopaque = NULL;
+ bdb->cnt_saveleaf = -1;
+ bdb->cnt_loadleaf = -1;
+ bdb->cnt_killleaf = -1;
+ bdb->cnt_adjleafc = -1;
+ bdb->cnt_savenode = -1;
+ bdb->cnt_loadnode = -1;
+ bdb->cnt_adjnodec = -1;
+ TCDODEBUG(bdb->cnt_saveleaf = 0);
+ TCDODEBUG(bdb->cnt_loadleaf = 0);
+ TCDODEBUG(bdb->cnt_killleaf = 0);
+ TCDODEBUG(bdb->cnt_adjleafc = 0);
+ TCDODEBUG(bdb->cnt_savenode = 0);
+ TCDODEBUG(bdb->cnt_loadnode = 0);
+ TCDODEBUG(bdb->cnt_adjnodec = 0);
+}
+
+
+/* Serialize meta data into the opaque field.
+ `bdb' specifies the B+ tree database object. */
+static void tcdumpmeta(TCBDB *bdb){
+ assert(bdb);
+ memset(bdb->opaque, 0, 64);
+ char *wp = bdb->opaque;
+ if(bdb->cmp == tcbdbcmplexical){
+ *(uint8_t *)(wp++) = 0x0;
+ } else if(bdb->cmp == tcbdbcmpdecimal){
+ *(uint8_t *)(wp++) = 0x1;
+ } else if(bdb->cmp == tcbdbcmpint32){
+ *(uint8_t *)(wp++) = 0x2;
+ } else if(bdb->cmp == tcbdbcmpint64){
+ *(uint8_t *)(wp++) = 0x3;
+ } else {
+ *(uint8_t *)(wp++) = 0xff;
+ }
+ wp += 7;
+ uint32_t lnum;
+ lnum = bdb->lmemb;
+ lnum = TCHTOIL(lnum);
+ memcpy(wp, &lnum, sizeof(lnum));
+ wp += sizeof(lnum);
+ lnum = bdb->nmemb;
+ lnum = TCHTOIL(lnum);
+ memcpy(wp, &lnum, sizeof(lnum));
+ wp += sizeof(lnum);
+ uint64_t llnum;
+ llnum = bdb->root;
+ llnum = TCHTOILL(llnum);
+ memcpy(wp, &llnum, sizeof(llnum));
+ wp += sizeof(llnum);
+ llnum = bdb->first;
+ llnum = TCHTOILL(llnum);
+ memcpy(wp, &llnum, sizeof(llnum));
+ wp += sizeof(llnum);
+ llnum = bdb->last;
+ llnum = TCHTOILL(llnum);
+ memcpy(wp, &llnum, sizeof(llnum));
+ wp += sizeof(llnum);
+ llnum = bdb->lnum;
+ llnum = TCHTOILL(llnum);
+ memcpy(wp, &llnum, sizeof(llnum));
+ wp += sizeof(llnum);
+ llnum = bdb->nnum;
+ llnum = TCHTOILL(llnum);
+ memcpy(wp, &llnum, sizeof(llnum));
+ wp += sizeof(llnum);
+ llnum = bdb->rnum;
+ llnum = TCHTOILL(llnum);
+ memcpy(wp, &llnum, sizeof(llnum));
+ wp += sizeof(llnum);
+}
+
+
+/* Deserialize meta data from the opaque field.
+ `bdb' specifies the B+ tree database object. */
+static void tcloadmeta(TCBDB *bdb){
+ const char *rp = bdb->opaque;
+ uint8_t cnum = *(uint8_t *)(rp++);
+ if(cnum == 0x0){
+ bdb->cmp = tcbdbcmplexical;
+ } else if(cnum == 0x1){
+ bdb->cmp = tcbdbcmpdecimal;
+ } else if(cnum == 0x2){
+ bdb->cmp = tcbdbcmpint32;
+ } else if(cnum == 0x3){
+ bdb->cmp = tcbdbcmpint64;
+ }
+ rp += 7;
+ uint32_t lnum;
+ memcpy(&lnum, rp, sizeof(lnum));
+ rp += sizeof(lnum);
+ bdb->lmemb = TCITOHL(lnum);
+ memcpy(&lnum, rp, sizeof(lnum));
+ rp += sizeof(lnum);
+ bdb->nmemb = TCITOHL(lnum);
+ uint64_t llnum;
+ memcpy(&llnum, rp, sizeof(llnum));
+ bdb->root = TCITOHLL(llnum);
+ rp += sizeof(llnum);
+ memcpy(&llnum, rp, sizeof(llnum));
+ bdb->first = TCITOHLL(llnum);
+ rp += sizeof(llnum);
+ memcpy(&llnum, rp, sizeof(llnum));
+ bdb->last = TCITOHLL(llnum);
+ rp += sizeof(llnum);
+ memcpy(&llnum, rp, sizeof(llnum));
+ bdb->lnum = TCITOHLL(llnum);
+ rp += sizeof(llnum);
+ memcpy(&llnum, rp, sizeof(llnum));
+ bdb->nnum = TCITOHLL(llnum);
+ rp += sizeof(llnum);
+ memcpy(&llnum, rp, sizeof(llnum));
+ bdb->rnum = TCITOHLL(llnum);
+ rp += sizeof(llnum);
+}
+
+
+/* Create a new leaf.
+ `bdb' specifies the B+ tree database object.
+ `prev' specifies the ID number of the previous leaf.
+ `next' specifies the ID number of the next leaf.
+ The return value is the new leaf object. */
+static BDBLEAF *tcbdbleafnew(TCBDB *bdb, uint64_t prev, uint64_t next){
+ assert(bdb);
+ BDBLEAF lent;
+ lent.id = ++bdb->lnum;
+ lent.recs = tclistnew2(bdb->lmemb + 1);
+ lent.prev = prev;
+ lent.next = next;
+ lent.dirty = true;
+ lent.dead = false;
+ tcmapputkeep(bdb->leafc, &(lent.id), sizeof(lent.id), &lent, sizeof(lent));
+ int rsiz;
+ return (BDBLEAF *)tcmapget(bdb->leafc, &(lent.id), sizeof(lent.id), &rsiz);
+}
+
+
+/* Remove a leaf from the cache.
+ `bdb' specifies the B+ tree database object.
+ `leaf' specifies the leaf object.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbleafcacheout(TCBDB *bdb, BDBLEAF *leaf){
+ assert(bdb && leaf);
+ bool err = false;
+ if(leaf->dirty && !tcbdbleafsave(bdb, leaf)) err = true;
+ TCLIST *recs = leaf->recs;
+ int ln = TCLISTNUM(recs);
+ for(int i = 0; i < ln; i++){
+ BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, i);
+ free(recp->kbuf);
+ free(recp->vbuf);
+ if(recp->rest) tclistdel(recp->rest);
+ }
+ tclistdel(recs);
+ tcmapout(bdb->leafc, &(leaf->id), sizeof(leaf->id));
+ return !err;
+}
+
+
+/* Save a leaf into the internal database.
+ `bdb' specifies the B+ tree database object.
+ `leaf' specifies the leaf object.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbleafsave(TCBDB *bdb, BDBLEAF *leaf){
+ assert(bdb && leaf);
+ TCDODEBUG(bdb->cnt_saveleaf++);
+ TCXSTR *rbuf = tcxstrnew3(BDBPAGEBUFSIZ);
+ char hbuf[(sizeof(uint64_t)+1)*3];
+ char *wp = hbuf;
+ uint64_t llnum;
+ int step;
+ llnum = leaf->prev;
+ TCSETVNUMBUF64(step, wp, llnum);
+ wp += step;
+ llnum = leaf->next;
+ TCSETVNUMBUF64(step, wp, llnum);
+ wp += step;
+ TCXSTRCAT(rbuf, hbuf, wp - hbuf);
+ TCLIST *recs = leaf->recs;
+ int ln = TCLISTNUM(recs);
+ for(int i = 0; i < ln; i++){
+ BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, i);
+ int lnum;
+ wp = hbuf;
+ lnum = recp->ksiz;
+ TCSETVNUMBUF(step, wp, lnum);
+ wp += step;
+ lnum = recp->vsiz;
+ TCSETVNUMBUF(step, wp, lnum);
+ wp += step;
+ TCLIST *rest = recp->rest;
+ int rnum = rest ? TCLISTNUM(rest) : 0;
+ TCSETVNUMBUF(step, wp, rnum);
+ wp += step;
+ TCXSTRCAT(rbuf, hbuf, wp - hbuf);
+ TCXSTRCAT(rbuf, recp->kbuf, recp->ksiz);
+ TCXSTRCAT(rbuf, recp->vbuf, recp->vsiz);
+ for(int j = 0; j < rnum; j++){
+ int vsiz;
+ const char *vbuf = tclistval(rest, j, &vsiz);
+ TCSETVNUMBUF(step, hbuf, vsiz);
+ TCXSTRCAT(rbuf, hbuf, step);
+ TCXSTRCAT(rbuf, vbuf, vsiz);
+ }
+ }
+ bool err = false;
+ step = sprintf(hbuf, "%llx", (unsigned long long)leaf->id);
+ if(ln < 1 && !tchdbout(bdb->hdb, hbuf, step) && tchdbecode(bdb->hdb) != TCENOREC)
+ err = true;
+ if(!leaf->dead && !tchdbput(bdb->hdb, hbuf, step, TCXSTRPTR(rbuf), TCXSTRSIZE(rbuf)))
+ err = true;
+ tcxstrdel(rbuf);
+ leaf->dirty = false;
+ return !err;
+}
+
+
+/* Load a leaf from the internal database.
+ `bdb' specifies the B+ tree database object.
+ `id' specifies the ID number of the leaf.
+ The return value is the leaf object or `NULL' on failure. */
+static BDBLEAF *tcbdbleafload(TCBDB *bdb, uint64_t id){
+ assert(bdb && id > 0);
+ bool clk = BDBLOCKCACHE(bdb);
+ int rsiz;
+ BDBLEAF *leaf = (BDBLEAF *)tcmapget3(bdb->leafc, &id, sizeof(id), &rsiz);
+ if(leaf){
+ if(clk) BDBUNLOCKCACHE(bdb);
+ return leaf;
+ }
+ if(clk) BDBUNLOCKCACHE(bdb);
+ TCDODEBUG(bdb->cnt_loadleaf++);
+ char hbuf[(sizeof(uint64_t)+1)*3];
+ int step;
+ step = sprintf(hbuf, "%llx", (unsigned long long)id);
+ char *rbuf = NULL;
+ char wbuf[BDBPAGEBUFSIZ];
+ const char *rp = NULL;
+ rsiz = tchdbget3(bdb->hdb, hbuf, step, wbuf, BDBPAGEBUFSIZ);
+ if(rsiz < 1){
+ tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__);
+ return false;
+ } else if(rsiz < BDBPAGEBUFSIZ){
+ rp = wbuf;
+ } else {
+ if(!(rbuf = tchdbget(bdb->hdb, hbuf, step, &rsiz))){
+ tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ rp = rbuf;
+ }
+ BDBLEAF lent;
+ lent.id = id;
+ uint64_t llnum;
+ TCREADVNUMBUF64(rp, llnum, step);
+ lent.prev = llnum;
+ rp += step;
+ rsiz -= step;
+ TCREADVNUMBUF64(rp, llnum, step);
+ lent.next = llnum;
+ rp += step;
+ rsiz -= step;
+ lent.dirty = false;
+ lent.dead = false;
+ lent.recs = tclistnew2(bdb->lmemb + 1);
+ bool err = false;
+ while(rsiz >= 3){
+ BDBREC rec;
+ TCREADVNUMBUF(rp, rec.ksiz, step);
+ rp += step;
+ rsiz -= step;
+ TCREADVNUMBUF(rp, rec.vsiz, step);
+ rp += step;
+ rsiz -= step;
+ int rnum;
+ TCREADVNUMBUF(rp, rnum, step);
+ rp += step;
+ rsiz -= step;
+ if(rsiz < rec.ksiz + rec.vsiz + rnum){
+ err = true;
+ break;
+ }
+ TCMEMDUP(rec.kbuf, rp, rec.ksiz);
+ rp += rec.ksiz;
+ rsiz -= rec.ksiz;
+ TCMEMDUP(rec.vbuf, rp, rec.vsiz);
+ rp += rec.vsiz;
+ rsiz -= rec.vsiz;
+ if(rnum > 0){
+ rec.rest = tclistnew2(rnum);
+ while(rnum-- > 0 && rsiz > 0){
+ int vsiz;
+ TCREADVNUMBUF(rp, vsiz, step);
+ rp += step;
+ rsiz -= step;
+ if(rsiz < vsiz){
+ err = true;
+ break;
+ }
+ TCLISTPUSH(rec.rest, rp, vsiz);
+ rp += vsiz;
+ rsiz -= vsiz;
+ }
+ } else {
+ rec.rest = NULL;
+ }
+ TCLISTPUSH(lent.recs, &rec, sizeof(rec));
+ }
+ free(rbuf);
+ if(err || rsiz != 0){
+ tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__);
+ return NULL;
+ }
+ clk = BDBLOCKCACHE(bdb);
+ tcmapputkeep(bdb->leafc, &(lent.id), sizeof(lent.id), &lent, sizeof(lent));
+ leaf = (BDBLEAF *)tcmapget(bdb->leafc, &(lent.id), sizeof(lent.id), &rsiz);
+ if(clk) BDBUNLOCKCACHE(bdb);
+ return leaf;
+}
+
+
+/* Load the historical leaf from the internal database.
+ `bdb' specifies the B+ tree database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is the pointer to the leaf, else, it is `NULL'. */
+static BDBLEAF *tcbdbgethistleaf(TCBDB *bdb, const char *kbuf, int ksiz){
+ assert(bdb && kbuf && ksiz >= 0);
+ BDBLEAF *leaf = tcbdbleafload(bdb, bdb->hleaf);
+ if(!leaf) return NULL;
+ int ln = TCLISTNUM(leaf->recs);
+ if(ln < 2) return NULL;
+ BDBREC *recp = (BDBREC *)TCLISTVALPTR(leaf->recs, 0);
+ int rv;
+ if(bdb->cmp == tcbdbcmplexical){
+ TCCMPLEXICAL(rv, kbuf, ksiz, recp->kbuf, recp->ksiz);
+ } else {
+ rv = bdb->cmp(kbuf, ksiz, recp->kbuf, recp->ksiz, bdb->cmpop);
+ }
+ if(rv == 0) return leaf;
+ if(rv < 0) return NULL;
+ recp = (BDBREC *)TCLISTVALPTR(leaf->recs, ln - 1);
+ if(bdb->cmp == tcbdbcmplexical){
+ TCCMPLEXICAL(rv, kbuf, ksiz, recp->kbuf, recp->ksiz);
+ } else {
+ rv = bdb->cmp(kbuf, ksiz, recp->kbuf, recp->ksiz, bdb->cmpop);
+ }
+ if(rv <= 0 || leaf->next < 1) return leaf;
+ return NULL;
+}
+
+
+/* Add a record to a leaf.
+ `bdb' specifies the B+ tree database object.
+ `leaf' specifies the leaf object.
+ `dmode' specifies behavior when the key overlaps.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbleafaddrec(TCBDB *bdb, BDBLEAF *leaf, int dmode,
+ const char *kbuf, int ksiz, const char *vbuf, int vsiz){
+ assert(bdb && leaf && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ BDBCMP cmp = bdb->cmp;
+ void *cmpop = bdb->cmpop;
+ TCLIST *recs = leaf->recs;
+ int ln = TCLISTNUM(recs);
+ int left = 0;
+ int right = ln;
+ int i = (left + right) / 2;
+ while(right >= left && i < ln){
+ BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, i);
+ int rv;
+ if(cmp == tcbdbcmplexical){
+ TCCMPLEXICAL(rv, kbuf, ksiz, recp->kbuf, recp->ksiz);
+ } else {
+ rv = cmp(kbuf, ksiz, recp->kbuf, recp->ksiz, cmpop);
+ }
+ if(rv == 0){
+ break;
+ } else if(rv <= 0){
+ right = i - 1;
+ } else {
+ left = i + 1;
+ }
+ i = (left + right) / 2;
+ }
+ while(i < ln){
+ BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, i);
+ int rv;
+ if(cmp == tcbdbcmplexical){
+ TCCMPLEXICAL(rv, kbuf, ksiz, recp->kbuf, recp->ksiz);
+ } else {
+ rv = cmp(kbuf, ksiz, recp->kbuf, recp->ksiz, cmpop);
+ }
+ if(rv == 0){
+ switch(dmode){
+ case BDBPDKEEP:
+ return false;
+ case BDBPDCAT:
+ TCREALLOC(recp->vbuf, recp->vbuf, recp->vsiz + vsiz + 1);
+ memcpy(recp->vbuf + recp->vsiz, vbuf, vsiz);
+ recp->vsiz += vsiz;
+ recp->vbuf[recp->vsiz] = '\0';
+ break;
+ case BDBPDDUP:
+ if(!recp->rest) recp->rest = tclistnew();
+ TCLISTPUSH(recp->rest, vbuf, vsiz);
+ bdb->rnum++;
+ break;
+ case BDBPDDUPB:
+ if(!recp->rest) recp->rest = tclistnew();
+ tclistunshift(recp->rest, recp->vbuf, recp->vsiz);
+ if(vsiz > recp->vsiz) TCREALLOC(recp->vbuf, recp->vbuf, vsiz + 1);
+ memcpy(recp->vbuf, vbuf, vsiz);
+ recp->vbuf[vsiz] = '\0';
+ recp->vsiz = vsiz;
+ bdb->rnum++;
+ break;
+ default:
+ if(vsiz > recp->vsiz) TCREALLOC(recp->vbuf, recp->vbuf, vsiz + 1);
+ memcpy(recp->vbuf, vbuf, vsiz);
+ recp->vbuf[vsiz] = '\0';
+ recp->vsiz = vsiz;
+ break;
+ }
+ break;
+ } else if(rv < 0){
+ BDBREC rec;
+ TCMEMDUP(rec.kbuf, kbuf, ksiz);
+ rec.ksiz = ksiz;
+ TCMEMDUP(rec.vbuf, vbuf, vsiz);
+ rec.vsiz = vsiz;
+ rec.rest = NULL;
+ tclistinsert(recs, i, &rec, sizeof(rec));
+ bdb->rnum++;
+ break;
+ }
+ i++;
+ }
+ if(i >= ln){
+ BDBREC rec;
+ TCMEMDUP(rec.kbuf, kbuf, ksiz);
+ rec.ksiz = ksiz;
+ TCMEMDUP(rec.vbuf, vbuf, vsiz);
+ rec.vsiz = vsiz;
+ rec.rest = NULL;
+ TCLISTPUSH(recs, &rec, sizeof(rec));
+ bdb->rnum++;
+ }
+ leaf->dirty = true;
+ return true;
+}
+
+
+/* Calculate the size of data of a leaf object.
+ `bdb' specifies the B+ tree database object.
+ `leaf' specifies the leaf object.
+ The return value is size of data of the leaf. */
+static int tcbdbleafdatasize(BDBLEAF *leaf){
+ assert(leaf);
+ int sum = 0;
+ TCLIST *recs = leaf->recs;
+ int ln = TCLISTNUM(recs);
+ for(int i = 0; i < ln; i++){
+ BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, i);
+ sum += recp->ksiz + recp->vsiz;
+ if(recp->rest){
+ TCLIST *rest = recp->rest;
+ int rnum = TCLISTNUM(rest);
+ for(int j = 0; j < rnum; j++){
+ int vsiz;
+ tclistval(rest, j, &vsiz);
+ sum += vsiz;
+ }
+ }
+ }
+ return sum;
+}
+
+
+/* Divide a leaf into two.
+ `bdb' specifies the B+ tree database object.
+ `leaf' specifies the leaf object.
+ The return value is the new leaf object or `NULL' on failure. */
+static BDBLEAF *tcbdbleafdivide(TCBDB *bdb, BDBLEAF *leaf){
+ assert(bdb && leaf);
+ bdb->hleaf = 0;
+ TCLIST *recs = leaf->recs;
+ int mid = TCLISTNUM(recs) / 2;
+ BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, mid);
+ BDBLEAF *newleaf = tcbdbleafnew(bdb, leaf->id, leaf->next);
+ if(newleaf->next > 0){
+ BDBLEAF *nextleaf = tcbdbleafload(bdb, newleaf->next);
+ if(!nextleaf) return NULL;
+ nextleaf->prev = newleaf->id;
+ nextleaf->dirty = true;
+ }
+ leaf->next = newleaf->id;
+ leaf->dirty = true;
+ int ln = TCLISTNUM(recs);
+ TCLIST *newrecs = newleaf->recs;
+ for(int i = mid; i < ln; i++){
+ recp = (BDBREC *)TCLISTVALPTR(recs, i);
+ TCLISTPUSH(newrecs, recp, sizeof(*recp));
+ }
+ ln = TCLISTNUM(newrecs);
+ for(int i = 0; i < ln; i++){
+ int rsiz;
+ free(tclistpop(recs, &rsiz));
+ }
+ return newleaf;
+}
+
+
+/* Cut off the path to a leaf and mark it dead.
+ `bdb' specifies the B+ tree database object.
+ `leaf' specifies the leaf object.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbleafkill(TCBDB *bdb, BDBLEAF *leaf){
+ assert(bdb && leaf);
+ BDBNODE *node = tcbdbnodeload(bdb, bdb->hist[bdb->hnum-1]);
+ if(!node) return false;
+ if(tcbdbnodesubidx(bdb, node, leaf->id)){
+ TCDODEBUG(bdb->cnt_killleaf++);
+ if(bdb->hleaf == leaf->id) bdb->hleaf = 0;
+ if(leaf->prev > 0){
+ BDBLEAF *tleaf = tcbdbleafload(bdb, leaf->prev);
+ if(!tleaf) return false;
+ tleaf->next = leaf->next;
+ tleaf->dirty = true;
+ if(bdb->last == leaf->id) bdb->last = leaf->prev;
+ }
+ if(leaf->next > 0){
+ BDBLEAF *tleaf = tcbdbleafload(bdb, leaf->next);
+ if(!tleaf) return false;
+ tleaf->prev = leaf->prev;
+ tleaf->dirty = true;
+ if(bdb->first == leaf->id) bdb->first = leaf->next;
+ }
+ leaf->dead = true;
+ }
+ return true;
+}
+
+
+/* Create a new node.
+ `bdb' specifies the B+ tree database object.
+ `heir' specifies the ID of the child before the first index.
+ The return value is the new node object. */
+static BDBNODE *tcbdbnodenew(TCBDB *bdb, uint64_t heir){
+ assert(bdb && heir > 0);
+ BDBNODE nent;
+ nent.id = ++bdb->nnum + BDBNODEIDBASE;
+ nent.idxs = tclistnew2(bdb->nmemb + 1);
+ nent.heir = heir;
+ nent.dirty = true;
+ tcmapputkeep(bdb->nodec, &(nent.id), sizeof(nent.id), &nent, sizeof(nent));
+ int rsiz;
+ return (BDBNODE *)tcmapget(bdb->nodec, &(nent.id), sizeof(nent.id), &rsiz);
+}
+
+
+/* Remove a node from the cache.
+ `bdb' specifies the B+ tree database object.
+ `node' specifies the node object.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbnodecacheout(TCBDB *bdb, BDBNODE *node){
+ assert(bdb && node);
+ bool err = false;
+ if(node->dirty && !tcbdbnodesave(bdb, node)) err = true;
+ TCLIST *idxs = node->idxs;
+ int ln = TCLISTNUM(idxs);
+ for(int i = 0; i < ln; i++){
+ BDBIDX *idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+ free(idxp->kbuf);
+ }
+ tclistdel(idxs);
+ tcmapout(bdb->nodec, &(node->id), sizeof(node->id));
+ return !err;
+}
+
+
+/* Save a node into the internal database.
+ `bdb' specifies the B+ tree database object.
+ `node' specifies the node object.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbnodesave(TCBDB *bdb, BDBNODE *node){
+ assert(bdb && node);
+ TCDODEBUG(bdb->cnt_savenode++);
+ TCXSTR *rbuf = tcxstrnew3(BDBPAGEBUFSIZ);
+ char hbuf[(sizeof(uint64_t)+1)*2];
+ uint64_t llnum;
+ int step;
+ llnum = node->heir;
+ TCSETVNUMBUF64(step, hbuf, llnum);
+ TCXSTRCAT(rbuf, hbuf, step);
+ TCLIST *idxs = node->idxs;
+ int ln = TCLISTNUM(idxs);
+ for(int i = 0; i < ln; i++){
+ BDBIDX *idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+ char *wp = hbuf;
+ llnum = idxp->pid;
+ TCSETVNUMBUF64(step, wp, llnum);
+ wp += step;
+ uint32_t lnum = idxp->ksiz;
+ TCSETVNUMBUF(step, wp, lnum);
+ wp += step;
+ TCXSTRCAT(rbuf, hbuf, wp - hbuf);
+ TCXSTRCAT(rbuf, idxp->kbuf, idxp->ksiz);
+ }
+ bool err = false;
+ step = sprintf(hbuf, "#%llx", (unsigned long long)(node->id - BDBNODEIDBASE));
+ if(!tchdbput(bdb->hdb, hbuf, step, TCXSTRPTR(rbuf), TCXSTRSIZE(rbuf))) err = true;
+ tcxstrdel(rbuf);
+ node->dirty = false;
+ return !err;
+}
+
+
+/* Load a node from the internal database.
+ `bdb' specifies the B+ tree database object.
+ `id' specifies the ID number of the node.
+ The return value is the node object or `NULL' on failure. */
+static BDBNODE *tcbdbnodeload(TCBDB *bdb, uint64_t id){
+ assert(bdb && id > BDBNODEIDBASE);
+ bool clk = BDBLOCKCACHE(bdb);
+ int rsiz;
+ BDBNODE *node = (BDBNODE *)tcmapget3(bdb->nodec, &id, sizeof(id), &rsiz);
+ if(node){
+ if(clk) BDBUNLOCKCACHE(bdb);
+ return node;
+ }
+ if(clk) BDBUNLOCKCACHE(bdb);
+ TCDODEBUG(bdb->cnt_loadnode++);
+ char hbuf[(sizeof(uint64_t)+1)*2];
+ int step;
+ step = sprintf(hbuf, "#%llx", (unsigned long long)(id - BDBNODEIDBASE));
+ char *rbuf = NULL;
+ char wbuf[BDBPAGEBUFSIZ];
+ const char *rp = NULL;
+ rsiz = tchdbget3(bdb->hdb, hbuf, step, wbuf, BDBPAGEBUFSIZ);
+ if(rsiz < 1){
+ tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__);
+ return false;
+ } else if(rsiz < BDBPAGEBUFSIZ){
+ rp = wbuf;
+ } else {
+ if(!(rbuf = tchdbget(bdb->hdb, hbuf, step, &rsiz))){
+ tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ rp = rbuf;
+ }
+ BDBNODE nent;
+ nent.id = id;
+ uint64_t llnum;
+ TCREADVNUMBUF64(rp, llnum, step);
+ nent.heir = llnum;
+ rp += step;
+ rsiz -= step;
+ nent.dirty = false;
+ nent.idxs = tclistnew2(bdb->nmemb + 1);
+ bool err = false;
+ while(rsiz >= 2){
+ BDBIDX idx;
+ TCREADVNUMBUF64(rp, idx.pid, step);
+ rp += step;
+ rsiz -= step;
+ TCREADVNUMBUF(rp, idx.ksiz, step);
+ rp += step;
+ rsiz -= step;
+ if(rsiz < idx.ksiz){
+ err = true;
+ break;
+ }
+ TCMEMDUP(idx.kbuf, rp, idx.ksiz);
+ rp += idx.ksiz;
+ rsiz -= idx.ksiz;
+ TCLISTPUSH(nent.idxs, &idx, sizeof(idx));
+ }
+ free(rbuf);
+ if(err || rsiz != 0){
+ tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__);
+ return NULL;
+ }
+ clk = BDBLOCKCACHE(bdb);
+ tcmapputkeep(bdb->nodec, &(nent.id), sizeof(nent.id), &nent, sizeof(nent));
+ node = (BDBNODE *)tcmapget(bdb->nodec, &(nent.id), sizeof(nent.id), &rsiz);
+ if(clk) BDBUNLOCKCACHE(bdb);
+ return node;
+}
+
+
+/* Add an index to a node.
+ `bdb' specifies the B+ tree database object.
+ `node' specifies the node object.
+ `order' specifies whether the calling sequence is orderd or not.
+ `pid' specifies the ID number of referred page.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key. */
+static void tcbdbnodeaddidx(TCBDB *bdb, BDBNODE *node, bool order, uint64_t pid,
+ const char *kbuf, int ksiz){
+ assert(bdb && node && pid > 0 && kbuf && ksiz >= 0);
+ BDBIDX idx;
+ idx.pid = pid;
+ TCMEMDUP(idx.kbuf, kbuf, ksiz);
+ idx.ksiz = ksiz;
+ BDBCMP cmp = bdb->cmp;
+ void *cmpop = bdb->cmpop;
+ TCLIST *idxs = node->idxs;
+ if(order){
+ TCLISTPUSH(idxs, &idx, sizeof(idx));
+ } else {
+ int ln = TCLISTNUM(idxs);
+ int left = 0;
+ int right = ln;
+ int i = (left + right) / 2;
+ while(right >= left && i < ln){
+ BDBIDX *idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+ int rv;
+ if(cmp == tcbdbcmplexical){
+ TCCMPLEXICAL(rv, kbuf, ksiz, idxp->kbuf, idxp->ksiz);
+ } else {
+ rv = cmp(kbuf, ksiz, idxp->kbuf, idxp->ksiz, cmpop);
+ }
+ if(rv == 0){
+ break;
+ } else if(rv <= 0){
+ right = i - 1;
+ } else {
+ left = i + 1;
+ }
+ i = (left + right) / 2;
+ }
+ while(i < ln){
+ BDBIDX *idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+ int rv;
+ if(cmp == tcbdbcmplexical){
+ TCCMPLEXICAL(rv, kbuf, ksiz, idxp->kbuf, idxp->ksiz);
+ } else {
+ rv = cmp(kbuf, ksiz, idxp->kbuf, idxp->ksiz, cmpop);
+ }
+ if(rv < 0){
+ tclistinsert(idxs, i, &idx, sizeof(idx));
+ break;
+ }
+ i++;
+ }
+ if(i >= ln) TCLISTPUSH(idxs, &idx, sizeof(idx));
+ }
+ node->dirty = true;
+}
+
+
+/* Subtract an index from a node.
+ `bdb' specifies the B+ tree database object.
+ `node' specifies the node object.
+ `pid' specifies the ID number of referred page.
+ The return value is whether the subtraction is completed. */
+static bool tcbdbnodesubidx(TCBDB *bdb, BDBNODE *node, uint64_t pid){
+ assert(bdb && node && pid > 0);
+ TCLIST *idxs = node->idxs;
+ int ln = TCLISTNUM(idxs);
+ if(ln < 2) return false;
+ if(node->heir == pid){
+ int rsiz;
+ BDBIDX *idxp = (BDBIDX *)tclistshift(idxs, &rsiz);
+ node->heir = idxp->pid;
+ free(idxp->kbuf);
+ free(idxp);
+ node->dirty = true;
+ return true;
+ } else {
+ int ln = TCLISTNUM(idxs);
+ for(int i = 0; i < ln; i++){
+ BDBIDX *idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+ if(idxp->pid == pid){
+ int rsiz;
+ free(idxp->kbuf);
+ free(tclistremove(idxs, i, &rsiz));
+ node->dirty = true;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+/* Search the leaf object corresponding to a key.
+ `bdb' specifies the B+ tree database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ The return value is the ID number of the leaf object or 0 on failure. */
+static uint64_t tcbdbsearchleaf(TCBDB *bdb, const char *kbuf, int ksiz){
+ assert(bdb && kbuf && ksiz >= 0);
+ BDBCMP cmp = bdb->cmp;
+ void *cmpop = bdb->cmpop;
+ uint64_t *hist = bdb->hist;
+ uint64_t pid = bdb->root;
+ int hnum = 0;
+ bdb->hleaf = 0;
+ while(pid > BDBNODEIDBASE){
+ BDBNODE *node = tcbdbnodeload(bdb, pid);
+ if(!node){
+ tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ TCLIST *idxs = node->idxs;
+ int ln = TCLISTNUM(idxs);
+ if(ln < 1){
+ tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ hist[hnum++] = node->id;
+ int left = 0;
+ int right = ln;
+ int i = (left + right) / 2;
+ BDBIDX *idxp = NULL;
+ while(right >= left && i < ln){
+ idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+ int rv;
+ if(cmp == tcbdbcmplexical){
+ TCCMPLEXICAL(rv, kbuf, ksiz, idxp->kbuf, idxp->ksiz);
+ } else {
+ rv = cmp(kbuf, ksiz, idxp->kbuf, idxp->ksiz, cmpop);
+ }
+ if(rv == 0){
+ break;
+ } else if(rv <= 0){
+ right = i - 1;
+ } else {
+ left = i + 1;
+ }
+ i = (left + right) / 2;
+ }
+ if(i > 0) i--;
+ while(i < ln){
+ idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+ int rv;
+ if(cmp == tcbdbcmplexical){
+ TCCMPLEXICAL(rv, kbuf, ksiz, idxp->kbuf, idxp->ksiz);
+ } else {
+ rv = cmp(kbuf, ksiz, idxp->kbuf, idxp->ksiz, cmpop);
+ }
+ if(rv < 0){
+ if(i == 0){
+ pid = node->heir;
+ break;
+ }
+ idxp = (BDBIDX *)TCLISTVALPTR(idxs, i - 1);
+ pid = idxp->pid;
+ break;
+ }
+ i++;
+ }
+ if(i >= ln) pid = idxp->pid;
+ }
+ if(!bdb->mmtx){
+ if(bdb->lleaf == pid) bdb->hleaf = pid;
+ bdb->lleaf = pid;
+ }
+ bdb->hnum = hnum;
+ return pid;
+}
+
+
+/* Search a record of a leaf.
+ `bdb' specifies the B+ tree database object.
+ `leaf' specifies the leaf object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `ip' specifies the pointer to a variable to fetch the index of the correspnding record.
+ The return value is the pointer to a corresponding record or `NULL' on failure. */
+static BDBREC *tcbdbsearchrec(TCBDB *bdb, BDBLEAF *leaf, const char *kbuf, int ksiz, int *ip){
+ assert(bdb && leaf && kbuf && ksiz >= 0);
+ BDBCMP cmp = bdb->cmp;
+ void *cmpop = bdb->cmpop;
+ TCLIST *recs = leaf->recs;
+ int ln = TCLISTNUM(recs);
+ int left = 0;
+ int right = ln;
+ int i = (left + right) / 2;
+ while(right >= left && i < ln){
+ BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, i);
+ int rv;
+ if(cmp == tcbdbcmplexical){
+ TCCMPLEXICAL(rv, kbuf, ksiz, recp->kbuf, recp->ksiz);
+ } else {
+ rv = cmp(kbuf, ksiz, recp->kbuf, recp->ksiz, cmpop);
+ }
+ if(rv == 0){
+ if(ip) *ip = i;
+ return recp;
+ } else if(rv <= 0){
+ right = i - 1;
+ } else {
+ left = i + 1;
+ }
+ i = (left + right) / 2;
+ }
+ if(ip) *ip = i;
+ return NULL;
+}
+
+
+/* Adjust the caches for leaves and nodes.
+ `bdb' specifies the B+ tree database object.
+ The return value is true if successful, else, it is false. */
+static bool tcbdbcacheadjust(TCBDB *bdb){
+ bool err = false;
+ if(TCMAPRNUM(bdb->leafc) > bdb->lcnum){
+ TCDODEBUG(bdb->cnt_adjleafc++);
+ bool clk = BDBLOCKCACHE(bdb);
+ TCMAP *leafc = bdb->leafc;
+ tcmapiterinit(leafc);
+ for(int i = 0; i < BDBCACHEOUT; i++){
+ int rsiz;
+ if(!tcbdbleafcacheout(bdb, (BDBLEAF *)tcmapiterval(tcmapiternext(leafc, &rsiz), &rsiz)))
+ err = true;
+ }
+ if(clk) BDBUNLOCKCACHE(bdb);
+ }
+ if(TCMAPRNUM(bdb->nodec) > bdb->ncnum){
+ TCDODEBUG(bdb->cnt_adjnodec++);
+ bool clk = BDBLOCKCACHE(bdb);
+ TCMAP *nodec = bdb->nodec;
+ tcmapiterinit(nodec);
+ for(int i = 0; i < BDBCACHEOUT; i++){
+ int rsiz;
+ if(!tcbdbnodecacheout(bdb, (BDBNODE *)tcmapiterval(tcmapiternext(nodec, &rsiz), &rsiz)))
+ err = true;
+ }
+ if(clk) BDBUNLOCKCACHE(bdb);
+ }
+ return !err;
+}
+
+
+/* Purge dirty pages of caches for leaves and nodes.
+ `bdb' specifies the B+ tree database object. */
+static void tcbdbcachepurge(TCBDB *bdb){
+ bool clk = BDBLOCKCACHE(bdb);
+ int tsiz;
+ const char *tmp;
+ tcmapiterinit(bdb->leafc);
+ while((tmp = tcmapiternext(bdb->leafc, &tsiz)) != NULL){
+ int lsiz;
+ BDBLEAF *leaf = (BDBLEAF *)tcmapiterval(tmp, &lsiz);
+ if(!leaf->dirty) continue;
+ TCLIST *recs = leaf->recs;
+ int ln = TCLISTNUM(recs);
+ for(int i = 0; i < ln; i++){
+ BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, i);
+ free(recp->kbuf);
+ free(recp->vbuf);
+ if(recp->rest) tclistdel(recp->rest);
+ }
+ tclistdel(recs);
+ tcmapout(bdb->leafc, tmp, tsiz);
+ }
+ tcmapiterinit(bdb->nodec);
+ while((tmp = tcmapiternext(bdb->nodec, &tsiz)) != NULL){
+ int nsiz;
+ BDBNODE *node = (BDBNODE *)tcmapiterval(tmp, &nsiz);
+ if(!node->dirty) continue;
+ TCLIST *idxs = node->idxs;
+ int ln = TCLISTNUM(idxs);
+ for(int i = 0; i < ln; i++){
+ BDBIDX *idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+ free(idxp->kbuf);
+ }
+ tclistdel(idxs);
+ tcmapout(bdb->nodec, tmp, tsiz);
+ }
+ if(clk) BDBUNLOCKCACHE(bdb);
+}
+
+
+/* Open a database file and connect a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `path' specifies the path of the internal database file.
+ `omode' specifies the connection mode.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbopenimpl(TCBDB *bdb, const char *path, int omode){
+ assert(bdb && path);
+ int homode = HDBOREADER;
+ if(omode & BDBOWRITER){
+ homode = HDBOWRITER;
+ if(omode & BDBOCREAT) homode |= HDBOCREAT;
+ if(omode & BDBOTRUNC) homode |= HDBOTRUNC;
+ bdb->wmode = true;
+ } else {
+ bdb->wmode = false;
+ }
+ if(omode & BDBONOLCK) homode |= HDBONOLCK;
+ if(omode & BDBOLCKNB) homode |= HDBOLCKNB;
+ tchdbsettype(bdb->hdb, HDBTBTREE);
+ if(!tchdbopen(bdb->hdb, path, homode)) return false;
+ bdb->root = 0;
+ bdb->first = 0;
+ bdb->last = 0;
+ bdb->lnum = 0;
+ bdb->nnum = 0;
+ bdb->rnum = 0;
+ bdb->opaque = tchdbopaque(bdb->hdb);
+ bdb->leafc = tcmapnew2(bdb->lcnum * 2 + 1);
+ bdb->nodec = tcmapnew2(bdb->ncnum * 2 + 1);
+ if(bdb->wmode && tchdbrnum(bdb->hdb) < 1){
+ BDBLEAF *leaf = tcbdbleafnew(bdb, 0, 0);
+ bdb->root = leaf->id;
+ bdb->first = leaf->id;
+ bdb->last = leaf->id;
+ bdb->lnum = 1;
+ bdb->nnum = 0;
+ bdb->rnum = 0;
+ if(!bdb->cmp){
+ bdb->cmp = tcbdbcmplexical;
+ bdb->cmpop = NULL;
+ }
+ tcdumpmeta(bdb);
+ if(!tcbdbleafsave(bdb, leaf)){
+ tcmapdel(bdb->nodec);
+ tcmapdel(bdb->leafc);
+ tchdbclose(bdb->hdb);
+ return false;
+ }
+ }
+ tcloadmeta(bdb);
+ if(!bdb->cmp){
+ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ tcmapdel(bdb->nodec);
+ tcmapdel(bdb->leafc);
+ tchdbclose(bdb->hdb);
+ return false;
+ }
+ if(bdb->lmemb < BDBMINLMEMB || bdb->nmemb < BDBMINNMEMB ||
+ bdb->root < 1 || bdb->first < 1 || bdb->last < 1 ||
+ bdb->lnum < 0 || bdb->nnum < 0 || bdb->rnum < 0){
+ tcbdbsetecode(bdb, TCEMETA, __FILE__, __LINE__, __func__);
+ tcmapdel(bdb->nodec);
+ tcmapdel(bdb->leafc);
+ tchdbclose(bdb->hdb);
+ return false;
+ }
+ bdb->open = true;
+ uint8_t hopts = tchdbopts(bdb->hdb);
+ uint8_t opts = 0;
+ if(hopts & HDBTLARGE) opts |= BDBTLARGE;
+ if(hopts & HDBTDEFLATE) opts |= BDBTDEFLATE;
+ if(hopts & HDBTTCBS) opts |= BDBTTCBS;
+ bdb->opts = opts;
+ bdb->hleaf = 0;
+ bdb->lleaf = 0;
+ bdb->tran = false;
+ bdb->rbopaque = NULL;
+ return true;
+}
+
+
+/* Close a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbcloseimpl(TCBDB *bdb){
+ assert(bdb);
+ if(bdb->tran){
+ tcbdbcachepurge(bdb);
+ memcpy(bdb->opaque, bdb->rbopaque, BDBOPAQUESIZ);
+ tcloadmeta(bdb);
+ free(bdb->rbopaque);
+ bdb->tran = false;
+ bdb->rbopaque = NULL;
+ BDBUNLOCKTRAN(bdb);
+ }
+ bool err = false;
+ bdb->open = false;
+ const char *vbuf;
+ int vsiz;
+ TCMAP *leafc = bdb->leafc;
+ tcmapiterinit(leafc);
+ while((vbuf = tcmapiternext(leafc, &vsiz)) != NULL){
+ if(!tcbdbleafcacheout(bdb, (BDBLEAF *)tcmapiterval(vbuf, &vsiz))) err = true;
+ }
+ TCMAP *nodec = bdb->nodec;
+ tcmapiterinit(nodec);
+ while((vbuf = tcmapiternext(nodec, &vsiz)) != NULL){
+ if(!tcbdbnodecacheout(bdb, (BDBNODE *)tcmapiterval(vbuf, &vsiz))) err = true;
+ }
+ if(bdb->wmode) tcdumpmeta(bdb);
+ tcmapdel(bdb->nodec);
+ tcmapdel(bdb->leafc);
+ if(!tchdbclose(bdb->hdb)) err = true;
+ return !err;
+}
+
+
+/* Store a record into a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ `dmode' specifies behavior when the key overlaps.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbputimpl(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz,
+ int dmode){
+ assert(bdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ BDBLEAF *leaf = NULL;
+ if(bdb->hleaf < 1 || !(leaf = tcbdbgethistleaf(bdb, kbuf, ksiz))){
+ uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz);
+ if(pid < 1) return false;
+ if(!(leaf = tcbdbleafload(bdb, pid))) return false;
+ }
+ if(!tcbdbleafaddrec(bdb, leaf, dmode, kbuf, ksiz, vbuf, vsiz)){
+ tcbdbsetecode(bdb, TCEKEEP, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ int rnum = TCLISTNUM(leaf->recs);
+ if(rnum > bdb->lmemb ||
+ (bdb->lsmax > 0 && rnum > BDBMINLMEMB && (bdb->lschk++ & (0x8 - 1)) == 0 &&
+ tcbdbleafdatasize(leaf) > bdb->lsmax)){
+ bdb->lschk = 0;
+ BDBLEAF *newleaf = tcbdbleafdivide(bdb, leaf);
+ if(!newleaf) return false;
+ if(leaf->id == bdb->last) bdb->last = newleaf->id;
+ uint64_t heir = leaf->id;
+ uint64_t pid = newleaf->id;
+ BDBREC *recp = (BDBREC *)TCLISTVALPTR(newleaf->recs, 0);
+ int ksiz = recp->ksiz;
+ char *kbuf;
+ TCMEMDUP(kbuf, recp->kbuf, ksiz);
+ while(true){
+ BDBNODE *node;
+ if(bdb->hnum < 1){
+ node = tcbdbnodenew(bdb, heir);
+ tcbdbnodeaddidx(bdb, node, true, pid, kbuf, ksiz);
+ bdb->root = node->id;
+ free(kbuf);
+ break;
+ }
+ uint64_t parent = bdb->hist[--bdb->hnum];
+ if(!(node = tcbdbnodeload(bdb, parent))){
+ free(kbuf);
+ return false;
+ }
+ tcbdbnodeaddidx(bdb, node, false, pid, kbuf, ksiz);
+ free(kbuf);
+ TCLIST *idxs = node->idxs;
+ int ln = TCLISTNUM(idxs);
+ if(ln <= bdb->nmemb) break;
+ int mid = ln / 2;
+ BDBIDX *idxp = (BDBIDX *)TCLISTVALPTR(idxs, mid);
+ BDBNODE *newnode = tcbdbnodenew(bdb, idxp->pid);
+ heir = node->id;
+ pid = newnode->id;
+ TCMEMDUP(kbuf, idxp->kbuf, idxp->ksiz);
+ ksiz = idxp->ksiz;
+ for(int i = mid + 1; i < ln; i++){
+ idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+ tcbdbnodeaddidx(bdb, newnode, true, idxp->pid, idxp->kbuf, idxp->ksiz);
+ }
+ ln = TCLISTNUM(newnode->idxs);
+ for(int i = 0; i < ln; i++){
+ int rsiz;
+ idxp = (BDBIDX *)tclistpop(idxs, &rsiz);
+ free(idxp->kbuf);
+ free(idxp);
+ }
+ node->dirty = true;
+ }
+ if(bdb->capnum > 0 && bdb->rnum > bdb->capnum){
+ uint64_t xnum = bdb->rnum - bdb->capnum;
+ BDBCUR *cur = tcbdbcurnew(bdb);
+ tcbdbcurfirstimpl(cur);
+ while((xnum--) > 0){
+ if(!tcbdbcuroutimpl(cur)){
+ tcbdbcurdel(cur);
+ return false;
+ }
+ }
+ tcbdbcurdel(cur);
+ }
+ }
+ if(!bdb->tran && !tcbdbcacheadjust(bdb)) return false;
+ return true;
+}
+
+
+/* Remove a record of a B+ tree database object.
+ `hdb' specifies the B+ tree database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdboutimpl(TCBDB *bdb, const char *kbuf, int ksiz){
+ assert(bdb && kbuf && ksiz >= 0);
+ BDBLEAF *leaf = NULL;
+ if(bdb->hleaf < 1 || !(leaf = tcbdbgethistleaf(bdb, kbuf, ksiz))){
+ uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz);
+ if(pid < 1) return false;
+ if(!(leaf = tcbdbleafload(bdb, pid))) return false;
+ }
+ int ri;
+ BDBREC *recp = tcbdbsearchrec(bdb, leaf, kbuf, ksiz, &ri);
+ if(!recp){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ if(recp->rest){
+ free(recp->vbuf);
+ recp->vbuf = tclistshift(recp->rest, &(recp->vsiz));
+ if(TCLISTNUM(recp->rest) < 1){
+ tclistdel(recp->rest);
+ recp->rest = NULL;
+ }
+ } else {
+ free(recp->vbuf);
+ free(recp->kbuf);
+ int rsiz;
+ free(tclistremove(leaf->recs, ri, &rsiz));
+ }
+ leaf->dirty = true;
+ bdb->rnum--;
+ if(TCLISTNUM(leaf->recs) < 1 && bdb->hnum > 0 && !tcbdbleafkill(bdb, leaf)) return false;
+ if(!bdb->tran && !tcbdbcacheadjust(bdb)) return false;
+ return true;
+}
+
+
+/* Remove records of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdboutlist(TCBDB *bdb, const char *kbuf, int ksiz){
+ assert(bdb && kbuf && ksiz >= 0);
+ BDBLEAF *leaf = NULL;
+ if(bdb->hleaf < 1 || !(leaf = tcbdbgethistleaf(bdb, kbuf, ksiz))){
+ uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz);
+ if(pid < 1) return false;
+ if(!(leaf = tcbdbleafload(bdb, pid))) return false;
+ }
+ int ri;
+ BDBREC *recp = tcbdbsearchrec(bdb, leaf, kbuf, ksiz, &ri);
+ if(!recp){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ int rnum = 1;
+ if(recp->rest){
+ rnum += TCLISTNUM(recp->rest);
+ tclistdel(recp->rest);
+ }
+ free(recp->vbuf);
+ free(recp->kbuf);
+ int rsiz;
+ free(tclistremove(leaf->recs, ri, &rsiz));
+ leaf->dirty = true;
+ bdb->rnum -= rnum;
+ if(TCLISTNUM(leaf->recs) < 1 && bdb->hnum > 0 && !tcbdbleafkill(bdb, leaf)) return false;
+ if(!bdb->tran && !tcbdbcacheadjust(bdb)) return false;
+ return true;
+}
+
+
+/* Retrieve a record in a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the value of the corresponding
+ record. */
+static const char *tcbdbgetimpl(TCBDB *bdb, const char *kbuf, int ksiz, int *sp){
+ assert(bdb && kbuf && ksiz >= 0 && sp);
+ BDBLEAF *leaf = NULL;
+ if(bdb->hleaf < 1 || !(leaf = tcbdbgethistleaf(bdb, kbuf, ksiz))){
+ uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz);
+ if(pid < 1) return NULL;
+ if(!(leaf = tcbdbleafload(bdb, pid))) return NULL;
+ }
+ BDBREC *recp = tcbdbsearchrec(bdb, leaf, kbuf, ksiz, NULL);
+ if(!recp){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ return NULL;
+ }
+ *sp = recp->vsiz;
+ return recp->vbuf;
+}
+
+
+/* Get the number of records corresponding a key in a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is the number of the corresponding records, else, it is 0. */
+static int tcbdbgetnum(TCBDB *bdb, const char *kbuf, int ksiz){
+ assert(bdb && kbuf && ksiz >= 0);
+ BDBLEAF *leaf = NULL;
+ if(bdb->hleaf < 1 || !(leaf = tcbdbgethistleaf(bdb, kbuf, ksiz))){
+ uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz);
+ if(pid < 1) return 0;
+ if(!(leaf = tcbdbleafload(bdb, pid))) return 0;
+ }
+ BDBREC *recp = tcbdbsearchrec(bdb, leaf, kbuf, ksiz, NULL);
+ if(!recp){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return recp->rest ? TCLISTNUM(recp->rest) + 1 : 1;
+}
+
+
+/* Retrieve records in a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is a list object of the values of the corresponding records. */
+static TCLIST *tcbdbgetlist(TCBDB *bdb, const char *kbuf, int ksiz){
+ assert(bdb && kbuf && ksiz >= 0);
+ BDBLEAF *leaf = NULL;
+ if(bdb->hleaf < 1 || !(leaf = tcbdbgethistleaf(bdb, kbuf, ksiz))){
+ uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz);
+ if(pid < 1) return NULL;
+ if(!(leaf = tcbdbleafload(bdb, pid))) return NULL;
+ }
+ BDBREC *recp = tcbdbsearchrec(bdb, leaf, kbuf, ksiz, NULL);
+ if(!recp){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ return NULL;
+ }
+ TCLIST *vals;
+ TCLIST *rest = recp->rest;
+ if(rest){
+ int ln = TCLISTNUM(rest);
+ vals = tclistnew2(ln + 1);
+ TCLISTPUSH(vals, recp->vbuf, recp->vsiz);
+ for(int i = 0; i < ln; i++){
+ int vsiz;
+ const char *vbuf = tclistval(rest, i, &vsiz);
+ TCLISTPUSH(vals, vbuf, vsiz);
+ }
+ } else {
+ vals = tclistnew2(1);
+ TCLISTPUSH(vals, recp->vbuf, recp->vsiz);
+ }
+ return vals;
+}
+
+
+/* Get keys of ranged records in a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `bkbuf' specifies the pointer to the region of the key of the beginning border.
+ `bksiz' specifies the size of the region of the beginning key.
+ `binc' specifies whether the beginning border is inclusive or not.
+ `ekbuf' specifies the pointer to the region of the key of the ending border.
+ `eksiz' specifies the size of the region of the ending key.
+ `einc' specifies whether the ending border is inclusive or not.
+ `max' specifies the maximum number of keys to be fetched.
+ `keys' specifies a list object to store the result.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbrangeimpl(TCBDB *bdb, const char *bkbuf, int bksiz, bool binc,
+ const char *ekbuf, int eksiz, bool einc, int max, TCLIST *keys){
+ assert(bdb && keys);
+ bool err = false;
+ BDBCUR *cur = tcbdbcurnew(bdb);
+ if(bkbuf){
+ tcbdbcurjumpimpl(cur, bkbuf, bksiz, true);
+ } else {
+ tcbdbcurfirstimpl(cur);
+ }
+ BDBCMP cmp = bdb->cmp;
+ void *cmpop = bdb->cmpop;
+ const char *lbuf = NULL;
+ int lsiz = 0;
+ while(cur->id > 0){
+ const char *kbuf, *vbuf;
+ int ksiz, vsiz;
+ if(!tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){
+ if(tchdbecode(bdb->hdb) != TCEINVALID && tchdbecode(bdb->hdb) != TCENOREC) err = true;
+ break;
+ }
+ if(bkbuf && !binc){
+ if(cmp(kbuf, ksiz, bkbuf, bksiz, cmpop) == 0){
+ tcbdbcurnext(cur);
+ continue;
+ }
+ bkbuf = NULL;
+ }
+ if(ekbuf){
+ if(einc){
+ if(cmp(kbuf, ksiz, ekbuf, eksiz, cmpop) > 0) break;
+ } else {
+ if(cmp(kbuf, ksiz, ekbuf, eksiz, cmpop) >= 0) break;
+ }
+ }
+ if(!lbuf || lsiz != ksiz || memcmp(kbuf, lbuf, ksiz)){
+ TCLISTPUSH(keys, kbuf, ksiz);
+ if(max >= 0 && TCLISTNUM(keys) >= max) break;
+ lbuf = kbuf;
+ lsiz = ksiz;
+ }
+ tcbdbcurnextimpl(cur);
+ }
+ tcbdbcurdel(cur);
+ return !err;
+}
+
+
+/* Get forward matching keys in a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `pbuf' specifies the pointer to the region of the prefix.
+ `psiz' specifies the size of the region of the prefix.
+ `max' specifies the maximum number of keys to be fetched.
+ `keys' specifies a list object to store the result.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbrangefwm(TCBDB *bdb, const char *pbuf, int psiz, int max, TCLIST *keys){
+ assert(bdb && pbuf && psiz >= 0 && keys);
+ bool err = false;
+ if(max < 0) max = INT_MAX;
+ BDBCUR *cur = tcbdbcurnew(bdb);
+ tcbdbcurjumpimpl(cur, pbuf, psiz, true);
+ const char *lbuf = NULL;
+ int lsiz = 0;
+ while(cur->id > 0){
+ const char *kbuf, *vbuf;
+ int ksiz, vsiz;
+ if(!tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){
+ if(tchdbecode(bdb->hdb) != TCEINVALID && tchdbecode(bdb->hdb) != TCENOREC) err = true;
+ break;
+ }
+ if(ksiz < psiz || memcmp(kbuf, pbuf, psiz)) break;
+ if(!lbuf || lsiz != ksiz || memcmp(kbuf, lbuf, ksiz)){
+ TCLISTPUSH(keys, kbuf, ksiz);
+ if(TCLISTNUM(keys) >= max) break;
+ lbuf = kbuf;
+ lsiz = ksiz;
+ }
+ tcbdbcurnextimpl(cur);
+ }
+ tcbdbcurdel(cur);
+ return !err;
+}
+
+
+/* Optimize the file of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `lmemb' specifies the number of members in each leaf page.
+ `nmemb' specifies the number of members in each non-leaf page.
+ `bnum' specifies the number of elements of the bucket array.
+ `apow' specifies the size of record alignment by power of 2.
+ `fpow' specifies the maximum number of elements of the free block pool by power of 2.
+ `opts' specifies options by bitwise or.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdboptimizeimpl(TCBDB *bdb, int32_t lmemb, int32_t nmemb,
+ int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){
+ assert(bdb);
+ if(lmemb < 1) lmemb = bdb->lmemb;
+ if(nmemb < 1) nmemb = bdb->nmemb;
+ if(bnum < 1) bnum = tchdbrnum(bdb->hdb) * 2 + 1;
+ if(opts == UINT8_MAX) opts = bdb->opts;
+ const char *path = tchdbpath(bdb->hdb);
+ char *tpath = tcsprintf("%s%ctmp%c%llu", path, MYEXTCHR, MYEXTCHR, tchdbinode(bdb->hdb));
+ TCBDB *tbdb = tcbdbnew();
+ tcbdbsetcmpfunc(tbdb, bdb->cmp, bdb->cmpop);
+ tcbdbtune(tbdb, lmemb, nmemb, bnum, apow, fpow, opts);
+ tcbdbsetlsmax(tbdb, bdb->lsmax);
+ if(!tcbdbopen(tbdb, tpath, BDBOWRITER | BDBOCREAT | BDBOTRUNC)){
+ tcbdbsetecode(bdb, tcbdbecode(tbdb), __FILE__, __LINE__, __func__);
+ tcbdbdel(tbdb);
+ free(tpath);
+ return false;
+ }
+ bool err = false;
+ BDBCUR *cur = tcbdbcurnew(bdb);
+ tcbdbcurfirstimpl(cur);
+ const char *kbuf, *vbuf;
+ int ksiz, vsiz;
+ while(!err && cur->id > 0 && tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){
+ if(!tcbdbputdup(tbdb, kbuf, ksiz, vbuf, vsiz)){
+ tcbdbsetecode(bdb, tcbdbecode(tbdb), __FILE__, __LINE__, __func__);
+ err = true;
+ }
+ tcbdbcurnextimpl(cur);
+ }
+ tcbdbcurdel(cur);
+ if(!tcbdbclose(tbdb)){
+ tcbdbsetecode(bdb, tcbdbecode(tbdb), __FILE__, __LINE__, __func__);
+ err = true;
+ }
+ tcbdbdel(tbdb);
+ if(unlink(path) == -1){
+ tcbdbsetecode(bdb, TCEUNLINK, __FILE__, __LINE__, __func__);
+ err = true;
+ }
+ if(rename(tpath, path) == -1){
+ tcbdbsetecode(bdb, TCERENAME, __FILE__, __LINE__, __func__);
+ err = true;
+ }
+ free(tpath);
+ if(err) return false;
+ tpath = tcstrdup(path);
+ int omode = (tchdbomode(bdb->hdb) & ~BDBOCREAT) & ~BDBOTRUNC;
+ if(!tcbdbcloseimpl(bdb)){
+ free(tpath);
+ return false;
+ }
+ bool rv = tcbdbopenimpl(bdb, tpath, omode);
+ free(tpath);
+ return rv;
+}
+
+
+/* Remove all records of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbvanishimpl(TCBDB *bdb){
+ assert(bdb);
+ char *path = tcstrdup(tchdbpath(bdb->hdb));
+ int omode = tchdbomode(bdb->hdb);
+ bool err = false;
+ if(!tcbdbcloseimpl(bdb)) err = true;
+ if(!tcbdbopenimpl(bdb, path, BDBOTRUNC | omode)) err = true;
+ free(path);
+ return !err;
+}
+
+
+/* Lock a method of the B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `wr' specifies whether the lock is writer or not.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdblockmethod(TCBDB *bdb, bool wr){
+ assert(bdb);
+ if(wr ? pthread_rwlock_wrlock(bdb->mmtx) != 0 : pthread_rwlock_rdlock(bdb->mmtx) != 0){
+ tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ TCTESTYIELD();
+ return true;
+}
+
+
+/* Unlock a method of the B+ tree database object.
+ `hdb' specifies the B+ tree database object.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbunlockmethod(TCBDB *bdb){
+ assert(bdb);
+ if(pthread_rwlock_unlock(bdb->mmtx) != 0){
+ tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ TCTESTYIELD();
+ return true;
+}
+
+
+/* Lock the cache of the B+ tree database object.
+ `hdb' specifies the B+ tree database object.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdblockcache(TCBDB *bdb){
+ assert(bdb);
+ if(pthread_mutex_lock(bdb->cmtx) != 0){
+ tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ TCTESTYIELD();
+ return true;
+}
+
+
+/* Unlock the cache of the B+ tree database object.
+ `hdb' specifies the B+ tree database object.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbunlockcache(TCBDB *bdb){
+ assert(bdb);
+ if(pthread_mutex_unlock(bdb->cmtx) != 0){
+ tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ TCTESTYIELD();
+ return true;
+}
+
+
+/* Lock the transaction of the B+ tree database object.
+ `hdb' specifies the B+ tree database object.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdblocktran(TCBDB *bdb){
+ assert(bdb);
+ if(pthread_mutex_lock(bdb->tmtx) != 0){
+ tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ TCTESTYIELD();
+ return true;
+}
+
+
+/* Unlock the transaction of the B+ tree database object.
+ `hdb' specifies the B+ tree database object.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbunlocktran(TCBDB *bdb){
+ assert(bdb);
+ if(pthread_mutex_unlock(bdb->tmtx) != 0){
+ tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ TCTESTYIELD();
+ return true;
+}
+
+
+/* Move a cursor object to the first record.
+ `cur' specifies the cursor object.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbcurfirstimpl(BDBCUR *cur){
+ assert(cur);
+ cur->id = cur->bdb->first;
+ cur->kidx = 0;
+ cur->vidx = 0;
+ return tcbdbcuradjust(cur, true);
+}
+
+
+/* Move a cursor object to the last record.
+ `cur' specifies the cursor object.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbcurlastimpl(BDBCUR *cur){
+ assert(cur);
+ cur->id = cur->bdb->last;
+ cur->kidx = INT_MAX;
+ cur->vidx = INT_MAX;
+ return tcbdbcuradjust(cur, false);
+}
+
+
+/* Move a cursor object to around records corresponding a key.
+ `cur' specifies the cursor object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `forward' specifies whether the cursor is to be the front of records.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbcurjumpimpl(BDBCUR *cur, const char *kbuf, int ksiz, bool forward){
+ assert(cur && kbuf && ksiz >= 0);
+ TCBDB *bdb = cur->bdb;
+ uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz);
+ if(pid < 1){
+ cur->id = 0;
+ cur->kidx = 0;
+ cur->vidx = 0;
+ return false;
+ }
+ BDBLEAF *leaf = tcbdbleafload(bdb, pid);
+ if(!leaf){
+ cur->id = 0;
+ cur->kidx = 0;
+ cur->vidx = 0;
+ return false;
+ }
+ if(TCLISTNUM(leaf->recs) < 1){
+ cur->id = pid;
+ cur->kidx = 0;
+ cur->vidx = 0;
+ return forward ? tcbdbcurnextimpl(cur) : tcbdbcurprevimpl(cur);
+ }
+ int ri;
+ BDBREC *recp = tcbdbsearchrec(bdb, leaf, kbuf, ksiz, &ri);
+ if(recp){
+ cur->id = pid;
+ cur->kidx = ri;
+ if(forward){
+ cur->vidx = 0;
+ } else {
+ cur->vidx = recp->rest ? TCLISTNUM(recp->rest) : 0;
+ }
+ return true;
+ }
+ cur->id = leaf->id;
+ if(ri > 0 && ri >= TCLISTNUM(leaf->recs)) ri = TCLISTNUM(leaf->recs) - 1;
+ cur->kidx = ri;
+ recp = (BDBREC *)TCLISTVALPTR(leaf->recs, ri);
+ if(forward){
+ int rv;
+ if(bdb->cmp == tcbdbcmplexical){
+ TCCMPLEXICAL(rv, kbuf, ksiz, recp->kbuf, recp->ksiz);
+ } else {
+ rv = bdb->cmp(kbuf, ksiz, recp->kbuf, recp->ksiz, bdb->cmpop);
+ }
+ if(rv < 0) return true;
+ cur->vidx = recp->rest ? TCLISTNUM(recp->rest) : 0;
+ return tcbdbcurnextimpl(cur);
+ }
+ int rv;
+ if(bdb->cmp == tcbdbcmplexical){
+ TCCMPLEXICAL(rv, kbuf, ksiz, recp->kbuf, recp->ksiz);
+ } else {
+ rv = bdb->cmp(kbuf, ksiz, recp->kbuf, recp->ksiz, bdb->cmpop);
+ }
+ if(rv > 0) return true;
+ cur->vidx = 0;
+ return tcbdbcurprevimpl(cur);
+}
+
+
+/* Adjust a cursor object forward to the suitable record.
+ `cur' specifies the cursor object.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbcuradjust(BDBCUR *cur, bool forward){
+ assert(cur);
+ TCBDB *bdb = cur->bdb;
+ while(true){
+ if(cur->id < 1){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ cur->id = 0;
+ cur->kidx = 0;
+ cur->vidx = 0;
+ return false;
+ }
+ BDBLEAF *leaf = tcbdbleafload(bdb, cur->id);
+ if(!leaf) return false;
+ TCLIST *recs = leaf->recs;
+ int knum = TCLISTNUM(recs);
+ if(cur->kidx < 0){
+ if(forward){
+ cur->kidx = 0;
+ cur->vidx = 0;
+ } else {
+ cur->id = leaf->prev;
+ cur->kidx = INT_MAX;
+ cur->vidx = INT_MAX;
+ }
+ } else if(cur->kidx >= knum){
+ if(forward){
+ cur->id = leaf->next;
+ cur->kidx = 0;
+ cur->vidx = 0;
+ } else {
+ cur->kidx = knum - 1;
+ cur->vidx = INT_MAX;
+ }
+ } else {
+ BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, cur->kidx);
+ int vnum = recp->rest ? TCLISTNUM(recp->rest) + 1 : 1;
+ if(cur->vidx < 0){
+ if(forward){
+ cur->vidx = 0;
+ } else {
+ cur->kidx--;
+ cur->vidx = INT_MAX;
+ }
+ } else if(cur->vidx >= vnum){
+ if(forward){
+ cur->kidx++;
+ cur->vidx = 0;
+ if(cur->kidx >= knum){
+ cur->id = leaf->next;
+ cur->kidx = 0;
+ cur->vidx = 0;
+ } else {
+ break;
+ }
+ } else {
+ cur->vidx = vnum - 1;
+ if(cur->vidx >= 0) break;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+
+/* Move a cursor object to the previous record.
+ `cur' specifies the cursor object.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbcurprevimpl(BDBCUR *cur){
+ assert(cur);
+ cur->vidx--;
+ return tcbdbcuradjust(cur, false);
+}
+
+
+/* Move a cursor object to the next record.
+ `cur' specifies the cursor object.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbcurnextimpl(BDBCUR *cur){
+ assert(cur);
+ cur->vidx++;
+ return tcbdbcuradjust(cur, true);
+}
+
+
+/* Insert a record around a cursor object.
+ `cur' specifies the cursor object.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ `cpmode' specifies detail adjustment.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbcurputimpl(BDBCUR *cur, const char *vbuf, int vsiz, int cpmode){
+ assert(cur && vbuf && vsiz >= 0);
+ TCBDB *bdb = cur->bdb;
+ BDBLEAF *leaf = tcbdbleafload(bdb, cur->id);
+ if(!leaf) return false;
+ TCLIST *recs = leaf->recs;
+ if(cur->kidx >= TCLISTNUM(recs)){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, cur->kidx);
+ int vnum = recp->rest ? TCLISTNUM(recp->rest) + 1 : 1;
+ if(cur->vidx >= vnum){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ switch(cpmode){
+ case BDBCPCURRENT:
+ if(cur->vidx < 1){
+ if(vsiz > recp->vsiz) TCREALLOC(recp->vbuf, recp->vbuf, vsiz + 1);
+ memcpy(recp->vbuf, vbuf, vsiz);
+ recp->vbuf[vsiz] = '\0';
+ recp->vsiz = vsiz;
+ } else {
+ tclistover(recp->rest, cur->vidx - 1, vbuf, vsiz);
+ }
+ break;
+ case BDBCPBEFORE:
+ if(cur->vidx < 1){
+ if(!recp->rest) recp->rest = tclistnew();
+ tclistunshift(recp->rest, recp->vbuf, recp->vsiz);
+ if(vsiz > recp->vsiz) TCREALLOC(recp->vbuf, recp->vbuf, vsiz + 1);
+ memcpy(recp->vbuf, vbuf, vsiz);
+ recp->vbuf[vsiz] = '\0';
+ recp->vsiz = vsiz;
+ } else {
+ tclistinsert(recp->rest, cur->vidx - 1, vbuf, vsiz);
+ }
+ bdb->rnum++;
+ break;
+ case BDBCPAFTER:
+ if(!recp->rest) recp->rest = tclistnew();
+ tclistinsert(recp->rest, cur->vidx, vbuf, vsiz);
+ cur->vidx++;
+ bdb->rnum++;
+ break;
+ }
+ leaf->dirty = true;
+ return true;
+}
+
+
+/* Delete the record where a cursor object is.
+ `cur' specifies the cursor object.
+ If successful, the return value is true, else, it is false. */
+static bool tcbdbcuroutimpl(BDBCUR *cur){
+ assert(cur);
+ TCBDB *bdb = cur->bdb;
+ BDBLEAF *leaf = tcbdbleafload(bdb, cur->id);
+ if(!leaf) return false;
+ TCLIST *recs = leaf->recs;
+ if(cur->kidx >= TCLISTNUM(recs)){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, cur->kidx);
+ int vnum = recp->rest ? TCLISTNUM(recp->rest) + 1 : 1;
+ if(cur->vidx >= vnum){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ if(recp->rest){
+ if(cur->vidx < 1){
+ free(recp->vbuf);
+ recp->vbuf = tclistshift(recp->rest, &(recp->vsiz));
+ } else {
+ int vsiz;
+ free(tclistremove(recp->rest, cur->vidx - 1, &vsiz));
+ }
+ if(TCLISTNUM(recp->rest) < 1){
+ tclistdel(recp->rest);
+ recp->rest = NULL;
+ }
+ } else {
+ if(TCLISTNUM(recs) < 2 && bdb->hnum > 0){
+ uint64_t pid = tcbdbsearchleaf(bdb, recp->kbuf, recp->ksiz);
+ if(pid < 1) return false;
+ if(!(leaf = tcbdbleafload(bdb, pid))) return false;
+ if(!tcbdbleafkill(bdb, leaf)) return false;
+ }
+ free(recp->vbuf);
+ free(recp->kbuf);
+ int rsiz;
+ free(tclistremove(leaf->recs, cur->kidx, &rsiz));
+ }
+ bdb->rnum--;
+ leaf->dirty = true;
+ return tcbdbcuradjust(cur, true) || tchdbecode(bdb->hdb) == TCENOREC;
+}
+
+
+/* Get the key and the value of the current record of the cursor object.
+ `cur' specifies the cursor object.
+ `kbp' specifies the pointer to the variable into which the pointer to the region of the key is
+ assgined.
+ `ksp' specifies the pointer to the variable into which the size of the key region is assigned.
+ `vbp' specifies the pointer to the variable into which the pointer to the region of the value
+ is assgined.
+ `vsp' specifies the pointer to the variable into which the size of the value region is
+ assigned. */
+static bool tcbdbcurrecimpl(BDBCUR *cur, const char **kbp, int *ksp, const char **vbp, int *vsp){
+ assert(cur && kbp && ksp && vbp && vsp);
+ TCBDB *bdb = cur->bdb;
+ BDBLEAF *leaf = tcbdbleafload(bdb, cur->id);
+ if(!leaf) return false;
+ TCLIST *recs = leaf->recs;
+ if(cur->kidx >= TCLISTNUM(recs)){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, cur->kidx);
+ int vnum = recp->rest ? TCLISTNUM(recp->rest) + 1 : 1;
+ if(cur->vidx >= vnum){
+ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ *kbp = recp->kbuf;
+ *ksp = recp->ksiz;
+ if(cur->vidx > 0){
+ *vbp = tclistval(recp->rest, cur->vidx - 1, vsp);
+ } else {
+ *vbp = recp->vbuf;
+ *vsp = recp->vsiz;
+ }
+ return true;
+}
+
+
+
+/*************************************************************************************************
+ * debugging functions
+ *************************************************************************************************/
+
+
+/* Print meta data of the header into the debugging output.
+ `bdb' specifies the B+ tree database object. */
+void tcbdbprintmeta(TCBDB *bdb){
+ assert(bdb);
+ int dbgfd = tchdbdbgfd(bdb->hdb);
+ if(dbgfd < 0) return;
+ char buf[BDBPAGEBUFSIZ];
+ char *wp = buf;
+ wp += sprintf(wp, "META:");
+ wp += sprintf(wp, " mmtx=%p", (void *)bdb->mmtx);
+ wp += sprintf(wp, " cmtx=%p", (void *)bdb->cmtx);
+ wp += sprintf(wp, " tmtx=%p", (void *)bdb->tmtx);
+ wp += sprintf(wp, " hdb=%p", (void *)bdb->hdb);
+ wp += sprintf(wp, " opaque=%p", (void *)bdb->opaque);
+ wp += sprintf(wp, " open=%d", bdb->open);
+ wp += sprintf(wp, " wmode=%d", bdb->wmode);
+ wp += sprintf(wp, " lmemb=%u", bdb->lmemb);
+ wp += sprintf(wp, " nmemb=%u", bdb->nmemb);
+ wp += sprintf(wp, " opts=%u", bdb->opts);
+ wp += sprintf(wp, " root=%llu", (unsigned long long)bdb->root);
+ wp += sprintf(wp, " first=%llu", (unsigned long long)bdb->first);
+ wp += sprintf(wp, " last=%llu", (unsigned long long)bdb->last);
+ wp += sprintf(wp, " lnum=%llu", (unsigned long long)bdb->lnum);
+ wp += sprintf(wp, " nnum=%llu", (unsigned long long)bdb->nnum);
+ wp += sprintf(wp, " rnum=%llu", (unsigned long long)bdb->rnum);
+ wp += sprintf(wp, " leafc=%p", (void *)bdb->leafc);
+ wp += sprintf(wp, " nodec=%p", (void *)bdb->nodec);
+ wp += sprintf(wp, " cmp=%p", (void *)(intptr_t)bdb->cmp);
+ wp += sprintf(wp, " cmpop=%p", (void *)bdb->cmpop);
+ wp += sprintf(wp, " lcnum=%u", bdb->lcnum);
+ wp += sprintf(wp, " ncnum=%u", bdb->ncnum);
+ wp += sprintf(wp, " lsmax=%u", bdb->lsmax);
+ wp += sprintf(wp, " lschk=%u", bdb->lschk);
+ wp += sprintf(wp, " capnum=%llu", (unsigned long long)bdb->capnum);
+ wp += sprintf(wp, " hist=%p", (void *)bdb->hist);
+ wp += sprintf(wp, " hnum=%d", bdb->hnum);
+ wp += sprintf(wp, " hleaf=%llu", (unsigned long long)bdb->hleaf);
+ wp += sprintf(wp, " lleaf=%llu", (unsigned long long)bdb->lleaf);
+ wp += sprintf(wp, " tran=%d", bdb->tran);
+ wp += sprintf(wp, " rbopaque=%p", (void *)bdb->rbopaque);
+ wp += sprintf(wp, " cnt_saveleaf=%lld", (long long)bdb->cnt_saveleaf);
+ wp += sprintf(wp, " cnt_loadleaf=%lld", (long long)bdb->cnt_loadleaf);
+ wp += sprintf(wp, " cnt_killleaf=%lld", (long long)bdb->cnt_killleaf);
+ wp += sprintf(wp, " cnt_adjleafc=%lld", (long long)bdb->cnt_adjleafc);
+ wp += sprintf(wp, " cnt_savenode=%lld", (long long)bdb->cnt_savenode);
+ wp += sprintf(wp, " cnt_loadnode=%lld", (long long)bdb->cnt_loadnode);
+ wp += sprintf(wp, " cnt_adjnodec=%lld", (long long)bdb->cnt_adjnodec);
+ *(wp++) = '\n';
+ tcwrite(dbgfd, buf, wp - buf);
+}
+
+
+/* Print records of a leaf object into the debugging output.
+ `bdb' specifies the B+ tree database object.
+ `leaf' specifies the leaf object. */
+void tcbdbprintleaf(TCBDB *bdb, BDBLEAF *leaf){
+ assert(bdb && leaf);
+ int dbgfd = tchdbdbgfd(bdb->hdb);
+ TCLIST *recs = leaf->recs;
+ if(dbgfd < 0) return;
+ char buf[BDBPAGEBUFSIZ];
+ char *wp = buf;
+ wp += sprintf(wp, "LEAF:");
+ wp += sprintf(wp, " id:%llx", (unsigned long long)leaf->id);
+ wp += sprintf(wp, " prev:%llx", (unsigned long long)leaf->prev);
+ wp += sprintf(wp, " next:%llx", (unsigned long long)leaf->next);
+ wp += sprintf(wp, " dirty:%d", leaf->dirty);
+ wp += sprintf(wp, " dead:%d", leaf->dead);
+ wp += sprintf(wp, " rnum:%d", TCLISTNUM(recs));
+ *(wp++) = ' ';
+ for(int i = 0; i < TCLISTNUM(recs); i++){
+ tcwrite(dbgfd, buf, wp - buf);
+ wp = buf;
+ BDBREC *recp = (BDBREC *)TCLISTVALPTR(recs, i);
+ wp += sprintf(wp, " [%s:%s]", recp->kbuf, recp->vbuf);
+ TCLIST *rest = recp->rest;
+ if(rest){
+ for(int j = 0; j < TCLISTNUM(rest); j++){
+ wp += sprintf(wp, ":%s", (char *)TCLISTVALPTR(rest, j));
+ }
+ }
+ }
+ *(wp++) = '\n';
+ tcwrite(dbgfd, buf, wp - buf);
+}
+
+
+/* Print indexes of a node object into the debugging output.
+ `bdb' specifies the B+ tree database object.
+ `node' specifies the node object. */
+void tcbdbprintnode(TCBDB *bdb, BDBNODE *node){
+ assert(bdb && node);
+ int dbgfd = tchdbdbgfd(bdb->hdb);
+ TCLIST *idxs = node->idxs;
+ if(dbgfd < 0) return;
+ char buf[BDBPAGEBUFSIZ];
+ char *wp = buf;
+ wp += sprintf(wp, "NODE:");
+ wp += sprintf(wp, " id:%llx", (unsigned long long)node->id);
+ wp += sprintf(wp, " heir:%llx", (unsigned long long)node->heir);
+ wp += sprintf(wp, " dirty:%d", node->dirty);
+ wp += sprintf(wp, " rnum:%d", TCLISTNUM(idxs));
+ *(wp++) = ' ';
+ for(int i = 0; i < TCLISTNUM(idxs); i++){
+ tcwrite(dbgfd, buf, wp - buf);
+ wp = buf;
+ BDBIDX *idxp = (BDBIDX *)TCLISTVALPTR(idxs, i);
+ wp += sprintf(wp, " [%llx:%s]", (unsigned long long)idxp->pid, idxp->kbuf);
+ }
+ *(wp++) = '\n';
+ tcwrite(dbgfd, buf, wp - buf);
+}
+
+
+
+// END OF FILE
--- /dev/null
+/*************************************************************************************************
+ * The B+ tree database API of Tokyo Cabinet
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _TCBDB_H /* duplication check */
+#define _TCBDB_H
+
+#if defined(__cplusplus)
+#define __TCBDB_CLINKAGEBEGIN extern "C" {
+#define __TCBDB_CLINKAGEEND }
+#else
+#define __TCBDB_CLINKAGEBEGIN
+#define __TCBDB_CLINKAGEEND
+#endif
+__TCBDB_CLINKAGEBEGIN
+
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <time.h>
+#include <tcutil.h>
+#include <tchdb.h>
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+/* type of the pointer to a comparison function.
+ `aptr' specifies the pointer to the region of one key.
+ `asiz' specifies the size of the region of one key.
+ `bptr' specifies the pointer to the region of the other key.
+ `bsiz' specifies the size of the region of the other key.
+ `op' specifies the pointer to the optional opaque object.
+ The return value is positive if the former is big, negative if the latter is big, 0 if both
+ are equivalent. */
+typedef int (*BDBCMP)(const char *aptr, int asiz, const char *bptr, int bsiz, void *op);
+
+typedef struct { /* type of structure for a B+ tree database */
+ void *mmtx; /* mutex for method */
+ void *cmtx; /* mutex for cache */
+ void *tmtx; /* mutex for transaction */
+ TCHDB *hdb; /* internal database object */
+ char *opaque; /* opaque buffer */
+ bool open; /* whether the internal database is opened */
+ bool wmode; /* whether to be writable */
+ uint32_t lmemb; /* number of members in each leaf */
+ uint32_t nmemb; /* number of members in each node */
+ uint8_t opts; /* options */
+ uint64_t root; /* ID number of the root page */
+ uint64_t first; /* ID number of the first leaf */
+ uint64_t last; /* ID number of the last leaf */
+ uint64_t lnum; /* number of leaves */
+ uint64_t nnum; /* number of nodes */
+ uint64_t rnum; /* number of records */
+ TCMAP *leafc; /* cache for leaves */
+ TCMAP *nodec; /* cache for nodes */
+ BDBCMP cmp; /* pointer to the comparison function */
+ void *cmpop; /* opaque object for the comparison function */
+ uint32_t lcnum; /* max number of cached leaves */
+ uint32_t ncnum; /* max number of cached nodes */
+ uint32_t lsmax; /* max size of each leaf */
+ uint32_t lschk; /* counter for leaf size checking */
+ uint64_t capnum; /* capacity number of records */
+ uint64_t *hist; /* history array of visited nodes */
+ int hnum; /* number of element of the history array */
+ uint64_t hleaf; /* ID number of the leaf referred by the history */
+ uint64_t lleaf; /* ID number of the last visited leaf */
+ bool tran; /* whether in the transaction */
+ char *rbopaque; /* opaque for rollback */
+ int64_t cnt_saveleaf; /* tesing counter for leaf save times */
+ int64_t cnt_loadleaf; /* tesing counter for leaf load times */
+ int64_t cnt_killleaf; /* tesing counter for leaf kill times */
+ int64_t cnt_adjleafc; /* tesing counter for node cache adjust times */
+ int64_t cnt_savenode; /* tesing counter for node save times */
+ int64_t cnt_loadnode; /* tesing counter for node load times */
+ int64_t cnt_adjnodec; /* tesing counter for node cache adjust times */
+} TCBDB;
+
+enum { /* enumeration for additional flags */
+ BDBFOPEN = HDBFOPEN, /* whether opened */
+ BDBFFATAL = HDBFFATAL /* whetehr with fatal error */
+};
+
+enum { /* enumeration for tuning options */
+ BDBTLARGE = 1 << 0, /* use 64-bit bucket array */
+ BDBTDEFLATE = 1 << 1, /* compress each page with Deflate */
+ BDBTTCBS = 1 << 2 /* compress each page with TCBS */
+};
+
+enum { /* enumeration for open modes */
+ BDBOREADER = 1 << 0, /* open as a reader */
+ BDBOWRITER = 1 << 1, /* open as a writer */
+ BDBOCREAT = 1 << 2, /* writer creating */
+ BDBOTRUNC = 1 << 3, /* writer truncating */
+ BDBONOLCK = 1 << 4, /* open without locking */
+ BDBOLCKNB = 1 << 5 /* lock without blocking */
+};
+
+typedef struct { /* type of structure for a B+ tree cursor */
+ TCBDB *bdb; /* database object */
+ uint64_t id; /* ID number of the leaf */
+ int32_t kidx; /* number of the key */
+ int32_t vidx; /* number of the value */
+} BDBCUR;
+
+enum { /* enumeration for cursor put mode */
+ BDBCPCURRENT, /* current */
+ BDBCPBEFORE, /* before */
+ BDBCPAFTER /* after */
+};
+
+
+/* Get the message string corresponding to an error code.
+ `ecode' specifies the error code.
+ The return value is the message string of the error code. */
+const char *tcbdberrmsg(int ecode);
+
+
+/* Create a B+ tree database object.
+ The return value is the new B+ tree database object. */
+TCBDB *tcbdbnew(void);
+
+
+/* Delete a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ If the database is not closed, it is closed implicitly. Note that the deleted object and its
+ derivatives can not be used anymore. */
+void tcbdbdel(TCBDB *bdb);
+
+
+/* Get the last happened error code of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ The return value is the last happened error code.
+ The following error code is defined: `TCESUCCESS' for success, `TCETHREAD' for threading
+ error, `TCEINVALID' for invalid operation, `TCENOFILE' for file not found, `TCENOPERM' for no
+ permission, `TCEMETA' for invalid meta data, `TCERHEAD' for invalid record header, `TCEOPEN'
+ for open error, `TCECLOSE' for close error, `TCETRUNC' for trunc error, `TCESYNC' for sync
+ error, `TCESTAT' for stat error, `TCESEEK' for seek error, `TCEREAD' for read error,
+ `TCEWRITE' for write error, `TCEMMAP' for mmap error, `TCELOCK' for lock error, `TCEUNLINK'
+ for unlink error, `TCERENAME' for rename error, `TCEMKDIR' for mkdir error, `TCERMDIR' for
+ rmdir error, `TCEKEEP' for existing record, `TCENOREC' for no record found, and `TCEMISC' for
+ miscellaneous error. */
+int tcbdbecode(TCBDB *bdb);
+
+
+/* Set mutual exclusion control of a B+ tree database object for threading.
+ `bdb' specifies the B+ tree database object which is not opened.
+ If successful, the return value is true, else, it is false.
+ Note that the mutual exclusion control is needed if the object is shared by plural threads and
+ this function should should be called before the database is opened. */
+bool tcbdbsetmutex(TCBDB *bdb);
+
+
+/* Set the custom comparison function of a B+ tree database object.
+ `bdb' specifies the B+ tree database object which is not opened.
+ `cmp' specifies the pointer to the custom comparison function.
+ `cmpop' specifies an arbitrary pointer to be given as a parameter of the comparison function.
+ If it is not needed, `NULL' can be specified.
+ If successful, the return value is true, else, it is false.
+ The default comparison function compares keys of two records by lexical order. The functions
+ `tcbdbcmplexical' (dafault), `tcbdbcmpdecimal', `tcbdbcmpint32', and `tcbdbcmpint64' are
+ built-in. Note that the comparison function should be set before the database is opened.
+ Moreover, user-defined comparison functions should be set every time the database is being
+ opened. */
+bool tcbdbsetcmpfunc(TCBDB *bdb, BDBCMP cmp, void *cmpop);
+
+
+/* Set the tuning parameters of a B+ tree database object.
+ `bdb' specifies the B+ tree database object which is not opened.
+ `lmemb' specifies the number of members in each leaf page. If it is not more than 0, the
+ default value is specified. The default value is 128.
+ `nmemb' specifies the number of members in each non-leaf page. If it is not more than 0, the
+ default value is specified. The default value is 256.
+ `bnum' specifies the number of elements of the bucket array. If it is not more than 0, the
+ default value is specified. The default value is 16381. Suggested size of the bucket array
+ is about from 1 to 4 times of the number of all pages to be stored.
+ `apow' specifies the size of record alignment by power of 2. If it is negative, the default
+ value is specified. The default value is 8 standing for 2^8=256.
+ `fpow' specifies the maximum number of elements of the free block pool by power of 2. If it
+ is negative, the default value is specified. The default value is 10 standing for 2^10=1024.
+ `opts' specifies options by bitwise or: `BDBTLARGE' specifies that the size of the database
+ can be larger than 2GB by using 64-bit bucket array, `BDBTDEFLATE' specifies that each page
+ is compressed with Deflate encoding, `BDBTTCBS' specifies that each page is compressed with
+ TCBS encoding.
+ If successful, the return value is true, else, it is false.
+ Note that the tuning parameters should be set before the database is opened. */
+bool tcbdbtune(TCBDB *bdb, int32_t lmemb, int32_t nmemb,
+ int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
+
+/* Set the caching parameters of a B+ tree database object.
+ `bdb' specifies the B+ tree database object which is not opened.
+ `lcnum' specifies the maximum number of leaf nodes to be cached. If it is not more than 0,
+ the default value is specified. The default value is 1024.
+ `ncnum' specifies the maximum number of non-leaf nodes to be cached. If it is not more than 0,
+ the default value is specified. The default value is 512.
+ If successful, the return value is true, else, it is false.
+ Note that the caching parameters should be set before the database is opened. */
+bool tcbdbsetcache(TCBDB *bdb, int32_t lcnum, int32_t ncnum);
+
+
+/* Open a database file and connect a B+ tree database object.
+ `bdb' specifies the B+ tree database object which is not opened.
+ `path' specifies the path of the database file.
+ `omode' specifies the connection mode: `BDBOWRITER' as a writer, `BDBOREADER' as a reader.
+ If the mode is `BDBOWRITER', the following may be added by bitwise or: `BDBOCREAT', which
+ means it creates a new database if not exist, `BDBOTRUNC', which means it creates a new database
+ regardless if one exists. Both of `BDBOREADER' and `BDBOWRITER' can be added to by
+ bitwise or: `BDBONOLCK', which means it opens the database file without file locking, or
+ `BDBOLCKNB', which means locking is performed without blocking.
+ If successful, the return value is true, else, it is false. */
+bool tcbdbopen(TCBDB *bdb, const char *path, int omode);
+
+
+/* Close a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ If successful, the return value is true, else, it is false.
+ Update of a database is assured to be written when the database is closed. If a writer opens
+ a database but does not close it appropriately, the database will be broken. */
+bool tcbdbclose(TCBDB *bdb);
+
+
+/* Store a record into a B+ tree database object.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, it is overwritten. */
+bool tcbdbput(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a string record into a B+ tree database object.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ `kstr' specifies the string of the key.
+ `vstr' specifies the string of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, it is overwritten. */
+bool tcbdbput2(TCBDB *bdb, const char *kstr, const char *vstr);
+
+
+/* Store a new record into a B+ tree database object.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, this function has no effect. */
+bool tcbdbputkeep(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a new string record into a B+ tree database object.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ `kstr' specifies the string of the key.
+ `vstr' specifies the string of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, this function has no effect. */
+bool tcbdbputkeep2(TCBDB *bdb, const char *kstr, const char *vstr);
+
+
+/* Concatenate a value at the end of the existing record in a B+ tree database object.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If successful, the return value is true, else, it is false.
+ If there is no corresponding record, a new record is created. */
+bool tcbdbputcat(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Concatenate a string value at the end of the existing record in a B+ tree database object.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ `kstr' specifies the string of the key.
+ `vstr' specifies the string of the value.
+ If successful, the return value is true, else, it is false.
+ If there is no corresponding record, a new record is created. */
+bool tcbdbputcat2(TCBDB *bdb, const char *kstr, const char *vstr);
+
+
+/* Store a record into a B+ tree database object with allowing duplication of keys.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, the new record is placed after the
+ existing one. */
+bool tcbdbputdup(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a string record into a B+ tree database object with allowing duplication of keys.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ `kstr' specifies the string of the key.
+ `vstr' specifies the string of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, the new record is placed after the
+ existing one. */
+bool tcbdbputdup2(TCBDB *bdb, const char *kstr, const char *vstr);
+
+
+/* Store records into a B+ tree database object with allowing duplication of keys.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ `kbuf' specifies the pointer to the region of the common key.
+ `ksiz' specifies the size of the region of the common key.
+ `vals' specifies a list object containing values.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, the new records are placed after the
+ existing one. */
+bool tcbdbputdup3(TCBDB *bdb, const void *kbuf, int ksiz, const TCLIST *vals);
+
+
+/* Remove a record of a B+ tree database object.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is true, else, it is false.
+ If the key of duplicated records is specified, the first one is selected. */
+bool tcbdbout(TCBDB *bdb, const void *kbuf, int ksiz);
+
+
+/* Remove a string record of a B+ tree database object.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ `kstr' specifies the string of the key.
+ If successful, the return value is true, else, it is false.
+ If the key of duplicated records is specified, the first one is selected. */
+bool tcbdbout2(TCBDB *bdb, const char *kstr);
+
+
+/* Remove records of a B+ tree database object.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is true, else, it is false.
+ If the key of duplicated records is specified, all of them are removed. */
+bool tcbdbout3(TCBDB *bdb, const void *kbuf, int ksiz);
+
+
+/* Retrieve a record in a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the value of the corresponding
+ record. `NULL' is returned if no record corresponds.
+ If the key of duplicated records is specified, the first one is selected. Because an
+ additional zero code is appended at the end of the region of the return value, the return
+ value can be treated as a character string. Because the region of the return value is
+ allocated with the `malloc' call, it should be released with the `free' call when it is no
+ longer in use. */
+void *tcbdbget(TCBDB *bdb, const void *kbuf, int ksiz, int *sp);
+
+
+/* Retrieve a string record in a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `kstr' specifies the string of the key.
+ If successful, the return value is the string of the value of the corresponding record.
+ `NULL' is returned if no record corresponds.
+ If the key of duplicated records is specified, the first one is selected. Because the region
+ of the return value is allocated with the `malloc' call, it should be released with the `free'
+ call when it is no longer in use. */
+char *tcbdbget2(TCBDB *bdb, const char *kstr);
+
+
+/* Retrieve a record in a B+ tree database object as a volatile buffer.
+ `bdb' specifies the B+ tree database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the value of the corresponding
+ record. `NULL' is returned if no record corresponds.
+ If the key of duplicated records is specified, the first one is selected. Because an
+ additional zero code is appended at the end of the region of the return value, the return
+ value can be treated as a character string. Because the region of the return value is
+ volatile and it may be spoiled by another operation of the database, the data should be copied
+ into another involatile buffer immediately. */
+const void *tcbdbget3(TCBDB *bdb, const void *kbuf, int ksiz, int *sp);
+
+
+/* Retrieve records in a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is a list object of the values of the corresponding records.
+ `NULL' is returned if no record corresponds.
+ Because the object of the return value is created with the function `tclistnew', it should
+ be deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcbdbget4(TCBDB *bdb, const void *kbuf, int ksiz);
+
+
+/* Get the number of records corresponding a key in a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is the number of the corresponding records, else, it is 0. */
+int tcbdbvnum(TCBDB *bdb, const void *kbuf, int ksiz);
+
+
+/* Get the number of records corresponding a string key in a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `kstr' specifies the string of the key.
+ If successful, the return value is the number of the corresponding records, else, it is 0. */
+int tcbdbvnum2(TCBDB *bdb, const char *kstr);
+
+
+/* Get the size of the value of a record in a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is the size of the value of the corresponding record, else,
+ it is -1.
+ If the key of duplicated records is specified, the first one is selected. */
+int tcbdbvsiz(TCBDB *bdb, const void *kbuf, int ksiz);
+
+
+/* Get the size of the value of a string record in a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `kstr' specifies the string of the key.
+ If successful, the return value is the size of the value of the corresponding record, else,
+ it is -1.
+ If the key of duplicated records is specified, the first one is selected. */
+int tcbdbvsiz2(TCBDB *bdb, const char *kstr);
+
+
+/* Get keys of ranged records in a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `bkbuf' specifies the pointer to the region of the key of the beginning border. If it is
+ `NULL', the first record is specified.
+ `bksiz' specifies the size of the region of the beginning key.
+ `binc' specifies whether the beginning border is inclusive or not.
+ `ekbuf' specifies the pointer to the region of the key of the ending border. If it is `NULL',
+ the last record is specified.
+ `eksiz' specifies the size of the region of the ending key.
+ `einc' specifies whether the ending border is inclusive or not.
+ `max' specifies the maximum number of keys to be fetched. If it is negative, no limit is
+ specified.
+ The return value is a list object of the keys of the corresponding records. This function
+ does never fail and return an empty list even if no record corresponds.
+ Because the object of the return value is created with the function `tclistnew', it should
+ be deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcbdbrange(TCBDB *bdb, const void *bkbuf, int bksiz, bool binc,
+ const void *ekbuf, int eksiz, bool einc, int max);
+
+
+/* Get string keys of ranged records in a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `bkstr' specifies the string of the key of the beginning border. If it is `NULL', the first
+ record is specified.
+ `binc' specifies whether the beginning border is inclusive or not.
+ `ekstr' specifies the string of the key of the ending border. If it is `NULL', the last
+ record is specified.
+ `einc' specifies whether the ending border is inclusive or not.
+ `max' specifies the maximum number of keys to be fetched. If it is negative, no limit is
+ specified.
+ The return value is a list object of the keys of the corresponding records. This function
+ does never fail and return an empty list even if no record corresponds.
+ Because the object of the return value is created with the function `tclistnew', it should
+ be deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcbdbrange2(TCBDB *bdb, const char *bkstr, bool binc,
+ const char *ekstr, bool einc, int max);
+
+
+/* Get forward matching keys in a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `pbuf' specifies the pointer to the region of the prefix.
+ `psiz' specifies the size of the region of the prefix.
+ `max' specifies the maximum number of keys to be fetched. If it is negative, no limit is
+ specified.
+ The return value is a list object of the corresponding keys. This function does never fail
+ and return an empty list even if no key corresponds.
+ Because the object of the return value is created with the function `tclistnew', it should be
+ deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcbdbfwmkeys(TCBDB *bdb, const void *pbuf, int psiz, int max);
+
+
+/* Get forward matching string keys in a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `pstr' specifies the string of the prefix.
+ `max' specifies the maximum number of keys to be fetched. If it is negative, no limit is
+ specified.
+ The return value is a list object of the corresponding keys. This function does never fail
+ and return an empty list even if no key corresponds.
+ Because the object of the return value is created with the function `tclistnew', it should be
+ deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcbdbfwmkeys2(TCBDB *bdb, const char *pstr, int max);
+
+
+/* Synchronize updated contents of a B+ tree database object with the file and the device.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ If successful, the return value is true, else, it is false.
+ This function is useful when another process connects the same database file. */
+bool tcbdbsync(TCBDB *bdb);
+
+
+/* Optimize the file of a B+ tree database object.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ `lmemb' specifies the number of members in each leaf page. If it is not more than 0, the
+ current setting is not changed.
+ `nmemb' specifies the number of members in each non-leaf page. If it is not more than 0, the
+ current setting is not changed.
+ `bnum' specifies the number of elements of the bucket array. If it is not more than 0, the
+ default value is specified. The default value is two times of the number of pages.
+ `apow' specifies the size of record alignment by power of 2. If it is negative, the current
+ setting is not changed.
+ `fpow' specifies the maximum number of elements of the free block pool by power of 2. If it
+ is negative, the current setting is not changed.
+ `opts' specifies options by bitwise or: `BDBTLARGE' specifies that the size of the database
+ can be larger than 2GB by using 64-bit bucket array, `BDBTDEFLATE' specifies that each record
+ is compressed with Deflate encoding, `BDBTTCBS' specifies that each page is compressed with
+ TCBS encoding. If it is `UINT8_MAX', the current setting is not changed.
+ If successful, the return value is true, else, it is false.
+ This function is useful to reduce the size of the database file with data fragmentation by
+ successive updating. */
+bool tcbdboptimize(TCBDB *bdb, int32_t lmemb, int32_t nmemb,
+ int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
+
+/* Remove all records of a B+ tree database object.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ If successful, the return value is true, else, it is false. */
+bool tcbdbvanish(TCBDB *bdb);
+
+
+/* Copy the database file of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `path' specifies the path of the destination file. If it begins with `@', the trailing
+ substring is executed as a command line.
+ If successful, the return value is true, else, it is false. False is returned if the executed
+ command returns non-zero code.
+ The database file is assured to be kept synchronized and not modified while the copying or
+ executing operation is in progress. So, this function is useful to create a backup file of
+ the database file. */
+bool tcbdbcopy(TCBDB *bdb, const char *path);
+
+
+/* Begin the transaction of a B+ tree database object.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ If successful, the return value is true, else, it is false.
+ The database is locked by the thread while the transaction so that only one transaction can be
+ activated with a database object at the same time. Thus, the serializable isolation level is
+ assumed if every database operation is performed in the transaction. If the database is
+ closed during transaction, the transaction is aborted implicitly. */
+bool tcbdbtranbegin(TCBDB *bdb);
+
+
+/* Commit the transaction of a B+ tree database object.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ If successful, the return value is true, else, it is false.
+ Update in the transaction is fixed when it is committed successfully. */
+bool tcbdbtrancommit(TCBDB *bdb);
+
+
+/* Abort the transaction of a B+ tree database object.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ If successful, the return value is true, else, it is false.
+ Update in the transaction is discarded when it is aborted. The state of the database is
+ rollbacked to before transaction. */
+bool tcbdbtranabort(TCBDB *bdb);
+
+
+/* Get the file path of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ The return value is the path of the database file or `NULL' if the object does not connect to
+ any database file. */
+const char *tcbdbpath(TCBDB *bdb);
+
+
+/* Get the number of records of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ The return value is the number of records or 0 if the object does not connect to any database
+ file. */
+uint64_t tcbdbrnum(TCBDB *bdb);
+
+
+/* Get the size of the database file of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ The return value is the size of the database file or 0 if the object does not connect to any
+ database file. */
+uint64_t tcbdbfsiz(TCBDB *bdb);
+
+
+/* Create a cursor object.
+ `bdb' specifies the B+ tree database object.
+ The return value is the new cursor object.
+ Note that the cursor is available only after initialization with the `tcbdbcurfirst' or the
+ `tcbdbcurjump' functions and so on. Moreover, the position of the cursor will be indefinite
+ when the database is updated after the initialization of the cursor. */
+BDBCUR *tcbdbcurnew(TCBDB *bdb);
+
+
+/* Delete a cursor object.
+ `cur' specifies the cursor object. */
+void tcbdbcurdel(BDBCUR *cur);
+
+
+/* Move a cursor object to the first record.
+ `cur' specifies the cursor object.
+ If successful, the return value is true, else, it is false. False is returned if there is
+ no record in the database. */
+bool tcbdbcurfirst(BDBCUR *cur);
+
+
+/* Move a cursor object to the last record.
+ `cur' specifies the cursor object.
+ If successful, the return value is true, else, it is false. False is returned if there is
+ no record in the database. */
+bool tcbdbcurlast(BDBCUR *cur);
+
+
+/* Move a cursor object to the front of records corresponding a key.
+ `cur' specifies the cursor object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is true, else, it is false. False is returned if there is
+ no record corresponding the condition.
+ The cursor is set to the first record corresponding the key or the next substitute if
+ completely matching record does not exist. */
+bool tcbdbcurjump(BDBCUR *cur, const void *kbuf, int ksiz);
+
+
+/* Move a cursor object to the front of records corresponding a key string.
+ `cur' specifies the cursor object.
+ `kstr' specifies the string of the key.
+ If successful, the return value is true, else, it is false. False is returned if there is
+ no record corresponding the condition.
+ The cursor is set to the first record corresponding the key or the next substitute if
+ completely matching record does not exist. */
+bool tcbdbcurjump2(BDBCUR *cur, const char *kstr);
+
+
+/* Move a cursor object to the previous record.
+ `cur' specifies the cursor object.
+ If successful, the return value is true, else, it is false. False is returned if there is
+ no previous record. */
+bool tcbdbcurprev(BDBCUR *cur);
+
+
+/* Move a cursor object to the next record.
+ `cur' specifies the cursor object.
+ If successful, the return value is true, else, it is false. False is returned if there is
+ no next record. */
+bool tcbdbcurnext(BDBCUR *cur);
+
+
+/* Insert a record around a cursor object.
+ `cur' specifies the cursor object of writer connection.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ `cpmode' specifies detail adjustment: `BDBCPCURRENT', which means that the value of the
+ current record is overwritten, `BDBCPBEFORE', which means that the new record is inserted
+ before the current record, `BDBCPAFTER', which means that the new record is inserted after the
+ current record.
+ If successful, the return value is true, else, it is false. False is returned when the cursor
+ is at invalid position.
+ After insertion, the cursor is moved to the inserted record. */
+bool tcbdbcurput(BDBCUR *cur, const void *vbuf, int vsiz, int cpmode);
+
+
+/* Insert a string record around a cursor object.
+ `cur' specifies the cursor object of writer connection.
+ `vstr' specifies the string of the value.
+ `cpmode' specifies detail adjustment: `BDBCPCURRENT', which means that the value of the
+ current record is overwritten, `BDBCPBEFORE', which means that the new record is inserted
+ before the current record, `BDBCPAFTER', which means that the new record is inserted after the
+ current record.
+ If successful, the return value is true, else, it is false. False is returned when the cursor
+ is at invalid position.
+ After insertion, the cursor is moved to the inserted record. */
+bool tcbdbcurput2(BDBCUR *cur, const char *vstr, int cpmode);
+
+
+/* Delete the record where a cursor object is.
+ `cur' specifies the cursor object of writer connection.
+ If successful, the return value is true, else, it is false. False is returned when the cursor
+ is at invalid position.
+ After deletion, the cursor is moved to the next record if possible. */
+bool tcbdbcurout(BDBCUR *cur);
+
+
+/* Get the key of the record where the cursor object is.
+ `cur' specifies the cursor object.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the key, else, it is `NULL'.
+ `NULL' is returned when the cursor is at invalid position.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when
+ it is no longer in use. */
+char *tcbdbcurkey(BDBCUR *cur, int *sp);
+
+
+/* Get the key string of the record where the cursor object is.
+ `cur' specifies the cursor object.
+ If successful, the return value is the string of the key, else, it is `NULL'. `NULL' is
+ returned when the cursor is at invalid position.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. */
+char *tcbdbcurkey2(BDBCUR *cur);
+
+
+/* Get the key of the record where the cursor object is, as a volatile buffer.
+ `cur' specifies the cursor object.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the key, else, it is `NULL'.
+ `NULL' is returned when the cursor is at invalid position.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return value
+ is volatile and it may be spoiled by another operation of the database, the data should be
+ copied into another involatile buffer immediately. */
+const char *tcbdbcurkey3(BDBCUR *cur, int *sp);
+
+
+/* Get the value of the record where the cursor object is.
+ `cur' specifies the cursor object.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the value, else, it is `NULL'.
+ `NULL' is returned when the cursor is at invalid position.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when
+ it is no longer in use. */
+char *tcbdbcurval(BDBCUR *cur, int *sp);
+
+
+/* Get the value string of the record where the cursor object is.
+ `cur' specifies the cursor object.
+ If successful, the return value is the string of the value, else, it is `NULL'. `NULL' is
+ returned when the cursor is at invalid position.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. */
+char *tcbdbcurval2(BDBCUR *cur);
+
+
+/* Get the value of the record where the cursor object is, as a volatile buffer.
+ `cur' specifies the cursor object.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the value, else, it is `NULL'.
+ `NULL' is returned when the cursor is at invalid position.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return value
+ is volatile and it may be spoiled by another operation of the database, the data should be
+ copied into another involatile buffer immediately. */
+const char *tcbdbcurval3(BDBCUR *cur, int *sp);
+
+
+/* Get the key and the value of the record where the cursor object is.
+ `cur' specifies the cursor object.
+ `kxstr' specifies the object into which the key is wrote down.
+ `vxstr' specifies the object into which the value is wrote down.
+ If successful, the return value is true, else, it is false. False is returned when the cursor
+ is at invalid position. */
+bool tcbdbcurrec(BDBCUR *cur, TCXSTR *kxstr, TCXSTR *vxstr);
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Set the error code of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ `ecode' specifies the error code.
+ `file' specifies the file name of the code.
+ `line' specifies the line number of the code.
+ `func' specifies the function name of the code. */
+void tcbdbsetecode(TCBDB *bdb, int ecode, const char *filename, int line, const char *func);
+
+
+/* Set the file descriptor for debugging output.
+ `bdb' specifies the B+ tree database object.
+ `fd' specifies the file descriptor for debugging output. */
+void tcbdbsetdbgfd(TCBDB *bdb, int fd);
+
+
+/* Get the file descriptor for debugging output.
+ `bdb' specifies the B+ tree database object.
+ The return value is the file descriptor for debugging output. */
+int tcbdbdbgfd(TCBDB *bdb);
+
+
+/* Synchronize updating contents on memory.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ `phys' specifies whether to synchronize physically.
+ If successful, the return value is true, else, it is false. */
+bool tcbdbmemsync(TCBDB *bdb, bool phys);
+
+
+/* Get the comparison function of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ The return value is the pointer to the comparison function. */
+BDBCMP tcbdbcmpfunc(TCBDB *bdb);
+
+
+/* Get the opaque object for the comparison function of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ The return value is the opaque object for the comparison function. */
+void *tcbdbcmpop(TCBDB *bdb);
+
+
+/* Get the maximum number of cached leaf nodes of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ The return value is the maximum number of cached leaf nodes. */
+uint32_t tcbdblmemb(TCBDB *bdb);
+
+
+/* Get the maximum number of cached non-leaf nodes of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ The return value is the maximum number of cached non-leaf nodes. */
+uint32_t tcbdbnmemb(TCBDB *bdb);
+
+
+/* Get the number of the leaf nodes of B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ If successful, the return value is the number of the leaf nodes or 0 if the object does not
+ connect to any database file. */
+uint64_t tcbdblnum(TCBDB *bdb);
+
+
+/* Get the number of the non-leaf nodes of B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ If successful, the return value is the number of the non-leaf nodes or 0 if the object does
+ not connect to any database file. */
+uint64_t tcbdbnnum(TCBDB *bdb);
+
+
+/* Get the number of elements of the bucket array of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ The return value is the number of elements of the bucket array or 0 if the object does not
+ connect to any database file. */
+uint64_t tcbdbbnum(TCBDB *bdb);
+
+
+/* Get the record alignment of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ The return value is the record alignment or 0 if the object does not connect to any database
+ file. */
+uint32_t tcbdbalign(TCBDB *bdb);
+
+
+/* Get the maximum number of the free block pool of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ The return value is the maximum number of the free block pool or 0 if the object does not
+ connect to any database file. */
+uint32_t tcbdbfbpmax(TCBDB *bdb);
+
+
+/* Get the inode number of the database file of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ The return value is the inode number of the database file or 0 the object does not connect to
+ any database file. */
+uint64_t tcbdbinode(TCBDB *bdb);
+
+
+/* Get the modification time of the database file of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ The return value is the inode number of the database file or 0 the object does not connect to
+ any database file. */
+time_t tcbdbmtime(TCBDB *bdb);
+
+
+/* Get the additional flags of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ The return value is the additional flags. */
+uint8_t tcbdbflags(TCBDB *bdb);
+
+
+/* Get the options of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ The return value is the options. */
+uint8_t tcbdbopts(TCBDB *bdb);
+
+
+/* Get the pointer to the opaque field of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ The return value is the pointer to the opaque field whose size is 128 bytes. */
+char *tcbdbopaque(TCBDB *bdb);
+
+
+/* Get the number of used elements of the bucket array of a B+ tree database object.
+ `bdb' specifies the B+ tree database object.
+ The return value is the number of used elements of the bucket array or 0 if the object does not
+ connect to any database file. */
+uint64_t tcbdbbnumused(TCBDB *bdb);
+
+
+/* Set the maximum size of each leaf node.
+ `bdb' specifies the B+ tree database object which is not opened.
+ `lsmax' specifies the maximum size of each leaf node. If it is not more than 0, the maximum
+ size is unlimited.
+ If successful, the return value is true, else, it is false.
+ Note that the tuning parameters of the database should be set before the database is opened. */
+bool tcbdbsetlsmax(TCBDB *bdb, uint32_t lsmax);
+
+
+/* Set the capacity number of records.
+ `bdb' specifies the B+ tree database object which is not opened.
+ `capnum' specifies the capacity number of records. If it is not more than 0, the capacity is
+ unlimited.
+ If successful, the return value is true, else, it is false.
+ When the number of records exceeds the capacity, forehand records are removed implicitly.
+ Note that the tuning parameters of the database should be set before the database is opened. */
+bool tcbdbsetcapnum(TCBDB *bdb, uint64_t capnum);
+
+
+/* Store a new record into a B+ tree database object with backward duplication.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, the new record is placed after the
+ existing one. */
+bool tcbdbputdupback(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a new string record into a B+ tree database object with backward duplication.
+ `bdb' specifies the B+ tree database object connected as a writer.
+ `kstr' specifies the string of the key.
+ `vstr' specifies the string of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, the new record is placed after the
+ existing one. */
+bool tcbdbputdupback2(TCBDB *bdb, const char *kstr, const char *vstr);
+
+
+/* Move a cursor object to the rear of records corresponding a key.
+ `cur' specifies the cursor object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is true, else, it is false. False is returned if there is
+ no record corresponding the condition.
+ The cursor is set to the last record corresponding the key or the previous substitute if
+ completely matching record does not exist. */
+bool tcbdbcurjumpback(BDBCUR *cur, const void *kbuf, int ksiz);
+
+
+/* Move a cursor object to the rear of records corresponding a key string.
+ `cur' specifies the cursor object.
+ `kstr' specifies the string of the key.
+ If successful, the return value is true, else, it is false. False is returned if there is
+ no record corresponding the condition.
+ The cursor is set to the last record corresponding the key or the previous substitute if
+ completely matching record does not exist. */
+bool tcbdbcurjumpback2(BDBCUR *cur, const char *kstr);
+
+
+/* Compare two keys by lexical order.
+ `aptr' specifies the pointer to the region of one key.
+ `asiz' specifies the size of the region of one key.
+ `bptr' specifies the pointer to the region of the other key.
+ `bsiz' specifies the size of the region of the other key.
+ `op' specifies the pointer to the optional opaque object.
+ The return value is positive if the former is big, negative if the latter is big, 0 if both
+ are equivalent. */
+int tcbdbcmplexical(const char *aptr, int asiz, const char *bptr, int bsiz, void *op);
+
+
+/* Compare two keys as decimal strings of real numbers.
+ `aptr' specifies the pointer to the region of one key.
+ `asiz' specifies the size of the region of one key.
+ `bptr' specifies the pointer to the region of the other key.
+ `bsiz' specifies the size of the region of the other key.
+ `op' is ignored.
+ The return value is positive if the former is big, negative if the latter is big, 0 if both
+ are equivalent. */
+int tcbdbcmpdecimal(const char *aptr, int asiz, const char *bptr, int bsiz, void *op);
+
+
+/* Compare two keys as 32-bit integers in the native byte order.
+ `aptr' specifies the pointer to the region of one key.
+ `asiz' specifies the size of the region of one key.
+ `bptr' specifies the pointer to the region of the other key.
+ `bsiz' specifies the size of the region of the other key.
+ `op' is ignored.
+ The return value is positive if the former is big, negative if the latter is big, 0 if both
+ are equivalent. */
+int tcbdbcmpint32(const char *aptr, int asiz, const char *bptr, int bsiz, void *op);
+
+
+/* Compare two keys as 64-bit integers in the native byte order.
+ `aptr' specifies the pointer to the region of one key.
+ `asiz' specifies the size of the region of one key.
+ `bptr' specifies the pointer to the region of the other key.
+ `bsiz' specifies the size of the region of the other key.
+ `op' is ignored.
+ The return value is positive if the former is big, negative if the latter is big, 0 if both
+ are equivalent. */
+int tcbdbcmpint64(const char *aptr, int asiz, const char *bptr, int bsiz, void *op);
+
+
+/* tricks for backward compatibility */
+#define tcbdbrange3 tcbdbfwmkeys2
+
+
+
+__TCBDB_CLINKAGEEND
+#endif /* duplication check */
+
+
+/* END OF FILE */
--- /dev/null
+/*************************************************************************************************
+ * The command line utility of the B+ tree database API
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include <tcbdb.h>
+#include "myconf.h"
+
+
+/* global variables */
+const char *g_progname; // program name
+int g_dbgfd; // debugging output
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void printerr(TCBDB *bdb);
+static int printdata(const char *ptr, int size, bool px);
+static char *hextoobj(const char *str, int *sp);
+static char *mygetline(FILE *ifp);
+static int mycmpfunc(const char *aptr, int asiz, const char *bptr, int bsiz, void *op);
+static int runcreate(int argc, char **argv);
+static int runinform(int argc, char **argv);
+static int runput(int argc, char **argv);
+static int runout(int argc, char **argv);
+static int runget(int argc, char **argv);
+static int runlist(int argc, char **argv);
+static int runoptimize(int argc, char **argv);
+static int runimporttsv(int argc, char **argv);
+static int runversion(int argc, char **argv);
+static int proccreate(const char *path, int lmemb, int nmemb,
+ int bnum, int apow, int fpow, BDBCMP cmp, int opts);
+static int procinform(const char *path, int omode);
+static int procput(const char *path, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+ BDBCMP cmp, int omode, int dmode);
+static int procout(const char *path, const char *kbuf, int ksiz, BDBCMP cmp, int omode);
+static int procget(const char *path, const char *kbuf, int ksiz, BDBCMP cmp, int omode,
+ bool px, bool pz);
+static int proclist(const char *path, BDBCMP cmp, int omode, int max, bool pv, bool px, bool bk,
+ const char *jstr, const char *bstr, const char *estr, const char *fmstr);
+static int procoptimize(const char *path, int lmemb, int nmemb,
+ int bnum, int apow, int fpow, BDBCMP cmp, int opts, int omode);
+static int procimporttsv(const char *path, const char *file, int omode, bool sc);
+static int procversion(void);
+
+
+/* main routine */
+int main(int argc, char **argv){
+ g_progname = argv[0];
+ g_dbgfd = -1;
+ const char *ebuf = getenv("TCDBGFD");
+ if(ebuf) g_dbgfd = atoi(ebuf);
+ if(argc < 2) usage();
+ int rv = 0;
+ if(!strcmp(argv[1], "create")){
+ rv = runcreate(argc, argv);
+ } else if(!strcmp(argv[1], "inform")){
+ rv = runinform(argc, argv);
+ } else if(!strcmp(argv[1], "put")){
+ rv = runput(argc, argv);
+ } else if(!strcmp(argv[1], "out")){
+ rv = runout(argc, argv);
+ } else if(!strcmp(argv[1], "get")){
+ rv = runget(argc, argv);
+ } else if(!strcmp(argv[1], "list")){
+ rv = runlist(argc, argv);
+ } else if(!strcmp(argv[1], "optimize")){
+ rv = runoptimize(argc, argv);
+ } else if(!strcmp(argv[1], "importtsv")){
+ rv = runimporttsv(argc, argv);
+ } else if(!strcmp(argv[1], "version") || !strcmp(argv[1], "--version")){
+ rv = runversion(argc, argv);
+ } else {
+ usage();
+ }
+ return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+ fprintf(stderr, "%s: the command line utility of the B+ tree database API\n", g_progname);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, " %s create [-cd|-ci|-cj] [-tl] [-td|-tb] path"
+ " [lmemb [nmemb [bnum [apow [fpow]]]]]\n", g_progname);
+ fprintf(stderr, " %s inform [-nl|-nb] path\n", g_progname);
+ fprintf(stderr, " %s put [-cd|-ci|-cj] [-nl|-nb] [-sx] [-dk|-dc|-dd|-db] path"
+ " key value\n", g_progname);
+ fprintf(stderr, " %s out [-cd|-ci|-cj] [-nl|-nb] [-sx] path key\n", g_progname);
+ fprintf(stderr, " %s get [-cd|-ci|-cj] [-nl|-nb] [-sx] [-px] [-pz] path key\n", g_progname);
+ fprintf(stderr, " %s list [-cd|-ci|-cj] [-nl|-nb] [-m num] [-bk] [-pv] [-px] [-j str]"
+ " [-rb bkey ekey] [-fm str] path\n", g_progname);
+ fprintf(stderr, " %s optimize [-cd|-ci|-cj] [-tl] [-td|-tb] [-tz] [-nl|-nb] path"
+ " [lmemb [nmemb [bnum [apow [fpow]]]]]\n", g_progname);
+ fprintf(stderr, " %s importtsv [-nl|-nb] [-sc] path [file]\n", g_progname);
+ fprintf(stderr, " %s version\n", g_progname);
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+
+/* print error information */
+static void printerr(TCBDB *bdb){
+ const char *path = tcbdbpath(bdb);
+ int ecode = tcbdbecode(bdb);
+ fprintf(stderr, "%s: %s: %d: %s\n", g_progname, path ? path : "-", ecode, tcbdberrmsg(ecode));
+}
+
+
+/* print record data */
+static int printdata(const char *ptr, int size, bool px){
+ int len = 0;
+ while(size-- > 0){
+ if(px){
+ if(len > 0) putchar(' ');
+ len += printf("%02X", *(unsigned char *)ptr);
+ } else {
+ putchar(*ptr);
+ len++;
+ }
+ ptr++;
+ }
+ return len;
+}
+
+
+/* create a binary object from a hexadecimal string */
+static char *hextoobj(const char *str, int *sp){
+ int len = strlen(str);
+ char *buf = tcmalloc(len + 1);
+ int j = 0;
+ for(int i = 0; i < len; i += 2){
+ while(strchr(" \n\r\t\f\v", str[i])){
+ i++;
+ }
+ char mbuf[3];
+ if((mbuf[0] = str[i]) == '\0') break;
+ if((mbuf[1] = str[i+1]) == '\0') break;
+ mbuf[2] = '\0';
+ buf[j++] = (char)strtol(mbuf, NULL, 16);
+ }
+ buf[j] = '\0';
+ *sp = j;
+ return buf;
+}
+
+
+/* read a line from a file descriptor */
+static char *mygetline(FILE *ifp){
+ char *buf;
+ int c, len, blen;
+ buf = NULL;
+ len = 0;
+ blen = 256;
+ while((c = fgetc(ifp)) != EOF){
+ if(blen <= len) blen *= 2;
+ buf = tcrealloc(buf, blen + 1);
+ if(c == '\n' || c == '\r') c = '\0';
+ buf[len++] = c;
+ if(c == '\0') break;
+ }
+ if(!buf) return NULL;
+ buf[len] = '\0';
+ return buf;
+}
+
+
+/* dummy comparison function */
+static int mycmpfunc(const char *aptr, int asiz, const char *bptr, int bsiz, void *op){
+ return 0;
+}
+
+
+/* parse arguments of create command */
+static int runcreate(int argc, char **argv){
+ char *path = NULL;
+ char *lmstr = NULL;
+ char *nmstr = NULL;
+ char *bstr = NULL;
+ char *astr = NULL;
+ char *fstr = NULL;
+ BDBCMP cmp = NULL;
+ int opts = 0;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-cd")){
+ cmp = tcbdbcmpdecimal;
+ } else if(!strcmp(argv[i], "-ci")){
+ cmp = tcbdbcmpint32;
+ } else if(!strcmp(argv[i], "-cj")){
+ cmp = tcbdbcmpint64;
+ } else if(!strcmp(argv[i], "-tl")){
+ opts |= BDBTLARGE;
+ } else if(!strcmp(argv[i], "-td")){
+ opts |= BDBTDEFLATE;
+ } else if(!strcmp(argv[i], "-tb")){
+ opts |= BDBTTCBS;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!lmstr){
+ lmstr = argv[i];
+ } else if(!nmstr){
+ nmstr = argv[i];
+ } else if(!bstr){
+ bstr = argv[i];
+ } else if(!astr){
+ astr = argv[i];
+ } else if(!fstr){
+ fstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path) usage();
+ int lmemb = lmstr ? atoi(lmstr) : -1;
+ int nmemb = nmstr ? atoi(nmstr) : -1;
+ int bnum = bstr ? atoi(bstr) : -1;
+ int apow = astr ? atoi(astr) : -1;
+ int fpow = fstr ? atoi(fstr) : -1;
+ int rv = proccreate(path, lmemb, nmemb, bnum, apow, fpow, cmp, opts);
+ return rv;
+}
+
+
+/* parse arguments of inform command */
+static int runinform(int argc, char **argv){
+ char *path = NULL;
+ int omode = 0;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-nl")){
+ omode |= BDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= BDBOLCKNB;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path) usage();
+ int rv = procinform(path, omode);
+ return rv;
+}
+
+
+/* parse arguments of put command */
+static int runput(int argc, char **argv){
+ char *path = NULL;
+ char *key = NULL;
+ char *value = NULL;
+ BDBCMP cmp = NULL;
+ int omode = 0;
+ int dmode = 0;
+ bool sx = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-cd")){
+ cmp = tcbdbcmpdecimal;
+ } else if(!strcmp(argv[i], "-ci")){
+ cmp = tcbdbcmpint32;
+ } else if(!strcmp(argv[i], "-cj")){
+ cmp = tcbdbcmpint64;
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= BDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= BDBOLCKNB;
+ } else if(!strcmp(argv[i], "-dk")){
+ dmode = -1;
+ } else if(!strcmp(argv[i], "-dc")){
+ dmode = 1;
+ } else if(!strcmp(argv[i], "-dd")){
+ dmode = 2;
+ } else if(!strcmp(argv[i], "-db")){
+ dmode = 3;
+ } else if(!strcmp(argv[i], "-sx")){
+ sx = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!key){
+ key = argv[i];
+ } else if(!value){
+ value = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !key || !value) usage();
+ int ksiz, vsiz;
+ char *kbuf, *vbuf;
+ if(sx){
+ kbuf = hextoobj(key, &ksiz);
+ vbuf = hextoobj(value, &vsiz);
+ } else {
+ ksiz = strlen(key);
+ kbuf = tcmemdup(key, ksiz);
+ vsiz = strlen(value);
+ vbuf = tcmemdup(value, vsiz);
+ }
+ int rv = procput(path, kbuf, ksiz, vbuf, vsiz, cmp, omode, dmode);
+ free(vbuf);
+ free(kbuf);
+ return rv;
+}
+
+
+/* parse arguments of out command */
+static int runout(int argc, char **argv){
+ char *path = NULL;
+ char *key = NULL;
+ BDBCMP cmp = NULL;
+ int omode = 0;
+ bool sx = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-cd")){
+ cmp = tcbdbcmpdecimal;
+ } else if(!strcmp(argv[i], "-ci")){
+ cmp = tcbdbcmpint32;
+ } else if(!strcmp(argv[i], "-cj")){
+ cmp = tcbdbcmpint64;
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= BDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= BDBOLCKNB;
+ } else if(!strcmp(argv[i], "-sx")){
+ sx = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!key){
+ key = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !key) usage();
+ int ksiz;
+ char *kbuf;
+ if(sx){
+ kbuf = hextoobj(key, &ksiz);
+ } else {
+ ksiz = strlen(key);
+ kbuf = tcmemdup(key, ksiz);
+ }
+ int rv = procout(path, kbuf, ksiz, cmp, omode);
+ free(kbuf);
+ return rv;
+}
+
+
+/* parse arguments of get command */
+static int runget(int argc, char **argv){
+ char *path = NULL;
+ char *key = NULL;
+ BDBCMP cmp = NULL;
+ int omode = 0;
+ bool sx = false;
+ bool px = false;
+ bool pz = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-cd")){
+ cmp = tcbdbcmpdecimal;
+ } else if(!strcmp(argv[i], "-ci")){
+ cmp = tcbdbcmpint32;
+ } else if(!strcmp(argv[i], "-cj")){
+ cmp = tcbdbcmpint64;
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= BDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= BDBOLCKNB;
+ } else if(!strcmp(argv[i], "-sx")){
+ sx = true;
+ } else if(!strcmp(argv[i], "-px")){
+ px = true;
+ } else if(!strcmp(argv[i], "-pz")){
+ pz = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!key){
+ key = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !key) usage();
+ int ksiz;
+ char *kbuf;
+ if(sx){
+ kbuf = hextoobj(key, &ksiz);
+ } else {
+ ksiz = strlen(key);
+ kbuf = tcmemdup(key, ksiz);
+ }
+ int rv = procget(path, kbuf, ksiz, cmp, omode, px, pz);
+ free(kbuf);
+ return rv;
+}
+
+
+/* parse arguments of list command */
+static int runlist(int argc, char **argv){
+ char *path = NULL;
+ BDBCMP cmp = NULL;
+ int omode = 0;
+ int max = -1;
+ bool pv = false;
+ bool px = false;
+ bool bk = false;
+ char *jstr = NULL;
+ char *bstr = NULL;
+ char *estr = NULL;
+ char *fmstr = NULL;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-cd")){
+ cmp = tcbdbcmpdecimal;
+ } else if(!strcmp(argv[i], "-ci")){
+ cmp = tcbdbcmpint32;
+ } else if(!strcmp(argv[i], "-cj")){
+ cmp = tcbdbcmpint64;
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= BDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= BDBOLCKNB;
+ } else if(!strcmp(argv[i], "-m")){
+ if(++i >= argc) usage();
+ max = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-bk")){
+ bk = true;
+ } else if(!strcmp(argv[i], "-pv")){
+ pv = true;
+ } else if(!strcmp(argv[i], "-px")){
+ px = true;
+ } else if(!strcmp(argv[i], "-j")){
+ if(++i >= argc) usage();
+ jstr = argv[i];
+ } else if(!strcmp(argv[i], "-rb")){
+ if(++i >= argc) usage();
+ bstr = argv[i];
+ if(++i >= argc) usage();
+ estr = argv[i];
+ } else if(!strcmp(argv[i], "-fm")){
+ if(++i >= argc) usage();
+ fmstr = argv[i];
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path) usage();
+ int rv = proclist(path, cmp, omode, max, pv, px, bk, jstr, bstr, estr, fmstr);
+ return rv;
+}
+
+
+/* parse arguments of optimize command */
+static int runoptimize(int argc, char **argv){
+ char *path = NULL;
+ char *lmstr = NULL;
+ char *nmstr = NULL;
+ char *bstr = NULL;
+ char *astr = NULL;
+ char *fstr = NULL;
+ BDBCMP cmp = NULL;
+ int opts = UINT8_MAX;
+ int omode = 0;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-cd")){
+ cmp = tcbdbcmpdecimal;
+ } else if(!strcmp(argv[i], "-ci")){
+ cmp = tcbdbcmpint32;
+ } else if(!strcmp(argv[i], "-cj")){
+ cmp = tcbdbcmpint64;
+ } else if(!strcmp(argv[i], "-tl")){
+ if(opts == UINT8_MAX) opts = 0;
+ opts |= BDBTLARGE;
+ } else if(!strcmp(argv[i], "-td")){
+ if(opts == UINT8_MAX) opts = 0;
+ opts |= BDBTDEFLATE;
+ } else if(!strcmp(argv[i], "-tb")){
+ if(opts == UINT8_MAX) opts = 0;
+ opts |= BDBTTCBS;
+ } else if(!strcmp(argv[i], "-tz")){
+ if(opts == UINT8_MAX) opts = 0;
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= BDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= BDBOLCKNB;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!lmstr){
+ lmstr = argv[i];
+ } else if(!nmstr){
+ nmstr = argv[i];
+ } else if(!bstr){
+ bstr = argv[i];
+ } else if(!astr){
+ astr = argv[i];
+ } else if(!fstr){
+ fstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path) usage();
+ int lmemb = lmstr ? atoi(lmstr) : -1;
+ int nmemb = nmstr ? atoi(nmstr) : -1;
+ int bnum = bstr ? atoi(bstr) : -1;
+ int apow = astr ? atoi(astr) : -1;
+ int fpow = fstr ? atoi(fstr) : -1;
+ int rv = procoptimize(path, lmemb, nmemb, bnum, apow, fpow, cmp, opts, omode);
+ return rv;
+}
+
+
+/* parse arguments of importtsv command */
+static int runimporttsv(int argc, char **argv){
+ char *path = NULL;
+ char *file = NULL;
+ int omode = 0;
+ bool sc = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-nl")){
+ omode |= BDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= BDBOLCKNB;
+ } else if(!strcmp(argv[i], "-sc")){
+ sc = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!file){
+ file = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path) usage();
+ int rv = procimporttsv(path, file, omode, sc);
+ return rv;
+}
+
+
+/* parse arguments of version command */
+static int runversion(int argc, char **argv){
+ int rv = procversion();
+ return rv;
+}
+
+
+/* perform create command */
+static int proccreate(const char *path, int lmemb, int nmemb,
+ int bnum, int apow, int fpow, BDBCMP cmp, int opts){
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)) printerr(bdb);
+ if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){
+ printerr(bdb);
+ tcbdbdel(bdb);
+ return 1;
+ }
+ if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC)){
+ printerr(bdb);
+ tcbdbdel(bdb);
+ return 1;
+ }
+ bool err = false;
+ if(!tcbdbclose(bdb)){
+ printerr(bdb);
+ err = true;
+ }
+ tcbdbdel(bdb);
+ return err ? 1 : 0;
+}
+
+
+/* perform inform command */
+static int procinform(const char *path, int omode){
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ tcbdbsetcmpfunc(bdb, mycmpfunc, NULL);
+ if(!tcbdbopen(bdb, path, BDBOREADER | omode)){
+ printerr(bdb);
+ tcbdbdel(bdb);
+ return 1;
+ }
+ bool err = false;
+ const char *npath = tcbdbpath(bdb);
+ if(!npath) npath = "(unknown)";
+ printf("path: %s\n", npath);
+ printf("database type: btree\n");
+ uint8_t flags = tcbdbflags(bdb);
+ printf("additional flags:");
+ if(flags & BDBFOPEN) printf(" open");
+ if(flags & BDBFFATAL) printf(" fatal");
+ printf("\n");
+ BDBCMP cmp = tcbdbcmpfunc(bdb);
+ printf("comparison function: ");
+ if(cmp == tcbdbcmplexical){
+ printf("lexical");
+ } else if(cmp == tcbdbcmpdecimal){
+ printf("decimal");
+ } else if(cmp == tcbdbcmpint32){
+ printf("int32");
+ } else if(cmp == tcbdbcmpint64){
+ printf("int64");
+ } else {
+ printf("custom");
+ }
+ printf("\n");
+ printf("max leaf member: %d\n", tcbdblmemb(bdb));
+ printf("max node member: %d\n", tcbdbnmemb(bdb));
+ printf("leaf number: %llu\n", (unsigned long long)tcbdblnum(bdb));
+ printf("node number: %llu\n", (unsigned long long)tcbdbnnum(bdb));
+ printf("bucket number: %llu\n", (unsigned long long)tcbdbbnum(bdb));
+ if(bdb->hdb->cnt_writerec >= 0)
+ printf("used bucket number: %lld\n", (long long)tcbdbbnumused(bdb));
+ printf("alignment: %u\n", tcbdbalign(bdb));
+ printf("free block pool: %u\n", tcbdbfbpmax(bdb));
+ printf("inode number: %lld\n", (long long)tcbdbinode(bdb));
+ char date[48];
+ tcdatestrwww(tcbdbmtime(bdb), INT_MAX, date);
+ printf("modified time: %s\n", date);
+ uint8_t opts = tcbdbopts(bdb);
+ printf("options:");
+ if(opts & BDBTLARGE) printf(" large");
+ if(opts & BDBTDEFLATE) printf(" deflate");
+ if(opts & BDBTTCBS) printf(" tcbs");
+ printf("\n");
+ printf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+ printf("file size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+ if(!tcbdbclose(bdb)){
+ if(!err) printerr(bdb);
+ err = true;
+ }
+ tcbdbdel(bdb);
+ return err ? 1 : 0;
+}
+
+
+/* perform put command */
+static int procput(const char *path, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+ BDBCMP cmp, int omode, int dmode){
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)) printerr(bdb);
+ if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){
+ printerr(bdb);
+ tcbdbdel(bdb);
+ return 1;
+ }
+ bool err = false;
+ switch(dmode){
+ case -1:
+ if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz)){
+ printerr(bdb);
+ err = true;
+ }
+ break;
+ case 1:
+ if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){
+ printerr(bdb);
+ err = true;
+ }
+ break;
+ case 2:
+ if(!tcbdbputdup(bdb, kbuf, ksiz, vbuf, vsiz)){
+ printerr(bdb);
+ err = true;
+ }
+ break;
+ case 3:
+ if(!tcbdbputdupback(bdb, kbuf, ksiz, vbuf, vsiz)){
+ printerr(bdb);
+ err = true;
+ }
+ break;
+ default:
+ if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){
+ printerr(bdb);
+ err = true;
+ }
+ break;
+ }
+ if(!tcbdbclose(bdb)){
+ if(!err) printerr(bdb);
+ err = true;
+ }
+ tcbdbdel(bdb);
+ return err ? 1 : 0;
+}
+
+
+/* perform out command */
+static int procout(const char *path, const char *kbuf, int ksiz, BDBCMP cmp, int omode){
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)) printerr(bdb);
+ if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){
+ printerr(bdb);
+ tcbdbdel(bdb);
+ return 1;
+ }
+ bool err = false;
+ if(!tcbdbout(bdb, kbuf, ksiz)){
+ printerr(bdb);
+ err = true;
+ }
+ if(!tcbdbclose(bdb)){
+ if(!err) printerr(bdb);
+ err = true;
+ }
+ tcbdbdel(bdb);
+ return err ? 1 : 0;
+}
+
+
+/* perform get command */
+static int procget(const char *path, const char *kbuf, int ksiz, BDBCMP cmp, int omode,
+ bool px, bool pz){
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)) printerr(bdb);
+ if(!tcbdbopen(bdb, path, BDBOREADER | omode)){
+ printerr(bdb);
+ tcbdbdel(bdb);
+ return 1;
+ }
+ bool err = false;
+ int vsiz;
+ char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz);
+ if(vbuf){
+ printdata(vbuf, vsiz, px);
+ if(!pz) putchar('\n');
+ free(vbuf);
+ } else {
+ printerr(bdb);
+ err = true;
+ }
+ if(!tcbdbclose(bdb)){
+ if(!err) printerr(bdb);
+ err = true;
+ }
+ tcbdbdel(bdb);
+ return err ? 1 : 0;
+}
+
+
+/* perform list command */
+static int proclist(const char *path, BDBCMP cmp, int omode, int max, bool pv, bool px, bool bk,
+ const char *jstr, const char *bstr, const char *estr, const char *fmstr){
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)) printerr(bdb);
+ if(!tcbdbopen(bdb, path, BDBOREADER | omode)){
+ printerr(bdb);
+ tcbdbdel(bdb);
+ return 1;
+ }
+ bool err = false;
+ if(bstr || fmstr){
+ TCLIST *keys = fmstr ? tcbdbfwmkeys2(bdb, fmstr, max) :
+ tcbdbrange(bdb, bstr, strlen(bstr), true, estr, strlen(estr), true, max);
+ int cnt = 0;
+ for(int i = 0; i < tclistnum(keys); i++){
+ int ksiz;
+ const char *kbuf = tclistval(keys, i, &ksiz);
+ if(pv){
+ TCLIST *vals = tcbdbget4(bdb, kbuf, ksiz);
+ if(vals){
+ for(int j = 0; j < tclistnum(vals); j++){
+ int vsiz;
+ const char *vbuf = tclistval(vals, j, &vsiz);
+ printdata(kbuf, ksiz, px);
+ putchar('\t');
+ printdata(vbuf, vsiz, px);
+ putchar('\n');
+ if(max >= 0 && ++cnt >= max) break;
+ }
+ tclistdel(vals);
+ }
+ } else {
+ int num = tcbdbvnum(bdb, kbuf, ksiz);
+ for(int j = 0; j < num; j++){
+ printdata(kbuf, ksiz, px);
+ putchar('\n');
+ if(max >= 0 && ++cnt >= max) break;
+ }
+ }
+ if(max >= 0 && cnt >= max) break;
+ }
+ tclistdel(keys);
+ } else {
+ BDBCUR *cur = tcbdbcurnew(bdb);
+ if(bk){
+ if(jstr){
+ if(!tcbdbcurjumpback(cur, jstr, strlen(jstr)) && tcbdbecode(bdb) != TCENOREC){
+ printerr(bdb);
+ err = true;
+ }
+ } else {
+ if(!tcbdbcurlast(cur) && tcbdbecode(bdb) != TCENOREC){
+ printerr(bdb);
+ err = true;
+ }
+ }
+ } else {
+ if(jstr){
+ if(!tcbdbcurjump(cur, jstr, strlen(jstr)) && tcbdbecode(bdb) != TCENOREC){
+ printerr(bdb);
+ err = true;
+ }
+ } else {
+ if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
+ printerr(bdb);
+ err = true;
+ }
+ }
+ }
+ TCXSTR *key = tcxstrnew();
+ TCXSTR *val = tcxstrnew();
+ int cnt = 0;
+ while(tcbdbcurrec(cur, key, val)){
+ printdata(tcxstrptr(key), tcxstrsize(key), px);
+ if(pv){
+ putchar('\t');
+ printdata(tcxstrptr(val), tcxstrsize(val), px);
+ }
+ putchar('\n');
+ if(bk){
+ if(!tcbdbcurprev(cur) && tcbdbecode(bdb) != TCENOREC){
+ printerr(bdb);
+ err = true;
+ }
+ } else {
+ if(!tcbdbcurnext(cur) && tcbdbecode(bdb) != TCENOREC){
+ printerr(bdb);
+ err = true;
+ }
+ }
+ if(max >= 0 && ++cnt >= max) break;
+ }
+ tcxstrdel(val);
+ tcxstrdel(key);
+ tcbdbcurdel(cur);
+ }
+ if(!tcbdbclose(bdb)){
+ if(!err) printerr(bdb);
+ err = true;
+ }
+ tcbdbdel(bdb);
+ return err ? 1 : 0;
+}
+
+
+/* perform optimize command */
+static int procoptimize(const char *path, int lmemb, int nmemb,
+ int bnum, int apow, int fpow, BDBCMP cmp, int opts, int omode){
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)) printerr(bdb);
+ if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){
+ printerr(bdb);
+ tcbdbdel(bdb);
+ return 1;
+ }
+ bool err = false;
+ if(!tcbdboptimize(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){
+ printerr(bdb);
+ err = true;
+ }
+ if(!tcbdbclose(bdb)){
+ if(!err) printerr(bdb);
+ err = true;
+ }
+ tcbdbdel(bdb);
+ return err ? 1 : 0;
+}
+
+
+/* perform importtsv command */
+static int procimporttsv(const char *path, const char *file, int omode, bool sc){
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ FILE *ifp = file ? fopen(file, "rb") : stdin;
+ if(!ifp){
+ fprintf(stderr, "%s: could not open\n", file ? file : "(stdin)");
+ tcbdbdel(bdb);
+ return 1;
+ }
+ if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | omode)){
+ printerr(bdb);
+ tcbdbdel(bdb);
+ return 1;
+ }
+ bool err = false;
+ char *line;
+ int cnt = 0;
+ while(!err && (line = mygetline(ifp)) != NULL){
+ char *pv = strchr(line, '\t');
+ if(!pv) continue;
+ *pv = '\0';
+ if(sc) tcstrtolower(line);
+ if(!tcbdbputdup2(bdb, line, pv + 1) && tcbdbecode(bdb) != TCEKEEP){
+ printerr(bdb);
+ err = true;
+ }
+ free(line);
+ if(cnt > 0 && cnt % 100 == 0){
+ putchar('.');
+ fflush(stdout);
+ if(cnt % 5000 == 0) printf(" (%08d)\n", cnt);
+ }
+ cnt++;
+ }
+ printf(" (%08d)\n", cnt);
+ if(!tcbdbclose(bdb)){
+ if(!err) printerr(bdb);
+ err = true;
+ }
+ tcbdbdel(bdb);
+ if(ifp != stdin) fclose(ifp);
+ return err ? 1 : 0;
+}
+
+
+/* perform version command */
+static int procversion(void){
+ printf("Tokyo Cabinet version %s (%d:%s)\n", tcversion, _TC_LIBVER, _TC_FORMATVER);
+ printf("Copyright (C) 2006-2008 Mikio Hirabayashi\n");
+ return 0;
+}
+
+
+
+// END OF FILE
--- /dev/null
+/*************************************************************************************************
+ * The test cases of the B+ tree database API
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include <tcbdb.h>
+#include "myconf.h"
+
+#define RECBUFSIZ 32 // buffer for records
+
+typedef struct { // type of structure for write thread
+ TCBDB *bdb;
+ int rnum;
+ bool rnd;
+ int id;
+} TARGWRITE;
+
+typedef struct { // type of structure for read thread
+ TCBDB *bdb;
+ int rnum;
+ bool wb;
+ bool rnd;
+ int id;
+} TARGREAD;
+
+typedef struct { // type of structure for remove thread
+ TCBDB *bdb;
+ int rnum;
+ bool rnd;
+ int id;
+} TARGREMOVE;
+
+typedef struct { // type of structure for wicked thread
+ TCBDB *bdb;
+ int rnum;
+ bool nc;
+ int id;
+ TCMAP *map;
+} TARGWICKED;
+
+typedef struct { // type of structure for typical thread
+ TCBDB *bdb;
+ int rnum;
+ bool nc;
+ int rratio;
+ int id;
+} TARGTYPICAL;
+
+
+/* global variables */
+const char *g_progname; // program name
+int g_dbgfd; // debugging output
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void iprintf(const char *format, ...);
+static void eprint(TCBDB *bdb, const char *func);
+static void mprint(TCBDB *bdb);
+static int myrand(int range);
+static int myrandnd(int range);
+static int runwrite(int argc, char **argv);
+static int runread(int argc, char **argv);
+static int runremove(int argc, char **argv);
+static int runwicked(int argc, char **argv);
+static int runtypical(int argc, char **argv);
+static int procwrite(const char *path, int tnum, int rnum, int lmemb, int nmemb,
+ int bnum, int apow, int fpow, int opts, int omode, bool rnd);
+static int procread(const char *path, int tnum, int omode, bool wb, bool rnd);
+static int procremove(const char *path, int tnum, int omode, bool rnd);
+static int procwicked(const char *path, int tnum, int rnum, int opts, int omode, bool nc);
+static int proctypical(const char *path, int tnum, int rnum, int lmemb, int nmemb,
+ int bnum, int apow, int fpow, int opts, int omode, bool nc, int rratio);
+static void *threadwrite(void *targ);
+static void *threadread(void *targ);
+static void *threadremove(void *targ);
+static void *threadwicked(void *targ);
+static void *threadtypical(void *targ);
+
+
+/* main routine */
+int main(int argc, char **argv){
+ g_progname = argv[0];
+ g_dbgfd = -1;
+ const char *ebuf = getenv("TCDBGFD");
+ if(ebuf) g_dbgfd = atoi(ebuf);
+ srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+ if(argc < 2) usage();
+ int rv = 0;
+ if(!strcmp(argv[1], "write")){
+ rv = runwrite(argc, argv);
+ } else if(!strcmp(argv[1], "read")){
+ rv = runread(argc, argv);
+ } else if(!strcmp(argv[1], "remove")){
+ rv = runremove(argc, argv);
+ } else if(!strcmp(argv[1], "wicked")){
+ rv = runwicked(argc, argv);
+ } else if(!strcmp(argv[1], "typical")){
+ rv = runtypical(argc, argv);
+ } else {
+ usage();
+ }
+ return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+ fprintf(stderr, "%s: test cases of the B+ tree database API of Tokyo Cabinet\n", g_progname);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, " %s write [-tl] [-td|-tb] [-nl|-nb] [-rnd] path tnum rnum"
+ " [lmemb [nmemb [bnum [apow [fpow]]]]]\n", g_progname);
+ fprintf(stderr, " %s read [-nl|-nb] [-wb] [-rnd] path tnum\n", g_progname);
+ fprintf(stderr, " %s remove [-nl|-nb] [-rnd] path tnum\n", g_progname);
+ fprintf(stderr, " %s wicked [-tl] [-td|-tb] [-nl|-nb] [-nc] path tnum rnum\n",
+ g_progname);
+ fprintf(stderr, " %s typical [-tl] [-td|-tb] [-nl|-nb] [-nc] [-rr] path tnum rnum"
+ " [lmemb [nmemb [bnum [apow [fpow]]]]]\n", g_progname);
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+
+/* print formatted information string and flush the buffer */
+static void iprintf(const char *format, ...){
+ va_list ap;
+ va_start(ap, format);
+ vprintf(format, ap);
+ fflush(stdout);
+ va_end(ap);
+}
+
+
+/* print error message of hash database */
+static void eprint(TCBDB *bdb, const char *func){
+ const char *path = tcbdbpath(bdb);
+ int ecode = tcbdbecode(bdb);
+ fprintf(stderr, "%s: %s: %s: error: %d: %s\n",
+ g_progname, path ? path : "-", func, ecode, tcbdberrmsg(ecode));
+}
+
+
+/* print members of hash database */
+static void mprint(TCBDB *bdb){
+ if(bdb->hdb->cnt_writerec < 0) return;
+ iprintf("max leaf member: %d\n", tcbdblmemb(bdb));
+ iprintf("max node member: %d\n", tcbdbnmemb(bdb));
+ iprintf("leaf number: %d\n", tcbdblnum(bdb));
+ iprintf("node number: %d\n", tcbdbnnum(bdb));
+ iprintf("bucket number: %lld\n", (long long)tcbdbbnum(bdb));
+ iprintf("used bucket number: %lld\n", (long long)tcbdbbnumused(bdb));
+ iprintf("cnt_saveleaf: %lld\n", (long long)bdb->cnt_saveleaf);
+ iprintf("cnt_loadleaf: %lld\n", (long long)bdb->cnt_loadleaf);
+ iprintf("cnt_killleaf: %lld\n", (long long)bdb->cnt_killleaf);
+ iprintf("cnt_adjleafc: %lld\n", (long long)bdb->cnt_adjleafc);
+ iprintf("cnt_savenode: %lld\n", (long long)bdb->cnt_savenode);
+ iprintf("cnt_loadnode: %lld\n", (long long)bdb->cnt_loadnode);
+ iprintf("cnt_adjnodec: %lld\n", (long long)bdb->cnt_adjnodec);
+ iprintf("cnt_writerec: %lld\n", (long long)bdb->hdb->cnt_writerec);
+ iprintf("cnt_reuserec: %lld\n", (long long)bdb->hdb->cnt_reuserec);
+ iprintf("cnt_moverec: %lld\n", (long long)bdb->hdb->cnt_moverec);
+ iprintf("cnt_readrec: %lld\n", (long long)bdb->hdb->cnt_readrec);
+ iprintf("cnt_searchfbp: %lld\n", (long long)bdb->hdb->cnt_searchfbp);
+ iprintf("cnt_insertfbp: %lld\n", (long long)bdb->hdb->cnt_insertfbp);
+ iprintf("cnt_splicefbp: %lld\n", (long long)bdb->hdb->cnt_splicefbp);
+ iprintf("cnt_dividefbp: %lld\n", (long long)bdb->hdb->cnt_dividefbp);
+ iprintf("cnt_mergefbp: %lld\n", (long long)bdb->hdb->cnt_mergefbp);
+ iprintf("cnt_reducefbp: %lld\n", (long long)bdb->hdb->cnt_reducefbp);
+ iprintf("cnt_appenddrp: %lld\n", (long long)bdb->hdb->cnt_appenddrp);
+ iprintf("cnt_deferdrp: %lld\n", (long long)bdb->hdb->cnt_deferdrp);
+ iprintf("cnt_flushdrp: %lld\n", (long long)bdb->hdb->cnt_flushdrp);
+ iprintf("cnt_adjrecc: %lld\n", (long long)bdb->hdb->cnt_adjrecc);
+}
+
+
+/* get a random number */
+static int myrand(int range){
+ return (int)((double)range * rand() / (RAND_MAX + 1.0));
+}
+
+
+/* get a random number based on normal distribution */
+static int myrandnd(int range){
+ int num = (int)tcdrandnd(range >> 1, range / 10);
+ return (num < 0 || num >= range) ? 0 : num;
+}
+
+
+/* parse arguments of write command */
+static int runwrite(int argc, char **argv){
+ char *path = NULL;
+ char *tstr = NULL;
+ char *rstr = NULL;
+ char *lmstr = NULL;
+ char *nmstr = NULL;
+ char *bstr = NULL;
+ char *astr = NULL;
+ char *fstr = NULL;
+ int opts = 0;
+ int omode = 0;
+ bool rnd = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-tl")){
+ opts |= BDBTLARGE;
+ } else if(!strcmp(argv[i], "-td")){
+ opts |= BDBTDEFLATE;
+ } else if(!strcmp(argv[i], "-tb")){
+ opts |= BDBTTCBS;
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= BDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= BDBOLCKNB;
+ } else if(!strcmp(argv[i], "-rnd")){
+ rnd = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!tstr){
+ tstr = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else if(!lmstr){
+ lmstr = argv[i];
+ } else if(!nmstr){
+ nmstr = argv[i];
+ } else if(!bstr){
+ bstr = argv[i];
+ } else if(!astr){
+ astr = argv[i];
+ } else if(!fstr){
+ fstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !tstr || !rstr) usage();
+ int tnum = atoi(tstr);
+ int rnum = atoi(rstr);
+ if(tnum < 1 || rnum < 1) usage();
+ int lmemb = lmstr ? atoi(lmstr) : -1;
+ int nmemb = nmstr ? atoi(nmstr) : -1;
+ int bnum = bstr ? atoi(bstr) : -1;
+ int apow = astr ? atoi(astr) : -1;
+ int fpow = fstr ? atoi(fstr) : -1;
+ int rv = procwrite(path, tnum, rnum, lmemb, nmemb, bnum, apow, fpow, opts, omode, rnd);
+ return rv;
+}
+
+
+/* parse arguments of read command */
+static int runread(int argc, char **argv){
+ char *path = NULL;
+ char *tstr = NULL;
+ int omode = 0;
+ bool rnd = false;
+ bool wb = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-nl")){
+ omode |= BDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= BDBOLCKNB;
+ } else if(!strcmp(argv[i], "-wb")){
+ wb = true;
+ } else if(!strcmp(argv[i], "-rnd")){
+ rnd = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!tstr){
+ tstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !tstr) usage();
+ int tnum = atoi(tstr);
+ if(tnum < 1) usage();
+ int rv = procread(path, tnum, omode, wb, rnd);
+ return rv;
+}
+
+
+/* parse arguments of remove command */
+static int runremove(int argc, char **argv){
+ char *path = NULL;
+ char *tstr = NULL;
+ int omode = 0;
+ bool rnd = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-nl")){
+ omode |= BDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= BDBOLCKNB;
+ } else if(!strcmp(argv[i], "-rnd")){
+ rnd = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!tstr){
+ tstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !tstr) usage();
+ int tnum = atoi(tstr);
+ if(tnum < 1) usage();
+ int rv = procremove(path, tnum, omode, rnd);
+ return rv;
+}
+
+
+/* parse arguments of wicked command */
+static int runwicked(int argc, char **argv){
+ char *path = NULL;
+ char *tstr = NULL;
+ char *rstr = NULL;
+ int opts = 0;
+ int omode = 0;
+ bool nc = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-tl")){
+ opts |= BDBTLARGE;
+ } else if(!strcmp(argv[i], "-td")){
+ opts |= BDBTDEFLATE;
+ } else if(!strcmp(argv[i], "-tb")){
+ opts |= BDBTTCBS;
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= BDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= BDBOLCKNB;
+ } else if(!strcmp(argv[i], "-nc")){
+ nc = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!tstr){
+ tstr = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !tstr || !rstr) usage();
+ int tnum = atoi(tstr);
+ int rnum = atoi(rstr);
+ if(tnum < 1 || rnum < 1) usage();
+ int rv = procwicked(path, tnum, rnum, opts, omode, nc);
+ return rv;
+}
+
+
+/* parse arguments of typical command */
+static int runtypical(int argc, char **argv){
+ char *path = NULL;
+ char *tstr = NULL;
+ char *rstr = NULL;
+ char *lmstr = NULL;
+ char *nmstr = NULL;
+ char *bstr = NULL;
+ char *astr = NULL;
+ char *fstr = NULL;
+ int opts = 0;
+ int omode = 0;
+ int rratio = -1;
+ bool nc = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-tl")){
+ opts |= BDBTLARGE;
+ } else if(!strcmp(argv[i], "-td")){
+ opts |= BDBTDEFLATE;
+ } else if(!strcmp(argv[i], "-tb")){
+ opts |= BDBTTCBS;
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= BDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= BDBOLCKNB;
+ } else if(!strcmp(argv[i], "-nc")){
+ nc = true;
+ } else if(!strcmp(argv[i], "-rr")){
+ if(++i >= argc) usage();
+ rratio = atoi(argv[i]);
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!tstr){
+ tstr = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else if(!lmstr){
+ lmstr = argv[i];
+ } else if(!nmstr){
+ nmstr = argv[i];
+ } else if(!bstr){
+ bstr = argv[i];
+ } else if(!astr){
+ astr = argv[i];
+ } else if(!fstr){
+ fstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !tstr || !rstr) usage();
+ int tnum = atoi(tstr);
+ int rnum = atoi(rstr);
+ if(tnum < 1 || rnum < 1) usage();
+ int lmemb = lmstr ? atoi(lmstr) : -1;
+ int nmemb = nmstr ? atoi(nmstr) : -1;
+ int bnum = bstr ? atoi(bstr) : -1;
+ int apow = astr ? atoi(astr) : -1;
+ int fpow = fstr ? atoi(fstr) : -1;
+ int rv = proctypical(path, tnum, rnum, lmemb, nmemb, bnum, apow, fpow, opts, omode, nc, rratio);
+ return rv;
+}
+
+
+/* perform write command */
+static int procwrite(const char *path, int tnum, int rnum, int lmemb, int nmemb,
+ int bnum, int apow, int fpow, int opts, int omode, bool rnd){
+ iprintf("<Writing Test>\n path=%s tnum=%d rnum=%d lmemb=%d nmemb=%d"
+ " bnum=%d apow=%d fpow=%d opts=%d omode=%d rnd=%d\n\n",
+ path, tnum, rnum, lmemb, nmemb, bnum, apow, fpow, opts, omode, rnd);
+ bool err = false;
+ double stime = tctime();
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ if(!tcbdbsetmutex(bdb)){
+ eprint(bdb, "tcbdbsetmutex");
+ err = true;
+ }
+ if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){
+ eprint(bdb, "tcbdbtune");
+ err = true;
+ }
+ if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){
+ eprint(bdb, "tcbdbopen");
+ err = true;
+ }
+ TARGWRITE targs[tnum];
+ pthread_t threads[tnum];
+ if(tnum == 1){
+ targs[0].bdb = bdb;
+ targs[0].rnum = rnum;
+ targs[0].rnd = rnd;
+ targs[0].id = 0;
+ if(threadwrite(targs) != NULL) err = true;
+ } else {
+ for(int i = 0; i < tnum; i++){
+ targs[i].bdb = bdb;
+ targs[i].rnum = rnum;
+ targs[i].rnd = rnd;
+ targs[i].id = i;
+ if(pthread_create(threads + i, NULL, threadwrite, targs + i) != 0){
+ eprint(bdb, "pthread_create");
+ targs[i].id = -1;
+ err = true;
+ }
+ }
+ for(int i = 0; i < tnum; i++){
+ if(targs[i].id == -1) continue;
+ void *rv;
+ if(pthread_join(threads[i], &rv) != 0){
+ eprint(bdb, "pthread_join");
+ err = true;
+ } else if(rv){
+ err = true;
+ }
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+ iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+ mprint(bdb);
+ if(!tcbdbclose(bdb)){
+ eprint(bdb, "tcbdbclose");
+ err = true;
+ }
+ tcbdbdel(bdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform read command */
+static int procread(const char *path, int tnum, int omode, bool wb, bool rnd){
+ iprintf("<Reading Test>\n path=%s tnum=%d omode=%d wb=%d rnd=%d\n\n",
+ path, tnum, omode, wb, rnd);
+ bool err = false;
+ double stime = tctime();
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ if(!tcbdbsetmutex(bdb)){
+ eprint(bdb, "tcbdbsetmutex");
+ err = true;
+ }
+ if(!tcbdbopen(bdb, path, BDBOREADER | omode)){
+ eprint(bdb, "tcbdbopen");
+ err = true;
+ }
+ int rnum = tcbdbrnum(bdb) / tnum;
+ TARGREAD targs[tnum];
+ pthread_t threads[tnum];
+ if(tnum == 1){
+ targs[0].bdb = bdb;
+ targs[0].rnum = rnum;
+ targs[0].wb = wb;
+ targs[0].rnd = rnd;
+ targs[0].id = 0;
+ if(threadread(targs) != NULL) err = true;
+ } else {
+ for(int i = 0; i < tnum; i++){
+ targs[i].bdb = bdb;
+ targs[i].rnum = rnum;
+ targs[i].wb = wb;
+ targs[i].rnd = rnd;
+ targs[i].id = i;
+ if(pthread_create(threads + i, NULL, threadread, targs + i) != 0){
+ eprint(bdb, "pthread_create");
+ targs[i].id = -1;
+ err = true;
+ }
+ }
+ for(int i = 0; i < tnum; i++){
+ if(targs[i].id == -1) continue;
+ void *rv;
+ if(pthread_join(threads[i], &rv) != 0){
+ eprint(bdb, "pthread_join");
+ err = true;
+ } else if(rv){
+ err = true;
+ }
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+ iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+ mprint(bdb);
+ if(!tcbdbclose(bdb)){
+ eprint(bdb, "tcbdbclose");
+ err = true;
+ }
+ tcbdbdel(bdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform remove command */
+static int procremove(const char *path, int tnum, int omode, bool rnd){
+ iprintf("<Removing Test>\n path=%s tnum=%d omode=%d rnd=%d\n\n", path, tnum, omode, rnd);
+ bool err = false;
+ double stime = tctime();
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ if(!tcbdbsetmutex(bdb)){
+ eprint(bdb, "tcbdbsetmutex");
+ err = true;
+ }
+ if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){
+ eprint(bdb, "tcbdbopen");
+ err = true;
+ }
+ int rnum = tcbdbrnum(bdb) / tnum;
+ TARGREMOVE targs[tnum];
+ pthread_t threads[tnum];
+ if(tnum == 1){
+ targs[0].bdb = bdb;
+ targs[0].rnum = rnum;
+ targs[0].rnd = rnd;
+ targs[0].id = 0;
+ if(threadremove(targs) != NULL) err = true;
+ } else {
+ for(int i = 0; i < tnum; i++){
+ targs[i].bdb = bdb;
+ targs[i].rnum = rnum;
+ targs[i].rnd = rnd;
+ targs[i].id = i;
+ if(pthread_create(threads + i, NULL, threadremove, targs + i) != 0){
+ eprint(bdb, "pthread_create");
+ targs[i].id = -1;
+ err = true;
+ }
+ }
+ for(int i = 0; i < tnum; i++){
+ if(targs[i].id == -1) continue;
+ void *rv;
+ if(pthread_join(threads[i], &rv) != 0){
+ eprint(bdb, "pthread_join");
+ err = true;
+ } else if(rv){
+ err = true;
+ }
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+ iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+ mprint(bdb);
+ if(!tcbdbclose(bdb)){
+ eprint(bdb, "tcbdbclose");
+ err = true;
+ }
+ tcbdbdel(bdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform wicked command */
+static int procwicked(const char *path, int tnum, int rnum, int opts, int omode, bool nc){
+ iprintf("<Writing Test>\n path=%s tnum=%d rnum=%d opts=%d omode=%d nc=%d\n\n",
+ path, tnum, rnum, opts, omode, nc);
+ bool err = false;
+ double stime = tctime();
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ if(!tcbdbsetmutex(bdb)){
+ eprint(bdb, "tcbdbsetmutex");
+ err = true;
+ }
+ if(!tcbdbtune(bdb, 10, 10, rnum / 50, 10, -1, opts)){
+ eprint(bdb, "tcbdbtune");
+ err = true;
+ }
+ if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){
+ eprint(bdb, "tcbdbopen");
+ err = true;
+ }
+ TARGWICKED targs[tnum];
+ pthread_t threads[tnum];
+ TCMAP *map = tcmapnew();
+ if(tnum == 1){
+ targs[0].bdb = bdb;
+ targs[0].rnum = rnum;
+ targs[0].nc = nc;
+ targs[0].id = 0;
+ targs[0].map = map;
+ if(threadwicked(targs) != NULL) err = true;
+ } else {
+ for(int i = 0; i < tnum; i++){
+ targs[i].bdb = bdb;
+ targs[i].rnum = rnum;
+ targs[i].nc = nc;
+ targs[i].id = i;
+ targs[i].map = map;
+ targs[i].map = map;
+ if(pthread_create(threads + i, NULL, threadwicked, targs + i) != 0){
+ eprint(bdb, "pthread_create");
+ targs[i].id = -1;
+ err = true;
+ }
+ }
+ for(int i = 0; i < tnum; i++){
+ if(targs[i].id == -1) continue;
+ void *rv;
+ if(pthread_join(threads[i], &rv) != 0){
+ eprint(bdb, "pthread_join");
+ err = true;
+ } else if(rv){
+ err = true;
+ }
+ }
+ }
+ if(!nc){
+ if(!tcbdbsync(bdb)){
+ eprint(bdb, "tcbdbsync");
+ err = true;
+ }
+ if(tcbdbrnum(bdb) != tcmaprnum(map)){
+ eprint(bdb, "(validation)");
+ err = true;
+ }
+ int end = rnum * tnum;
+ for(int i = 1; i <= end && !err; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", i - 1);
+ int vsiz;
+ const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz);
+ int rsiz;
+ char *rbuf = tcbdbget(bdb, kbuf, ksiz, &rsiz);
+ if(vbuf){
+ putchar('.');
+ if(!rbuf){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+ eprint(bdb, "(validation)");
+ err = true;
+ }
+ } else {
+ putchar('*');
+ if(rbuf || tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "(validation)");
+ err = true;
+ }
+ }
+ free(rbuf);
+ if(i % 50 == 0) iprintf(" (%08d)\n", i);
+ }
+ if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
+ }
+ tcmapdel(map);
+ iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+ iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+ mprint(bdb);
+ if(!tcbdbclose(bdb)){
+ eprint(bdb, "tcbdbclose");
+ err = true;
+ }
+ tcbdbdel(bdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform typical command */
+static int proctypical(const char *path, int tnum, int rnum, int lmemb, int nmemb,
+ int bnum, int apow, int fpow, int opts, int omode, bool nc, int rratio){
+ iprintf("<Typical Access Test>\n path=%s tnum=%d rnum=%d lmemb=%d nmemb=%d"
+ " bnum=%d apow=%d fpow=%d opts=%d omode=%d nc=%d rratio=%d\n\n",
+ path, tnum, rnum, lmemb, nmemb, bnum, apow, fpow, opts, omode, nc, rratio);
+ bool err = false;
+ double stime = tctime();
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ if(!tcbdbsetmutex(bdb)){
+ eprint(bdb, "tcbdbsetmutex");
+ err = true;
+ }
+ if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){
+ eprint(bdb, "tcbdbtune");
+ err = true;
+ }
+ if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){
+ eprint(bdb, "tcbdbopen");
+ err = true;
+ }
+ TARGTYPICAL targs[tnum];
+ pthread_t threads[tnum];
+ if(tnum == 1){
+ targs[0].bdb = bdb;
+ targs[0].rnum = rnum;
+ targs[0].nc = nc;
+ targs[0].rratio = rratio;
+ targs[0].id = 0;
+ if(threadtypical(targs) != NULL) err = true;
+ } else {
+ for(int i = 0; i < tnum; i++){
+ targs[i].bdb = bdb;
+ targs[i].rnum = rnum;
+ targs[i].nc = nc;
+ targs[i].rratio = rratio;
+ targs[i].id = i;
+ if(pthread_create(threads + i, NULL, threadtypical, targs + i) != 0){
+ eprint(bdb, "pthread_create");
+ targs[i].id = -1;
+ err = true;
+ }
+ }
+ for(int i = 0; i < tnum; i++){
+ if(targs[i].id == -1) continue;
+ void *rv;
+ if(pthread_join(threads[i], &rv) != 0){
+ eprint(bdb, "pthread_join");
+ err = true;
+ } else if(rv){
+ err = true;
+ }
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+ iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+ mprint(bdb);
+ if(!tcbdbclose(bdb)){
+ eprint(bdb, "tcbdbclose");
+ err = true;
+ }
+ tcbdbdel(bdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* thread the write function */
+static void *threadwrite(void *targ){
+ TCBDB *bdb = ((TARGWRITE *)targ)->bdb;
+ int rnum = ((TARGWRITE *)targ)->rnum;
+ bool rnd = ((TARGWRITE *)targ)->rnd;
+ int id = ((TARGWRITE *)targ)->id;
+ bool err = false;
+ int base = id * rnum;
+ for(int i = 1; i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len = sprintf(buf, "%08d", base + (rnd ? myrand(i) : i));
+ if(!tcbdbput(bdb, buf, len, buf, len)){
+ eprint(bdb, "tcbdbput");
+ err = true;
+ break;
+ }
+ if(id <= 0 && rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ return err ? "error" : NULL;
+}
+
+
+/* thread the read function */
+static void *threadread(void *targ){
+ TCBDB *bdb = ((TARGREAD *)targ)->bdb;
+ int rnum = ((TARGREAD *)targ)->rnum;
+ bool wb = ((TARGREAD *)targ)->wb;
+ bool rnd = ((TARGREAD *)targ)->rnd;
+ int id = ((TARGREAD *)targ)->id;
+ bool err = false;
+ int base = id * rnum;
+ for(int i = 1; i <= rnum && !err; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrandnd(i) : i));
+ int vsiz;
+ if(wb){
+ int vsiz;
+ const char *vbuf = tcbdbget3(bdb, kbuf, ksiz, &vsiz);
+ if(!vbuf && (!rnd || tcbdbecode(bdb) != TCENOREC)){
+ eprint(bdb, "tcbdbget3");
+ err = true;
+ }
+ } else {
+ char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz);
+ if(!vbuf && (!rnd || tcbdbecode(bdb) != TCENOREC)){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ }
+ free(vbuf);
+ }
+ if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ return err ? "error" : NULL;
+}
+
+
+/* thread the remove function */
+static void *threadremove(void *targ){
+ TCBDB *bdb = ((TARGREMOVE *)targ)->bdb;
+ int rnum = ((TARGREMOVE *)targ)->rnum;
+ bool rnd = ((TARGREMOVE *)targ)->rnd;
+ int id = ((TARGREMOVE *)targ)->id;
+ bool err = false;
+ int base = id * rnum;
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrand(i + 1) : i));
+ if(!tcbdbout(bdb, kbuf, ksiz) && (!rnd || tcbdbecode(bdb) != TCENOREC)){
+ eprint(bdb, "tcbdbout");
+ err = true;
+ break;
+ }
+ if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ return err ? "error" : NULL;
+}
+
+
+/* thread the wicked function */
+static void *threadwicked(void *targ){
+ TCBDB *bdb = ((TARGWICKED *)targ)->bdb;
+ int rnum = ((TARGWICKED *)targ)->rnum;
+ bool nc = ((TARGWICKED *)targ)->nc;
+ int id = ((TARGWICKED *)targ)->id;
+ TCMAP *map = ((TARGWICKED *)targ)->map;
+ BDBCUR *cur = tcbdbcurnew(bdb);
+ bool err = false;
+ for(int i = 1; i <= rnum && !err; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", myrand(rnum * (id + 1)));
+ char vbuf[RECBUFSIZ];
+ int vsiz = myrand(RECBUFSIZ);
+ memset(vbuf, '*', vsiz);
+ vbuf[vsiz] = '\0';
+ char *rbuf;
+ if(!nc) tcglobalmutexlock();
+ switch(myrand(16)){
+ case 0:
+ if(id == 0) putchar('0');
+ if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(bdb, "tcbdbput");
+ err = true;
+ }
+ if(!nc) tcmapput(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 1:
+ if(id == 0) putchar('1');
+ if(!tcbdbput2(bdb, kbuf, vbuf)){
+ eprint(bdb, "tcbdbput2");
+ err = true;
+ }
+ if(!nc) tcmapput2(map, kbuf, vbuf);
+ break;
+ case 2:
+ if(id == 0) putchar('2');
+ if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz) && tcbdbecode(bdb) != TCEKEEP){
+ eprint(bdb, "tcbdbputkeep");
+ err = true;
+ }
+ if(!nc) tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 3:
+ if(id == 0) putchar('3');
+ if(!tcbdbputkeep2(bdb, kbuf, vbuf) && tcbdbecode(bdb) != TCEKEEP){
+ eprint(bdb, "tcbdbputkeep2");
+ err = true;
+ }
+ if(!nc) tcmapputkeep2(map, kbuf, vbuf);
+ break;
+ case 4:
+ if(id == 0) putchar('4');
+ if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(bdb, "tcbdbputcat");
+ err = true;
+ }
+ if(!nc) tcmapputcat(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 5:
+ if(id == 0) putchar('5');
+ if(!tcbdbputcat2(bdb, kbuf, vbuf)){
+ eprint(bdb, "tcbdbputcat2");
+ err = true;
+ }
+ if(!nc) tcmapputcat2(map, kbuf, vbuf);
+ break;
+ case 6:
+ if(id == 0) putchar('6');
+ if(nc){
+ if(!tcbdbputdup(bdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(bdb, "tcbdbputdup");
+ err = true;
+ }
+ }
+ break;
+ case 7:
+ if(id == 0) putchar('7');
+ if(nc){
+ if(!tcbdbputdup2(bdb, kbuf, vbuf)){
+ eprint(bdb, "tcbdbputdup2");
+ err = true;
+ }
+ }
+ break;
+ case 8:
+ if(id == 0) putchar('8');
+ if(myrand(10) == 0){
+ if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbout");
+ err = true;
+ }
+ if(!nc) tcmapout(map, kbuf, ksiz);
+ }
+ break;
+ case 9:
+ if(id == 0) putchar('9');
+ if(myrand(10) == 0){
+ if(!tcbdbout2(bdb, kbuf) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbout2");
+ err = true;
+ }
+ if(!nc) tcmapout2(map, kbuf);
+ }
+ break;
+ case 10:
+ if(id == 0) putchar('A');
+ if(!(rbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz))){
+ if(tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ }
+ rbuf = tcsprintf("[%d]", myrand(i + 1));
+ vsiz = strlen(rbuf);
+ }
+ vsiz += myrand(vsiz);
+ if(myrand(3) == 0) vsiz += PATH_MAX;
+ rbuf = tcrealloc(rbuf, vsiz + 1);
+ for(int j = 0; j < vsiz; j++){
+ rbuf[j] = myrand(0x100);
+ }
+ if(!tcbdbput(bdb, kbuf, ksiz, rbuf, vsiz)){
+ eprint(bdb, "tcbdbput");
+ err = true;
+ }
+ if(!nc) tcmapput(map, kbuf, ksiz, rbuf, vsiz);
+ free(rbuf);
+ break;
+ case 11:
+ if(id == 0) putchar('B');
+ if(!(rbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz)) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ }
+ free(rbuf);
+ break;
+ case 12:
+ if(id == 0) putchar('C');
+ if(!(rbuf = tcbdbget2(bdb, kbuf)) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ }
+ free(rbuf);
+ break;
+ case 13:
+ if(id == 0) putchar('D');
+ if(!tcbdbget3(bdb, kbuf, ksiz, &vsiz) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ }
+ break;
+ case 14:
+ if(id == 0) putchar('E');
+ if(myrand(rnum / 50) == 0){
+ switch(myrand(5)){
+ case 0:
+ if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurfirst");
+ err = true;
+ }
+ break;
+ case 1:
+ if(!tcbdbcurlast(cur) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurfirst");
+ err = true;
+ }
+ break;
+ default:
+ if(!tcbdbcurjump(cur, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurjump");
+ err = true;
+ }
+ break;
+ }
+ }
+ TCXSTR *ikey = tcxstrnew();
+ TCXSTR *ival = tcxstrnew();
+ for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){
+ if(j % 3 == 0){
+ if(!tcbdbcurrec(cur, ikey, ival)){
+ int ecode = tcbdbecode(bdb);
+ if(ecode != TCEINVALID && ecode != TCENOREC){
+ eprint(bdb, "tcbdbcurrec");
+ err = true;
+ }
+ }
+ } else {
+ int iksiz;
+ if(!tcbdbcurkey3(cur, &iksiz)){
+ int ecode = tcbdbecode(bdb);
+ if(ecode != TCEINVALID && ecode != TCENOREC){
+ eprint(bdb, "tcbdbcurkey3");
+ err = true;
+ }
+ }
+ }
+ if(myrand(5) == 0){
+ if(!tcbdbcurprev(cur)){
+ int ecode = tcbdbecode(bdb);
+ if(ecode != TCEINVALID && ecode != TCENOREC){
+ eprint(bdb, "tcbdbcurprev");
+ err = true;
+ }
+ }
+ } else {
+ if(!tcbdbcurnext(cur)){
+ int ecode = tcbdbecode(bdb);
+ if(ecode != TCEINVALID && ecode != TCENOREC){
+ eprint(bdb, "tcbdbcurnext");
+ err = true;
+ }
+ }
+ }
+ }
+ tcxstrdel(ival);
+ tcxstrdel(ikey);
+ break;
+ default:
+ if(id == 0) putchar('@');
+ if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+ break;
+ }
+ if(!nc) tcglobalmutexunlock();
+ if(id == 0){
+ if(i % 50 == 0) iprintf(" (%08d)\n", i);
+ if(id == 0 && i == rnum / 4){
+ if(!tcbdboptimize(bdb, -1, -1, -1, -1, -1, -1)){
+ eprint(bdb, "tcbdboptimize");
+ err = true;
+ }
+ if(!tcbdbcurfirst(cur)){
+ eprint(bdb, "tcbdbcurfirst");
+ err = true;
+ }
+ }
+ }
+ }
+ tcbdbcurdel(cur);
+ return err ? "error" : NULL;
+}
+
+
+/* thread the typical function */
+static void *threadtypical(void *targ){
+ TCBDB *bdb = ((TARGTYPICAL *)targ)->bdb;
+ int rnum = ((TARGTYPICAL *)targ)->rnum;
+ bool nc = ((TARGTYPICAL *)targ)->nc;
+ int rratio = ((TARGTYPICAL *)targ)->rratio;
+ int id = ((TARGTYPICAL *)targ)->id;
+ bool err = false;
+ TCMAP *map = (!nc && id == 0) ? tcmapnew2(rnum + 1) : NULL;
+ int base = id * rnum;
+ int mrange = tclmax(50 + rratio, 100);
+ BDBCUR *cur = tcbdbcurnew(bdb);
+ for(int i = 1; !err && i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len = sprintf(buf, "%08d", base + myrandnd(i));
+ int rnd = myrand(mrange);
+ if(rnd < 10){
+ if(!tcbdbput(bdb, buf, len, buf, len)){
+ eprint(bdb, "tcbdbput");
+ err = true;
+ }
+ if(map) tcmapput(map, buf, len, buf, len);
+ } else if(rnd < 15){
+ if(!tcbdbputkeep(bdb, buf, len, buf, len) && tcbdbecode(bdb) != TCEKEEP){
+ eprint(bdb, "tcbdbputkeep");
+ err = true;
+ }
+ if(map) tcmapputkeep(map, buf, len, buf, len);
+ } else if(rnd < 20){
+ if(!tcbdbputcat(bdb, buf, len, buf, len)){
+ eprint(bdb, "tcbdbputcat");
+ err = true;
+ }
+ if(map) tcmapputcat(map, buf, len, buf, len);
+ } else if(rnd < 25){
+ if(!tcbdbout(bdb, buf, len) && tcbdbecode(bdb) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbout");
+ err = true;
+ }
+ if(map) tcmapout(map, buf, len);
+ } else if(rnd < 27){
+ switch(myrand(3)){
+ case 0:
+ if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurfirst");
+ err = true;
+ }
+ for(int j = 0; !err && j < 10; j++){
+ int ksiz;
+ char *kbuf = tcbdbcurkey(cur, &ksiz);
+ if(kbuf){
+ int vsiz;
+ char *vbuf = tcbdbcurval(cur, &vsiz);
+ if(vbuf){
+ free(vbuf);
+ } else if(tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurval");
+ err = true;
+ }
+ free(kbuf);
+ } else if(tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurkey");
+ err = true;
+ }
+ tcbdbcurnext(cur);
+ }
+ break;
+ case 1:
+ if(!tcbdbcurlast(cur) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurlast");
+ err = true;
+ }
+ for(int j = 0; !err && j < 10; j++){
+ int ksiz;
+ char *kbuf = tcbdbcurkey(cur, &ksiz);
+ if(kbuf){
+ int vsiz;
+ char *vbuf = tcbdbcurval(cur, &vsiz);
+ if(vbuf){
+ free(vbuf);
+ } else if(tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurval");
+ err = true;
+ }
+ free(kbuf);
+ } else if(tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurkey");
+ err = true;
+ }
+ tcbdbcurprev(cur);
+ }
+ break;
+ case 2:
+ if(!tcbdbcurjump(cur, buf, len) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurjump");
+ err = true;
+ }
+ for(int j = 0; !err && j < 10; j++){
+ int ksiz;
+ char *kbuf = tcbdbcurkey(cur, &ksiz);
+ if(kbuf){
+ int vsiz;
+ char *vbuf = tcbdbcurval(cur, &vsiz);
+ if(vbuf){
+ free(vbuf);
+ } else if(tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurval");
+ err = true;
+ }
+ free(kbuf);
+ } else if(tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurkey");
+ err = true;
+ }
+ tcbdbcurnext(cur);
+ }
+ break;
+ }
+ } else {
+ int vsiz;
+ char *vbuf = tcbdbget(bdb, buf, len, &vsiz);
+ if(vbuf){
+ if(map){
+ int msiz;
+ const char *mbuf = tcmapget(map, buf, len, &msiz);
+ if(msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){
+ eprint(bdb, "(validation)");
+ err = true;
+ }
+ }
+ free(vbuf);
+ } else {
+ if(tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ }
+ if(map && tcmapget(map, buf, len, &vsiz)){
+ eprint(bdb, "(validation)");
+ err = true;
+ }
+ }
+ }
+ if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ tcbdbcurdel(cur);
+ if(map){
+ tcmapiterinit(map);
+ int ksiz;
+ const char *kbuf;
+ while(!err && (kbuf = tcmapiternext(map, &ksiz)) != NULL){
+ int vsiz;
+ char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz);
+ if(vbuf){
+ int msiz;
+ const char *mbuf = tcmapget(map, kbuf, ksiz, &msiz);
+ if(!mbuf || msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){
+ eprint(bdb, "(validation)");
+ err = true;
+ }
+ free(vbuf);
+ } else {
+ eprint(bdb, "(validation)");
+ err = true;
+ }
+ }
+ tcmapdel(map);
+ }
+ return err ? "error" : NULL;
+}
+
+
+
+// END OF FILE
--- /dev/null
+/*************************************************************************************************
+ * The test cases of the B+ tree database API
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include <tcbdb.h>
+#include "myconf.h"
+
+#define RECBUFSIZ 32 // buffer for records
+
+
+/* global variables */
+const char *g_progname; // program name
+int g_dbgfd; // debugging output
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void iprintf(const char *format, ...);
+static void eprint(TCBDB *bdb, const char *func);
+static void mprint(TCBDB *bdb);
+static int myrand(int range);
+static int runwrite(int argc, char **argv);
+static int runread(int argc, char **argv);
+static int runremove(int argc, char **argv);
+static int runrcat(int argc, char **argv);
+static int runqueue(int argc, char **argv);
+static int runmisc(int argc, char **argv);
+static int runwicked(int argc, char **argv);
+static int procwrite(const char *path, int rnum, int lmemb, int nmemb, int bnum,
+ int apow, int fpow, bool mt, BDBCMP cmp, int opts,
+ int lcnum, int ncnum, int lsmax, int capnum, int omode);
+static int procread(const char *path, bool mt, BDBCMP cmp, int lcnum, int ncnum,
+ int omode, bool wb);
+static int procremove(const char *path, bool mt, BDBCMP cmp, int lcnum, int ncnum, int omode);
+static int procrcat(const char *path, int rnum,
+ int lmemb, int nmemb, int bnum, int apow, int fpow,
+ bool mt, BDBCMP cmp, int opts, int lcnum, int ncnum, int lsmax, int capnum,
+ int omode, int pnum, bool rl);
+static int procqueue(const char *path, int rnum, int lmemb, int nmemb, int bnum,
+ int apow, int fpow, bool mt, BDBCMP cmp, int opts,
+ int lcnum, int ncnum, int lsmax, int capnum, int omode);
+static int procmisc(const char *path, int rnum, bool mt, int opts, int omode);
+static int procwicked(const char *path, int rnum, bool mt, int opts, int omode);
+
+
+/* main routine */
+int main(int argc, char **argv){
+ g_progname = argv[0];
+ g_dbgfd = -1;
+ const char *ebuf = getenv("TCDBGFD");
+ if(ebuf) g_dbgfd = atoi(ebuf);
+ srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+ if(argc < 2) usage();
+ int rv = 0;
+ if(!strcmp(argv[1], "write")){
+ rv = runwrite(argc, argv);
+ } else if(!strcmp(argv[1], "read")){
+ rv = runread(argc, argv);
+ } else if(!strcmp(argv[1], "remove")){
+ rv = runremove(argc, argv);
+ } else if(!strcmp(argv[1], "rcat")){
+ rv = runrcat(argc, argv);
+ } else if(!strcmp(argv[1], "queue")){
+ rv = runqueue(argc, argv);
+ } else if(!strcmp(argv[1], "misc")){
+ rv = runmisc(argc, argv);
+ } else if(!strcmp(argv[1], "wicked")){
+ rv = runwicked(argc, argv);
+ } else {
+ usage();
+ }
+ return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+ fprintf(stderr, "%s: test cases of the B+ tree database API of Tokyo Cabinet\n", g_progname);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, " %s write [-mt] [-cd|-ci|-cj] [-tl] [-td|-tb] [-lc num] [-nc num]"
+ " [-ls num] [-ca num] [-nl|-nb] path rnum [lmemb [nmemb [bnum [apow [fpow]]]]]\n",
+ g_progname);
+ fprintf(stderr, " %s read [-mt] [-cd|-ci|-cj] [-lc num] [-nc num] [-nl|-nb] [-wb]"
+ " path\n", g_progname);
+ fprintf(stderr, " %s remove [-mt] [-cd|-ci|-cj] [-lc num] [-nc num] [-nl|-nb]"
+ " path\n", g_progname);
+ fprintf(stderr, " %s rcat [-mt] [-cd|-ci|-cj] [-tl] [-td|-tb] [-lc num] [-nc num]"
+ " [-ls num] [-ca num] [-nl|-nb] [-pn num] [-rl] path rnum"
+ " [lmemb [nmemb [bnum [apow [fpow]]]]]\n", g_progname);
+ fprintf(stderr, " %s queue [-mt] [-cd|-ci|-cj] [-tl] [-td|-tb] [-lc num] [-nc num]"
+ " [-ls num] [-ca num] [-nl|-nb] path rnum [lmemb [nmemb [bnum [apow [fpow]]]]]\n",
+ g_progname);
+ fprintf(stderr, " %s misc [-mt] [-tl] [-td|-tb] [-nl|-nb] path rnum\n", g_progname);
+ fprintf(stderr, " %s wicked [-mt] [-tl] [-td|-tb] [-nl|-nb] path rnum\n", g_progname);
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+
+/* print formatted information string and flush the buffer */
+static void iprintf(const char *format, ...){
+ va_list ap;
+ va_start(ap, format);
+ vprintf(format, ap);
+ fflush(stdout);
+ va_end(ap);
+}
+
+
+/* print error message of hash database */
+static void eprint(TCBDB *bdb, const char *func){
+ const char *path = tcbdbpath(bdb);
+ int ecode = tcbdbecode(bdb);
+ fprintf(stderr, "%s: %s: %s: error: %d: %s\n",
+ g_progname, path ? path : "-", func, ecode, tcbdberrmsg(ecode));
+}
+
+
+/* print members of B+ tree database */
+static void mprint(TCBDB *bdb){
+ if(bdb->hdb->cnt_writerec < 0) return;
+ iprintf("max leaf member: %d\n", tcbdblmemb(bdb));
+ iprintf("max node member: %d\n", tcbdbnmemb(bdb));
+ iprintf("leaf number: %d\n", tcbdblnum(bdb));
+ iprintf("node number: %d\n", tcbdbnnum(bdb));
+ iprintf("bucket number: %lld\n", (long long)tcbdbbnum(bdb));
+ iprintf("used bucket number: %lld\n", (long long)tcbdbbnumused(bdb));
+ iprintf("cnt_saveleaf: %lld\n", (long long)bdb->cnt_saveleaf);
+ iprintf("cnt_loadleaf: %lld\n", (long long)bdb->cnt_loadleaf);
+ iprintf("cnt_killleaf: %lld\n", (long long)bdb->cnt_killleaf);
+ iprintf("cnt_adjleafc: %lld\n", (long long)bdb->cnt_adjleafc);
+ iprintf("cnt_savenode: %lld\n", (long long)bdb->cnt_savenode);
+ iprintf("cnt_loadnode: %lld\n", (long long)bdb->cnt_loadnode);
+ iprintf("cnt_adjnodec: %lld\n", (long long)bdb->cnt_adjnodec);
+ iprintf("cnt_writerec: %lld\n", (long long)bdb->hdb->cnt_writerec);
+ iprintf("cnt_reuserec: %lld\n", (long long)bdb->hdb->cnt_reuserec);
+ iprintf("cnt_moverec: %lld\n", (long long)bdb->hdb->cnt_moverec);
+ iprintf("cnt_readrec: %lld\n", (long long)bdb->hdb->cnt_readrec);
+ iprintf("cnt_searchfbp: %lld\n", (long long)bdb->hdb->cnt_searchfbp);
+ iprintf("cnt_insertfbp: %lld\n", (long long)bdb->hdb->cnt_insertfbp);
+ iprintf("cnt_splicefbp: %lld\n", (long long)bdb->hdb->cnt_splicefbp);
+ iprintf("cnt_dividefbp: %lld\n", (long long)bdb->hdb->cnt_dividefbp);
+ iprintf("cnt_mergefbp: %lld\n", (long long)bdb->hdb->cnt_mergefbp);
+ iprintf("cnt_reducefbp: %lld\n", (long long)bdb->hdb->cnt_reducefbp);
+ iprintf("cnt_appenddrp: %lld\n", (long long)bdb->hdb->cnt_appenddrp);
+ iprintf("cnt_deferdrp: %lld\n", (long long)bdb->hdb->cnt_deferdrp);
+ iprintf("cnt_flushdrp: %lld\n", (long long)bdb->hdb->cnt_flushdrp);
+ iprintf("cnt_adjrecc: %lld\n", (long long)bdb->hdb->cnt_adjrecc);
+}
+
+
+/* get a random number */
+static int myrand(int range){
+ return (int)((double)range * rand() / (RAND_MAX + 1.0));
+}
+
+
+/* parse arguments of write command */
+static int runwrite(int argc, char **argv){
+ char *path = NULL;
+ char *rstr = NULL;
+ char *lmstr = NULL;
+ char *nmstr = NULL;
+ char *bstr = NULL;
+ char *astr = NULL;
+ char *fstr = NULL;
+ bool mt = false;
+ BDBCMP cmp = NULL;
+ int opts = 0;
+ int lcnum = 0;
+ int ncnum = 0;
+ int lsmax = 0;
+ int capnum = 0;
+ int omode = 0;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-mt")){
+ mt = true;
+ } else if(!strcmp(argv[i], "-cd")){
+ cmp = tcbdbcmpdecimal;
+ } else if(!strcmp(argv[i], "-ci")){
+ cmp = tcbdbcmpint32;
+ } else if(!strcmp(argv[i], "-cj")){
+ cmp = tcbdbcmpint64;
+ } else if(!strcmp(argv[i], "-tl")){
+ opts |= BDBTLARGE;
+ } else if(!strcmp(argv[i], "-td")){
+ opts |= BDBTDEFLATE;
+ } else if(!strcmp(argv[i], "-tb")){
+ opts |= BDBTTCBS;
+ } else if(!strcmp(argv[i], "-lc")){
+ if(++i >= argc) usage();
+ lcnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-nc")){
+ if(++i >= argc) usage();
+ ncnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-ls")){
+ if(++i >= argc) usage();
+ lsmax = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-ca")){
+ if(++i >= argc) usage();
+ capnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= BDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= BDBOLCKNB;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else if(!lmstr){
+ lmstr = argv[i];
+ } else if(!nmstr){
+ nmstr = argv[i];
+ } else if(!bstr){
+ bstr = argv[i];
+ } else if(!astr){
+ astr = argv[i];
+ } else if(!fstr){
+ fstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !rstr) usage();
+ int rnum = atoi(rstr);
+ if(rnum < 1) usage();
+ int lmemb = lmstr ? atoi(lmstr) : -1;
+ int nmemb = nmstr ? atoi(nmstr) : -1;
+ int bnum = bstr ? atoi(bstr) : -1;
+ int apow = astr ? atoi(astr) : -1;
+ int fpow = fstr ? atoi(fstr) : -1;
+ int rv = procwrite(path, rnum, lmemb, nmemb, bnum, apow, fpow,
+ mt, cmp, opts, lcnum, ncnum, lsmax, capnum, omode);
+ return rv;
+}
+
+
+/* parse arguments of read command */
+static int runread(int argc, char **argv){
+ char *path = NULL;
+ bool mt = false;
+ BDBCMP cmp = NULL;
+ int lcnum = 0;
+ int ncnum = 0;
+ int omode = 0;
+ bool wb = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-mt")){
+ mt = true;
+ } else if(!strcmp(argv[i], "-cd")){
+ cmp = tcbdbcmpdecimal;
+ } else if(!strcmp(argv[i], "-ci")){
+ cmp = tcbdbcmpint32;
+ } else if(!strcmp(argv[i], "-cj")){
+ cmp = tcbdbcmpint64;
+ } else if(!strcmp(argv[i], "-lc")){
+ if(++i >= argc) usage();
+ lcnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-nc")){
+ if(++i >= argc) usage();
+ ncnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= BDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= BDBOLCKNB;
+ } else if(!strcmp(argv[i], "-wb")){
+ wb = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path) usage();
+ int rv = procread(path, mt, cmp, lcnum, ncnum, omode, wb);
+ return rv;
+}
+
+
+/* parse arguments of remove command */
+static int runremove(int argc, char **argv){
+ char *path = NULL;
+ bool mt = false;
+ BDBCMP cmp = NULL;
+ int lcnum = 0;
+ int ncnum = 0;
+ int omode = 0;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-mt")){
+ mt = true;
+ } else if(!strcmp(argv[i], "-cd")){
+ cmp = tcbdbcmpdecimal;
+ } else if(!strcmp(argv[i], "-ci")){
+ cmp = tcbdbcmpint32;
+ } else if(!strcmp(argv[i], "-cj")){
+ cmp = tcbdbcmpint64;
+ } else if(!strcmp(argv[i], "-lc")){
+ if(++i >= argc) usage();
+ lcnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-nc")){
+ if(++i >= argc) usage();
+ ncnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= BDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= BDBOLCKNB;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path) usage();
+ int rv = procremove(path, mt, cmp, lcnum, ncnum, omode);
+ return rv;
+}
+
+
+/* parse arguments of rcat command */
+static int runrcat(int argc, char **argv){
+ char *path = NULL;
+ char *rstr = NULL;
+ char *lmstr = NULL;
+ char *nmstr = NULL;
+ char *bstr = NULL;
+ char *astr = NULL;
+ char *fstr = NULL;
+ bool mt = false;
+ BDBCMP cmp = NULL;
+ int opts = 0;
+ int lcnum = 0;
+ int ncnum = 0;
+ int lsmax = 0;
+ int capnum = 0;
+ int omode = 0;
+ int pnum = 0;
+ bool rl = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-mt")){
+ mt = true;
+ } else if(!strcmp(argv[i], "-cd")){
+ cmp = tcbdbcmpdecimal;
+ } else if(!strcmp(argv[i], "-ci")){
+ cmp = tcbdbcmpint32;
+ } else if(!strcmp(argv[i], "-cj")){
+ cmp = tcbdbcmpint64;
+ } else if(!strcmp(argv[i], "-tl")){
+ opts |= BDBTLARGE;
+ } else if(!strcmp(argv[i], "-td")){
+ opts |= BDBTDEFLATE;
+ } else if(!strcmp(argv[i], "-tb")){
+ opts |= BDBTTCBS;
+ } else if(!strcmp(argv[i], "-lc")){
+ if(++i >= argc) usage();
+ lcnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-nc")){
+ if(++i >= argc) usage();
+ ncnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-ls")){
+ if(++i >= argc) usage();
+ lsmax = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-ca")){
+ if(++i >= argc) usage();
+ capnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= BDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= BDBOLCKNB;
+ } else if(!strcmp(argv[i], "-pn")){
+ if(++i >= argc) usage();
+ pnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-rl")){
+ rl = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else if(!lmstr){
+ lmstr = argv[i];
+ } else if(!nmstr){
+ nmstr = argv[i];
+ } else if(!bstr){
+ bstr = argv[i];
+ } else if(!astr){
+ astr = argv[i];
+ } else if(!fstr){
+ fstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !rstr) usage();
+ int rnum = atoi(rstr);
+ if(rnum < 1) usage();
+ int lmemb = lmstr ? atoi(lmstr) : -1;
+ int nmemb = nmstr ? atoi(nmstr) : -1;
+ int bnum = bstr ? atoi(bstr) : -1;
+ int apow = astr ? atoi(astr) : -1;
+ int fpow = fstr ? atoi(fstr) : -1;
+ int rv = procrcat(path, rnum, lmemb, nmemb, bnum, apow, fpow, mt, cmp, opts,
+ lcnum, ncnum, lsmax, capnum, omode, pnum, rl);
+ return rv;
+}
+
+
+/* parse arguments of queue command */
+static int runqueue(int argc, char **argv){
+ char *path = NULL;
+ char *rstr = NULL;
+ char *lmstr = NULL;
+ char *nmstr = NULL;
+ char *bstr = NULL;
+ char *astr = NULL;
+ char *fstr = NULL;
+ bool mt = false;
+ BDBCMP cmp = NULL;
+ int opts = 0;
+ int lcnum = 0;
+ int ncnum = 0;
+ int lsmax = 0;
+ int capnum = 0;
+ int omode = 0;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-mt")){
+ mt = true;
+ } else if(!strcmp(argv[i], "-cd")){
+ cmp = tcbdbcmpdecimal;
+ } else if(!strcmp(argv[i], "-ci")){
+ cmp = tcbdbcmpint32;
+ } else if(!strcmp(argv[i], "-cj")){
+ cmp = tcbdbcmpint64;
+ } else if(!strcmp(argv[i], "-tl")){
+ opts |= BDBTLARGE;
+ } else if(!strcmp(argv[i], "-td")){
+ opts |= BDBTDEFLATE;
+ } else if(!strcmp(argv[i], "-tb")){
+ opts |= BDBTTCBS;
+ } else if(!strcmp(argv[i], "-lc")){
+ if(++i >= argc) usage();
+ lcnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-nc")){
+ if(++i >= argc) usage();
+ ncnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-ls")){
+ if(++i >= argc) usage();
+ lsmax = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-ca")){
+ if(++i >= argc) usage();
+ capnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= BDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= BDBOLCKNB;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else if(!lmstr){
+ lmstr = argv[i];
+ } else if(!nmstr){
+ nmstr = argv[i];
+ } else if(!bstr){
+ bstr = argv[i];
+ } else if(!astr){
+ astr = argv[i];
+ } else if(!fstr){
+ fstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !rstr) usage();
+ int rnum = atoi(rstr);
+ if(rnum < 1) usage();
+ int lmemb = lmstr ? atoi(lmstr) : -1;
+ int nmemb = nmstr ? atoi(nmstr) : -1;
+ int bnum = bstr ? atoi(bstr) : -1;
+ int apow = astr ? atoi(astr) : -1;
+ int fpow = fstr ? atoi(fstr) : -1;
+ int rv = procqueue(path, rnum, lmemb, nmemb, bnum, apow, fpow,
+ mt, cmp, opts, lcnum, ncnum, lsmax, capnum, omode);
+ return rv;
+}
+
+
+/* parse arguments of misc command */
+static int runmisc(int argc, char **argv){
+ char *path = NULL;
+ char *rstr = NULL;
+ bool mt = false;
+ int opts = 0;
+ int omode = 0;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-mt")){
+ mt = true;
+ } else if(!strcmp(argv[i], "-tl")){
+ opts |= BDBTLARGE;
+ } else if(!strcmp(argv[i], "-td")){
+ opts |= BDBTDEFLATE;
+ } else if(!strcmp(argv[i], "-tb")){
+ opts |= BDBTTCBS;
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= BDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= BDBOLCKNB;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !rstr) usage();
+ int rnum = atoi(rstr);
+ if(rnum < 1) usage();
+ int rv = procmisc(path, rnum, mt, opts, omode);
+ return rv;
+}
+
+
+/* parse arguments of wicked command */
+static int runwicked(int argc, char **argv){
+ char *path = NULL;
+ char *rstr = NULL;
+ bool mt = false;
+ int opts = 0;
+ int omode = 0;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-mt")){
+ mt = true;
+ } else if(!strcmp(argv[i], "-tl")){
+ opts |= BDBTLARGE;
+ } else if(!strcmp(argv[i], "-td")){
+ opts |= BDBTDEFLATE;
+ } else if(!strcmp(argv[i], "-tb")){
+ opts |= BDBTTCBS;
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= BDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= BDBOLCKNB;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !rstr) usage();
+ int rnum = atoi(rstr);
+ if(rnum < 1) usage();
+ int rv = procwicked(path, rnum, mt, opts, omode);
+ return rv;
+}
+
+
+/* perform write command */
+static int procwrite(const char *path, int rnum, int lmemb, int nmemb, int bnum,
+ int apow, int fpow, bool mt, BDBCMP cmp, int opts,
+ int lcnum, int ncnum, int lsmax, int capnum, int omode){
+ iprintf("<Writing Test>\n path=%s rnum=%d lmemb=%d nmemb=%d bnum=%d apow=%d fpow=%d"
+ " mt=%d cmp=%p opts=%d lcnum=%d ncnum=%d lsmax=%d capnum=%d omode=%d\n\n",
+ path, rnum, lmemb, nmemb, bnum, apow, fpow, mt, (void *)(intptr_t)cmp,
+ opts, lcnum, ncnum, lsmax, capnum, omode);
+ bool err = false;
+ double stime = tctime();
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ if(mt && !tcbdbsetmutex(bdb)){
+ eprint(bdb, "tcbdbsetmutex");
+ err = true;
+ }
+ if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)){
+ eprint(bdb, "tcbdbsetcmpfunc");
+ err = true;
+ }
+ if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){
+ eprint(bdb, "tcbdbtune");
+ err = true;
+ }
+ if(!tcbdbsetcache(bdb, lcnum, ncnum)){
+ eprint(bdb, "tcbdbsetcache");
+ err = true;
+ }
+ if(!tcbdbsetlsmax(bdb, lsmax)){
+ eprint(bdb, "tcbdbsetlsmax");
+ err = true;
+ }
+ if(!tcbdbsetcapnum(bdb, capnum)){
+ eprint(bdb, "tcbdbsetcapnum");
+ err = true;
+ }
+ if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC)){
+ eprint(bdb, "tcbdbopen");
+ err = true;
+ }
+ for(int i = 1; i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len;
+ if(cmp == tcbdbcmpdecimal){
+ len = sprintf(buf, "%d", i);
+ } else if(cmp == tcbdbcmpint32){
+ int32_t lnum = i;
+ memcpy(buf, &lnum, sizeof(lnum));
+ len = sizeof(lnum);
+ } else if(cmp == tcbdbcmpint64){
+ int64_t llnum = i;
+ memcpy(buf, &llnum, sizeof(llnum));
+ len = sizeof(llnum);
+ } else {
+ len = sprintf(buf, "%08d", i);
+ }
+ if(!tcbdbput(bdb, buf, len, buf, len)){
+ eprint(bdb, "tcbdbput");
+ err = true;
+ break;
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+ iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+ mprint(bdb);
+ if(!tcbdbclose(bdb)){
+ eprint(bdb, "tcbdbclose");
+ err = true;
+ }
+ tcbdbdel(bdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform read command */
+static int procread(const char *path, bool mt, BDBCMP cmp, int lcnum, int ncnum,
+ int omode, bool wb){
+ iprintf("<Reading Test>\n path=%s mt=%d cmp=%p lcnum=%d ncnum=%d omode=%d wb=%d\n",
+ path, mt, (void *)(intptr_t)cmp, lcnum, ncnum, omode, wb);
+ bool err = false;
+ double stime = tctime();
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ if(mt && !tcbdbsetmutex(bdb)){
+ eprint(bdb, "tcbdbsetmutex");
+ err = true;
+ }
+ if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)){
+ eprint(bdb, "tcbdbsetcmpfunc");
+ err = true;
+ }
+ if(!tcbdbsetcache(bdb, lcnum, ncnum)){
+ eprint(bdb, "tcbdbsetcache");
+ err = true;
+ }
+ if(!tcbdbopen(bdb, path, BDBOREADER | omode)){
+ eprint(bdb, "tcbdbopen");
+ err = true;
+ }
+ int rnum = tcbdbrnum(bdb);
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz;
+ if(cmp == tcbdbcmpdecimal){
+ ksiz = sprintf(kbuf, "%d", i);
+ } else if(cmp == tcbdbcmpint32){
+ int32_t lnum = i;
+ memcpy(kbuf, &lnum, sizeof(lnum));
+ ksiz = sizeof(lnum);
+ } else if(cmp == tcbdbcmpint64){
+ int64_t llnum = i;
+ memcpy(kbuf, &llnum, sizeof(llnum));
+ ksiz = sizeof(llnum);
+ } else {
+ ksiz = sprintf(kbuf, "%08d", i);
+ }
+ int vsiz;
+ if(wb){
+ int vsiz;
+ const char *vbuf = tcbdbget3(bdb, kbuf, ksiz, &vsiz);
+ if(!vbuf){
+ eprint(bdb, "tcbdbget3");
+ err = true;
+ break;
+ }
+ } else {
+ char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz);
+ if(!vbuf){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ break;
+ }
+ free(vbuf);
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+ iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+ mprint(bdb);
+ if(!tcbdbclose(bdb)){
+ eprint(bdb, "tcbdbclose");
+ err = true;
+ }
+ tcbdbdel(bdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform remove command */
+static int procremove(const char *path, bool mt, BDBCMP cmp, int lcnum, int ncnum, int omode){
+ iprintf("<Removing Test>\n path=%s mt=%d cmp=%p lcnum=%d ncnum=%d omode=%d\n",
+ path, mt, (void *)(intptr_t)cmp, lcnum, ncnum, omode);
+ bool err = false;
+ double stime = tctime();
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ if(mt && !tcbdbsetmutex(bdb)){
+ eprint(bdb, "tcbdbsetmutex");
+ err = true;
+ }
+ if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)){
+ eprint(bdb, "tcbdbsetcmpfunc");
+ err = true;
+ }
+ if(!tcbdbsetcache(bdb, lcnum, ncnum)){
+ eprint(bdb, "tcbdbsetcache");
+ err = true;
+ }
+ if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){
+ eprint(bdb, "tcbdbopen");
+ err = true;
+ }
+ int rnum = tcbdbrnum(bdb);
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz;
+ if(cmp == tcbdbcmpdecimal){
+ ksiz = sprintf(kbuf, "%d", i);
+ } else if(cmp == tcbdbcmpint32){
+ int32_t lnum = i;
+ memcpy(kbuf, &lnum, sizeof(lnum));
+ ksiz = sizeof(lnum);
+ } else if(cmp == tcbdbcmpint64){
+ int64_t llnum = i;
+ memcpy(kbuf, &llnum, sizeof(llnum));
+ ksiz = sizeof(llnum);
+ } else {
+ ksiz = sprintf(kbuf, "%08d", i);
+ }
+ if(!tcbdbout(bdb, kbuf, ksiz)){
+ eprint(bdb, "tcbdbout");
+ err = true;
+ break;
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+ iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+ mprint(bdb);
+ if(!tcbdbclose(bdb)){
+ eprint(bdb, "tcbdbclose");
+ err = true;
+ }
+ tcbdbdel(bdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform rcat command */
+static int procrcat(const char *path, int rnum,
+ int lmemb, int nmemb, int bnum, int apow, int fpow,
+ bool mt, BDBCMP cmp, int opts, int lcnum, int ncnum, int lsmax, int capnum,
+ int omode, int pnum, bool rl){
+ iprintf("<Random Concatenating Test>\n"
+ " path=%s rnum=%d lmemb=%d nmemb=%d bnum=%d apow=%d fpow=%d"
+ " mt=%d cmp=%p opts=%d lcnum=%d ncnum=%d lsmax=%d capnum=%d"
+ " omode=%d pnum=%d rl=%d\n\n",
+ path, rnum, lmemb, nmemb, bnum, apow, fpow, mt, (void *)(intptr_t)cmp,
+ opts, lcnum, ncnum, lsmax, capnum, omode, pnum, rl);
+ if(pnum < 1) pnum = rnum;
+ bool err = false;
+ double stime = tctime();
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ if(mt && !tcbdbsetmutex(bdb)){
+ eprint(bdb, "tcbdbsetmutex");
+ err = true;
+ }
+ if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){
+ eprint(bdb, "tcbdbtune");
+ err = true;
+ }
+ if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)){
+ eprint(bdb, "tcbdbsetcmpfunc");
+ err = true;
+ }
+ if(!tcbdbsetcache(bdb, lcnum, ncnum)){
+ eprint(bdb, "tcbdbsetcache");
+ err = true;
+ }
+ if(!tcbdbsetlsmax(bdb, lsmax)){
+ eprint(bdb, "tcbdbsetlsmax");
+ err = true;
+ }
+ if(!tcbdbsetcapnum(bdb, capnum)){
+ eprint(bdb, "tcbdbsetcapnum");
+ err = true;
+ }
+ if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){
+ eprint(bdb, "tcbdbopen");
+ err = true;
+ }
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz;
+ if(cmp == tcbdbcmpdecimal){
+ ksiz = sprintf(kbuf, "%d", myrand(pnum));
+ } else if(cmp == tcbdbcmpint32){
+ int32_t lnum = myrand(pnum);
+ memcpy(kbuf, &lnum, sizeof(lnum));
+ ksiz = sizeof(lnum);
+ } else if(cmp == tcbdbcmpint64){
+ int64_t llnum = myrand(pnum);
+ memcpy(kbuf, &llnum, sizeof(llnum));
+ ksiz = sizeof(llnum);
+ } else {
+ ksiz = sprintf(kbuf, "%08d", myrand(pnum));
+ }
+ if(rl){
+ char vbuf[PATH_MAX];
+ int vsiz = myrand(PATH_MAX);
+ for(int j = 0; j < vsiz; j++){
+ vbuf[j] = myrand(0x100);
+ }
+ if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(bdb, "tcbdbputcat");
+ err = true;
+ break;
+ }
+ } else {
+ if(!tcbdbputcat(bdb, kbuf, ksiz, kbuf, ksiz)){
+ eprint(bdb, "tcbdbputcat");
+ err = true;
+ break;
+ }
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+ iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+ mprint(bdb);
+ if(!tcbdbclose(bdb)){
+ eprint(bdb, "tcbdbclose");
+ err = true;
+ }
+ tcbdbdel(bdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform queue command */
+static int procqueue(const char *path, int rnum, int lmemb, int nmemb, int bnum,
+ int apow, int fpow, bool mt, BDBCMP cmp, int opts,
+ int lcnum, int ncnum, int lsmax, int capnum, int omode){
+ iprintf("<Queueing Test>\n path=%s rnum=%d lmemb=%d nmemb=%d bnum=%d apow=%d fpow=%d"
+ " mt=%d cmp=%p opts=%d lcnum=%d ncnum=%d lsmax=%d capnum=%d omode=%d\n\n",
+ path, rnum, lmemb, nmemb, bnum, apow, fpow, mt, (void *)(intptr_t)cmp,
+ opts, lcnum, ncnum, lsmax, capnum, omode);
+ bool err = false;
+ double stime = tctime();
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ if(mt && !tcbdbsetmutex(bdb)){
+ eprint(bdb, "tcbdbsetmutex");
+ err = true;
+ }
+ if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)){
+ eprint(bdb, "tcbdbsetcmpfunc");
+ err = true;
+ }
+ if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){
+ eprint(bdb, "tcbdbtune");
+ err = true;
+ }
+ if(!tcbdbsetcache(bdb, lcnum, ncnum)){
+ eprint(bdb, "tcbdbsetcache");
+ err = true;
+ }
+ if(!tcbdbsetlsmax(bdb, lsmax)){
+ eprint(bdb, "tcbdbsetlsmax");
+ err = true;
+ }
+ if(!tcbdbsetcapnum(bdb, capnum)){
+ eprint(bdb, "tcbdbsetcapnum");
+ err = true;
+ }
+ if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC)){
+ eprint(bdb, "tcbdbopen");
+ err = true;
+ }
+ int deqfreq = (lmemb > 0) ? lmemb * 2 : 256;
+ BDBCUR *cur = tcbdbcurnew(bdb);
+ for(int i = 1; i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len;
+ if(cmp == tcbdbcmpdecimal){
+ len = sprintf(buf, "%d", i);
+ } else if(cmp == tcbdbcmpint32){
+ int32_t lnum = i;
+ memcpy(buf, &lnum, sizeof(lnum));
+ len = sizeof(lnum);
+ } else if(cmp == tcbdbcmpint64){
+ int64_t llnum = i;
+ memcpy(buf, &llnum, sizeof(llnum));
+ len = sizeof(llnum);
+ } else {
+ len = sprintf(buf, "%08d", i);
+ }
+ if(!tcbdbput(bdb, buf, len, buf, len)){
+ eprint(bdb, "tcbdbput");
+ err = true;
+ break;
+ }
+ if(myrand(deqfreq) == 0){
+ if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurfirst");
+ err = true;
+ break;
+ }
+ int num = myrand(deqfreq * 2 + 1);
+ while(num >= 0){
+ if(tcbdbcurout(cur)){
+ num--;
+ } else {
+ if(tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurout");
+ err = true;
+ }
+ break;
+ }
+ }
+ if(err) break;
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurfirst");
+ err = true;
+ }
+ while(true){
+ if(tcbdbcurout(cur)) continue;
+ if(tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurout");
+ err = true;
+ }
+ break;
+ }
+ tcbdbcurdel(cur);
+ iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+ iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+ mprint(bdb);
+ if(!tcbdbclose(bdb)){
+ eprint(bdb, "tcbdbclose");
+ err = true;
+ }
+ tcbdbdel(bdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform misc command */
+static int procmisc(const char *path, int rnum, bool mt, int opts, int omode){
+ iprintf("<Miscellaneous Test>\n path=%s rnum=%d mt=%d opts=%d omode=%d\n\n",
+ path, rnum, mt, opts, omode);
+ bool err = false;
+ double stime = tctime();
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ if(mt && !tcbdbsetmutex(bdb)){
+ eprint(bdb, "tcbdbsetmutex");
+ err = true;
+ }
+ if(!tcbdbtune(bdb, 10, 10, rnum / 50, 100, -1, opts)){
+ eprint(bdb, "tcbdbtune");
+ err = true;
+ }
+ if(!tcbdbsetcache(bdb, 128, 256)){
+ eprint(bdb, "tcbdbsetcache");
+ err = true;
+ }
+ if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){
+ eprint(bdb, "tcbdbopen");
+ err = true;
+ }
+ iprintf("writing:\n");
+ for(int i = 1; i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len = sprintf(buf, "%08d", i);
+ if(!tcbdbputkeep(bdb, buf, len, buf, len)){
+ eprint(bdb, "tcbdbputkeep");
+ err = true;
+ break;
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("reading:\n");
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%08d", i);
+ int vsiz;
+ char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz);
+ if(!vbuf){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ break;
+ } else if(vsiz != ksiz || memcmp(vbuf, kbuf, vsiz)){
+ eprint(bdb, "(validation)");
+ err = true;
+ free(vbuf);
+ break;
+ }
+ free(vbuf);
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ if(tcbdbrnum(bdb) != rnum){
+ eprint(bdb, "(validation)");
+ err = true;
+ }
+ iprintf("random writing:\n");
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+ char vbuf[RECBUFSIZ];
+ int vsiz = myrand(RECBUFSIZ);
+ memset(vbuf, '*', vsiz);
+ if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(bdb, "tcbdbput");
+ err = true;
+ break;
+ }
+ int rsiz;
+ char *rbuf = tcbdbget(bdb, kbuf, ksiz, &rsiz);
+ if(!rbuf){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ break;
+ }
+ if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+ eprint(bdb, "(validation)");
+ err = true;
+ free(rbuf);
+ break;
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ free(rbuf);
+ }
+ iprintf("word writing:\n");
+ const char *words[] = {
+ "a", "A", "bb", "BB", "ccc", "CCC", "dddd", "DDDD", "eeeee", "EEEEEE",
+ "mikio", "hirabayashi", "tokyo", "cabinet", "hyper", "estraier", "19780211", "birth day",
+ "one", "first", "two", "second", "three", "third", "four", "fourth", "five", "fifth",
+ "_[1]_", "uno", "_[2]_", "dos", "_[3]_", "tres", "_[4]_", "cuatro", "_[5]_", "cinco",
+ "[\xe5\xb9\xb3\xe6\x9e\x97\xe5\xb9\xb9\xe9\x9b\x84]", "[\xe9\xa6\xac\xe9\xb9\xbf]", NULL
+ };
+ for(int i = 0; words[i] != NULL; i += 2){
+ const char *kbuf = words[i];
+ int ksiz = strlen(kbuf);
+ const char *vbuf = words[i+1];
+ int vsiz = strlen(vbuf);
+ if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(bdb, "tcbdbputkeep");
+ err = true;
+ break;
+ }
+ if(rnum > 250) putchar('.');
+ }
+ if(rnum > 250) iprintf(" (%08d)\n", sizeof(words) / sizeof(*words));
+ iprintf("random erasing:\n");
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+ if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbout");
+ err = true;
+ break;
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("writing:\n");
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "[%d]", i);
+ char vbuf[RECBUFSIZ];
+ int vsiz = i % RECBUFSIZ;
+ memset(vbuf, '*', vsiz);
+ if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(bdb, "tcbdbputkeep");
+ err = true;
+ break;
+ }
+ if(vsiz < 1){
+ char tbuf[PATH_MAX];
+ for(int j = 0; j < PATH_MAX; j++){
+ tbuf[j] = myrand(0x100);
+ }
+ if(!tcbdbput(bdb, kbuf, ksiz, tbuf, PATH_MAX)){
+ eprint(bdb, "tcbdbput");
+ err = true;
+ break;
+ }
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("erasing:\n");
+ for(int i = 1; i <= rnum; i++){
+ if(i % 2 == 1){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "[%d]", i);
+ if(!tcbdbout(bdb, kbuf, ksiz)){
+ eprint(bdb, "tcbdbout");
+ err = true;
+ break;
+ }
+ if(tcbdbout(bdb, kbuf, ksiz) || tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbout");
+ err = true;
+ break;
+ }
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("random writing and reopening:\n");
+ for(int i = 1; i <= rnum; i++){
+ if(myrand(10) == 0){
+ int ksiz, vsiz;
+ char *kbuf, *vbuf;
+ ksiz = (myrand(5) == 0) ? myrand(UINT16_MAX) : myrand(RECBUFSIZ);
+ kbuf = tcmalloc(ksiz + 1);
+ for(int j = 0; j < ksiz; j++){
+ kbuf[j] = 128 + myrand(128);
+ }
+ vsiz = (myrand(5) == 0) ? myrand(UINT16_MAX) : myrand(RECBUFSIZ);
+ vbuf = tcmalloc(vsiz + 1);
+ for(int j = 0; j < vsiz; j++){
+ vbuf[j] = myrand(256);
+ }
+ switch(myrand(5)){
+ case 0:
+ if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(bdb, "tcbdbput");
+ err = true;
+ }
+ break;
+ case 1:
+ if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(bdb, "tcbdbputcat");
+ err = true;
+ }
+ break;
+ case 2:
+ if(!tcbdbputdup(bdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(bdb, "tcbdbputdup");
+ err = true;
+ }
+ break;
+ case 3:
+ if(!tcbdbputdupback(bdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(bdb, "tcbdbputdupback");
+ err = true;
+ }
+ break;
+ default:
+ if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbout");
+ err = true;
+ }
+ break;
+ }
+ free(vbuf);
+ free(kbuf);
+ } else {
+ char kbuf[RECBUFSIZ];
+ int ksiz = myrand(RECBUFSIZ);
+ memset(kbuf, '@', ksiz);
+ char vbuf[RECBUFSIZ];
+ int vsiz = myrand(RECBUFSIZ);
+ memset(vbuf, '@', vsiz);
+ if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(bdb, "tcbdbputcat");
+ err = true;
+ break;
+ }
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ if(!tcbdbclose(bdb)){
+ eprint(bdb, "tcbdbclose");
+ err = true;
+ }
+ if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){
+ eprint(bdb, "tcbdbopen");
+ err = true;
+ }
+ iprintf("checking:\n");
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "[%d]", i);
+ int vsiz;
+ char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz);
+ if(i % 2 == 0){
+ if(!vbuf){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ break;
+ }
+ if(vsiz != i % RECBUFSIZ && vsiz != PATH_MAX){
+ eprint(bdb, "(validation)");
+ err = true;
+ free(vbuf);
+ break;
+ }
+ } else {
+ if(vbuf || tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "(validation)");
+ err = true;
+ free(vbuf);
+ break;
+ }
+ }
+ free(vbuf);
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("writing:\n");
+ for(int i = 1; i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len = sprintf(buf, "%08d", i);
+ if(!tcbdbput(bdb, buf, len, buf, len)){
+ eprint(bdb, "tcbdbput");
+ err = true;
+ break;
+ }
+ if(i % 10 == 0){
+ TCLIST *vals = tclistnew();
+ for(int j = myrand(5) + 1; j >= 0; j--){
+ tclistpush(vals, buf, len);
+ }
+ if(!tcbdbputdup3(bdb, buf, len, vals)){
+ eprint(bdb, "tcbdbput3");
+ err = true;
+ break;
+ }
+ tclistdel(vals);
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("reading:\n");
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%08d", i);
+ int vsiz;
+ char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz);
+ if(!vbuf){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ break;
+ } else if(vsiz != ksiz || memcmp(vbuf, kbuf, vsiz)){
+ eprint(bdb, "(validation)");
+ err = true;
+ free(vbuf);
+ break;
+ }
+ free(vbuf);
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("word checking:\n");
+ for(int i = 0; words[i] != NULL; i += 2){
+ const char *kbuf = words[i];
+ int ksiz = strlen(kbuf);
+ const char *vbuf = words[i+1];
+ int vsiz = strlen(vbuf);
+ int rsiz;
+ char *rbuf = tcbdbget(bdb, kbuf, ksiz, &rsiz);
+ if(!rbuf){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ break;
+ } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+ eprint(bdb, "(validation)");
+ err = true;
+ free(rbuf);
+ break;
+ }
+ free(rbuf);
+ if(rnum > 250) putchar('.');
+ }
+ if(rnum > 250) iprintf(" (%08d)\n", sizeof(words) / sizeof(*words));
+ iprintf("cursor checking:\n");
+ BDBCUR *cur = tcbdbcurnew(bdb);
+ if(!tcbdbcurfirst(cur)){
+ eprint(bdb, "tcbdbcurfirst");
+ err = true;
+ }
+ char *kbuf;
+ int ksiz;
+ int inum = 0;
+ for(int i = 1; (kbuf = tcbdbcurkey(cur, &ksiz)) != NULL; i++, inum++){
+ int vsiz;
+ char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz);
+ if(!vbuf){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ free(kbuf);
+ break;
+ }
+ free(vbuf);
+ free(kbuf);
+ tcbdbcurnext(cur);
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ if(rnum > 250) iprintf(" (%08d)\n", inum);
+ if(tcbdbecode(bdb) != TCENOREC || inum != tcbdbrnum(bdb)){
+ eprint(bdb, "(validation)");
+ err = true;
+ }
+ iprintf("cursor updating:\n");
+ if(!tcbdbcurfirst(cur)){
+ eprint(bdb, "tcbdbcurfirst");
+ err = true;
+ }
+ inum = 0;
+ for(int i = 1; !err && (kbuf = tcbdbcurkey(cur, &ksiz)) != NULL; i++, inum++){
+ switch(myrand(6)){
+ case 0:
+ if(!tcbdbputdup(bdb, kbuf, ksiz, "0123456789", 10)){
+ eprint(bdb, "tcbdbputcat");
+ err = true;
+ }
+ break;
+ case 1:
+ if(!tcbdbout(bdb, kbuf, ksiz)){
+ eprint(bdb, "tcbdbout");
+ err = true;
+ }
+ break;
+ case 2:
+ if(!tcbdbcurput(cur, kbuf, ksiz, BDBCPCURRENT)){
+ eprint(bdb, "tcbdbcurput");
+ err = true;
+ }
+ break;
+ case 3:
+ if(!tcbdbcurput(cur, kbuf, ksiz, BDBCPBEFORE)){
+ eprint(bdb, "tcbdbcurput");
+ err = true;
+ }
+ break;
+ case 4:
+ if(!tcbdbcurput(cur, kbuf, ksiz, BDBCPAFTER)){
+ eprint(bdb, "tcbdbcurput");
+ err = true;
+ }
+ break;
+ default:
+ if(!tcbdbcurout(cur) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurout");
+ err = true;
+ }
+ break;
+ }
+ free(kbuf);
+ tcbdbcurnext(cur);
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ if(rnum > 250) iprintf(" (%08d)\n", inum);
+ if(tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "(validation)");
+ err = true;
+ }
+ if(!tcbdbsync(bdb)){
+ eprint(bdb, "tcbdbsync");
+ err = true;
+ }
+ iprintf("cursor updating from empty:\n");
+ tcbdbcurfirst(cur);
+ inum = 0;
+ for(int i = 1; (kbuf = tcbdbcurkey(cur, &ksiz)) != NULL; i++, inum++){
+ free(kbuf);
+ if(!tcbdbcurout(cur) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurout");
+ err = true;
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ if(rnum > 250) iprintf(" (%08d)\n", inum);
+ if(tcbdbrnum(bdb) != 0){
+ eprint(bdb, "(validation)");
+ err = true;
+ }
+ if(!tcbdbput2(bdb, "one", "first")){
+ eprint(bdb, "tcbdbput");
+ err = true;
+ }
+ if(!tcbdbcurlast(cur)){
+ eprint(bdb, "tcbdbcurlast");
+ err = true;
+ }
+ if(!tcbdbcurput2(cur, "second", BDBCPCURRENT) || !tcbdbcurput2(cur, "first", BDBCPBEFORE) ||
+ !tcbdbcurput2(cur, "zero", BDBCPBEFORE) || !tcbdbcurput2(cur, "top", BDBCPBEFORE)){
+ eprint(bdb, "tcbdbcurput2");
+ err = true;
+ }
+ if(!tcbdbcurlast(cur)){
+ eprint(bdb, "tcbdbcurlast");
+ err = true;
+ }
+ if(!tcbdbcurput2(cur, "third", BDBCPAFTER) || !tcbdbcurput2(cur, "fourth", BDBCPAFTER) ||
+ !tcbdbcurput2(cur, "end", BDBCPCURRENT) || !tcbdbcurput2(cur, "bottom", BDBCPAFTER)){
+ eprint(bdb, "tcbdbcurput2");
+ err = true;
+ }
+ iprintf("checking transaction commit:\n");
+ if(!tcbdbtranbegin(bdb)){
+ eprint(bdb, "tcbdbtranbegin");
+ err = true;
+ }
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+ if(!tcbdbputdup(bdb, kbuf, ksiz, kbuf, ksiz)){
+ eprint(bdb, "tcbdbputdup");
+ err = true;
+ break;
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ if(!tcbdbtrancommit(bdb)){
+ eprint(bdb, "tcbdbtrancommit");
+ err = true;
+ }
+ iprintf("checking transaction abort:\n");
+ uint64_t ornum = tcbdbrnum(bdb);
+ if(!tcbdbtranbegin(bdb)){
+ eprint(bdb, "tcbdbtranbegin");
+ err = true;
+ }
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+ if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbout");
+ err = true;
+ break;
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ if(!tcbdbtranabort(bdb)){
+ eprint(bdb, "tcbdbtranabort");
+ err = true;
+ }
+ if(tcbdbrnum(bdb) != ornum){
+ eprint(bdb, "(validation)");
+ err = true;
+ }
+ if(ornum > 1000){
+ if(!tcbdbcurfirst(cur)){
+ eprint(bdb, "tcbdbcurfirst");
+ err = true;
+ }
+ for(int i = 1; i < 500 && !err && (kbuf = tcbdbcurkey(cur, &ksiz)) != NULL; i++){
+ int vsiz;
+ if(myrand(20) == 0){
+ if(!tcbdbget3(bdb, kbuf, ksiz, &vsiz)){
+ eprint(bdb, "tcbdbget3");
+ err = true;
+ }
+ if(myrand(2) == 0 && !tcbdbout(bdb, kbuf, ksiz)){
+ eprint(bdb, "tcbdbget3");
+ err = true;
+ }
+ if(myrand(2) == 0 && !tcbdbputdup(bdb, kbuf, ksiz, kbuf, ksiz)){
+ eprint(bdb, "tcbdbput");
+ err = true;
+ }
+ } else {
+ if(!tcbdbcurout(cur)){
+ eprint(bdb, "tcbdbcurout");
+ err = true;
+ }
+ }
+ free(kbuf);
+ if(myrand(30) == 0 && !tcbdbcurfirst(cur)){
+ eprint(bdb, "tcbdbcurfirst");
+ err = true;
+ }
+ }
+ }
+ if(!tcbdbvanish(bdb)){
+ eprint(bdb, "tcbdbvanish");
+ err = true;
+ }
+ if(!tcbdbtranbegin(bdb)){
+ eprint(bdb, "tcbdbtranbegin");
+ err = true;
+ }
+ if(!tcbdbput2(bdb, "mikio", "hirabayashi")){
+ eprint(bdb, "tcbdbput2");
+ err = true;
+ }
+ tcbdbcurdel(cur);
+ iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+ iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+ mprint(bdb);
+ if(!tcbdbclose(bdb)){
+ eprint(bdb, "tcbdbclose");
+ err = true;
+ }
+ tcbdbdel(bdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform wicked command */
+static int procwicked(const char *path, int rnum, bool mt, int opts, int omode){
+ iprintf("<Wicked Writing Test>\n path=%s rnum=%d mt=%d opts=%d omode=%d\n\n",
+ path, rnum, mt, opts, omode);
+ bool err = false;
+ double stime = tctime();
+ TCBDB *bdb = tcbdbnew();
+ if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd);
+ if(mt && !tcbdbsetmutex(bdb)){
+ eprint(bdb, "tcbdbsetmutex");
+ err = true;
+ }
+ if(!tcbdbtune(bdb, 10, 10, rnum / 50, 100, -1, opts)){
+ eprint(bdb, "tcbdbtune");
+ err = true;
+ }
+ if(!tcbdbsetcache(bdb, 128, 256)){
+ eprint(bdb, "tcbdbsetcache");
+ err = true;
+ }
+ if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){
+ eprint(bdb, "tcbdbopen");
+ err = true;
+ }
+ BDBCUR *cur = tcbdbcurnew(bdb);
+ if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurfirst");
+ err = true;
+ }
+ TCMAP *map = tcmapnew2(rnum / 5);
+ for(int i = 1; i <= rnum && !err; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+ char vbuf[RECBUFSIZ];
+ int vsiz = myrand(RECBUFSIZ);
+ memset(vbuf, '*', vsiz);
+ vbuf[vsiz] = '\0';
+ char *rbuf;
+ switch(myrand(16)){
+ case 0:
+ putchar('0');
+ if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(bdb, "tcbdbput");
+ err = true;
+ }
+ tcmapput(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 1:
+ putchar('1');
+ if(!tcbdbput2(bdb, kbuf, vbuf)){
+ eprint(bdb, "tcbdbput2");
+ err = true;
+ }
+ tcmapput2(map, kbuf, vbuf);
+ break;
+ case 2:
+ putchar('2');
+ if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz) && tcbdbecode(bdb) != TCEKEEP){
+ eprint(bdb, "tcbdbputkeep");
+ err = true;
+ }
+ tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 3:
+ putchar('3');
+ if(!tcbdbputkeep2(bdb, kbuf, vbuf) && tcbdbecode(bdb) != TCEKEEP){
+ eprint(bdb, "tcbdbputkeep2");
+ err = true;
+ }
+ tcmapputkeep2(map, kbuf, vbuf);
+ break;
+ case 4:
+ putchar('4');
+ if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(bdb, "tcbdbputcat");
+ err = true;
+ }
+ tcmapputcat(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 5:
+ putchar('5');
+ if(!tcbdbputcat2(bdb, kbuf, vbuf)){
+ eprint(bdb, "tcbdbputcat2");
+ err = true;
+ }
+ tcmapputcat2(map, kbuf, vbuf);
+ break;
+ case 6:
+ putchar('6');
+ if(myrand(10) == 0){
+ if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbout");
+ err = true;
+ }
+ tcmapout(map, kbuf, ksiz);
+ }
+ break;
+ case 7:
+ putchar('7');
+ if(myrand(10) == 0){
+ if(!tcbdbout2(bdb, kbuf) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbout2");
+ err = true;
+ }
+ tcmapout2(map, kbuf);
+ }
+ break;
+ case 8:
+ putchar('8');
+ if(!(rbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz))){
+ if(tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ }
+ rbuf = tcsprintf("[%d]", myrand(i + 1));
+ vsiz = strlen(rbuf);
+ }
+ vsiz += myrand(vsiz);
+ if(myrand(3) == 0) vsiz += PATH_MAX;
+ rbuf = tcrealloc(rbuf, vsiz + 1);
+ for(int j = 0; j < vsiz; j++){
+ rbuf[j] = myrand(0x100);
+ }
+ if(!tcbdbput(bdb, kbuf, ksiz, rbuf, vsiz)){
+ eprint(bdb, "tcbdbput");
+ err = true;
+ }
+ tcmapput(map, kbuf, ksiz, rbuf, vsiz);
+ free(rbuf);
+ break;
+ case 9:
+ putchar('9');
+ if(!(rbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz)) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ }
+ free(rbuf);
+ break;
+ case 10:
+ putchar('A');
+ if(!(rbuf = tcbdbget2(bdb, kbuf)) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ }
+ free(rbuf);
+ break;
+ case 11:
+ putchar('B');
+ if(myrand(1) == 0) vsiz = 1;
+ if(!tcbdbget3(bdb, kbuf, ksiz, &vsiz) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ }
+ break;
+ case 12:
+ putchar('C');
+ if(myrand(rnum / 50) == 0){
+ if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurfirst");
+ err = true;
+ }
+ }
+ TCXSTR *ikey = tcxstrnew();
+ TCXSTR *ival = tcxstrnew();
+ for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){
+ if(j % 3 == 0){
+ if(tcbdbcurrec(cur, ikey, ival)){
+ if(tcbdbvnum(bdb, tcxstrptr(ikey), tcxstrsize(ikey)) != 1){
+ eprint(bdb, "(validation)");
+ err = true;
+ }
+ if(tcxstrsize(ival) != tcbdbvsiz(bdb, tcxstrptr(ikey), tcxstrsize(ikey))){
+ eprint(bdb, "(validation)");
+ err = true;
+ }
+ } else {
+ int ecode = tcbdbecode(bdb);
+ if(ecode != TCEINVALID && ecode != TCENOREC){
+ eprint(bdb, "tcbdbcurrec");
+ err = true;
+ }
+ }
+ } else {
+ int iksiz;
+ char *ikbuf = tcbdbcurkey(cur, &iksiz);
+ if(ikbuf){
+ free(ikbuf);
+ } else {
+ int ecode = tcbdbecode(bdb);
+ if(ecode != TCEINVALID && ecode != TCENOREC){
+ eprint(bdb, "tcbdbcurkey");
+ err = true;
+ }
+ }
+ }
+ tcbdbcurnext(cur);
+ }
+ tcxstrdel(ival);
+ tcxstrdel(ikey);
+ break;
+ default:
+ putchar('@');
+ if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+ if(myrand(rnum / 32 + 1) == 0){
+ if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurfirst");
+ err = true;
+ }
+ int cnt = myrand(30);
+ for(int j = 0; j < rnum && !err; j++){
+ ksiz = sprintf(kbuf, "%d", i + j);
+ if(myrand(4) == 0){
+ if(tcbdbout3(bdb, kbuf, ksiz)){
+ cnt--;
+ } else if(tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbout3");
+ err = true;
+ }
+ tcmapout(map, kbuf, ksiz);
+ } else if(myrand(30) == 0){
+ int tksiz;
+ char *tkbuf = tcbdbcurkey(cur, &tksiz);
+ if(tkbuf){
+ if(tcbdbcurout(cur)){
+ cnt--;
+ } else {
+ eprint(bdb, "tcbdbcurout");
+ err = true;
+ }
+ tcmapout(map, tkbuf, tksiz);
+ free(tkbuf);
+ } else if(tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurfirst");
+ err = true;
+ }
+ } else {
+ if(tcbdbout(bdb, kbuf, ksiz)){
+ cnt--;
+ } else if(tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbout");
+ err = true;
+ }
+ tcmapout(map, kbuf, ksiz);
+ }
+ if(cnt < 0) break;
+ }
+ }
+ break;
+ }
+ if(i % 50 == 0) iprintf(" (%08d)\n", i);
+ if(i == rnum / 2){
+ if(!tcbdbclose(bdb)){
+ eprint(bdb, "tcbdbclose");
+ err = true;
+ }
+ if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){
+ eprint(bdb, "tcbdbopen");
+ err = true;
+ }
+ } else if(i == rnum / 4){
+ char *npath = tcsprintf("%s-tmp", path);
+ if(!tcbdbcopy(bdb, npath)){
+ eprint(bdb, "tcbdbcopy");
+ err = true;
+ }
+ TCBDB *nbdb = tcbdbnew();
+ if(!tcbdbopen(nbdb, npath, BDBOREADER | omode)){
+ eprint(nbdb, "tcbdbopen");
+ err = true;
+ }
+ tcbdbdel(nbdb);
+ unlink(npath);
+ free(npath);
+ if(!tcbdboptimize(bdb, -1, -1, -1, -1, -1, -1)){
+ eprint(bdb, "tcbdboptimize");
+ err = true;
+ }
+ if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "tcbdbcurfirst");
+ err = true;
+ }
+ } else if(i == rnum / 8){
+ if(!tcbdbtranbegin(bdb)){
+ eprint(bdb, "tcbdbtranbegin");
+ err = true;
+ }
+ } else if(i == rnum / 8 + rnum / 16){
+ if(!tcbdbtrancommit(bdb)){
+ eprint(bdb, "tcbdbtrancommit");
+ err = true;
+ }
+ }
+ }
+ if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
+ if(!tcbdbsync(bdb)){
+ eprint(bdb, "tcbdbsync");
+ err = true;
+ }
+ if(tcbdbrnum(bdb) != tcmaprnum(map)){
+ eprint(bdb, "(validation)");
+ err = true;
+ }
+ for(int i = 1; i <= rnum && !err; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", i - 1);
+ int vsiz;
+ const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz);
+ int rsiz;
+ char *rbuf = tcbdbget(bdb, kbuf, ksiz, &rsiz);
+ if(vbuf){
+ putchar('.');
+ if(!rbuf){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+ eprint(bdb, "(validation)");
+ err = true;
+ }
+ } else {
+ putchar('*');
+ if(rbuf || tcbdbecode(bdb) != TCENOREC){
+ eprint(bdb, "(validation)");
+ err = true;
+ }
+ }
+ free(rbuf);
+ if(i % 50 == 0) iprintf(" (%08d)\n", i);
+ }
+ if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
+ tcmapiterinit(map);
+ int ksiz;
+ const char *kbuf;
+ for(int i = 1; (kbuf = tcmapiternext(map, &ksiz)) != NULL; i++){
+ putchar('+');
+ int vsiz;
+ const char *vbuf = tcmapiterval(kbuf, &vsiz);
+ int rsiz;
+ char *rbuf = tcbdbget(bdb, kbuf, ksiz, &rsiz);
+ if(!rbuf){
+ eprint(bdb, "tcbdbget");
+ err = true;
+ } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+ eprint(bdb, "(validation)");
+ err = true;
+ }
+ free(rbuf);
+ if(!tcbdbout(bdb, kbuf, ksiz)){
+ eprint(bdb, "tcbdbout");
+ err = true;
+ }
+ if(i % 50 == 0) iprintf(" (%08d)\n", i);
+ }
+ int mrnum = tcmaprnum(map);
+ if(mrnum % 50 > 0) iprintf(" (%08d)\n", mrnum);
+ if(tcbdbrnum(bdb) != 0){
+ eprint(bdb, "(validation)");
+ err = true;
+ }
+ tcbdbcurdel(cur);
+ iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb));
+ iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb));
+ mprint(bdb);
+ tcmapdel(map);
+ if(!tcbdbclose(bdb)){
+ eprint(bdb, "tcbdbclose");
+ err = true;
+ }
+ tcbdbdel(bdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+
+// END OF FILE
--- /dev/null
+/*************************************************************************************************
+ * The hash database API of Tokyo Cabinet
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include "tcutil.h"
+#include "tchdb.h"
+#include "myconf.h"
+
+#define HDBFILEMODE 00644 // permission of created files
+#define HDBIOBUFSIZ 8192 // size of an I/O buffer
+
+#define HDBMAGICDATA "ToKyO CaBiNeT" // magic data for identification
+#define HDBHEADSIZ 256 // size of the reagion of the header
+#define HDBTYPEOFF 32 // offset of the region for the database type
+#define HDBFLAGSOFF 33 // offset of the region for the additional flags
+#define HDBAPOWOFF 34 // offset of the region for the alignment power
+#define HDBFPOWOFF 35 // offset of the region for the free block pool power
+#define HDBOPTSOFF 36 // offset of the region for the options
+#define HDBBNUMOFF 40 // offset of the region for the bucket number
+#define HDBRNUMOFF 48 // offset of the region for the record number
+#define HDBFSIZOFF 56 // offset of the region for the file size
+#define HDBFRECOFF 64 // offset of the region for the first record offset
+#define HDBOPAQUEOFF 128 // offset of the region for the opaque field
+
+#define HDBDEFBNUM 16381 // default bucket number
+#define HDBDEFAPOW 4 // default alignment power
+#define HDBMAXAPOW 16 // maximum alignment power
+#define HDBDEFFPOW 10 // default free block pool power
+#define HDBMAXFPOW 20 // maximum free block pool power
+#define HDBMINRUNIT 48 // minimum record reading unit
+#define HDBMAXHSIZ 32 // maximum record header size
+#define HDBFBPBSIZ 64 // base region size of the free block pool
+#define HDBFBPESIZ 4 // size of each region of the free block pool
+#define HDBFBPMGFREQ 256 // frequency to merge the free block pool
+#define HDBDRPUNIT 65536 // unit size of the delayed record pool
+#define HDBDRPLAT 2048 // latitude size of the delayed record pool
+#define HDBCACHEOUT 128 // number of records in a process of cacheout
+
+typedef struct { // type of structure for a record
+ uint64_t off; // offset of the record
+ uint32_t rsiz; // size of the whole record
+ uint8_t magic; // magic number
+ uint8_t hash; // second hash value
+ uint64_t left; // offset of the left child record
+ uint64_t right; // offset of the right child record
+ uint32_t ksiz; // size of the key
+ uint32_t vsiz; // size of the value
+ uint16_t psiz; // size of the padding
+ const char *kbuf; // pointer to the key
+ const char *vbuf; // pointer to the value
+ uint64_t boff; // offset of the body
+ char *bbuf; // buffer of the body
+} TCHREC;
+
+typedef struct { // type of structure for a free block
+ uint64_t off; // offset of the block
+ uint32_t rsiz; // size of the block
+} HDBFB;
+
+enum { // enumeration for magic data
+ HDBMAGICREC = 0xc8, // for data block
+ HDBMAGICFB = 0xb0 // for free block
+};
+
+enum { // enumeration for duplication behavior
+ HDBPDOVER, // overwrite an existing value
+ HDBPDKEEP, // keep the existing value
+ HDBPDCAT // concatenate values
+};
+
+
+/* private macros */
+#define HDBLOCKMETHOD(TC_hdb, TC_wr) \
+ ((TC_hdb)->mmtx ? tchdblockmethod((TC_hdb), (TC_wr)) : true)
+#define HDBUNLOCKMETHOD(TC_hdb) \
+ ((TC_hdb)->mmtx ? tchdbunlockmethod(TC_hdb) : true)
+#define HDBLOCKDRP(TC_hdb) \
+ ((TC_hdb)->mmtx ? tchdblockdrp(TC_hdb) : true)
+#define HDBUNLOCKDRP(TC_hdb) \
+ ((TC_hdb)->mmtx ? tchdbunlockdrp(TC_hdb) : true)
+
+
+/* private function prototypes */
+static bool tcseekwrite(TCHDB *hdb, off_t off, const void *buf, size_t size);
+static bool tcseekread(TCHDB *hdb, off_t off, void *buf, size_t size);
+static void tcdumpmeta(TCHDB *hdb, char *hbuf);
+static void tcloadmeta(TCHDB *hdb, const char *hbuf);
+static uint64_t tcgetprime(uint64_t num);
+static void tchdbclear(TCHDB *hdb);
+static int32_t tchdbpadsize(TCHDB *hdb);
+static void tchdbsetflag(TCHDB *hdb, int flag, bool sign);
+static uint64_t tchdbbidx(TCHDB *hdb, const char *kbuf, int ksiz, uint8_t *hp);
+static off_t tchdbgetbucket(TCHDB *hdb, uint64_t bidx);
+static void tchdbsetbucket(TCHDB *hdb, uint64_t bidx, uint64_t off);
+static bool tchdbsavefbp(TCHDB *hdb);
+static bool tchdbloadfbp(TCHDB *hdb);
+static void tcfbpsortbyoff(HDBFB *fbpool, int fbpnum);
+static void tcfbpsortbyrsiz(HDBFB *fbpool, int fbpnum);
+static void tchdbfbpmerge(TCHDB *hdb);
+static void tchdbfbpinsert(TCHDB *hdb, uint64_t off, uint32_t rsiz);
+static bool tchdbfbpsearch(TCHDB *hdb, TCHREC *rec);
+static bool tchdbfbpsplice(TCHDB *hdb, TCHREC *rec, uint32_t nsiz);
+static bool tchdbwritefb(TCHDB *hdb, uint64_t off, uint32_t rsiz);
+static bool tchdbwriterec(TCHDB *hdb, TCHREC *rec, uint64_t bidx, off_t entoff);
+static bool tchdbreadrec(TCHDB *hdb, TCHREC *rec, char *rbuf);
+static bool tchdbreadrecbody(TCHDB *hdb, TCHREC *rec);
+static int tcreckeycmp(const char *abuf, int asiz, const char *bbuf, int bsiz);
+static bool tchdbflushdrp(TCHDB *hdb);
+static void tchdbcacheadjust(TCHDB *hdb);
+static bool tchdbopenimpl(TCHDB *hdb, const char *path, int omode);
+static bool tchdbcloseimpl(TCHDB *hdb);
+static bool tchdbputimpl(TCHDB *hdb, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+ int dmode);
+static void tchdbdrpappend(TCHDB *hdb, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+ uint8_t hash);
+static bool tchdbputasyncimpl(TCHDB *hdb, const char *kbuf, int ksiz, const char *vbuf, int vsiz);
+static bool tchdboutimpl(TCHDB *hdb, const char *kbuf, int ksiz);
+static char *tchdbgetimpl(TCHDB *hdb, const char *kbuf, int ksiz, int *sp);
+static int tchdbgetintobuf(TCHDB *hdb, const char *kbuf, int ksiz, char *vbuf, int max);
+static int tchdbvsizimpl(TCHDB *hdb, const char *kbuf, int ksiz);
+static bool tchdbiterinitimpl(TCHDB *hdb);
+static char *tchdbiternextimpl(TCHDB *hdb, int *sp);
+static bool tchdbiternextintoxstr(TCHDB *hdb, TCXSTR *kxstr, TCXSTR *vxstr);
+static bool tchdboptimizeimpl(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+static bool tchdbvanishimpl(TCHDB *hdb);
+static bool tchdblockmethod(TCHDB *hdb, bool wr);
+static bool tchdbunlockmethod(TCHDB *hdb);
+static bool tchdblockdrp(TCHDB *hdb);
+static bool tchdbunlockdrp(TCHDB *hdb);
+
+
+/* debugging function prototypes */
+void tchdbprintmeta(TCHDB *hdb);
+void tchdbprintrec(TCHDB *hdb, TCHREC *rec);
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+/* Get the message string corresponding to an error code. */
+const char *tchdberrmsg(int ecode){
+ switch(ecode){
+ case TCESUCCESS: return "success";
+ case TCETHREAD: return "threading error";
+ case TCEINVALID: return "invalid operation";
+ case TCENOFILE: return "file not found";
+ case TCENOPERM: return "no permission";
+ case TCEMETA: return "invalid meta data";
+ case TCERHEAD: return "invalid record header";
+ case TCEOPEN: return "open error";
+ case TCECLOSE: return "close error";
+ case TCETRUNC: return "trunc error";
+ case TCESYNC: return "sync error";
+ case TCESTAT: return "stat error";
+ case TCESEEK: return "seek error";
+ case TCEREAD: return "read error";
+ case TCEWRITE: return "write error";
+ case TCEMMAP: return "mmap error";
+ case TCELOCK: return "lock error";
+ case TCEUNLINK: return "unlink error";
+ case TCERENAME: return "rename error";
+ case TCEMKDIR: return "mkdir error";
+ case TCERMDIR: return "rmdir error";
+ case TCEKEEP: return "existing record";
+ case TCENOREC: return "no record found";
+ case TCEMISC: return "miscellaneous error";
+ }
+ return "unknown error";
+}
+
+
+/* Create a hash database object. */
+TCHDB *tchdbnew(void){
+ TCHDB *hdb;
+ TCMALLOC(hdb, sizeof(*hdb));
+ tchdbclear(hdb);
+ return hdb;
+}
+
+
+/* Delete a hash database object. */
+void tchdbdel(TCHDB *hdb){
+ assert(hdb);
+ if(hdb->fd >= 0) tchdbclose(hdb);
+ if(hdb->mmtx){
+ pthread_key_delete(*(pthread_key_t *)hdb->eckey);
+ pthread_mutex_destroy(hdb->dmtx);
+ pthread_rwlock_destroy(hdb->mmtx);
+ free(hdb->eckey);
+ free(hdb->dmtx);
+ free(hdb->mmtx);
+ }
+ free(hdb);
+}
+
+
+/* Get the last happened error code of a hash database object. */
+int tchdbecode(TCHDB *hdb){
+ assert(hdb);
+ return hdb->mmtx ?
+ (int)(intptr_t)pthread_getspecific(*(pthread_key_t *)hdb->eckey) : hdb->ecode;
+}
+
+
+/* Set mutual exclusion control of a hash database object for threading. */
+bool tchdbsetmutex(TCHDB *hdb){
+ assert(hdb);
+ if(!TCUSEPTHREAD) return true;
+ if(!tcglobalmutexlock()) return false;
+ if(hdb->mmtx || hdb->fd >= 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ tcglobalmutexunlock();
+ return false;
+ }
+ pthread_mutexattr_t rma;
+ pthread_mutexattr_init(&rma);
+ TCMALLOC(hdb->mmtx, sizeof(pthread_rwlock_t));
+ TCMALLOC(hdb->dmtx, sizeof(pthread_mutex_t));
+ TCMALLOC(hdb->eckey, sizeof(pthread_key_t));
+ if(pthread_mutexattr_settype(&rma, PTHREAD_MUTEX_RECURSIVE) != 0 ||
+ pthread_rwlock_init(hdb->mmtx, NULL) != 0 || pthread_mutex_init(hdb->dmtx, &rma) != 0 ||
+ pthread_key_create(hdb->eckey, NULL) != 0){
+ tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
+ pthread_mutexattr_destroy(&rma);
+ free(hdb->eckey);
+ free(hdb->dmtx);
+ free(hdb->mmtx);
+ hdb->eckey = NULL;
+ hdb->dmtx = NULL;
+ hdb->mmtx = NULL;
+ tcglobalmutexunlock();
+ return false;
+ }
+ pthread_mutexattr_destroy(&rma);
+ tcglobalmutexunlock();
+ return true;
+}
+
+
+/* Set the tuning parameters of a hash database object. */
+bool tchdbtune(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){
+ assert(hdb);
+ if(hdb->fd >= 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ hdb->bnum = (bnum > 0) ? tcgetprime(bnum) : HDBDEFBNUM;
+ hdb->apow = (apow >= 0) ? tclmin(apow, HDBMAXAPOW) : HDBDEFAPOW;
+ hdb->fpow = (fpow >= 0) ? tclmin(fpow, HDBMAXFPOW) : HDBDEFFPOW;
+ hdb->opts = opts;
+ if(!_tc_deflate) hdb->opts &= ~HDBTDEFLATE;
+ return true;
+}
+
+
+/* Set the caching parameters of a hash database object. */
+bool tchdbsetcache(TCHDB *hdb, int32_t rcnum){
+ assert(hdb);
+ if(hdb->fd >= 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ hdb->rcnum = (rcnum > 0) ? tclmin(tclmax(rcnum, HDBCACHEOUT * 2), INT_MAX / 4) : 0;
+ return true;
+}
+
+
+/* Open a database file and connect a hash database object. */
+bool tchdbopen(TCHDB *hdb, const char *path, int omode){
+ assert(hdb && path);
+ if(!HDBLOCKMETHOD(hdb, true)) return false;
+ if(hdb->fd >= 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ bool rv = tchdbopenimpl(hdb, path, omode);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+}
+
+
+/* Close a database object. */
+bool tchdbclose(TCHDB *hdb){
+ assert(hdb);
+ if(!HDBLOCKMETHOD(hdb, true)) return false;
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ bool rv = tchdbcloseimpl(hdb);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+}
+
+
+/* Store a record into a hash database object. */
+bool tchdbput(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+ assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ if(!HDBLOCKMETHOD(hdb, true)) return false;
+ if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ if(hdb->async && !tchdbflushdrp(hdb)){
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ if(hdb->zmode){
+ char *zbuf;
+ if(hdb->opts & HDBTDEFLATE){
+ zbuf = _tc_deflate(vbuf, vsiz, &vsiz, _TCZMRAW);
+ } else {
+ zbuf = tcbsencode(vbuf, vsiz, &vsiz);
+ }
+ if(!zbuf){
+ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ bool rv = tchdbputimpl(hdb, kbuf, ksiz, zbuf, vsiz, HDBPDOVER);
+ free(zbuf);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+ }
+ bool rv = tchdbputimpl(hdb, kbuf, ksiz, vbuf, vsiz, HDBPDOVER);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+}
+
+
+/* Store a string record into a hash database object. */
+bool tchdbput2(TCHDB *hdb, const char *kstr, const char *vstr){
+ assert(hdb && kstr && vstr);
+ return tchdbput(hdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Store a new record into a hash database object. */
+bool tchdbputkeep(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+ assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ if(!HDBLOCKMETHOD(hdb, true)) return false;
+ if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ if(hdb->async && !tchdbflushdrp(hdb)){
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ if(hdb->zmode){
+ char *zbuf;
+ if(hdb->opts & HDBTDEFLATE){
+ zbuf = _tc_deflate(vbuf, vsiz, &vsiz, _TCZMRAW);
+ } else {
+ zbuf = tcbsencode(vbuf, vsiz, &vsiz);
+ }
+ if(!zbuf){
+ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ bool rv = tchdbputimpl(hdb, kbuf, ksiz, zbuf, vsiz, HDBPDKEEP);
+ free(zbuf);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+ }
+ bool rv = tchdbputimpl(hdb, kbuf, ksiz, vbuf, vsiz, HDBPDKEEP);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+}
+
+
+/* Store a new string record into a hash database object. */
+bool tchdbputkeep2(TCHDB *hdb, const char *kstr, const char *vstr){
+ return tchdbputkeep(hdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Concatenate a value at the end of the existing record in a hash database object. */
+bool tchdbputcat(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+ assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ if(!HDBLOCKMETHOD(hdb, true)) return false;
+ if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ if(hdb->async && !tchdbflushdrp(hdb)){
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ if(hdb->zmode){
+ char *zbuf;
+ int osiz;
+ char *obuf = tchdbgetimpl(hdb, kbuf, ksiz, &osiz);
+ if(obuf){
+ TCREALLOC(obuf, obuf, osiz + vsiz + 1);
+ memcpy(obuf + osiz, vbuf, vsiz);
+ if(hdb->opts & HDBTDEFLATE){
+ zbuf = _tc_deflate(obuf, osiz + vsiz, &vsiz, _TCZMRAW);
+ } else {
+ zbuf = tcbsencode(obuf, osiz + vsiz, &vsiz);
+ }
+ free(obuf);
+ } else {
+ if(hdb->opts & HDBTDEFLATE){
+ zbuf = _tc_deflate(vbuf, vsiz, &vsiz, _TCZMRAW);
+ } else {
+ zbuf = tcbsencode(vbuf, vsiz, &vsiz);
+ }
+ }
+ if(!zbuf){
+ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ bool rv = tchdbputimpl(hdb, kbuf, ksiz, zbuf, vsiz, HDBPDOVER);
+ free(zbuf);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+ }
+ bool rv = tchdbputimpl(hdb, kbuf, ksiz, vbuf, vsiz, HDBPDCAT);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+}
+
+
+/* Concatenate a string value at the end of the existing record in a hash database object. */
+bool tchdbputcat2(TCHDB *hdb, const char *kstr, const char *vstr){
+ assert(hdb && kstr && vstr);
+ return tchdbputcat(hdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Store a record into a hash database object in asynchronous fashion. */
+bool tchdbputasync(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+ assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ if(!HDBLOCKMETHOD(hdb, true)) return false;
+ hdb->async = true;
+ if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ if(hdb->zmode){
+ char *zbuf;
+ if(hdb->opts & HDBTDEFLATE){
+ zbuf = _tc_deflate(vbuf, vsiz, &vsiz, _TCZMRAW);
+ } else {
+ zbuf = tcbsencode(vbuf, vsiz, &vsiz);
+ }
+ if(!zbuf){
+ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ bool rv = tchdbputasyncimpl(hdb, kbuf, ksiz, zbuf, vsiz);
+ free(zbuf);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+ }
+ bool rv = tchdbputasyncimpl(hdb, kbuf, ksiz, vbuf, vsiz);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+}
+
+
+/* Store a string record into a hash database object in asynchronous fashion. */
+bool tchdbputasync2(TCHDB *hdb, const char *kstr, const char *vstr){
+ assert(hdb && kstr && vstr);
+ return tchdbputasync(hdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Remove a record of a hash database object. */
+bool tchdbout(TCHDB *hdb, const void *kbuf, int ksiz){
+ assert(hdb && kbuf && ksiz >= 0);
+ if(!HDBLOCKMETHOD(hdb, true)) return false;
+ if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ if(hdb->async && !tchdbflushdrp(hdb)){
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ bool rv = tchdboutimpl(hdb, kbuf, ksiz);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+}
+
+
+/* Remove a string record of a hash database object. */
+bool tchdbout2(TCHDB *hdb, const char *kstr){
+ assert(hdb && kstr);
+ return tchdbout(hdb, kstr, strlen(kstr));
+}
+
+
+/* Retrieve a record in a hash database object. */
+void *tchdbget(TCHDB *hdb, const void *kbuf, int ksiz, int *sp){
+ assert(hdb && kbuf && ksiz >= 0 && sp);
+ if(!HDBLOCKMETHOD(hdb, false)) return NULL;
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return NULL;
+ }
+ if(hdb->async && !tchdbflushdrp(hdb)){
+ HDBUNLOCKMETHOD(hdb);
+ return NULL;
+ }
+ char *rv = tchdbgetimpl(hdb, kbuf, ksiz, sp);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+}
+
+
+/* Retrieve a string record in a hash database object. */
+char *tchdbget2(TCHDB *hdb, const char *kstr){
+ assert(hdb && kstr);
+ int vsiz;
+ return tchdbget(hdb, kstr, strlen(kstr), &vsiz);
+}
+
+
+/* Retrieve a record in a hash database object and write the value into a buffer. */
+int tchdbget3(TCHDB *hdb, const void *kbuf, int ksiz, void *vbuf, int max){
+ assert(hdb && kbuf && ksiz >= 0 && vbuf && max >= 0);
+ if(!HDBLOCKMETHOD(hdb, false)) return -1;
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return -1;
+ }
+ if(hdb->async && !tchdbflushdrp(hdb)){
+ HDBUNLOCKMETHOD(hdb);
+ return -1;
+ }
+ int rv = tchdbgetintobuf(hdb, kbuf, ksiz, vbuf, max);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+}
+
+
+/* Get the size of the value of a record in a hash database object. */
+int tchdbvsiz(TCHDB *hdb, const void *kbuf, int ksiz){
+ assert(hdb && kbuf && ksiz >= 0);
+ if(!HDBLOCKMETHOD(hdb, false)) return -1;
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return -1;
+ }
+ if(hdb->async && !tchdbflushdrp(hdb)){
+ HDBUNLOCKMETHOD(hdb);
+ return -1;
+ }
+ int rv = tchdbvsizimpl(hdb, kbuf, ksiz);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+}
+
+
+/* Get the size of the value of a string record in a hash database object. */
+int tchdbvsiz2(TCHDB *hdb, const char *kstr){
+ assert(hdb && kstr);
+ return tchdbvsiz(hdb, kstr, strlen(kstr));
+}
+
+
+/* Initialize the iterator of a hash database object. */
+bool tchdbiterinit(TCHDB *hdb){
+ assert(hdb);
+ if(!HDBLOCKMETHOD(hdb, true)) return false;
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ if(hdb->async && !tchdbflushdrp(hdb)){
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ bool rv = tchdbiterinitimpl(hdb);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+}
+
+
+/* Get the next key of the iterator of a hash database object. */
+void *tchdbiternext(TCHDB *hdb, int *sp){
+ assert(hdb && sp);
+ if(!HDBLOCKMETHOD(hdb, true)) return NULL;
+ if(hdb->fd < 0 || hdb->iter < 1){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return NULL;
+ }
+ if(hdb->async && !tchdbflushdrp(hdb)){
+ HDBUNLOCKMETHOD(hdb);
+ return NULL;
+ }
+ char *rv = tchdbiternextimpl(hdb, sp);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+}
+
+
+/* Get the next key string of the iterator of a hash database object. */
+char *tchdbiternext2(TCHDB *hdb){
+ assert(hdb);
+ int vsiz;
+ return tchdbiternext(hdb, &vsiz);
+}
+
+
+/* Get the next extensible objects of the iterator of a hash database object. */
+bool tchdbiternext3(TCHDB *hdb, TCXSTR *kxstr, TCXSTR *vxstr){
+ assert(hdb && kxstr && vxstr);
+ if(!HDBLOCKMETHOD(hdb, true)) return false;
+ if(hdb->fd < 0 || hdb->iter < 1){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ if(hdb->async && !tchdbflushdrp(hdb)){
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ bool rv = tchdbiternextintoxstr(hdb, kxstr, vxstr);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+}
+
+
+/* Get forward matching keys in a hash database object. */
+TCLIST *tchdbfwmkeys(TCHDB *hdb, const void *pbuf, int psiz, int max){
+ assert(hdb && pbuf && psiz >= 0);
+ TCLIST* keys = tclistnew();
+ if(!HDBLOCKMETHOD(hdb, true)) return keys;
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return keys;
+ }
+ if(hdb->async && !tchdbflushdrp(hdb)){
+ HDBUNLOCKMETHOD(hdb);
+ return keys;
+ }
+ if(max < 0) max = INT_MAX;
+ uint64_t iter = hdb->iter;
+ tchdbiterinitimpl(hdb);
+ char *kbuf;
+ int ksiz;
+ while(TCLISTNUM(keys) < max && (kbuf = tchdbiternextimpl(hdb, &ksiz)) != NULL){
+ if(ksiz >= psiz && !memcmp(kbuf, pbuf, psiz)){
+ tclistpushmalloc(keys, kbuf, ksiz);
+ } else {
+ free(kbuf);
+ }
+ }
+ hdb->iter = iter;
+ HDBUNLOCKMETHOD(hdb);
+ return keys;
+}
+
+
+/* Get forward matching string keys in a hash database object. */
+TCLIST *tchdbfwmkeys2(TCHDB *hdb, const char *pstr, int max){
+ assert(hdb && pstr);
+ return tchdbfwmkeys(hdb, pstr, strlen(pstr), max);
+}
+
+
+/* Synchronize updated contents of a hash database object with the file and the device. */
+bool tchdbsync(TCHDB *hdb){
+ assert(hdb);
+ if(!HDBLOCKMETHOD(hdb, true)) return false;
+ if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ if(hdb->async && !tchdbflushdrp(hdb)){
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ bool rv = tchdbmemsync(hdb, true);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+}
+
+
+/* Optimize the file of a hash database object. */
+bool tchdboptimize(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){
+ assert(hdb);
+ if(!HDBLOCKMETHOD(hdb, true)) return false;
+ if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ if(hdb->async && !tchdbflushdrp(hdb)){
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ bool rv = tchdboptimizeimpl(hdb, bnum, apow, fpow, opts);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+}
+
+
+/* Remove all records of a hash database object. */
+bool tchdbvanish(TCHDB *hdb){
+ assert(hdb);
+ if(!HDBLOCKMETHOD(hdb, true)) return false;
+ if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ if(hdb->async && !tchdbflushdrp(hdb)){
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ bool rv = tchdbvanishimpl(hdb);
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+}
+
+
+/* Copy the database file of a hash database object. */
+bool tchdbcopy(TCHDB *hdb, const char *path){
+ assert(hdb && path);
+ if(!HDBLOCKMETHOD(hdb, false)) return false;
+ if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ if(hdb->async && !tchdbflushdrp(hdb)){
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ if(!HDBLOCKDRP(hdb)){
+ HDBUNLOCKMETHOD(hdb);
+ return false;
+ }
+ bool err = false;
+ hdb->flags &= ~HDBFOPEN;
+ if(hdb->omode & HDBOWRITER){
+ if(!tchdbsavefbp(hdb)) err = true;
+ if(!tchdbmemsync(hdb, false)) err = true;
+ }
+ if(*path == '@'){
+ int len = strlen(hdb->path);
+ char name[len*2+1];
+ char *wp = name;
+ for(int i = 0; i < len; i++){
+ switch(hdb->path[i]){
+ case '\\':
+ case '$':
+ *(wp++) = '\\';
+ *(wp++) = hdb->path[i];
+ break;
+ default:
+ *(wp++) = hdb->path[i];
+ break;
+ }
+ }
+ *wp = '\0';
+ char *cmd = tcsprintf("%s \"%s\" \"%llu\"",
+ path + 1, name, (unsigned long long)(tctime() * 1000000));
+ if(system(cmd) != 0) err = true;
+ free(cmd);
+ } else {
+ if(!tccopyfile(hdb->path, path)){
+ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
+ err = true;
+ }
+ }
+ hdb->flags |= HDBFOPEN;
+ HDBUNLOCKDRP(hdb);
+ HDBUNLOCKMETHOD(hdb);
+ return !err;
+}
+
+
+/* Get the file path of a hash database object. */
+const char *tchdbpath(TCHDB *hdb){
+ assert(hdb);
+ if(!HDBLOCKMETHOD(hdb, false)) return NULL;
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return NULL;
+ }
+ const char *rv = hdb->path;
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+}
+
+
+/* Get the number of records of a hash database object. */
+uint64_t tchdbrnum(TCHDB *hdb){
+ assert(hdb);
+ if(!HDBLOCKMETHOD(hdb, false)) return 0;
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return 0;
+ }
+ uint64_t rv = hdb->rnum;
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+}
+
+
+/* Get the size of the database file of a hash database object. */
+uint64_t tchdbfsiz(TCHDB *hdb){
+ assert(hdb);
+ if(!HDBLOCKMETHOD(hdb, false)) return 0;
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ HDBUNLOCKMETHOD(hdb);
+ return 0;
+ }
+ uint64_t rv = hdb->fsiz;
+ HDBUNLOCKMETHOD(hdb);
+ return rv;
+}
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Set the error code of a hash database object. */
+void tchdbsetecode(TCHDB *hdb, int ecode, const char *filename, int line, const char *func){
+ assert(hdb && filename && line >= 1 && func);
+ if(!hdb->fatal){
+ hdb->ecode = ecode;
+ if(hdb->mmtx) pthread_setspecific(*(pthread_key_t *)hdb->eckey, (void *)(intptr_t)ecode);
+ }
+ if(ecode != TCEINVALID && ecode != TCEKEEP && ecode != TCENOREC){
+ hdb->fatal = true;
+ if(hdb->omode & HDBOWRITER) tchdbsetflag(hdb, HDBFFATAL, true);
+ }
+ if(hdb->dbgfd >= 0){
+ char obuf[HDBIOBUFSIZ];
+ int osiz = sprintf(obuf, "ERROR:%s:%d:%s:%s:%d:%s\n", filename, line, func,
+ hdb->path ? hdb->path : "-", ecode, tchdberrmsg(ecode));
+ tcwrite(hdb->dbgfd, obuf, osiz);
+ }
+}
+
+
+/* Set the type of a hash database object. */
+void tchdbsettype(TCHDB *hdb, uint8_t type){
+ assert(hdb);
+ if(hdb->fd >= 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return;
+ }
+ hdb->type = type;
+}
+
+
+/* Set the file descriptor for debugging output. */
+void tchdbsetdbgfd(TCHDB *hdb, int fd){
+ assert(hdb && fd >= 0);
+ hdb->dbgfd = fd;
+}
+
+
+/* Get the file descriptor for debugging output. */
+int tchdbdbgfd(TCHDB *hdb){
+ assert(hdb);
+ return hdb->dbgfd;
+}
+
+
+/* Synchronize updating contents on memory. */
+bool tchdbmemsync(TCHDB *hdb, bool phys){
+ assert(hdb);
+ if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ bool err = false;
+ char hbuf[HDBHEADSIZ];
+ tcdumpmeta(hdb, hbuf);
+ memcpy(hdb->map, hbuf, HDBOPAQUEOFF);
+ if(phys){
+ if(msync(hdb->map, hdb->msiz, MS_SYNC) == -1){
+ tchdbsetecode(hdb, TCEMMAP, __FILE__, __LINE__, __func__);
+ err = true;
+ }
+ if(fsync(hdb->fd) == -1){
+ tchdbsetecode(hdb, TCESYNC, __FILE__, __LINE__, __func__);
+ err = true;
+ }
+ }
+ return !err;
+}
+
+
+/* Get the number of elements of the bucket array of a hash database object. */
+uint64_t tchdbbnum(TCHDB *hdb){
+ assert(hdb);
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return hdb->bnum;
+}
+
+
+/* Get the record alignment a hash database object. */
+uint32_t tchdbalign(TCHDB *hdb){
+ assert(hdb);
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return hdb->align;
+}
+
+
+/* Get the maximum number of the free block pool of a a hash database object. */
+uint32_t tchdbfbpmax(TCHDB *hdb){
+ assert(hdb);
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return hdb->fbpmax;
+}
+
+
+/* Get the inode number of the database file of a hash database object. */
+uint64_t tchdbinode(TCHDB *hdb){
+ assert(hdb);
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return hdb->inode;
+}
+
+
+/* Get the modification time of the database file of a hash database object. */
+time_t tchdbmtime(TCHDB *hdb){
+ assert(hdb);
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return hdb->mtime;
+}
+
+
+/* Get the connection mode of a hash database object. */
+int tchdbomode(TCHDB *hdb){
+ assert(hdb);
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return hdb->omode;
+}
+
+
+/* Get the database type of a hash database object. */
+uint8_t tchdbtype(TCHDB *hdb){
+ assert(hdb);
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return hdb->type;
+}
+
+
+/* Get the additional flags of a hash database object. */
+uint8_t tchdbflags(TCHDB *hdb){
+ assert(hdb);
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return hdb->flags;
+}
+
+
+/* Get the options of a hash database object. */
+uint8_t tchdbopts(TCHDB *hdb){
+ assert(hdb);
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ return hdb->opts;
+}
+
+
+/* Get the pointer to the opaque field of a hash database object. */
+char *tchdbopaque(TCHDB *hdb){
+ assert(hdb);
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return NULL;
+ }
+ return hdb->map + HDBOPAQUEOFF;
+}
+
+
+/* Get the number of used elements of the bucket array of a hash database object. */
+uint64_t tchdbbnumused(TCHDB *hdb){
+ assert(hdb);
+ if(hdb->fd < 0){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ return 0;
+ }
+ uint64_t unum = 0;
+ if(hdb->ba64){
+ uint64_t *buckets = hdb->ba64;
+ for(int i = 0; i < hdb->bnum; i++){
+ if(buckets[i]) unum++;
+ }
+ } else {
+ uint32_t *buckets = hdb->ba32;
+ for(int i = 0; i < hdb->bnum; i++){
+ if(buckets[i]) unum++;
+ }
+ }
+ return unum;
+}
+
+
+
+/*************************************************************************************************
+ * private features
+ *************************************************************************************************/
+
+
+/* Seek and read data from a file.
+ `hdb' specifies the hash database object.
+ `off' specifies the offset of the region to seek.
+ `buf' specifies the buffer to store into.
+ `size' specifies the size of the buffer.
+ The return value is true if successful, else, it is false. */
+static bool tcseekwrite(TCHDB *hdb, off_t off, const void *buf, size_t size){
+ assert(hdb && off >= 0 && buf && size >= 0);
+ while(true){
+ int wb = pwrite(hdb->fd, buf, size, off);
+ if(wb >= size){
+ return true;
+ } else if(wb > 0){
+ buf = (char *)buf + wb;
+ size -= wb;
+ off += wb;
+ } else if(wb == -1){
+ if(errno != EINTR){
+ tchdbsetecode(hdb, TCEWRITE, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ } else {
+ if(size > 0){
+ tchdbsetecode(hdb, TCEWRITE, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+
+/* Seek and read data from a file.
+ `hdb' specifies the hash database object.
+ `off' specifies the offset of the region to seek.
+ `buf' specifies the buffer to store into.
+ `size' specifies the size of the buffer.
+ The return value is true if successful, else, it is false. */
+static bool tcseekread(TCHDB *hdb, off_t off, void *buf, size_t size){
+ assert(hdb && off >= 0 && buf && size >= 0);
+ while(true){
+ int rb = pread(hdb->fd, buf, size, off);
+ if(rb >= size){
+ break;
+ } else if(rb > 0){
+ buf = (char *)buf + rb;
+ size -= rb;
+ off += rb;
+ } else if(rb == -1){
+ if(errno != EINTR){
+ tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ } else {
+ if(size > 0){
+ tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+
+/* Serialize meta data into a buffer.
+ `hdb' specifies the hash database object.
+ `hbuf' specifies the buffer. */
+static void tcdumpmeta(TCHDB *hdb, char *hbuf){
+ memset(hbuf, 0, HDBHEADSIZ);
+ sprintf(hbuf, "%s\n%s:%d\n", HDBMAGICDATA, _TC_FORMATVER, _TC_LIBVER);
+ memcpy(hbuf + HDBTYPEOFF, &(hdb->type), sizeof(hdb->type));
+ memcpy(hbuf + HDBFLAGSOFF, &(hdb->flags), sizeof(hdb->flags));
+ memcpy(hbuf + HDBAPOWOFF, &(hdb->apow), sizeof(hdb->apow));
+ memcpy(hbuf + HDBFPOWOFF, &(hdb->fpow), sizeof(hdb->fpow));
+ memcpy(hbuf + HDBOPTSOFF, &(hdb->opts), sizeof(hdb->opts));
+ uint64_t llnum;
+ llnum = hdb->bnum;
+ llnum = TCHTOILL(llnum);
+ memcpy(hbuf + HDBBNUMOFF, &llnum, sizeof(llnum));
+ llnum = hdb->rnum;
+ llnum = TCHTOILL(llnum);
+ memcpy(hbuf + HDBRNUMOFF, &llnum, sizeof(llnum));
+ llnum = hdb->fsiz;
+ llnum = TCHTOILL(llnum);
+ memcpy(hbuf + HDBFSIZOFF, &llnum, sizeof(llnum));
+ llnum = hdb->frec;
+ llnum = TCHTOILL(llnum);
+ memcpy(hbuf + HDBFRECOFF, &llnum, sizeof(llnum));
+}
+
+
+/* Deserialize meta data from a buffer.
+ `hdb' specifies the hash database object.
+ `hbuf' specifies the buffer. */
+static void tcloadmeta(TCHDB *hdb, const char *hbuf){
+ memcpy(&(hdb->type), hbuf + HDBTYPEOFF, sizeof(hdb->type));
+ memcpy(&(hdb->flags), hbuf + HDBFLAGSOFF, sizeof(hdb->flags));
+ memcpy(&(hdb->apow), hbuf + HDBAPOWOFF, sizeof(hdb->apow));
+ memcpy(&(hdb->fpow), hbuf + HDBFPOWOFF, sizeof(hdb->fpow));
+ memcpy(&(hdb->opts), hbuf + HDBOPTSOFF, sizeof(hdb->opts));
+ uint64_t llnum;
+ memcpy(&llnum, hbuf + HDBBNUMOFF, sizeof(llnum));
+ hdb->bnum = TCITOHLL(llnum);
+ memcpy(&llnum, hbuf + HDBRNUMOFF, sizeof(llnum));
+ hdb->rnum = TCITOHLL(llnum);
+ memcpy(&llnum, hbuf + HDBFSIZOFF, sizeof(llnum));
+ hdb->fsiz = TCITOHLL(llnum);
+ memcpy(&llnum, hbuf + HDBFRECOFF, sizeof(llnum));
+ hdb->frec = TCITOHLL(llnum);
+}
+
+
+/* Get a natural prime number not less than a floor number.
+ `num' specified the floor number.
+ The return value is a prime number not less than the floor number. */
+static uint64_t tcgetprime(uint64_t num){
+ uint64_t primes[] = {
+ 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 43, 47, 53, 59, 61, 71, 79, 83,
+ 89, 103, 109, 113, 127, 139, 157, 173, 191, 199, 223, 239, 251, 283, 317, 349,
+ 383, 409, 443, 479, 509, 571, 631, 701, 761, 829, 887, 953, 1021, 1151, 1279,
+ 1399, 1531, 1663, 1789, 1913, 2039, 2297, 2557, 2803, 3067, 3323, 3583, 3833,
+ 4093, 4603, 5119, 5623, 6143, 6653, 7159, 7673, 8191, 9209, 10223, 11261,
+ 12281, 13309, 14327, 15359, 16381, 18427, 20479, 22511, 24571, 26597, 28669,
+ 30713, 32749, 36857, 40949, 45053, 49139, 53239, 57331, 61417, 65521, 73727,
+ 81919, 90107, 98299, 106487, 114679, 122869, 131071, 147451, 163819, 180221,
+ 196597, 212987, 229373, 245759, 262139, 294911, 327673, 360439, 393209, 425977,
+ 458747, 491503, 524287, 589811, 655357, 720887, 786431, 851957, 917503, 982981,
+ 1048573, 1179641, 1310719, 1441771, 1572853, 1703903, 1835003, 1966079,
+ 2097143, 2359267, 2621431, 2883577, 3145721, 3407857, 3670013, 3932153,
+ 4194301, 4718579, 5242877, 5767129, 6291449, 6815741, 7340009, 7864301,
+ 8388593, 9437179, 10485751, 11534329, 12582893, 13631477, 14680063, 15728611,
+ 16777213, 18874367, 20971507, 23068667, 25165813, 27262931, 29360087, 31457269,
+ 33554393, 37748717, 41943023, 46137319, 50331599, 54525917, 58720253, 62914549,
+ 67108859, 75497467, 83886053, 92274671, 100663291, 109051903, 117440509,
+ 125829103, 134217689, 150994939, 167772107, 184549373, 201326557, 218103799,
+ 234881011, 251658227, 268435399, 301989881, 335544301, 369098707, 402653171,
+ 436207613, 469762043, 503316469, 536870909, 603979769, 671088637, 738197503,
+ 805306357, 872415211, 939524087, 1006632947, 1073741789, 1207959503,
+ 1342177237, 1476394991, 1610612711, 1744830457, 1879048183, 2013265907,
+ 2576980349, 3092376431, 3710851741, 4718021527, 6133428047, 7973456459,
+ 10365493393, 13475141413, 17517683831, 22772988923, 29604885677, 38486351381,
+ 50032256819, 65041933867, 84554514043, 109920868241, 0
+ };
+ int i;
+ for(i = 0; primes[i] > 0; i++){
+ if(num <= primes[i]) return primes[i];
+ }
+ return primes[i-1];
+}
+
+
+/* Clear all members.
+ `hdb' specifies the hash database object. */
+static void tchdbclear(TCHDB *hdb){
+ assert(hdb);
+ hdb->mmtx = NULL;
+ hdb->dmtx = NULL;
+ hdb->eckey = NULL;
+ hdb->type = HDBTHASH;
+ hdb->flags = 0;
+ hdb->bnum = HDBDEFBNUM;
+ hdb->apow = HDBDEFAPOW;
+ hdb->fpow = HDBDEFFPOW;
+ hdb->opts = 0;
+ hdb->path = NULL;
+ hdb->fd = -1;
+ hdb->omode = 0;
+ hdb->rnum = 0;
+ hdb->fsiz = 0;
+ hdb->frec = 0;
+ hdb->iter = 0;
+ hdb->map = NULL;
+ hdb->msiz = 0;
+ hdb->ba32 = NULL;
+ hdb->ba64 = NULL;
+ hdb->align = 0;
+ hdb->runit = 0;
+ hdb->zmode = false;
+ hdb->fbpmax = 0;
+ hdb->fbpsiz = 0;
+ hdb->fbpool = NULL;
+ hdb->fbpnum = 0;
+ hdb->fbpmis = 0;
+ hdb->async = false;
+ hdb->drpool = NULL;
+ hdb->drpdef = NULL;
+ hdb->drpoff = 0;
+ hdb->recc = NULL;
+ hdb->rcnum = 0;
+ hdb->ecode = TCESUCCESS;
+ hdb->fatal = false;
+ hdb->dbgfd = -1;
+ hdb->cnt_writerec = -1;
+ hdb->cnt_reuserec = -1;
+ hdb->cnt_moverec = -1;
+ hdb->cnt_readrec = -1;
+ hdb->cnt_searchfbp = -1;
+ hdb->cnt_insertfbp = -1;
+ hdb->cnt_splicefbp = -1;
+ hdb->cnt_dividefbp = -1;
+ hdb->cnt_mergefbp = -1;
+ hdb->cnt_reducefbp = -1;
+ hdb->cnt_appenddrp = -1;
+ hdb->cnt_deferdrp = -1;
+ hdb->cnt_flushdrp = -1;
+ hdb->cnt_adjrecc = -1;
+ TCDODEBUG(hdb->cnt_writerec = 0);
+ TCDODEBUG(hdb->cnt_reuserec = 0);
+ TCDODEBUG(hdb->cnt_moverec = 0);
+ TCDODEBUG(hdb->cnt_readrec = 0);
+ TCDODEBUG(hdb->cnt_searchfbp = 0);
+ TCDODEBUG(hdb->cnt_insertfbp = 0);
+ TCDODEBUG(hdb->cnt_splicefbp = 0);
+ TCDODEBUG(hdb->cnt_dividefbp = 0);
+ TCDODEBUG(hdb->cnt_mergefbp = 0);
+ TCDODEBUG(hdb->cnt_reducefbp = 0);
+ TCDODEBUG(hdb->cnt_appenddrp = 0);
+ TCDODEBUG(hdb->cnt_deferdrp = 0);
+ TCDODEBUG(hdb->cnt_flushdrp = 0);
+ TCDODEBUG(hdb->cnt_adjrecc = 0);
+}
+
+
+/* Get the padding size to record alignment.
+ `hdb' specifies the hash database object.
+ The return value is the padding size. */
+static int32_t tchdbpadsize(TCHDB *hdb){
+ assert(hdb);
+ int32_t diff = hdb->fsiz & (hdb->align - 1);
+ return diff > 0 ? hdb->align - diff : 0;
+}
+
+
+/* Set the open flag.
+ `hdb' specifies the hash database object.
+ `flag' specifies the flag value.
+ `sign' specifies the sign. */
+static void tchdbsetflag(TCHDB *hdb, int flag, bool sign){
+ assert(hdb);
+ char *fp = (char *)hdb->map + HDBFLAGSOFF;
+ if(sign){
+ *fp |= (uint8_t)flag;
+ } else {
+ *fp &= ~(uint8_t)flag;
+ }
+ hdb->flags = *fp;
+}
+
+
+/* Get the bucket index of a record.
+ `hdb' specifies the hash database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `hp' specifies the pointer to the variable into which the second hash value is assigned.
+ The return value is the bucket index. */
+static uint64_t tchdbbidx(TCHDB *hdb, const char *kbuf, int ksiz, uint8_t *hp){
+ assert(hdb && kbuf && ksiz >= 0 && hp);
+ uint64_t idx = 19780211;
+ uint32_t hash = 751;
+ const char *rp = kbuf + ksiz;
+ while(ksiz--){
+ idx = (idx << 5) + (idx << 2) + idx + *(uint8_t *)kbuf++;
+ hash = ((hash << 5) - hash) ^ *(uint8_t *)--rp;
+ }
+ *hp = hash;
+ return idx % hdb->bnum;
+}
+
+
+/* Get the offset of the record of a bucket element.
+ `hdb' specifies the hash database object.
+ `bidx' specifies the index of the bucket.
+ The return value is the offset of the record. */
+static off_t tchdbgetbucket(TCHDB *hdb, uint64_t bidx){
+ assert(hdb && bidx >= 0);
+ if(hdb->ba64){
+ uint64_t llnum = hdb->ba64[bidx];
+ return TCITOHLL(llnum) << hdb->apow;
+ }
+ uint32_t lnum = hdb->ba32[bidx];
+ return (off_t)TCITOHL(lnum) << hdb->apow;
+}
+
+
+/* Get the offset of the record of a bucket element.
+ `hdb' specifies the hash database object.
+ `bidx' specifies the index of the record.
+ `off' specifies the offset of the record. */
+static void tchdbsetbucket(TCHDB *hdb, uint64_t bidx, uint64_t off){
+ assert(hdb && bidx >= 0);
+ if(hdb->ba64){
+ uint64_t llnum = off >> hdb->apow;
+ hdb->ba64[bidx] = TCHTOILL(llnum);
+ } else {
+ uint32_t lnum = off >> hdb->apow;
+ hdb->ba32[bidx] = TCHTOIL(lnum);
+ }
+}
+
+
+/* Load the free block pool from the file.
+ The return value is true if successful, else, it is false. */
+static bool tchdbsavefbp(TCHDB *hdb){
+ assert(hdb);
+ if(hdb->fbpnum > (hdb->fbpmax >> 1)){
+ tchdbfbpmerge(hdb);
+ } else if(hdb->fbpnum > 1){
+ tcfbpsortbyoff(hdb->fbpool, hdb->fbpnum);
+ }
+ int bsiz = hdb->fbpsiz;
+ char *buf;
+ TCMALLOC(buf, bsiz);
+ char *wp = buf;
+ HDBFB *cur = hdb->fbpool;
+ HDBFB *end = cur + hdb->fbpnum;
+ uint64_t base = 0;
+ bsiz -= sizeof(HDBFB) + sizeof(uint8_t) + sizeof(uint8_t);
+ while(cur < end && bsiz > 0){
+ uint64_t noff = cur->off >> hdb->apow;
+ int step;
+ uint64_t llnum = noff - base;
+ TCSETVNUMBUF64(step, wp, llnum);
+ wp += step;
+ bsiz -= step;
+ uint32_t lnum = cur->rsiz >> hdb->apow;
+ TCSETVNUMBUF(step, wp, lnum);
+ wp += step;
+ bsiz -= step;
+ base = noff;
+ cur++;
+ }
+ *(wp++) = '\0';
+ *(wp++) = '\0';
+ if(!tcseekwrite(hdb, hdb->msiz, buf, wp - buf)){
+ free(buf);
+ return false;
+ }
+ free(buf);
+ return true;
+}
+
+
+/* Save the free block pool into the file.
+ The return value is true if successful, else, it is false. */
+static bool tchdbloadfbp(TCHDB *hdb){
+ int bsiz = hdb->fbpsiz;
+ char *buf;
+ TCMALLOC(buf, bsiz);
+ if(!tcseekread(hdb, hdb->msiz, buf, bsiz)){
+ free(buf);
+ return false;
+ }
+ const char *rp = buf;
+ HDBFB *cur = hdb->fbpool;
+ HDBFB *end = cur + hdb->fbpmax;
+ uint64_t base = 0;
+ while(cur < end && *rp != '\0'){
+ int step;
+ uint64_t llnum;
+ TCREADVNUMBUF64(rp, llnum, step);
+ base += llnum << hdb->apow;
+ cur->off = base;
+ rp += step;
+ uint32_t lnum;
+ TCREADVNUMBUF(rp, lnum, step);
+ cur->rsiz = lnum << hdb->apow;
+ rp += step;
+ cur++;
+ }
+ hdb->fbpnum = cur - (HDBFB *)hdb->fbpool;
+ free(buf);
+ return true;
+}
+
+
+/* Sort the free block pool by offset.
+ `fbpool' specifies the free block pool.
+ `fbpnum' specifies the number of blocks. */
+static void tcfbpsortbyoff(HDBFB *fbpool, int fbpnum){
+ assert(fbpool && fbpnum >= 0);
+ fbpnum--;
+ int bottom = fbpnum / 2 + 1;
+ int top = fbpnum;
+ while(bottom > 0){
+ bottom--;
+ int mybot = bottom;
+ int i = 2 * mybot;
+ while(i <= top){
+ if(i < top && fbpool[i+1].off > fbpool[i].off) i++;
+ if(fbpool[mybot].off >= fbpool[i].off) break;
+ HDBFB swap = fbpool[mybot];
+ fbpool[mybot] = fbpool[i];
+ fbpool[i] = swap;
+ mybot = i;
+ i = 2 * mybot;
+ }
+ }
+ while(top > 0){
+ HDBFB swap = fbpool[0];
+ fbpool[0] = fbpool[top];
+ fbpool[top] = swap;
+ top--;
+ int mybot = bottom;
+ int i = 2 * mybot;
+ while(i <= top){
+ if(i < top && fbpool[i+1].off > fbpool[i].off) i++;
+ if(fbpool[mybot].off >= fbpool[i].off) break;
+ swap = fbpool[mybot];
+ fbpool[mybot] = fbpool[i];
+ fbpool[i] = swap;
+ mybot = i;
+ i = 2 * mybot;
+ }
+ }
+}
+
+
+/* Sort the free block pool by record size.
+ `fbpool' specifies the free block pool.
+ `fbpnum' specifies the number of blocks. */
+static void tcfbpsortbyrsiz(HDBFB *fbpool, int fbpnum){
+ assert(fbpool && fbpnum >= 0);
+ fbpnum--;
+ int bottom = fbpnum / 2 + 1;
+ int top = fbpnum;
+ while(bottom > 0){
+ bottom--;
+ int mybot = bottom;
+ int i = 2 * mybot;
+ while(i <= top){
+ if(i < top && fbpool[i+1].rsiz > fbpool[i].rsiz) i++;
+ if(fbpool[mybot].rsiz >= fbpool[i].rsiz) break;
+ HDBFB swap = fbpool[mybot];
+ fbpool[mybot] = fbpool[i];
+ fbpool[i] = swap;
+ mybot = i;
+ i = 2 * mybot;
+ }
+ }
+ while(top > 0){
+ HDBFB swap = fbpool[0];
+ fbpool[0] = fbpool[top];
+ fbpool[top] = swap;
+ top--;
+ int mybot = bottom;
+ int i = 2 * mybot;
+ while(i <= top){
+ if(i < top && fbpool[i+1].rsiz > fbpool[i].rsiz) i++;
+ if(fbpool[mybot].rsiz >= fbpool[i].rsiz) break;
+ swap = fbpool[mybot];
+ fbpool[mybot] = fbpool[i];
+ fbpool[i] = swap;
+ mybot = i;
+ i = 2 * mybot;
+ }
+ }
+}
+
+
+/* Merge successive records in the free block pool.
+ `hdb' specifies the hash database object. */
+static void tchdbfbpmerge(TCHDB *hdb){
+ assert(hdb);
+ TCDODEBUG(hdb->cnt_mergefbp++);
+ int32_t onum = hdb->fbpnum;
+ tcfbpsortbyoff(hdb->fbpool, hdb->fbpnum);
+ HDBFB *wp = hdb->fbpool;;
+ HDBFB *cur = wp;
+ HDBFB *end = wp + hdb->fbpnum - 1;
+ while(cur < end){
+ if(cur->off > 0){
+ HDBFB *next = cur + 1;
+ if(cur->off + cur->rsiz == next->off){
+ if(hdb->iter == next->off) hdb->iter += next->rsiz;
+ cur->rsiz += next->rsiz;
+ next->off = 0;
+ }
+ *(wp++) = *cur;
+ }
+ cur++;
+ }
+ if(end->off > 0) *(wp++) = *end;
+ hdb->fbpnum = wp - (HDBFB *)hdb->fbpool;
+ hdb->fbpmis = (hdb->fbpnum < onum) ? 0 : hdb->fbpnum * -2;
+}
+
+
+/* Insert a block into the free block pool.
+ `hdb' specifies the hash database object.
+ `off' specifies the offset of the block.
+ `rsiz' specifies the size of the block. */
+static void tchdbfbpinsert(TCHDB *hdb, uint64_t off, uint32_t rsiz){
+ assert(hdb && off > 0 && rsiz > 0);
+ TCDODEBUG(hdb->cnt_insertfbp++);
+ if(hdb->fpow < 1) return;
+ HDBFB *pv = hdb->fbpool;
+ if(hdb->fbpnum >= hdb->fbpmax){
+ tchdbfbpmerge(hdb);
+ tcfbpsortbyrsiz(hdb->fbpool, hdb->fbpnum);
+ if(hdb->fbpnum >= hdb->fbpmax){
+ TCDODEBUG(hdb->cnt_reducefbp++);
+ int32_t dnum = (hdb->fbpmax >> 2) + 1;
+ memmove(pv, pv + dnum, (hdb->fbpnum - dnum) * sizeof(*pv));
+ hdb->fbpnum -= dnum;
+ }
+ hdb->fbpmis = 0;
+ }
+ pv = pv + hdb->fbpnum;
+ pv->off = off;
+ pv->rsiz = rsiz;
+ hdb->fbpnum++;
+}
+
+
+/* Search the free block pool for the minimum region.
+ `hdb' specifies the hash database object.
+ `rec' specifies the record object to be stored.
+ The return value is true if successful, else, it is false. */
+static bool tchdbfbpsearch(TCHDB *hdb, TCHREC *rec){
+ assert(hdb && rec);
+ TCDODEBUG(hdb->cnt_searchfbp++);
+ if(hdb->fbpnum < 1){
+ rec->off = hdb->fsiz;
+ rec->rsiz = 0;
+ return true;
+ }
+ uint32_t rsiz = rec->rsiz;
+ HDBFB *pv = hdb->fbpool;
+ HDBFB *ep = pv + hdb->fbpnum;
+ while(pv < ep){
+ if(pv->rsiz > rsiz){
+ if(pv->rsiz > (rsiz << 1)){
+ uint64_t fsiz = hdb->fsiz;
+ hdb->fsiz = pv->off + rsiz;
+ uint32_t psiz = tchdbpadsize(hdb);
+ hdb->fsiz = fsiz;
+ uint64_t noff = pv->off + rsiz + psiz;
+ if(pv->rsiz >= ((noff - pv->off) << 1)){
+ TCDODEBUG(hdb->cnt_dividefbp++);
+ rec->off = pv->off;
+ rec->rsiz = noff - pv->off;
+ pv->off = noff;
+ pv->rsiz -= rec->rsiz;
+ return tchdbwritefb(hdb, pv->off, pv->rsiz);
+ }
+ }
+ rec->off = pv->off;
+ rec->rsiz = pv->rsiz;
+ ep--;
+ pv->off = ep->off;
+ pv->rsiz = ep->rsiz;
+ hdb->fbpnum--;
+ return true;
+ }
+ pv++;
+ }
+ rec->off = hdb->fsiz;
+ rec->rsiz = 0;
+ hdb->fbpmis++;
+ if(hdb->fbpmis >= HDBFBPMGFREQ){
+ tchdbfbpmerge(hdb);
+ tcfbpsortbyrsiz(hdb->fbpool, hdb->fbpnum);
+ }
+ return true;
+}
+
+
+/* Splice the trailing free block
+ `hdb' specifies the hash database object.
+ `rec' specifies the record object to be stored.
+ `nsiz' specifies the needed size.
+ The return value is whether splicing succeeded or not. */
+static bool tchdbfbpsplice(TCHDB *hdb, TCHREC *rec, uint32_t nsiz){
+ assert(hdb && rec && nsiz > 0);
+ if(hdb->fbpnum < 1) return false;
+ uint64_t off = rec->off + rec->rsiz;
+ uint32_t rsiz = rec->rsiz;
+ HDBFB *pv = hdb->fbpool;
+ HDBFB *ep = pv + hdb->fbpnum;
+ while(pv < ep){
+ if(pv->off == off && rsiz + pv->rsiz >= nsiz){
+ if(hdb->iter == pv->off) hdb->iter += pv->rsiz;
+ rec->rsiz += pv->rsiz;
+ ep--;
+ pv->off = ep->off;
+ pv->rsiz = ep->rsiz;
+ hdb->fbpnum--;
+ return true;
+ }
+ pv++;
+ }
+ return false;
+}
+
+
+/* Write a free block into the file.
+ `hdb' specifies the hash database object.
+ `off' specifies the offset of the block.
+ `rsiz' specifies the size of the block.
+ The return value is true if successful, else, it is false. */
+static bool tchdbwritefb(TCHDB *hdb, uint64_t off, uint32_t rsiz){
+ assert(hdb && off > 0 && rsiz > 0);
+ char rbuf[HDBMAXHSIZ];
+ char *wp = rbuf;
+ *(uint8_t *)(wp++) = HDBMAGICFB;
+ uint32_t lnum = TCHTOIL(rsiz);
+ memcpy(wp, &lnum, sizeof(lnum));
+ wp += sizeof(lnum);
+ if(!tcseekwrite(hdb, off, rbuf, wp - rbuf)) return false;
+ return true;
+}
+
+
+/* Write a record into the file.
+ `hdb' specifies the hash database object.
+ `rec' specifies the record object.
+ `bidx' specifies the index of the bucket.
+ `entoff' specifies the offset of the tree entry.
+ The return value is true if successful, else, it is false. */
+static bool tchdbwriterec(TCHDB *hdb, TCHREC *rec, uint64_t bidx, off_t entoff){
+ assert(hdb && rec);
+ TCDODEBUG(hdb->cnt_writerec++);
+ uint64_t ofsiz = hdb->fsiz;
+ char stack[HDBIOBUFSIZ];
+ int bsiz = rec->rsiz > 0 ? rec->rsiz : HDBMAXHSIZ + rec->ksiz + rec->vsiz + hdb->align;
+ char *rbuf;
+ if(bsiz <= HDBIOBUFSIZ){
+ rbuf = stack;
+ } else {
+ TCMALLOC(rbuf, bsiz);
+ }
+ char *wp = rbuf;
+ *(uint8_t *)(wp++) = HDBMAGICREC;
+ *(uint8_t *)(wp++) = rec->hash;
+ if(hdb->ba64){
+ uint64_t llnum;
+ llnum = rec->left >> hdb->apow;
+ llnum = TCHTOILL(llnum);
+ memcpy(wp, &llnum, sizeof(llnum));
+ wp += sizeof(llnum);
+ llnum = rec->right >> hdb->apow;
+ llnum = TCHTOILL(llnum);
+ memcpy(wp, &llnum, sizeof(llnum));
+ wp += sizeof(llnum);
+ } else {
+ uint32_t lnum;
+ lnum = rec->left >> hdb->apow;
+ lnum = TCHTOIL(lnum);
+ memcpy(wp, &lnum, sizeof(lnum));
+ wp += sizeof(lnum);
+ lnum = rec->right >> hdb->apow;
+ lnum = TCHTOIL(lnum);
+ memcpy(wp, &lnum, sizeof(lnum));
+ wp += sizeof(lnum);
+ }
+ uint16_t snum;
+ char *pwp = wp;
+ wp += sizeof(snum);
+ int step;
+ TCSETVNUMBUF(step, wp, rec->ksiz);
+ wp += step;
+ TCSETVNUMBUF(step, wp, rec->vsiz);
+ wp += step;
+ int32_t hsiz = wp - rbuf;
+ int32_t rsiz = hsiz + rec->ksiz + rec->vsiz;
+ if(rec->rsiz < 1){
+ hdb->fsiz += rsiz;
+ uint16_t psiz = tchdbpadsize(hdb);
+ rec->rsiz = rsiz + psiz;
+ rec->psiz = psiz;
+ hdb->fsiz += psiz;
+ } else if(rsiz > rec->rsiz){
+ if(rbuf != stack) free(rbuf);
+ if(tchdbfbpsplice(hdb, rec, rsiz)){
+ TCDODEBUG(hdb->cnt_splicefbp++);
+ return tchdbwriterec(hdb, rec, bidx, entoff);
+ }
+ TCDODEBUG(hdb->cnt_moverec++);
+ if(!tchdbwritefb(hdb, rec->off, rec->rsiz)) return false;
+ tchdbfbpinsert(hdb, rec->off, rec->rsiz);
+ rec->rsiz = rsiz;
+ if(!tchdbfbpsearch(hdb, rec)) return false;
+ return tchdbwriterec(hdb, rec, bidx, entoff);
+ } else {
+ TCDODEBUG(hdb->cnt_reuserec++);
+ uint32_t psiz = rec->rsiz - rsiz;
+ if(psiz > UINT16_MAX){
+ TCDODEBUG(hdb->cnt_dividefbp++);
+ uint64_t fsiz = hdb->fsiz;
+ hdb->fsiz = rec->off + rsiz;
+ psiz = tchdbpadsize(hdb);
+ hdb->fsiz = fsiz;
+ uint64_t noff = rec->off + rsiz + psiz;
+ uint32_t nsiz = rec->rsiz - rsiz - psiz;
+ rec->rsiz = noff - rec->off;
+ rec->psiz = psiz;
+ if(!tchdbwritefb(hdb, noff, nsiz)) return false;
+ tchdbfbpinsert(hdb, noff, nsiz);
+ }
+ rec->psiz = psiz;
+ }
+ snum = rec->psiz;
+ snum = TCHTOIS(snum);
+ memcpy(pwp, &snum, sizeof(snum));
+ rsiz = rec->rsiz;
+ rsiz -= hsiz;
+ memcpy(wp, rec->kbuf, rec->ksiz);
+ wp += rec->ksiz;
+ rsiz -= rec->ksiz;
+ memcpy(wp, rec->vbuf, rec->vsiz);
+ wp += rec->vsiz;
+ rsiz -= rec->vsiz;
+ memset(wp, 0, rsiz);
+ if(!tcseekwrite(hdb, rec->off, rbuf, rec->rsiz)){
+ if(rbuf != stack) free(rbuf);
+ hdb->fsiz = ofsiz;
+ return false;
+ }
+ if(hdb->fsiz != ofsiz){
+ uint64_t llnum = hdb->fsiz;
+ llnum = TCHTOILL(llnum);
+ memcpy(hdb->map + HDBFSIZOFF, &llnum, sizeof(llnum));
+ }
+ if(rbuf != stack) free(rbuf);
+ if(entoff > 0){
+ if(hdb->ba64){
+ uint64_t llnum = rec->off >> hdb->apow;
+ llnum = TCHTOILL(llnum);
+ if(!tcseekwrite(hdb, entoff, &llnum, sizeof(uint64_t))) return false;
+ } else {
+ uint32_t lnum = rec->off >> hdb->apow;
+ lnum = TCHTOIL(lnum);
+ if(!tcseekwrite(hdb, entoff, &lnum, sizeof(uint32_t))) return false;
+ }
+ } else {
+ tchdbsetbucket(hdb, bidx, rec->off);
+ }
+ return true;
+}
+
+
+/* Read a record from the file.
+ `hdb' specifies the hash database object.
+ `rec' specifies the record object.
+ `rbuf' specifies the buffer for reading.
+ The return value is true if successful, else, it is false. */
+static bool tchdbreadrec(TCHDB *hdb, TCHREC *rec, char *rbuf){
+ assert(hdb && rec && rbuf);
+ TCDODEBUG(hdb->cnt_readrec++);
+ off_t rsiz = hdb->fsiz - rec->off;
+ if(rsiz > hdb->runit){
+ rsiz = hdb->runit;
+ } else if(rsiz < (sizeof(uint8_t) + sizeof(uint32_t))){
+ tchdbsetecode(hdb, TCERHEAD, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ if(!tcseekread(hdb, rec->off, rbuf, rsiz)) return false;
+ const char *rp = rbuf;
+ rec->magic = *(uint8_t *)(rp++);
+ if(rec->magic == HDBMAGICFB){
+ uint32_t lnum;
+ memcpy(&lnum, rp, sizeof(lnum));
+ rec->rsiz = TCITOHL(lnum);
+ return true;
+ } else if(rec->magic != HDBMAGICREC){
+ tchdbsetecode(hdb, TCERHEAD, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ rec->hash = *(uint8_t *)(rp++);
+ if(hdb->ba64){
+ uint64_t llnum;
+ memcpy(&llnum, rp, sizeof(llnum));
+ rec->left = TCITOHLL(llnum) << hdb->apow;
+ rp += sizeof(llnum);
+ memcpy(&llnum, rp, sizeof(llnum));
+ rec->right = TCITOHLL(llnum) << hdb->apow;
+ rp += sizeof(llnum);
+ } else {
+ uint32_t lnum;
+ memcpy(&lnum, rp, sizeof(lnum));
+ rec->left = (uint64_t)TCITOHL(lnum) << hdb->apow;
+ rp += sizeof(lnum);
+ memcpy(&lnum, rp, sizeof(lnum));
+ rec->right = (uint64_t)TCITOHL(lnum) << hdb->apow;
+ rp += sizeof(lnum);
+ }
+ uint16_t snum;
+ memcpy(&snum, rp, sizeof(snum));
+ rec->psiz = TCITOHS(snum);
+ rp += sizeof(snum);
+ uint32_t lnum;
+ int step;
+ TCREADVNUMBUF(rp, lnum, step);
+ rec->ksiz = lnum;
+ rp += step;
+ TCREADVNUMBUF(rp, lnum, step);
+ rec->vsiz = lnum;
+ rp += step;
+ int32_t hsiz = rp - rbuf;
+ rec->rsiz = hsiz + rec->ksiz + rec->vsiz + rec->psiz;
+ rec->kbuf = NULL;
+ rec->vbuf = NULL;
+ rec->boff = rec->off + hsiz;
+ rec->bbuf = NULL;
+ rsiz -= hsiz;
+ if(rsiz >= rec->ksiz){
+ rec->kbuf = rp;
+ rsiz -= rec->ksiz;
+ rp += rec->ksiz;
+ if(rsiz >= rec->vsiz) rec->vbuf = rp;
+ }
+ return true;
+}
+
+
+/* Read the body of a record from the file.
+ `hdb' specifies the hash database object.
+ `rec' specifies the record object.
+ The return value is true if successful, else, it is false. */
+static bool tchdbreadrecbody(TCHDB *hdb, TCHREC *rec){
+ assert(hdb && rec);
+ int32_t bsiz = rec->ksiz + rec->vsiz;
+ TCMALLOC(rec->bbuf, bsiz + 1);
+ if(!tcseekread(hdb, rec->boff, rec->bbuf, bsiz)) return false;
+ rec->kbuf = rec->bbuf;
+ rec->vbuf = rec->bbuf + rec->ksiz;
+ return true;
+}
+
+
+/* Compare keys of two records.
+ `abuf' specifies the pointer to the region of the former.
+ `asiz' specifies the size of the region.
+ `bbuf' specifies the pointer to the region of the latter.
+ `bsiz' specifies the size of the region.
+ The return value is 0 if two equals, positive if the formar is big, else, negative. */
+static int tcreckeycmp(const char *abuf, int asiz, const char *bbuf, int bsiz){
+ assert(abuf && asiz >= 0 && bbuf && bsiz >= 0);
+ if(asiz > bsiz) return 1;
+ if(asiz < bsiz) return -1;
+ return memcmp(abuf, bbuf, asiz);
+}
+
+
+/* Flush the delayed record pool.
+ `hdb' specifies the hash database object.
+ The return value is true if successful, else, it is false. */
+static bool tchdbflushdrp(TCHDB *hdb){
+ assert(hdb);
+ if(!HDBLOCKDRP(hdb)) return false;
+ if(!hdb->drpool){
+ HDBUNLOCKDRP(hdb);
+ return true;
+ }
+ TCDODEBUG(hdb->cnt_flushdrp++);
+ if(!tcseekwrite(hdb, hdb->drpoff, TCXSTRPTR(hdb->drpool), TCXSTRSIZE(hdb->drpool))){
+ HDBUNLOCKDRP(hdb);
+ return false;
+ }
+ const char *rp = TCXSTRPTR(hdb->drpdef);
+ int size = TCXSTRSIZE(hdb->drpdef);
+ while(size > 0){
+ int ksiz, vsiz;
+ memcpy(&ksiz, rp, sizeof(int));
+ rp += sizeof(int);
+ memcpy(&vsiz, rp, sizeof(int));
+ rp += sizeof(int);
+ const char *kbuf = rp;
+ rp += ksiz;
+ const char *vbuf = rp;
+ rp += vsiz;
+ if(!tchdbputimpl(hdb, kbuf, ksiz, vbuf, vsiz, HDBPDOVER)){
+ tcxstrdel(hdb->drpdef);
+ tcxstrdel(hdb->drpool);
+ hdb->drpool = NULL;
+ hdb->drpdef = NULL;
+ hdb->drpoff = 0;
+ HDBUNLOCKDRP(hdb);
+ return false;
+ }
+ size -= sizeof(int) * 2 + ksiz + vsiz;
+ }
+ tcxstrdel(hdb->drpdef);
+ tcxstrdel(hdb->drpool);
+ hdb->drpool = NULL;
+ hdb->drpdef = NULL;
+ hdb->drpoff = 0;
+ uint64_t llnum;
+ llnum = hdb->rnum;
+ llnum = TCHTOILL(llnum);
+ memcpy(hdb->map + HDBRNUMOFF, &llnum, sizeof(llnum));
+ llnum = hdb->fsiz;
+ llnum = TCHTOILL(llnum);
+ memcpy(hdb->map + HDBFSIZOFF, &llnum, sizeof(llnum));
+ HDBUNLOCKDRP(hdb);
+ return true;
+}
+
+
+/* Adjust the caches for leaves and nodes.
+ `hdb' specifies the hash tree database object. */
+static void tchdbcacheadjust(TCHDB *hdb){
+ assert(hdb);
+ TCDODEBUG(hdb->cnt_adjrecc++);
+ tcmdbcutfront(hdb->recc, HDBCACHEOUT);
+}
+
+
+/* Open a database file and connect a hash database object.
+ `hdb' specifies the hash database object.
+ `path' specifies the path of the database file.
+ `omode' specifies the connection mode.
+ If successful, the return value is true, else, it is false. */
+static bool tchdbopenimpl(TCHDB *hdb, const char *path, int omode){
+ assert(hdb && path);
+ int mode = O_RDONLY;
+ if(omode & HDBOWRITER){
+ mode = O_RDWR;
+ if(omode & HDBOCREAT) mode |= O_CREAT;
+ }
+ int fd = open(path, mode, HDBFILEMODE);
+ if(fd < 0){
+ int ecode = TCEOPEN;
+ switch(errno){
+ case EACCES: ecode = TCENOPERM; break;
+ case ENOENT: ecode = TCENOFILE; break;
+ }
+ tchdbsetecode(hdb, ecode, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ if(!(omode & HDBONOLCK)){
+ if(!tclock(fd, omode & HDBOWRITER, omode & HDBOLCKNB)){
+ tchdbsetecode(hdb, TCELOCK, __FILE__, __LINE__, __func__);
+ close(fd);
+ return false;
+ }
+ }
+ if((omode & HDBOWRITER) && (omode & HDBOTRUNC)){
+ if(ftruncate(fd, 0) == -1){
+ tchdbsetecode(hdb, TCETRUNC, __FILE__, __LINE__, __func__);
+ close(fd);
+ return false;
+ }
+ }
+ struct stat sbuf;
+ if(fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)){
+ tchdbsetecode(hdb, TCESTAT, __FILE__, __LINE__, __func__);
+ close(fd);
+ return false;
+ }
+ char hbuf[HDBHEADSIZ];
+ if((omode & HDBOWRITER) && sbuf.st_size < 1){
+ hdb->rnum = 0;
+ uint32_t fbpmax = 1 << hdb->fpow;
+ uint32_t fbpsiz = HDBFBPBSIZ + fbpmax * HDBFBPESIZ;
+ int besiz = (hdb->opts & HDBTLARGE) ? sizeof(int64_t) : sizeof(int32_t);
+ hdb->align = 1 << hdb->apow;
+ hdb->fsiz = HDBHEADSIZ + besiz * hdb->bnum + fbpsiz;
+ uint64_t psiz = tchdbpadsize(hdb);
+ hdb->fsiz += psiz;
+ hdb->frec = hdb->fsiz;
+ tcdumpmeta(hdb, hbuf);
+ psiz += besiz * hdb->bnum + fbpsiz;
+ char pbuf[HDBIOBUFSIZ];
+ memset(pbuf, 0, HDBIOBUFSIZ);
+ bool err = false;
+ if(!tcwrite(fd, hbuf, HDBHEADSIZ)) err = true;
+ while(psiz > 0){
+ if(psiz > HDBIOBUFSIZ){
+ if(!tcwrite(fd, pbuf, HDBIOBUFSIZ)) err = true;
+ psiz -= HDBIOBUFSIZ;
+ } else {
+ if(!tcwrite(fd, pbuf, psiz)) err = true;
+ psiz = 0;
+ }
+ }
+ if(err){
+ tchdbsetecode(hdb, TCEWRITE, __FILE__, __LINE__, __func__);
+ close(fd);
+ return false;
+ }
+ sbuf.st_size = hdb->fsiz;
+ }
+ if(lseek(fd, 0, SEEK_SET) == -1){
+ tchdbsetecode(hdb, TCESEEK, __FILE__, __LINE__, __func__);
+ close(fd);
+ return false;
+ }
+ if(!tcread(fd, hbuf, HDBHEADSIZ)){
+ tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__);
+ close(fd);
+ return false;
+ }
+ int type = hdb->type;
+ tcloadmeta(hdb, hbuf);
+ uint32_t fbpmax = 1 << hdb->fpow;
+ uint32_t fbpsiz = HDBFBPBSIZ + fbpmax * HDBFBPESIZ;
+ if(!(omode & HDBONOLCK)){
+ if(memcmp(hbuf, HDBMAGICDATA, strlen(HDBMAGICDATA)) || hdb->type != type ||
+ hdb->frec < HDBHEADSIZ + fbpsiz || hdb->frec > hdb->fsiz || sbuf.st_size < hdb->fsiz){
+ tchdbsetecode(hdb, TCEMETA, __FILE__, __LINE__, __func__);
+ close(fd);
+ return false;
+ }
+ if(sbuf.st_size > hdb->fsiz) hdb->fsiz = sbuf.st_size;
+ }
+ if((hdb->opts & HDBTDEFLATE) && !_tc_deflate){
+ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__);
+ close(fd);
+ return false;
+ }
+ int besiz = (hdb->opts & HDBTLARGE) ? sizeof(int64_t) : sizeof(int32_t);
+ size_t msiz = HDBHEADSIZ + hdb->bnum * besiz;
+ void *map = mmap(0, msiz, PROT_READ | ((omode & HDBOWRITER) ? PROT_WRITE : 0),
+ MAP_SHARED, fd, 0);
+ if(map == MAP_FAILED){
+ tchdbsetecode(hdb, TCEMMAP, __FILE__, __LINE__, __func__);
+ close(fd);
+ return false;
+ }
+ hdb->fbpmax = fbpmax;
+ hdb->fbpsiz = fbpsiz;
+ if(omode & HDBOWRITER){
+ TCMALLOC(hdb->fbpool, fbpmax * sizeof(HDBFB));
+ } else {
+ hdb->fbpool = NULL;
+ }
+ hdb->fbpnum = 0;
+ hdb->fbpmis = 0;
+ hdb->async = false;
+ hdb->drpool = NULL;
+ hdb->drpdef = NULL;
+ hdb->drpoff = 0;
+ hdb->recc = (hdb->rcnum > 0) ? tcmdbnew2(hdb->rcnum * 2 + 1) : NULL;
+ hdb->path = tcstrdup(path);
+ hdb->omode = omode;
+ hdb->iter = 0;
+ hdb->map = map;
+ hdb->msiz = msiz;
+ if(hdb->opts & HDBTLARGE){
+ hdb->ba32 = NULL;
+ hdb->ba64 = (uint64_t *)((char *)map + HDBHEADSIZ);
+ } else {
+ hdb->ba32 = (uint32_t *)((char *)map + HDBHEADSIZ);
+ hdb->ba64 = NULL;
+ }
+ hdb->align = 1 << hdb->apow;
+ hdb->runit = tclmin(tclmax(hdb->align, HDBMINRUNIT), HDBIOBUFSIZ);
+ hdb->zmode = (hdb->opts & HDBTDEFLATE) || (hdb->opts & HDBTTCBS);
+ hdb->ecode = TCESUCCESS;
+ hdb->fatal = false;
+ hdb->fd = fd;
+ if(hdb->omode & HDBOWRITER){
+ bool err = false;
+ if(!(hdb->flags & HDBFOPEN) && !tchdbloadfbp(hdb)) err = true;
+ memset(hbuf, 0, 2);
+ if(!tcseekwrite(hdb, hdb->msiz, hbuf, 2)) err = true;
+ if(err){
+ free(hdb->path);
+ free(hdb->fbpool);
+ munmap(hdb->map, hdb->msiz);
+ close(fd);
+ hdb->fd = -1;
+ return false;
+ }
+ tchdbsetflag(hdb, HDBFOPEN, true);
+ }
+ hdb->inode = (uint64_t)sbuf.st_ino;
+ hdb->mtime = sbuf.st_mtime;
+ return true;
+}
+
+
+/* Close a hash database object.
+ `hdb' specifies the hash database object.
+ If successful, the return value is true, else, it is false. */
+static bool tchdbcloseimpl(TCHDB *hdb){
+ assert(hdb);
+ bool err = false;
+ if(hdb->recc){
+ tcmdbdel(hdb->recc);
+ hdb->recc = NULL;
+ }
+ if(hdb->omode & HDBOWRITER){
+ if(!tchdbflushdrp(hdb)) err = true;
+ if(!tchdbsavefbp(hdb)) err = true;
+ free(hdb->fbpool);
+ tchdbsetflag(hdb, HDBFOPEN, false);
+ }
+ free(hdb->path);
+ if((hdb->omode & HDBOWRITER) && !tchdbmemsync(hdb, false)) err = true;
+ if(munmap(hdb->map, hdb->msiz) == -1){
+ tchdbsetecode(hdb, TCEMMAP, __FILE__, __LINE__, __func__);
+ err = true;
+ }
+ if(close(hdb->fd) == -1){
+ tchdbsetecode(hdb, TCECLOSE, __FILE__, __LINE__, __func__);
+ err = true;
+ }
+ hdb->path = NULL;
+ hdb->fd = -1;
+ return !err;
+}
+
+
+/* Store a record.
+ `hdb' specifies the hash database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ `dmode' specifies behavior when the key overlaps.
+ If successful, the return value is true, else, it is false. */
+static bool tchdbputimpl(TCHDB *hdb, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+ int dmode){
+ assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ if(hdb->recc) tcmdbout(hdb->recc, kbuf, ksiz);
+ uint8_t hash;
+ uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
+ off_t off = tchdbgetbucket(hdb, bidx);
+ off_t entoff = 0;
+ TCHREC rec;
+ char rbuf[HDBIOBUFSIZ];
+ while(off > 0){
+ rec.off = off;
+ if(!tchdbreadrec(hdb, &rec, rbuf)) return false;
+ if(hash > rec.hash){
+ off = rec.left;
+ entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t));
+ } else if(hash < rec.hash){
+ off = rec.right;
+ entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) +
+ (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t));
+ } else {
+ if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return false;
+ int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz);
+ if(kcmp > 0){
+ off = rec.left;
+ free(rec.bbuf);
+ rec.kbuf = NULL;
+ rec.bbuf = NULL;
+ entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t));
+ } else if(kcmp < 0){
+ off = rec.right;
+ free(rec.bbuf);
+ rec.kbuf = NULL;
+ rec.bbuf = NULL;
+ entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) +
+ (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t));
+ } else {
+ switch(dmode){
+ case HDBPDKEEP:
+ free(rec.bbuf);
+ tchdbsetecode(hdb, TCEKEEP, __FILE__, __LINE__, __func__);
+ return false;
+ case HDBPDCAT:
+ if(vsiz < 1){
+ free(rec.bbuf);
+ return true;
+ }
+ if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)) return false;
+ int nvsiz = rec.vsiz + vsiz;
+ if(rec.bbuf){
+ TCREALLOC(rec.bbuf, rec.bbuf, rec.ksiz + nvsiz);
+ memcpy(rec.bbuf + rec.ksiz + rec.vsiz, vbuf, vsiz);
+ rec.kbuf = rec.bbuf;
+ rec.vbuf = rec.kbuf + rec.ksiz;
+ rec.vsiz = nvsiz;
+ } else {
+ TCMALLOC(rec.bbuf, nvsiz);
+ memcpy(rec.bbuf, rec.vbuf, rec.vsiz);
+ memcpy(rec.bbuf + rec.vsiz, vbuf, vsiz);
+ rec.vbuf = rec.bbuf;
+ rec.vsiz = nvsiz;
+ }
+ bool rv = tchdbwriterec(hdb, &rec, bidx, entoff);
+ free(rec.bbuf);
+ return rv;
+ default:
+ break;
+ }
+ free(rec.bbuf);
+ rec.ksiz = ksiz;
+ rec.vsiz = vsiz;
+ rec.kbuf = kbuf;
+ rec.vbuf = vbuf;
+ return tchdbwriterec(hdb, &rec, bidx, entoff);
+ }
+ }
+ }
+ rec.rsiz = HDBMAXHSIZ + ksiz + vsiz;
+ if(!tchdbfbpsearch(hdb, &rec)) return false;
+ rec.hash = hash;
+ rec.left = 0;
+ rec.right = 0;
+ rec.ksiz = ksiz;
+ rec.vsiz = vsiz;
+ rec.psiz = 0;
+ rec.kbuf = kbuf;
+ rec.vbuf = vbuf;
+ if(!tchdbwriterec(hdb, &rec, bidx, entoff)) return false;
+ hdb->rnum++;
+ uint64_t llnum = hdb->rnum;
+ llnum = TCHTOILL(llnum);
+ memcpy(hdb->map + HDBRNUMOFF, &llnum, sizeof(llnum));
+ return true;
+}
+
+
+/* Append a record to the delayed record pool.
+ `hdb' specifies the hash database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ `hash' specifies the second hash value. */
+static void tchdbdrpappend(TCHDB *hdb, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+ uint8_t hash){
+ assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ TCDODEBUG(hdb->cnt_appenddrp++);
+ char rbuf[HDBIOBUFSIZ];
+ char *wp = rbuf;
+ *(uint8_t *)(wp++) = HDBMAGICREC;
+ *(uint8_t *)(wp++) = hash;
+ if(hdb->ba64){
+ memset(wp, 0, sizeof(uint64_t) * 2);
+ wp += sizeof(uint64_t) * 2;
+ } else {
+ memset(wp, 0, sizeof(uint32_t) * 2);
+ wp += sizeof(uint32_t) * 2;
+ }
+ uint16_t snum;
+ char *pwp = wp;
+ wp += sizeof(snum);
+ int step;
+ TCSETVNUMBUF(step, wp, ksiz);
+ wp += step;
+ TCSETVNUMBUF(step, wp, vsiz);
+ wp += step;
+ int32_t hsiz = wp - rbuf;
+ int32_t rsiz = hsiz + ksiz + vsiz;
+ hdb->fsiz += rsiz;
+ uint16_t psiz = tchdbpadsize(hdb);
+ hdb->fsiz += psiz;
+ snum = TCHTOIS(psiz);
+ memcpy(pwp, &snum, sizeof(snum));
+ TCXSTR *drpool = hdb->drpool;
+ TCXSTRCAT(drpool, rbuf, hsiz);
+ TCXSTRCAT(drpool, kbuf, ksiz);
+ TCXSTRCAT(drpool, vbuf, vsiz);
+ if(psiz > 0){
+ char pbuf[psiz];
+ memset(pbuf, 0, psiz);
+ TCXSTRCAT(drpool, pbuf, psiz);
+ }
+}
+
+
+/* Store a record in asynchronus fashion.
+ `hdb' specifies the hash database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If successful, the return value is true, else, it is false. */
+static bool tchdbputasyncimpl(TCHDB *hdb, const char *kbuf, int ksiz, const char *vbuf, int vsiz){
+ assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ if(hdb->recc) tcmdbout(hdb->recc, kbuf, ksiz);
+ if(!hdb->drpool){
+ hdb->drpool = tcxstrnew3(HDBDRPUNIT + HDBDRPLAT);
+ hdb->drpdef = tcxstrnew3(HDBDRPUNIT);
+ hdb->drpoff = hdb->fsiz;
+ }
+ uint8_t hash;
+ uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
+ off_t off = tchdbgetbucket(hdb, bidx);
+ off_t entoff = 0;
+ TCHREC rec;
+ char rbuf[HDBIOBUFSIZ];
+ while(off > 0){
+ if(off >= hdb->drpoff - hdb->runit){
+ TCDODEBUG(hdb->cnt_deferdrp++);
+ TCXSTR *drpdef = hdb->drpdef;
+ TCXSTRCAT(drpdef, &ksiz, sizeof(ksiz));
+ TCXSTRCAT(drpdef, &vsiz, sizeof(vsiz));
+ TCXSTRCAT(drpdef, kbuf, ksiz);
+ TCXSTRCAT(drpdef, vbuf, vsiz);
+ if(TCXSTRSIZE(hdb->drpdef) > HDBDRPUNIT && !tchdbflushdrp(hdb)) return false;
+ return true;
+ }
+ rec.off = off;
+ if(!tchdbreadrec(hdb, &rec, rbuf)) return false;
+ if(hash > rec.hash){
+ off = rec.left;
+ entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t));
+ } else if(hash < rec.hash){
+ off = rec.right;
+ entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) +
+ (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t));
+ } else {
+ TCDODEBUG(hdb->cnt_deferdrp++);
+ TCXSTR *drpdef = hdb->drpdef;
+ TCXSTRCAT(drpdef, &ksiz, sizeof(ksiz));
+ TCXSTRCAT(drpdef, &vsiz, sizeof(vsiz));
+ TCXSTRCAT(drpdef, kbuf, ksiz);
+ TCXSTRCAT(drpdef, vbuf, vsiz);
+ if(TCXSTRSIZE(hdb->drpdef) > HDBDRPUNIT && !tchdbflushdrp(hdb)) return false;
+ return true;
+ }
+ }
+ if(entoff > 0){
+ if(hdb->ba64){
+ uint64_t llnum = hdb->fsiz >> hdb->apow;
+ llnum = TCHTOILL(llnum);
+ if(!tcseekwrite(hdb, entoff, &llnum, sizeof(uint64_t))) return false;
+ } else {
+ uint32_t lnum = hdb->fsiz >> hdb->apow;
+ lnum = TCHTOIL(lnum);
+ if(!tcseekwrite(hdb, entoff, &lnum, sizeof(uint32_t))) return false;
+ }
+ } else {
+ tchdbsetbucket(hdb, bidx, hdb->fsiz);
+ }
+ tchdbdrpappend(hdb, kbuf, ksiz, vbuf, vsiz, hash);
+ hdb->rnum++;
+ if(TCXSTRSIZE(hdb->drpool) > HDBDRPUNIT && !tchdbflushdrp(hdb)) return false;
+ return true;
+}
+
+
+/* Remove a record of a hash database object.
+ `hdb' specifies the hash database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is true, else, it is false. */
+static bool tchdboutimpl(TCHDB *hdb, const char *kbuf, int ksiz){
+ assert(hdb && kbuf && ksiz >= 0);
+ if(hdb->recc) tcmdbout(hdb->recc, kbuf, ksiz);
+ uint8_t hash;
+ uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
+ off_t off = tchdbgetbucket(hdb, bidx);
+ off_t entoff = 0;
+ TCHREC rec;
+ char rbuf[HDBIOBUFSIZ];
+ while(off > 0){
+ rec.off = off;
+ if(!tchdbreadrec(hdb, &rec, rbuf)) return false;
+ if(hash > rec.hash){
+ off = rec.left;
+ entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t));
+ } else if(hash < rec.hash){
+ off = rec.right;
+ entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) +
+ (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t));
+ } else {
+ if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return false;
+ int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz);
+ if(kcmp > 0){
+ off = rec.left;
+ free(rec.bbuf);
+ rec.kbuf = NULL;
+ rec.bbuf = NULL;
+ entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t));
+ } else if(kcmp < 0){
+ off = rec.right;
+ free(rec.bbuf);
+ rec.kbuf = NULL;
+ rec.bbuf = NULL;
+ entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) +
+ (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t));
+ } else {
+ free(rec.bbuf);
+ rec.bbuf = NULL;
+ if(!tchdbwritefb(hdb, rec.off, rec.rsiz)) return false;
+ tchdbfbpinsert(hdb, rec.off, rec.rsiz);
+ uint64_t child;
+ if(rec.left > 0 && rec.right < 1){
+ child = rec.left;
+ } else if(rec.left < 1 && rec.right > 0){
+ child = rec.right;
+ } else if(rec.left < 1 && rec.left < 1){
+ child = 0;
+ } else {
+ child = rec.left;
+ uint64_t right = rec.right;
+ rec.right = child;
+ while(rec.right > 0){
+ rec.off = rec.right;
+ if(!tchdbreadrec(hdb, &rec, rbuf)) return false;
+ }
+ if(hdb->ba64){
+ off_t toff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint64_t));
+ uint64_t llnum = right >> hdb->apow;
+ llnum = TCHTOILL(llnum);
+ if(!tcseekwrite(hdb, toff, &llnum, sizeof(uint64_t))) return false;
+ } else {
+ off_t toff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t));
+ uint32_t lnum = right >> hdb->apow;
+ lnum = TCHTOIL(lnum);
+ if(!tcseekwrite(hdb, toff, &lnum, sizeof(uint32_t))) return false;
+ }
+ }
+ if(entoff > 0){
+ if(hdb->ba64){
+ uint64_t llnum = child >> hdb->apow;
+ llnum = TCHTOILL(llnum);
+ if(!tcseekwrite(hdb, entoff, &llnum, sizeof(uint64_t))) return false;
+ } else {
+ uint32_t lnum = child >> hdb->apow;
+ lnum = TCHTOIL(lnum);
+ if(!tcseekwrite(hdb, entoff, &lnum, sizeof(uint32_t))) return false;
+ }
+ } else {
+ tchdbsetbucket(hdb, bidx, child);
+ }
+ hdb->rnum--;
+ uint64_t llnum = hdb->rnum;
+ llnum = TCHTOILL(llnum);
+ memcpy(hdb->map + HDBRNUMOFF, &llnum, sizeof(llnum));
+ return true;
+ }
+ }
+ }
+ tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
+ return false;
+}
+
+
+/* Retrieve a record in a hash database object.
+ `hdb' specifies the hash database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the value of the corresponding
+ record. */
+static char *tchdbgetimpl(TCHDB *hdb, const char *kbuf, int ksiz, int *sp){
+ assert(hdb && kbuf && ksiz >= 0 && sp);
+ if(hdb->recc){
+ int tvsiz;
+ char *tvbuf = tcmdbget(hdb->recc, kbuf, ksiz, &tvsiz);
+ if(tvbuf){
+ if(*tvbuf == '*'){
+ tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
+ free(tvbuf);
+ return NULL;
+ }
+ *sp = tvsiz - 1;
+ memmove(tvbuf, tvbuf + 1, tvsiz);
+ return tvbuf;
+ }
+ }
+ uint8_t hash;
+ uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
+ off_t off = tchdbgetbucket(hdb, bidx);
+ TCHREC rec;
+ char rbuf[HDBIOBUFSIZ];
+ while(off > 0){
+ rec.off = off;
+ if(!tchdbreadrec(hdb, &rec, rbuf)) return NULL;
+ if(hash > rec.hash){
+ off = rec.left;
+ } else if(hash < rec.hash){
+ off = rec.right;
+ } else {
+ if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return NULL;
+ int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz);
+ if(kcmp > 0){
+ off = rec.left;
+ free(rec.bbuf);
+ rec.kbuf = NULL;
+ rec.bbuf = NULL;
+ } else if(kcmp < 0){
+ off = rec.right;
+ free(rec.bbuf);
+ rec.kbuf = NULL;
+ rec.bbuf = NULL;
+ } else {
+ if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)) return NULL;
+ if(hdb->zmode){
+ int zsiz;
+ char *zbuf;
+ if(hdb->opts & HDBTDEFLATE){
+ zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW);
+ } else {
+ zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz);
+ }
+ free(rec.bbuf);
+ if(!zbuf){
+ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
+ return NULL;
+ }
+ if(hdb->recc){
+ if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
+ tcmdbput3(hdb->recc, kbuf, ksiz, "=", 1, zbuf, zsiz);
+ }
+ *sp = zsiz;
+ return zbuf;
+ }
+ if(rec.bbuf){
+ memmove(rec.bbuf, rec.vbuf, rec.vsiz);
+ rec.bbuf[rec.vsiz] = '\0';
+ *sp = rec.vsiz;
+ return rec.bbuf;
+ }
+ if(hdb->recc){
+ if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
+ tcmdbput3(hdb->recc, kbuf, ksiz, "=", 1, rec.vbuf, rec.vsiz);
+ }
+ *sp = rec.vsiz;
+ char *rv;
+ TCMEMDUP(rv, rec.vbuf, rec.vsiz);
+ return rv;
+ }
+ }
+ }
+ if(hdb->recc){
+ if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
+ tcmdbput(hdb->recc, kbuf, ksiz, "*", 1);
+ }
+ tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
+ return NULL;
+}
+
+
+/* Retrieve a record in a hash database object and write the value into a buffer.
+ `hdb' specifies the hash database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the buffer into which the value of the corresponding record is
+ written.
+ `max' specifies the size of the buffer.
+ If successful, the return value is the size of the written data, else, it is -1. */
+static int tchdbgetintobuf(TCHDB *hdb, const char *kbuf, int ksiz, char *vbuf, int max){
+ assert(hdb && kbuf && ksiz >= 0 && vbuf && max >= 0);
+ if(hdb->recc){
+ int tvsiz;
+ char *tvbuf = tcmdbget(hdb->recc, kbuf, ksiz, &tvsiz);
+ if(tvbuf){
+ if(*tvbuf == '*'){
+ tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
+ free(tvbuf);
+ return -1;
+ }
+ tvsiz = tclmin(tvsiz - 1, max);
+ memcpy(vbuf, tvbuf + 1, tvsiz);
+ free(tvbuf);
+ return tvsiz;
+ }
+ }
+ uint8_t hash;
+ uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
+ off_t off = tchdbgetbucket(hdb, bidx);
+ TCHREC rec;
+ char rbuf[HDBIOBUFSIZ];
+ while(off > 0){
+ rec.off = off;
+ if(!tchdbreadrec(hdb, &rec, rbuf)) return -1;
+ if(hash > rec.hash){
+ off = rec.left;
+ } else if(hash < rec.hash){
+ off = rec.right;
+ } else {
+ if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return -1;
+ int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz);
+ if(kcmp > 0){
+ off = rec.left;
+ free(rec.bbuf);
+ rec.kbuf = NULL;
+ rec.bbuf = NULL;
+ } else if(kcmp < 0){
+ off = rec.right;
+ free(rec.bbuf);
+ rec.kbuf = NULL;
+ rec.bbuf = NULL;
+ } else {
+ if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)) return -1;
+ if(hdb->zmode){
+ int zsiz;
+ char *zbuf;
+ if(hdb->opts & HDBTDEFLATE){
+ zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW);
+ } else {
+ zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz);
+ }
+ free(rec.bbuf);
+ if(!zbuf){
+ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
+ return -1;
+ }
+ if(hdb->recc){
+ if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
+ tcmdbput3(hdb->recc, kbuf, ksiz, "=", 1, zbuf, zsiz);
+ }
+ zsiz = tclmin(zsiz, max);
+ memcpy(vbuf, zbuf, zsiz);
+ free(zbuf);
+ return zsiz;
+ }
+ if(hdb->recc){
+ if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
+ tcmdbput3(hdb->recc, kbuf, ksiz, "=", 1, rec.vbuf, rec.vsiz);
+ }
+ int vsiz = tclmin(rec.vsiz, max);
+ memcpy(vbuf, rec.vbuf, vsiz);
+ free(rec.bbuf);
+ return vsiz;
+ }
+ }
+ }
+ if(hdb->recc){
+ if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
+ tcmdbput(hdb->recc, kbuf, ksiz, "*", 1);
+ }
+ tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
+ return -1;
+}
+
+
+/* Get the size of the value of a record in a hash database object.
+ `hdb' specifies the hash database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is the size of the value of the corresponding record, else,
+ it is -1. */
+static int tchdbvsizimpl(TCHDB *hdb, const char *kbuf, int ksiz){
+ assert(hdb && kbuf && ksiz >= 0);
+ if(hdb->recc){
+ int tvsiz;
+ char *tvbuf = tcmdbget(hdb->recc, kbuf, ksiz, &tvsiz);
+ if(tvbuf){
+ if(*tvbuf == '*'){
+ tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
+ free(tvbuf);
+ return -1;
+ }
+ free(tvbuf);
+ return tvsiz - 1;
+ }
+ }
+ uint8_t hash;
+ uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash);
+ off_t off = tchdbgetbucket(hdb, bidx);
+ TCHREC rec;
+ char rbuf[HDBIOBUFSIZ];
+ while(off > 0){
+ rec.off = off;
+ if(!tchdbreadrec(hdb, &rec, rbuf)) return -1;
+ if(hash > rec.hash){
+ off = rec.left;
+ } else if(hash < rec.hash){
+ off = rec.right;
+ } else {
+ if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return -1;
+ int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz);
+ if(kcmp > 0){
+ off = rec.left;
+ free(rec.bbuf);
+ rec.kbuf = NULL;
+ rec.bbuf = NULL;
+ } else if(kcmp < 0){
+ off = rec.right;
+ free(rec.bbuf);
+ rec.kbuf = NULL;
+ rec.bbuf = NULL;
+ } else {
+ if(hdb->zmode){
+ if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)) return -1;
+ int zsiz;
+ char *zbuf;
+ if(hdb->opts & HDBTDEFLATE){
+ zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW);
+ } else {
+ zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz);
+ }
+ free(rec.bbuf);
+ if(!zbuf){
+ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
+ return -1;
+ }
+ if(hdb->recc){
+ if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
+ tcmdbput3(hdb->recc, kbuf, ksiz, "=", 1, zbuf, zsiz);
+ }
+ free(zbuf);
+ return zsiz;
+ }
+ if(hdb->recc && rec.vbuf){
+ if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
+ tcmdbput3(hdb->recc, kbuf, ksiz, "=", 1, rec.vbuf, rec.vsiz);
+ }
+ free(rec.bbuf);
+ return rec.vsiz;
+ }
+ }
+ }
+ if(hdb->recc){
+ if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb);
+ tcmdbput(hdb->recc, kbuf, ksiz, "*", 1);
+ }
+ tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
+ return -1;
+}
+
+
+/* Initialize the iterator of a hash database object.
+ `hdb' specifies the hash database object.
+ If successful, the return value is true, else, it is false. */
+static bool tchdbiterinitimpl(TCHDB *hdb){
+ assert(hdb);
+ hdb->iter = hdb->frec;
+ return true;
+}
+
+
+/* Get the next key of the iterator of a hash database object.
+ `hdb' specifies the hash database object.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the next key, else, it is
+ `NULL'. */
+static char *tchdbiternextimpl(TCHDB *hdb, int *sp){
+ assert(hdb && sp);
+ TCHREC rec;
+ char rbuf[HDBIOBUFSIZ];
+ while(hdb->iter < hdb->fsiz){
+ rec.off = hdb->iter;
+ if(!tchdbreadrec(hdb, &rec, rbuf)) return NULL;
+ hdb->iter += rec.rsiz;
+ if(rec.magic == HDBMAGICREC){
+ if(rec.kbuf){
+ *sp = rec.ksiz;
+ char *rv;
+ TCMEMDUP(rv, rec.kbuf, rec.ksiz);
+ return rv;
+ }
+ if(!tchdbreadrecbody(hdb, &rec)) return NULL;
+ rec.bbuf[rec.ksiz] = '\0';
+ *sp = rec.ksiz;
+ return rec.bbuf;
+ }
+ }
+ tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
+ return NULL;
+}
+
+
+/* Get the next extensible objects of the iterator of a hash database object. */
+static bool tchdbiternextintoxstr(TCHDB *hdb, TCXSTR *kxstr, TCXSTR *vxstr){
+ assert(hdb && kxstr && vxstr);
+ TCHREC rec;
+ char rbuf[HDBIOBUFSIZ];
+ while(hdb->iter < hdb->fsiz){
+ rec.off = hdb->iter;
+ if(!tchdbreadrec(hdb, &rec, rbuf)) return false;
+ hdb->iter += rec.rsiz;
+ if(rec.magic == HDBMAGICREC){
+ if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)) return false;
+ tcxstrclear(kxstr);
+ TCXSTRCAT(kxstr, rec.kbuf, rec.ksiz);
+ tcxstrclear(vxstr);
+ if(hdb->zmode){
+ int zsiz;
+ char *zbuf;
+ if(hdb->opts & HDBTDEFLATE){
+ zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW);
+ } else {
+ zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz);
+ }
+ if(!zbuf){
+ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__);
+ free(rec.bbuf);
+ return false;
+ }
+ TCXSTRCAT(vxstr, zbuf, zsiz);
+ free(zbuf);
+ } else {
+ TCXSTRCAT(vxstr, rec.vbuf, rec.vsiz);
+ }
+ free(rec.bbuf);
+ return true;
+ }
+ }
+ tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__);
+ return false;
+}
+
+
+/* Optimize the file of a hash database object.
+ `hdb' specifies the hash database object.
+ `bnum' specifies the number of elements of the bucket array.
+ `apow' specifies the size of record alignment by power of 2.
+ `fpow' specifies the maximum number of elements of the free block pool by power of 2.
+ `opts' specifies options by bitwise or.
+ If successful, the return value is true, else, it is false. */
+static bool tchdboptimizeimpl(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){
+ assert(hdb);
+ if(bnum < 1){
+ bnum = hdb->rnum * 2 + 1;
+ if(bnum < HDBDEFBNUM) bnum = HDBDEFBNUM;
+ }
+ if(apow < 0) apow = hdb->apow;
+ if(fpow < 0) fpow = hdb->fpow;
+ if(opts == UINT8_MAX) opts = hdb->opts;
+ char *tpath = tcsprintf("%s%ctmp%c%llu", hdb->path, MYEXTCHR, MYEXTCHR, hdb->inode);
+ TCHDB *thdb = tchdbnew();
+ tchdbtune(thdb, bnum, apow, fpow, opts);
+ if(!tchdbopen(thdb, tpath, HDBOWRITER | HDBOCREAT | HDBOTRUNC)){
+ tchdbsetecode(hdb, thdb->ecode, __FILE__, __LINE__, __func__);
+ tchdbdel(thdb);
+ free(tpath);
+ return false;
+ }
+ bool err = false;
+ if(!tchdbiterinitimpl(hdb)) err = true;
+ TCXSTR *key = tcxstrnew();
+ TCXSTR *val = tcxstrnew();
+ while(!err && tchdbiternextintoxstr(hdb, key, val)){
+ if(!tchdbputkeep(thdb, TCXSTRPTR(key), TCXSTRSIZE(key), TCXSTRPTR(val), TCXSTRSIZE(val))){
+ tchdbsetecode(hdb, thdb->ecode, __FILE__, __LINE__, __func__);
+ err = true;
+ }
+ }
+ tcxstrdel(val);
+ tcxstrdel(key);
+ if(!tchdbclose(thdb)){
+ tchdbsetecode(hdb, thdb->ecode, __FILE__, __LINE__, __func__);
+ err = true;
+ }
+ tchdbdel(thdb);
+ if(unlink(hdb->path) == -1){
+ tchdbsetecode(hdb, TCEUNLINK, __FILE__, __LINE__, __func__);
+ err = true;
+ }
+ if(rename(tpath, hdb->path) == -1){
+ tchdbsetecode(hdb, TCERENAME, __FILE__, __LINE__, __func__);
+ err = true;
+ }
+ free(tpath);
+ if(err) return false;
+ tpath = tcstrdup(hdb->path);
+ int omode = (hdb->omode & ~HDBOCREAT) & ~HDBOTRUNC;
+ if(!tchdbcloseimpl(hdb)){
+ free(tpath);
+ return false;
+ }
+ bool rv = tchdbopenimpl(hdb, tpath, omode);
+ free(tpath);
+ return rv;
+}
+
+
+/* Remove all records of a hash database object.
+ `hdb' specifies the hash database object.
+ If successful, the return value is true, else, it is false. */
+static bool tchdbvanishimpl(TCHDB *hdb){
+ assert(hdb);
+ char *path = tcstrdup(hdb->path);
+ int omode = hdb->omode;
+ bool err = false;
+ if(!tchdbcloseimpl(hdb)) err = true;
+ if(!tchdbopenimpl(hdb, path, HDBOTRUNC | omode)) err = true;
+ free(path);
+ return !err;
+}
+
+
+/* Lock a method of the hash database object.
+ `hdb' specifies the hash database object.
+ `wr' specifies whether the lock is writer or not.
+ If successful, the return value is true, else, it is false. */
+static bool tchdblockmethod(TCHDB *hdb, bool wr){
+ assert(hdb);
+ if(wr ? pthread_rwlock_wrlock(hdb->mmtx) != 0 : pthread_rwlock_rdlock(hdb->mmtx) != 0){
+ tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ TCTESTYIELD();
+ return true;
+}
+
+
+/* Unlock a method of the hash database object.
+ `hdb' specifies the hash database object.
+ If successful, the return value is true, else, it is false. */
+static bool tchdbunlockmethod(TCHDB *hdb){
+ assert(hdb);
+ if(pthread_rwlock_unlock(hdb->mmtx) != 0){
+ tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ TCTESTYIELD();
+ return true;
+}
+
+
+/* Lock the delayed record pool of the hash database object.
+ `hdb' specifies the hash database object.
+ If successful, the return value is true, else, it is false. */
+static bool tchdblockdrp(TCHDB *hdb){
+ assert(hdb);
+ if(pthread_mutex_lock(hdb->dmtx) != 0){
+ tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ TCTESTYIELD();
+ return true;
+}
+
+
+/* Unlock the delayed record pool of the hash database object.
+ `hdb' specifies the hash database object.
+ If successful, the return value is true, else, it is false. */
+static bool tchdbunlockdrp(TCHDB *hdb){
+ assert(hdb);
+ if(pthread_mutex_unlock(hdb->dmtx) != 0){
+ tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__);
+ return false;
+ }
+ TCTESTYIELD();
+ return true;
+}
+
+
+
+/*************************************************************************************************
+ * debugging functions
+ *************************************************************************************************/
+
+
+/* Print meta data of the header into the debugging output.
+ `hdb' specifies the hash database object. */
+void tchdbprintmeta(TCHDB *hdb){
+ assert(hdb);
+ if(hdb->dbgfd < 0) return;
+ char buf[HDBIOBUFSIZ];
+ char *wp = buf;
+ wp += sprintf(wp, "META:");
+ wp += sprintf(wp, " mmtx=%p", (void *)hdb->mmtx);
+ wp += sprintf(wp, " dmtx=%p", (void *)hdb->dmtx);
+ wp += sprintf(wp, " eckey=%p", (void *)hdb->eckey);
+ wp += sprintf(wp, " type=%02X", hdb->type);
+ wp += sprintf(wp, " flags=%02X", hdb->flags);
+ wp += sprintf(wp, " bnum=%llu", (unsigned long long)hdb->bnum);
+ wp += sprintf(wp, " apow=%u", hdb->apow);
+ wp += sprintf(wp, " fpow=%u", hdb->fpow);
+ wp += sprintf(wp, " opts=%u", hdb->opts);
+ wp += sprintf(wp, " path=%s", hdb->path ? hdb->path : "-");
+ wp += sprintf(wp, " fd=%u", hdb->fd);
+ wp += sprintf(wp, " omode=%u", hdb->omode);
+ wp += sprintf(wp, " rnum=%llu", (unsigned long long)hdb->rnum);
+ wp += sprintf(wp, " fsiz=%llu", (unsigned long long)hdb->fsiz);
+ wp += sprintf(wp, " frec=%llu", (unsigned long long)hdb->frec);
+ wp += sprintf(wp, " iter=%llu", (unsigned long long)hdb->iter);
+ wp += sprintf(wp, " map=%p", (void *)hdb->map);
+ wp += sprintf(wp, " msiz=%llu", (unsigned long long)hdb->msiz);
+ wp += sprintf(wp, " ba32=%p", (void *)hdb->ba32);
+ wp += sprintf(wp, " ba64=%p", (void *)hdb->ba64);
+ wp += sprintf(wp, " align=%u", hdb->align);
+ wp += sprintf(wp, " runit=%u", hdb->runit);
+ wp += sprintf(wp, " zmode=%u", hdb->zmode);
+ wp += sprintf(wp, " fbpmax=%d", hdb->fbpmax);
+ wp += sprintf(wp, " fbpsiz=%d", hdb->fbpsiz);
+ wp += sprintf(wp, " fbpool=%p", (void *)hdb->fbpool);
+ wp += sprintf(wp, " fbpnum=%d", hdb->fbpnum);
+ wp += sprintf(wp, " fbpmis=%d", hdb->fbpmis);
+ wp += sprintf(wp, " drpool=%p", (void *)hdb->drpool);
+ wp += sprintf(wp, " drpdef=%p", (void *)hdb->drpdef);
+ wp += sprintf(wp, " drpoff=%llu", (unsigned long long)hdb->drpoff);
+ wp += sprintf(wp, " recc=%p", (void *)hdb->recc);
+ wp += sprintf(wp, " rcnum=%u", hdb->rcnum);
+ wp += sprintf(wp, " ecode=%d", hdb->ecode);
+ wp += sprintf(wp, " fatal=%u", hdb->fatal);
+ wp += sprintf(wp, " inode=%llu", (unsigned long long)(uint64_t)hdb->inode);
+ wp += sprintf(wp, " mtime=%llu", (unsigned long long)(uint64_t)hdb->mtime);
+ wp += sprintf(wp, " dbgfd=%d", hdb->dbgfd);
+ wp += sprintf(wp, " cnt_writerec=%lld", (long long)hdb->cnt_writerec);
+ wp += sprintf(wp, " cnt_reuserec=%lld", (long long)hdb->cnt_reuserec);
+ wp += sprintf(wp, " cnt_moverec=%lld", (long long)hdb->cnt_moverec);
+ wp += sprintf(wp, " cnt_readrec=%lld", (long long)hdb->cnt_readrec);
+ wp += sprintf(wp, " cnt_searchfbp=%lld", (long long)hdb->cnt_searchfbp);
+ wp += sprintf(wp, " cnt_insertfbp=%lld", (long long)hdb->cnt_insertfbp);
+ wp += sprintf(wp, " cnt_splicefbp=%lld", (long long)hdb->cnt_splicefbp);
+ wp += sprintf(wp, " cnt_dividefbp=%lld", (long long)hdb->cnt_dividefbp);
+ wp += sprintf(wp, " cnt_mergefbp=%lld", (long long)hdb->cnt_mergefbp);
+ wp += sprintf(wp, " cnt_reducefbp=%lld", (long long)hdb->cnt_reducefbp);
+ wp += sprintf(wp, " cnt_appenddrp=%lld", (long long)hdb->cnt_appenddrp);
+ wp += sprintf(wp, " cnt_deferdrp=%lld", (long long)hdb->cnt_deferdrp);
+ wp += sprintf(wp, " cnt_flushdrp=%lld", (long long)hdb->cnt_flushdrp);
+ wp += sprintf(wp, " cnt_adjrecc=%lld", (long long)hdb->cnt_adjrecc);
+ *(wp++) = '\n';
+ tcwrite(hdb->dbgfd, buf, wp - buf);
+}
+
+
+/* Print a record information into the debugging output.
+ `hdb' specifies the hash database object.
+ `rec' specifies the record. */
+void tchdbprintrec(TCHDB *hdb, TCHREC *rec){
+ assert(hdb && rec);
+ if(hdb->dbgfd < 0) return;
+ char buf[HDBIOBUFSIZ];
+ char *wp = buf;
+ wp += sprintf(wp, "REC:");
+ wp += sprintf(wp, " off=%llu", (unsigned long long)rec->off);
+ wp += sprintf(wp, " rsiz=%u", rec->rsiz);
+ wp += sprintf(wp, " magic=%02X", rec->magic);
+ wp += sprintf(wp, " hash=%02X", rec->hash);
+ wp += sprintf(wp, " left=%llu", (unsigned long long)rec->left);
+ wp += sprintf(wp, " right=%llu", (unsigned long long)rec->right);
+ wp += sprintf(wp, " ksiz=%u", rec->ksiz);
+ wp += sprintf(wp, " vsiz=%u", rec->vsiz);
+ wp += sprintf(wp, " psiz=%u", rec->psiz);
+ wp += sprintf(wp, " kbuf=%p", (void *)rec->kbuf);
+ wp += sprintf(wp, " vbuf=%p", (void *)rec->vbuf);
+ wp += sprintf(wp, " boff=%llu", (unsigned long long)rec->boff);
+ wp += sprintf(wp, " bbuf=%p", (void *)rec->bbuf);
+ *(wp++) = '\n';
+ tcwrite(hdb->dbgfd, buf, wp - buf);
+}
+
+
+
+// END OF FILE
--- /dev/null
+/*************************************************************************************************
+ * The hash database API of Tokyo Cabinet
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _TCHDB_H /* duplication check */
+#define _TCHDB_H
+
+#if defined(__cplusplus)
+#define __TCHDB_CLINKAGEBEGIN extern "C" {
+#define __TCHDB_CLINKAGEEND }
+#else
+#define __TCHDB_CLINKAGEBEGIN
+#define __TCHDB_CLINKAGEEND
+#endif
+__TCHDB_CLINKAGEBEGIN
+
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <time.h>
+#include <tcutil.h>
+
+
+
+/*************************************************************************************************
+ * API
+ *************************************************************************************************/
+
+
+typedef struct { /* type of structure for a hash database */
+ void *mmtx; /* mutex for method */
+ void *dmtx; /* mutex for DRP */
+ void *eckey; /* key for thread specific error code */
+ uint8_t type; /* database type */
+ uint8_t flags; /* additional flags */
+ uint64_t bnum; /* number of the bucket array */
+ uint8_t apow; /* power of record alignment */
+ uint8_t fpow; /* power of free block pool number */
+ uint8_t opts; /* options */
+ char *path; /* path of the database file */
+ int fd; /* file descriptor of the database file */
+ uint32_t omode; /* connection mode */
+ uint64_t rnum; /* number of the records */
+ uint64_t fsiz; /* size of the database file */
+ uint64_t frec; /* offset of the first record */
+ uint64_t lrec; /* offset of the last record */
+ uint64_t iter; /* offset of the iterator */
+ char *map; /* pointer to the mapped memory */
+ uint64_t msiz; /* size of the mapped memory */
+ uint32_t *ba32; /* 32-bit bucket array */
+ uint64_t *ba64; /* 64-bit bucket array */
+ uint32_t align; /* record alignment */
+ uint32_t runit; /* record reading unit */
+ bool zmode; /* whether compression is used */
+ int32_t fbpmax; /* maximum number of the free block pool */
+ int32_t fbpsiz; /* size of the free block pool */
+ void *fbpool; /* free block pool */
+ int32_t fbpnum; /* number of the free block pool */
+ int32_t fbpmis; /* number of missing retrieval of the free block pool */
+ bool async; /* whether asynchronous storing is called */
+ TCXSTR *drpool; /* delayed record pool */
+ TCXSTR *drpdef; /* deferred records of the delayed record pool */
+ uint64_t drpoff; /* offset of the delayed record pool */
+ TCMDB *recc; /* cache for records */
+ uint32_t rcnum; /* max number of cached records */
+ int ecode; /* last happened error code */
+ bool fatal; /* whether a fatal error occured */
+ uint64_t inode; /* inode number */
+ time_t mtime; /* modification time */
+ int dbgfd; /* file descriptor for debugging */
+ int64_t cnt_writerec; /* tesing counter for record write times */
+ int64_t cnt_reuserec; /* tesing counter for record reuse times */
+ int64_t cnt_moverec; /* tesing counter for record move times */
+ int64_t cnt_readrec; /* tesing counter for record read times */
+ int64_t cnt_searchfbp; /* tesing counter for FBP search times */
+ int64_t cnt_insertfbp; /* tesing counter for FBP insert times */
+ int64_t cnt_splicefbp; /* tesing counter for FBP splice times */
+ int64_t cnt_dividefbp; /* tesing counter for FBP divide times */
+ int64_t cnt_mergefbp; /* tesing counter for FBP merge times */
+ int64_t cnt_reducefbp; /* tesing counter for FBP reduce times */
+ int64_t cnt_appenddrp; /* tesing counter for DRP append times */
+ int64_t cnt_deferdrp; /* tesing counter for DRP defer times */
+ int64_t cnt_flushdrp; /* tesing counter for DRP flush times */
+ int64_t cnt_adjrecc; /* tesing counter for record cache adjust times */
+} TCHDB;
+
+enum { /* enumeration for error codes */
+ TCESUCCESS, /* success */
+ TCETHREAD, /* threading error */
+ TCEINVALID, /* invalid operation */
+ TCENOFILE, /* file not found */
+ TCENOPERM, /* no permission */
+ TCEMETA, /* invalid meta data */
+ TCERHEAD, /* invalid record header */
+ TCEOPEN, /* open error */
+ TCECLOSE, /* close error */
+ TCETRUNC, /* trunc error */
+ TCESYNC, /* sync error */
+ TCESTAT, /* stat error */
+ TCESEEK, /* seek error */
+ TCEREAD, /* read error */
+ TCEWRITE, /* write error */
+ TCEMMAP, /* mmap error */
+ TCELOCK, /* lock error */
+ TCEUNLINK, /* unlink error */
+ TCERENAME, /* rename error */
+ TCEMKDIR, /* mkdir error */
+ TCERMDIR, /* rmdir error */
+ TCEKEEP, /* existing record */
+ TCENOREC, /* no record found */
+ TCEMISC = 9999 /* miscellaneous error */
+};
+
+enum { /* enumeration for database type */
+ HDBTHASH, /* hash table */
+ HDBTBTREE /* B+ tree */
+};
+
+enum { /* enumeration for additional flags */
+ HDBFOPEN = 1 << 0, /* whether opened */
+ HDBFFATAL = 1 << 1 /* whetehr with fatal error */
+};
+
+enum { /* enumeration for tuning options */
+ HDBTLARGE = 1 << 0, /* use 64-bit bucket array */
+ HDBTDEFLATE = 1 << 1, /* compress each record with Deflate */
+ HDBTTCBS = 1 << 2 /* compress each record with TCBS */
+};
+
+enum { /* enumeration for open modes */
+ HDBOREADER = 1 << 0, /* open as a reader */
+ HDBOWRITER = 1 << 1, /* open as a writer */
+ HDBOCREAT = 1 << 2, /* writer creating */
+ HDBOTRUNC = 1 << 3, /* writer truncating */
+ HDBONOLCK = 1 << 4, /* open without locking */
+ HDBOLCKNB = 1 << 5 /* lock without blocking */
+};
+
+
+/* Get the message string corresponding to an error code.
+ `ecode' specifies the error code.
+ The return value is the message string of the error code. */
+const char *tchdberrmsg(int ecode);
+
+
+/* Create a hash database object.
+ The return value is the new hash database object. */
+TCHDB *tchdbnew(void);
+
+
+/* Delete a hash database object.
+ `hdb' specifies the hash database object.
+ If the database is not closed, it is closed implicitly. Note that the deleted object and its
+ derivatives can not be used anymore. */
+void tchdbdel(TCHDB *hdb);
+
+
+/* Get the last happened error code of a hash database object.
+ `hdb' specifies the hash database object.
+ The return value is the last happened error code.
+ The following error code is defined: `TCESUCCESS' for success, `TCETHREAD' for threading
+ error, `TCEINVALID' for invalid operation, `TCENOFILE' for file not found, `TCENOPERM' for no
+ permission, `TCEMETA' for invalid meta data, `TCERHEAD' for invalid record header, `TCEOPEN'
+ for open error, `TCECLOSE' for close error, `TCETRUNC' for trunc error, `TCESYNC' for sync
+ error, `TCESTAT' for stat error, `TCESEEK' for seek error, `TCEREAD' for read error,
+ `TCEWRITE' for write error, `TCEMMAP' for mmap error, `TCELOCK' for lock error, `TCEUNLINK'
+ for unlink error, `TCERENAME' for rename error, `TCEMKDIR' for mkdir error, `TCERMDIR' for
+ rmdir error, `TCEKEEP' for existing record, `TCENOREC' for no record found, and `TCEMISC' for
+ miscellaneous error. */
+int tchdbecode(TCHDB *hdb);
+
+
+/* Set mutual exclusion control of a hash database object for threading.
+ `hdb' specifies the hash database object which is not opened.
+ If successful, the return value is true, else, it is false.
+ Note that the mutual exclusion control is needed if the object is shared by plural threads and
+ this function should should be called before the database is opened. */
+bool tchdbsetmutex(TCHDB *hdb);
+
+
+/* Set the tuning parameters of a hash database object.
+ `hdb' specifies the hash database object which is not opened.
+ `bnum' specifies the number of elements of the bucket array. If it is not more than 0, the
+ default value is specified. The default value is 16381. Suggested size of the bucket array
+ is about from 0.5 to 4 times of the number of all records to be stored.
+ `apow' specifies the size of record alignment by power of 2. If it is negative, the default
+ value is specified. The default value is 4 standing for 2^4=16.
+ `fpow' specifies the maximum number of elements of the free block pool by power of 2. If it
+ is negative, the default value is specified. The default value is 10 standing for 2^10=1024.
+ `opts' specifies options by bitwise or: `HDBTLARGE' specifies that the size of the database
+ can be larger than 2GB by using 64-bit bucket array, `HDBTDEFLATE' specifies that each record
+ is compressed with Deflate encoding, `HDBTTCBS' specifies that each record is compressed with
+ TCBS encoding.
+ If successful, the return value is true, else, it is false.
+ Note that the tuning parameters should be set before the database is opened. */
+bool tchdbtune(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
+
+/* Set the caching parameters of a hash database object.
+ `hdb' specifies the hash database object which is not opened.
+ `rcnum' specifies the maximum number of records to be cached. If it is not more than 0, the
+ record cache is disabled. It is disabled by default.
+ If successful, the return value is true, else, it is false.
+ Note that the caching parameters should be set before the database is opened. */
+bool tchdbsetcache(TCHDB *hdb, int32_t rcnum);
+
+
+/* Open a database file and connect a hash database object.
+ `hdb' specifies the hash database object which is not opened.
+ `path' specifies the path of the database file.
+ `omode' specifies the connection mode: `HDBOWRITER' as a writer, `HDBOREADER' as a reader.
+ If the mode is `HDBOWRITER', the following may be added by bitwise or: `HDBOCREAT', which
+ means it creates a new database if not exist, `HDBOTRUNC', which means it creates a new
+ database regardless if one exists. Both of `HDBOREADER' and `HDBOWRITER' can be added to by
+ bitwise or: `HDBONOLCK', which means it opens the database file without file locking, or
+ `HDBOLCKNB', which means locking is performed without blocking.
+ If successful, the return value is true, else, it is false. */
+bool tchdbopen(TCHDB *hdb, const char *path, int omode);
+
+
+/* Close a hash database object.
+ `hdb' specifies the hash database object.
+ If successful, the return value is true, else, it is false.
+ Update of a database is assured to be written when the database is closed. If a writer opens
+ a database but does not close it appropriately, the database will be broken. */
+bool tchdbclose(TCHDB *hdb);
+
+
+/* Store a record into a hash database object.
+ `hdb' specifies the hash database object connected as a writer.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, it is overwritten. */
+bool tchdbput(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a string record into a hash database object.
+ `hdb' specifies the hash database object connected as a writer.
+ `kstr' specifies the string of the key.
+ `vstr' specifies the string of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, it is overwritten. */
+bool tchdbput2(TCHDB *hdb, const char *kstr, const char *vstr);
+
+
+/* Store a new record into a hash database object.
+ `hdb' specifies the hash database object connected as a writer.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, this function has no effect. */
+bool tchdbputkeep(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a new string record into a hash database object.
+ `hdb' specifies the hash database object connected as a writer.
+ `kstr' specifies the string of the key.
+ `vstr' specifies the string of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, this function has no effect. */
+bool tchdbputkeep2(TCHDB *hdb, const char *kstr, const char *vstr);
+
+
+/* Concatenate a value at the end of the existing record in a hash database object.
+ `hdb' specifies the hash database object connected as a writer.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If successful, the return value is true, else, it is false.
+ If there is no corresponding record, a new record is created. */
+bool tchdbputcat(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Concatenate a string value at the end of the existing record in a hash database object.
+ `hdb' specifies the hash database object connected as a writer.
+ `kstr' specifies the string of the key.
+ `vstr' specifies the string of the value.
+ If successful, the return value is true, else, it is false.
+ If there is no corresponding record, a new record is created. */
+bool tchdbputcat2(TCHDB *hdb, const char *kstr, const char *vstr);
+
+
+/* Store a record into a hash database object in asynchronous fashion.
+ `hdb' specifies the hash database object connected as a writer.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, it is overwritten. Records passed to
+ this function are accumulated into the inner buffer and wrote into the file at a blast. */
+bool tchdbputasync(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a string record into a hash database object in asynchronous fashion.
+ `hdb' specifies the hash database object connected as a writer.
+ `kstr' specifies the string of the key.
+ `vstr' specifies the string of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, it is overwritten. Records passed to
+ this function are accumulated into the inner buffer and wrote into the file at a blast. */
+bool tchdbputasync2(TCHDB *hdb, const char *kstr, const char *vstr);
+
+
+/* Remove a record of a hash database object.
+ `hdb' specifies the hash database object connected as a writer.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is true, else, it is false. */
+bool tchdbout(TCHDB *hdb, const void *kbuf, int ksiz);
+
+
+/* Remove a string record of a hash database object.
+ `hdb' specifies the hash database object connected as a writer.
+ `kstr' specifies the string of the key.
+ If successful, the return value is true, else, it is false. */
+bool tchdbout2(TCHDB *hdb, const char *kstr);
+
+
+/* Retrieve a record in a hash database object.
+ `hdb' specifies the hash database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the value of the corresponding
+ record. `NULL' is returned if no record corresponds.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when
+ it is no longer in use. */
+void *tchdbget(TCHDB *hdb, const void *kbuf, int ksiz, int *sp);
+
+
+/* Retrieve a string record in a hash database object.
+ `hdb' specifies the hash database object.
+ `kstr' specifies the string of the key.
+ If successful, the return value is the string of the value of the corresponding record.
+ `NULL' is returned if no record corresponds.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. */
+char *tchdbget2(TCHDB *hdb, const char *kstr);
+
+
+/* Retrieve a record in a hash database object and write the value into a buffer.
+ `hdb' specifies the hash database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the buffer into which the value of the corresponding record is
+ written.
+ `max' specifies the size of the buffer.
+ If successful, the return value is the size of the written data, else, it is -1. -1 is
+ returned if no record corresponds to the specified key.
+ Note that an additional zero code is not appended at the end of the region of the writing
+ buffer. */
+int tchdbget3(TCHDB *hdb, const void *kbuf, int ksiz, void *vbuf, int max);
+
+
+/* Get the size of the value of a record in a hash database object.
+ `hdb' specifies the hash database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is the size of the value of the corresponding record, else,
+ it is -1. */
+int tchdbvsiz(TCHDB *hdb, const void *kbuf, int ksiz);
+
+
+/* Get the size of the value of a string record in a hash database object.
+ `hdb' specifies the hash database object.
+ `kstr' specifies the string of the key.
+ If successful, the return value is the size of the value of the corresponding record, else,
+ it is -1. */
+int tchdbvsiz2(TCHDB *hdb, const char *kstr);
+
+
+/* Initialize the iterator of a hash database object.
+ `hdb' specifies the hash database object.
+ If successful, the return value is true, else, it is false.
+ The iterator is used in order to access the key of every record stored in a database. */
+bool tchdbiterinit(TCHDB *hdb);
+
+
+/* Get the next key of the iterator of a hash database object.
+ `hdb' specifies the hash database object.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the next key, else, it is
+ `NULL'. `NULL' is returned when no record is to be get out of the iterator.
+ Because an additional zero code is appended at the end of the region of the return value, the
+ return value can be treated as a character string. Because the region of the return value is
+ allocated with the `malloc' call, it should be released with the `free' call when it is no
+ longer in use. It is possible to access every record by iteration of calling this function.
+ It is allowed to update or remove records whose keys are fetched while the iteration.
+ However, it is not assured if updating the database is occurred while the iteration. Besides,
+ the order of this traversal access method is arbitrary, so it is not assured that the order of
+ storing matches the one of the traversal access. */
+void *tchdbiternext(TCHDB *hdb, int *sp);
+
+
+/* Get the next key string of the iterator of a hash database object.
+ `hdb' specifies the hash database object.
+ If successful, the return value is the string of the next key, else, it is `NULL'. `NULL' is
+ returned when no record is to be get out of the iterator.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. It is possible to access every
+ record by iteration of calling this function. However, it is not assured if updating the
+ database is occurred while the iteration. Besides, the order of this traversal access method
+ is arbitrary, so it is not assured that the order of storing matches the one of the traversal
+ access. */
+char *tchdbiternext2(TCHDB *hdb);
+
+
+/* Get the next extensible objects of the iterator of a hash database object.
+ `hdb' specifies the hash database object.
+ `kxstr' specifies the object into which the next key is wrote down.
+ `vxstr' specifies the object into which the next value is wrote down.
+ If successful, the return value is true, else, it is false. False is returned when no record
+ is to be get out of the iterator. */
+bool tchdbiternext3(TCHDB *hdb, TCXSTR *kxstr, TCXSTR *vxstr);
+
+
+/* Get forward matching keys in a hash database object.
+ `hdb' specifies the hash database object.
+ `pbuf' specifies the pointer to the region of the prefix.
+ `psiz' specifies the size of the region of the prefix.
+ `max' specifies the maximum number of keys to be fetched. If it is negative, no limit is
+ specified.
+ The return value is a list object of the corresponding keys. This function does never fail
+ and return an empty list even if no key corresponds.
+ Because the object of the return value is created with the function `tclistnew', it should be
+ deleted with the function `tclistdel' when it is no longer in use. Note that this function
+ may be very slow because every key in the database is scanned. */
+TCLIST *tchdbfwmkeys(TCHDB *hdb, const void *pbuf, int psiz, int max);
+
+
+/* Get forward matching string keys in a hash database object.
+ `hdb' specifies the hash database object.
+ `pstr' specifies the string of the prefix.
+ `max' specifies the maximum number of keys to be fetched. If it is negative, no limit is
+ specified.
+ The return value is a list object of the corresponding keys. This function does never fail
+ and return an empty list even if no key corresponds.
+ Because the object of the return value is created with the function `tclistnew', it should be
+ deleted with the function `tclistdel' when it is no longer in use. Note that this function
+ may be very slow because every key in the database is scanned. */
+TCLIST *tchdbfwmkeys2(TCHDB *hdb, const char *pstr, int max);
+
+
+/* Synchronize updated contents of a hash database object with the file and the device.
+ `hdb' specifies the hash database object connected as a writer.
+ If successful, the return value is true, else, it is false.
+ This function is useful when another process connects the same database file. */
+bool tchdbsync(TCHDB *hdb);
+
+
+/* Optimize the file of a hash database object.
+ `hdb' specifies the hash database object connected as a writer.
+ `bnum' specifies the number of elements of the bucket array. If it is not more than 0, the
+ default value is specified. The default value is two times of the number of records.
+ `apow' specifies the size of record alignment by power of 2. If it is negative, the current
+ setting is not changed.
+ `fpow' specifies the maximum number of elements of the free block pool by power of 2. If it
+ is negative, the current setting is not changed.
+ `opts' specifies options by bitwise or: `HDBTLARGE' specifies that the size of the database
+ can be larger than 2GB by using 64-bit bucket array, `HDBTDEFLATE' specifies that each record
+ is compressed with Deflate encoding, `HDBTTCBS' specifies that each record is compressed with
+ TCBS encoding. If it is `UINT8_MAX', the current setting is not changed.
+ If successful, the return value is true, else, it is false.
+ This function is useful to reduce the size of the database file with data fragmentation by
+ successive updating. */
+bool tchdboptimize(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
+
+/* Remove all records of a hash database object.
+ `hdb' specifies the hash database object connected as a writer.
+ If successful, the return value is true, else, it is false. */
+bool tchdbvanish(TCHDB *hdb);
+
+
+/* Copy the database file of a hash database object.
+ `hdb' specifies the hash database object.
+ `path' specifies the path of the destination file. If it begins with `@', the trailing
+ substring is executed as a command line.
+ If successful, the return value is true, else, it is false. False is returned if the executed
+ command returns non-zero code.
+ The database file is assured to be kept synchronized and not modified while the copying or
+ executing operation is in progress. So, this function is useful to create a backup file of
+ the database file. */
+bool tchdbcopy(TCHDB *hdb, const char *path);
+
+
+/* Get the file path of a hash database object.
+ `hdb' specifies the hash database object.
+ The return value is the path of the database file or `NULL' if the object does not connect to
+ any database file. */
+const char *tchdbpath(TCHDB *hdb);
+
+
+/* Get the number of records of a hash database object.
+ `hdb' specifies the hash database object.
+ The return value is the number of records or 0 if the object does not connect to any database
+ file. */
+uint64_t tchdbrnum(TCHDB *hdb);
+
+
+/* Get the size of the database file of a hash database object.
+ `hdb' specifies the hash database object.
+ The return value is the size of the database file or 0 if the object does not connect to any
+ database file. */
+uint64_t tchdbfsiz(TCHDB *hdb);
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+/* Set the error code of a hash database object.
+ `hdb' specifies the hash database object.
+ `ecode' specifies the error code.
+ `file' specifies the file name of the code.
+ `line' specifies the line number of the code.
+ `func' specifies the function name of the code. */
+void tchdbsetecode(TCHDB *hdb, int ecode, const char *filename, int line, const char *func);
+
+
+/* Set the type of a hash database object.
+ `hdb' specifies the hash database object.
+ `type' specifies the database type. */
+void tchdbsettype(TCHDB *hdb, uint8_t type);
+
+
+/* Set the file descriptor for debugging output.
+ `hdb' specifies the hash database object.
+ `fd' specifies the file descriptor for debugging output. */
+void tchdbsetdbgfd(TCHDB *hdb, int fd);
+
+
+/* Get the file descriptor for debugging output.
+ `hdb' specifies the hash database object.
+ The return value is the file descriptor for debugging output. */
+int tchdbdbgfd(TCHDB *hdb);
+
+
+/* Synchronize updating contents on memory.
+ `hdb' specifies the hash database object connected as a writer.
+ `phys' specifies whether to synchronize physically.
+ If successful, the return value is true, else, it is false. */
+bool tchdbmemsync(TCHDB *hdb, bool phys);
+
+
+/* Get the number of elements of the bucket array of a hash database object.
+ `hdb' specifies the hash database object.
+ The return value is the number of elements of the bucket array or 0 if the object does not
+ connect to any database file. */
+uint64_t tchdbbnum(TCHDB *hdb);
+
+
+/* Get the record alignment of a hash database object.
+ `hdb' specifies the hash database object.
+ The return value is the record alignment or 0 if the object does not connect to any database
+ file. */
+uint32_t tchdbalign(TCHDB *hdb);
+
+
+/* Get the maximum number of the free block pool of a a hash database object.
+ `hdb' specifies the hash database object.
+ The return value is the maximum number of the free block pool or 0 if the object does not
+ connect to any database file. */
+uint32_t tchdbfbpmax(TCHDB *hdb);
+
+
+/* Get the inode number of the database file of a hash database object.
+ `hdb' specifies the hash database object.
+ The return value is the inode number of the database file or 0 the object does not connect to
+ any database file. */
+uint64_t tchdbinode(TCHDB *hdb);
+
+
+/* Get the modification time of the database file of a hash database object.
+ `hdb' specifies the hash database object.
+ The return value is the inode number of the database file or 0 the object does not connect to
+ any database file. */
+time_t tchdbmtime(TCHDB *hdb);
+
+
+/* Get the connection mode of a hash database object.
+ `hdb' specifies the hash database object.
+ The return value is the connection mode. */
+int tchdbomode(TCHDB *hdb);
+
+
+/* Get the database type of a hash database object.
+ `hdb' specifies the hash database object.
+ The return value is the database type. */
+uint8_t tchdbtype(TCHDB *hdb);
+
+
+/* Get the additional flags of a hash database object.
+ `hdb' specifies the hash database object.
+ The return value is the additional flags. */
+uint8_t tchdbflags(TCHDB *hdb);
+
+
+/* Get the options of a hash database object.
+ `hdb' specifies the hash database object.
+ The return value is the options. */
+uint8_t tchdbopts(TCHDB *hdb);
+
+
+/* Get the pointer to the opaque field of a hash database object.
+ `hdb' specifies the hash database object.
+ The return value is the pointer to the opaque field whose size is 128 bytes. */
+char *tchdbopaque(TCHDB *hdb);
+
+
+/* Get the number of used elements of the bucket array of a hash database object.
+ `hdb' specifies the hash database object.
+ The return value is the number of used elements of the bucket array or 0 if the object does not
+ connect to any database file. */
+uint64_t tchdbbnumused(TCHDB *hdb);
+
+
+
+__TCHDB_CLINKAGEEND
+#endif /* duplication check */
+
+
+/* END OF FILE */
--- /dev/null
+/*************************************************************************************************
+ * The command line utility of the hash database API
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include <tchdb.h>
+#include "myconf.h"
+
+
+/* global variables */
+const char *g_progname; // program name
+int g_dbgfd; // debugging output
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void printerr(TCHDB *hdb);
+static int printdata(const char *ptr, int size, bool px);
+static char *hextoobj(const char *str, int *sp);
+static char *mygetline(FILE *ifp);
+static int runcreate(int argc, char **argv);
+static int runinform(int argc, char **argv);
+static int runput(int argc, char **argv);
+static int runout(int argc, char **argv);
+static int runget(int argc, char **argv);
+static int runlist(int argc, char **argv);
+static int runoptimize(int argc, char **argv);
+static int runimporttsv(int argc, char **argv);
+static int runversion(int argc, char **argv);
+static int proccreate(const char *path, int bnum, int apow, int fpow, int opts);
+static int procinform(const char *path, int omode);
+static int procput(const char *path, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+ int omode, int dmode);
+static int procout(const char *path, const char *kbuf, int ksiz, int omode);
+static int procget(const char *path, const char *kbuf, int ksiz, int omode, bool px, bool pz);
+static int proclist(const char *path, int omode, int max, bool pv, bool px, const char *fmstr);
+static int procoptimize(const char *path, int bnum, int apow, int fpow, int opts, int omode);
+static int procimporttsv(const char *path, const char *file, int omode, bool sc);
+static int procversion(void);
+
+
+/* main routine */
+int main(int argc, char **argv){
+ g_progname = argv[0];
+ g_dbgfd = -1;
+ const char *ebuf = getenv("TCDBGFD");
+ if(ebuf) g_dbgfd = atoi(ebuf);
+ if(argc < 2) usage();
+ int rv = 0;
+ if(!strcmp(argv[1], "create")){
+ rv = runcreate(argc, argv);
+ } else if(!strcmp(argv[1], "inform")){
+ rv = runinform(argc, argv);
+ } else if(!strcmp(argv[1], "put")){
+ rv = runput(argc, argv);
+ } else if(!strcmp(argv[1], "out")){
+ rv = runout(argc, argv);
+ } else if(!strcmp(argv[1], "get")){
+ rv = runget(argc, argv);
+ } else if(!strcmp(argv[1], "list")){
+ rv = runlist(argc, argv);
+ } else if(!strcmp(argv[1], "optimize")){
+ rv = runoptimize(argc, argv);
+ } else if(!strcmp(argv[1], "importtsv")){
+ rv = runimporttsv(argc, argv);
+ } else if(!strcmp(argv[1], "version") || !strcmp(argv[1], "--version")){
+ rv = runversion(argc, argv);
+ } else {
+ usage();
+ }
+ return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+ fprintf(stderr, "%s: the command line utility of the hash database API\n", g_progname);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, " %s create [-tl] [-td|-tb] path [bnum [apow [fpow]]]\n", g_progname);
+ fprintf(stderr, " %s inform [-nl|-nb] path\n", g_progname);
+ fprintf(stderr, " %s put [-nl|-nb] [-sx] [-dk|-dc] path key value\n", g_progname);
+ fprintf(stderr, " %s out [-nl|-nb] [-sx] path key\n", g_progname);
+ fprintf(stderr, " %s get [-nl|-nb] [-sx] [-px] [-pz] path key\n", g_progname);
+ fprintf(stderr, " %s list [-nl|-nb] [-m num] [-pv] [-px] [-fm str] path\n", g_progname);
+ fprintf(stderr, " %s optimize [-tl] [-td|-tb] [-tz] [-nl|-nb] path [bnum [apow [fpow]]]\n",
+ g_progname);
+ fprintf(stderr, " %s importtsv [-nl|-nb] [-sc] path [file]\n", g_progname);
+ fprintf(stderr, " %s version\n", g_progname);
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+
+/* print error information */
+static void printerr(TCHDB *hdb){
+ const char *path = tchdbpath(hdb);
+ int ecode = tchdbecode(hdb);
+ fprintf(stderr, "%s: %s: %d: %s\n", g_progname, path ? path : "-", ecode, tchdberrmsg(ecode));
+}
+
+
+/* print record data */
+static int printdata(const char *ptr, int size, bool px){
+ int len = 0;
+ while(size-- > 0){
+ if(px){
+ if(len > 0) putchar(' ');
+ len += printf("%02X", *(unsigned char *)ptr);
+ } else {
+ putchar(*ptr);
+ len++;
+ }
+ ptr++;
+ }
+ return len;
+}
+
+
+/* create a binary object from a hexadecimal string */
+static char *hextoobj(const char *str, int *sp){
+ int len = strlen(str);
+ char *buf = tcmalloc(len + 1);
+ int j = 0;
+ for(int i = 0; i < len; i += 2){
+ while(strchr(" \n\r\t\f\v", str[i])){
+ i++;
+ }
+ char mbuf[3];
+ if((mbuf[0] = str[i]) == '\0') break;
+ if((mbuf[1] = str[i+1]) == '\0') break;
+ mbuf[2] = '\0';
+ buf[j++] = (char)strtol(mbuf, NULL, 16);
+ }
+ buf[j] = '\0';
+ *sp = j;
+ return buf;
+}
+
+
+/* read a line from a file descriptor */
+static char *mygetline(FILE *ifp){
+ char *buf;
+ int c, len, blen;
+ buf = NULL;
+ len = 0;
+ blen = 256;
+ while((c = fgetc(ifp)) != EOF){
+ if(blen <= len) blen *= 2;
+ buf = tcrealloc(buf, blen + 1);
+ if(c == '\n' || c == '\r') c = '\0';
+ buf[len++] = c;
+ if(c == '\0') break;
+ }
+ if(!buf) return NULL;
+ buf[len] = '\0';
+ return buf;
+}
+
+
+/* parse arguments of create command */
+static int runcreate(int argc, char **argv){
+ char *path = NULL;
+ char *bstr = NULL;
+ char *astr = NULL;
+ char *fstr = NULL;
+ int opts = 0;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-tl")){
+ opts |= HDBTLARGE;
+ } else if(!strcmp(argv[i], "-td")){
+ opts |= HDBTDEFLATE;
+ } else if(!strcmp(argv[i], "-tb")){
+ opts |= HDBTTCBS;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!bstr){
+ bstr = argv[i];
+ } else if(!astr){
+ astr = argv[i];
+ } else if(!fstr){
+ fstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path) usage();
+ int bnum = bstr ? atoi(bstr) : -1;
+ int apow = astr ? atoi(astr) : -1;
+ int fpow = fstr ? atoi(fstr) : -1;
+ int rv = proccreate(path, bnum, apow, fpow, opts);
+ return rv;
+}
+
+
+/* parse arguments of inform command */
+static int runinform(int argc, char **argv){
+ char *path = NULL;
+ int omode = 0;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-nl")){
+ omode |= HDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= HDBOLCKNB;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path) usage();
+ int rv = procinform(path, omode);
+ return rv;
+}
+
+
+/* parse arguments of put command */
+static int runput(int argc, char **argv){
+ char *path = NULL;
+ char *key = NULL;
+ char *value = NULL;
+ int omode = 0;
+ int dmode = 0;
+ bool sx = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-nl")){
+ omode |= HDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= HDBOLCKNB;
+ } else if(!strcmp(argv[i], "-dk")){
+ dmode = -1;
+ } else if(!strcmp(argv[i], "-dc")){
+ dmode = 1;
+ } else if(!strcmp(argv[i], "-sx")){
+ sx = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!key){
+ key = argv[i];
+ } else if(!value){
+ value = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !key || !value) usage();
+ int ksiz, vsiz;
+ char *kbuf, *vbuf;
+ if(sx){
+ kbuf = hextoobj(key, &ksiz);
+ vbuf = hextoobj(value, &vsiz);
+ } else {
+ ksiz = strlen(key);
+ kbuf = tcmemdup(key, ksiz);
+ vsiz = strlen(value);
+ vbuf = tcmemdup(value, vsiz);
+ }
+ int rv = procput(path, kbuf, ksiz, vbuf, vsiz, omode, dmode);
+ free(vbuf);
+ free(kbuf);
+ return rv;
+}
+
+
+/* parse arguments of out command */
+static int runout(int argc, char **argv){
+ char *path = NULL;
+ char *key = NULL;
+ int omode = 0;
+ bool sx = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-nl")){
+ omode |= HDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= HDBOLCKNB;
+ } else if(!strcmp(argv[i], "-sx")){
+ sx = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!key){
+ key = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !key) usage();
+ int ksiz;
+ char *kbuf;
+ if(sx){
+ kbuf = hextoobj(key, &ksiz);
+ } else {
+ ksiz = strlen(key);
+ kbuf = tcmemdup(key, ksiz);
+ }
+ int rv = procout(path, kbuf, ksiz, omode);
+ free(kbuf);
+ return rv;
+}
+
+
+/* parse arguments of get command */
+static int runget(int argc, char **argv){
+ char *path = NULL;
+ char *key = NULL;
+ int omode = 0;
+ bool sx = false;
+ bool px = false;
+ bool pz = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-nl")){
+ omode |= HDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= HDBOLCKNB;
+ } else if(!strcmp(argv[i], "-sx")){
+ sx = true;
+ } else if(!strcmp(argv[i], "-px")){
+ px = true;
+ } else if(!strcmp(argv[i], "-pz")){
+ pz = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!key){
+ key = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !key) usage();
+ int ksiz;
+ char *kbuf;
+ if(sx){
+ kbuf = hextoobj(key, &ksiz);
+ } else {
+ ksiz = strlen(key);
+ kbuf = tcmemdup(key, ksiz);
+ }
+ int rv = procget(path, kbuf, ksiz, omode, px, pz);
+ free(kbuf);
+ return rv;
+}
+
+
+/* parse arguments of list command */
+static int runlist(int argc, char **argv){
+ char *path = NULL;
+ int omode = 0;
+ int max = -1;
+ bool pv = false;
+ bool px = false;
+ char *fmstr = NULL;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-nl")){
+ omode |= HDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= HDBOLCKNB;
+ } else if(!strcmp(argv[i], "-m")){
+ if(++i >= argc) usage();
+ max = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-pv")){
+ pv = true;
+ } else if(!strcmp(argv[i], "-px")){
+ px = true;
+ } else if(!strcmp(argv[i], "-fm")){
+ if(++i >= argc) usage();
+ fmstr = argv[i];
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path) usage();
+ int rv = proclist(path, omode, max, pv, px, fmstr);
+ return rv;
+}
+
+
+/* parse arguments of optimize command */
+static int runoptimize(int argc, char **argv){
+ char *path = NULL;
+ char *bstr = NULL;
+ char *astr = NULL;
+ char *fstr = NULL;
+ int opts = UINT8_MAX;
+ int omode = 0;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-tl")){
+ if(opts == UINT8_MAX) opts = 0;
+ opts |= HDBTLARGE;
+ } else if(!strcmp(argv[i], "-td")){
+ if(opts == UINT8_MAX) opts = 0;
+ opts |= HDBTDEFLATE;
+ } else if(!strcmp(argv[i], "-tb")){
+ if(opts == UINT8_MAX) opts = 0;
+ opts |= HDBTTCBS;
+ } else if(!strcmp(argv[i], "-tz")){
+ if(opts == UINT8_MAX) opts = 0;
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= HDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= HDBOLCKNB;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!bstr){
+ bstr = argv[i];
+ } else if(!astr){
+ astr = argv[i];
+ } else if(!fstr){
+ fstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path) usage();
+ int bnum = bstr ? atoi(bstr) : -1;
+ int apow = astr ? atoi(astr) : -1;
+ int fpow = fstr ? atoi(fstr) : -1;
+ int rv = procoptimize(path, bnum, apow, fpow, opts, omode);
+ return rv;
+}
+
+
+/* parse arguments of importtsv command */
+static int runimporttsv(int argc, char **argv){
+ char *path = NULL;
+ char *file = NULL;
+ int omode = 0;
+ bool sc = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-nl")){
+ omode |= HDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= HDBOLCKNB;
+ } else if(!strcmp(argv[i], "-sc")){
+ sc = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!file){
+ file = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path) usage();
+ int rv = procimporttsv(path, file, omode, sc);
+ return rv;
+}
+
+
+/* parse arguments of version command */
+static int runversion(int argc, char **argv){
+ int rv = procversion();
+ return rv;
+}
+
+
+/* perform create command */
+static int proccreate(const char *path, int bnum, int apow, int fpow, int opts){
+ TCHDB *hdb = tchdbnew();
+ if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+ if(!tchdbtune(hdb, bnum, apow, fpow, opts)){
+ printerr(hdb);
+ tchdbdel(hdb);
+ return 1;
+ }
+ if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC)){
+ printerr(hdb);
+ tchdbdel(hdb);
+ return 1;
+ }
+ bool err = false;
+ if(!tchdbclose(hdb)){
+ printerr(hdb);
+ err = true;
+ }
+ tchdbdel(hdb);
+ return err ? 1 : 0;
+}
+
+
+/* perform inform command */
+static int procinform(const char *path, int omode){
+ TCHDB *hdb = tchdbnew();
+ if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+ if(!tchdbopen(hdb, path, HDBOREADER | omode)){
+ printerr(hdb);
+ tchdbdel(hdb);
+ return 1;
+ }
+ bool err = false;
+ const char *npath = tchdbpath(hdb);
+ if(!npath) npath = "(unknown)";
+ printf("path: %s\n", npath);
+ const char *type = "(unknown)";
+ switch(tchdbtype(hdb)){
+ case HDBTHASH: type = "hash"; break;
+ case HDBTBTREE: type = "btree"; break;
+ }
+ printf("database type: %s\n", type);
+ uint8_t flags = tchdbflags(hdb);
+ printf("additional flags:");
+ if(flags & HDBFOPEN) printf(" open");
+ if(flags & HDBFFATAL) printf(" fatal");
+ printf("\n");
+ printf("bucket number: %llu\n", (unsigned long long)tchdbbnum(hdb));
+ if(hdb->cnt_writerec >= 0)
+ printf("used bucket number: %lld\n", (long long)tchdbbnumused(hdb));
+ printf("alignment: %u\n", tchdbalign(hdb));
+ printf("free block pool: %u\n", tchdbfbpmax(hdb));
+ printf("inode number: %lld\n", (long long)tchdbinode(hdb));
+ char date[48];
+ tcdatestrwww(tchdbmtime(hdb), INT_MAX, date);
+ printf("modified time: %s\n", date);
+ uint8_t opts = tchdbopts(hdb);
+ printf("options:");
+ if(opts & HDBTLARGE) printf(" large");
+ if(opts & HDBTDEFLATE) printf(" deflate");
+ if(opts & HDBTTCBS) printf(" tcbs");
+ printf("\n");
+ printf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+ printf("file size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+ if(!tchdbclose(hdb)){
+ if(!err) printerr(hdb);
+ err = true;
+ }
+ tchdbdel(hdb);
+ return err ? 1 : 0;
+}
+
+
+/* perform put command */
+static int procput(const char *path, const char *kbuf, int ksiz, const char *vbuf, int vsiz,
+ int omode, int dmode){
+ TCHDB *hdb = tchdbnew();
+ if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+ if(!tchdbopen(hdb, path, HDBOWRITER | omode)){
+ printerr(hdb);
+ tchdbdel(hdb);
+ return 1;
+ }
+ bool err = false;
+ switch(dmode){
+ case -1:
+ if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz)){
+ printerr(hdb);
+ err = true;
+ }
+ break;
+ case 1:
+ if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){
+ printerr(hdb);
+ err = true;
+ }
+ break;
+ default:
+ if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){
+ printerr(hdb);
+ err = true;
+ }
+ break;
+ }
+ if(!tchdbclose(hdb)){
+ if(!err) printerr(hdb);
+ err = true;
+ }
+ tchdbdel(hdb);
+ return err ? 1 : 0;
+}
+
+
+/* perform out command */
+static int procout(const char *path, const char *kbuf, int ksiz, int omode){
+ TCHDB *hdb = tchdbnew();
+ if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+ if(!tchdbopen(hdb, path, HDBOWRITER | omode)){
+ printerr(hdb);
+ tchdbdel(hdb);
+ return 1;
+ }
+ bool err = false;
+ if(!tchdbout(hdb, kbuf, ksiz)){
+ printerr(hdb);
+ err = true;
+ }
+ if(!tchdbclose(hdb)){
+ if(!err) printerr(hdb);
+ err = true;
+ }
+ tchdbdel(hdb);
+ return err ? 1 : 0;
+}
+
+
+/* perform get command */
+static int procget(const char *path, const char *kbuf, int ksiz, int omode, bool px, bool pz){
+ TCHDB *hdb = tchdbnew();
+ if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+ if(!tchdbopen(hdb, path, HDBOREADER | omode)){
+ printerr(hdb);
+ tchdbdel(hdb);
+ return 1;
+ }
+ bool err = false;
+ int vsiz;
+ char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
+ if(vbuf){
+ printdata(vbuf, vsiz, px);
+ if(!pz) putchar('\n');
+ free(vbuf);
+ } else {
+ printerr(hdb);
+ err = true;
+ }
+ if(!tchdbclose(hdb)){
+ if(!err) printerr(hdb);
+ err = true;
+ }
+ tchdbdel(hdb);
+ return err ? 1 : 0;
+}
+
+
+/* perform list command */
+static int proclist(const char *path, int omode, int max, bool pv, bool px, const char *fmstr){
+ TCHDB *hdb = tchdbnew();
+ if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+ if(!tchdbopen(hdb, path, HDBOREADER | omode)){
+ printerr(hdb);
+ tchdbdel(hdb);
+ return 1;
+ }
+ bool err = false;
+ if(fmstr){
+ TCLIST *keys = tchdbfwmkeys2(hdb, fmstr, max);
+ for(int i = 0; i < tclistnum(keys); i++){
+ int ksiz;
+ const char *kbuf = tclistval(keys, i, &ksiz);
+ printdata(kbuf, ksiz, px);
+ if(pv){
+ int vsiz;
+ char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
+ if(vbuf){
+ putchar('\t');
+ printdata(vbuf, vsiz, px);
+ free(vbuf);
+ }
+ }
+ putchar('\n');
+ }
+ tclistdel(keys);
+ } else {
+ if(!tchdbiterinit(hdb)){
+ printerr(hdb);
+ err = true;
+ }
+ TCXSTR *key = tcxstrnew();
+ TCXSTR *val = tcxstrnew();
+ int cnt = 0;
+ while(tchdbiternext3(hdb, key, val)){
+ printdata(tcxstrptr(key), tcxstrsize(key), px);
+ if(pv){
+ putchar('\t');
+ printdata(tcxstrptr(val), tcxstrsize(val), px);
+ }
+ putchar('\n');
+ if(max >= 0 && ++cnt >= max) break;
+ }
+ tcxstrdel(val);
+ tcxstrdel(key);
+ }
+ if(!tchdbclose(hdb)){
+ if(!err) printerr(hdb);
+ err = true;
+ }
+ tchdbdel(hdb);
+ return err ? 1 : 0;
+}
+
+
+/* perform optimize command */
+static int procoptimize(const char *path, int bnum, int apow, int fpow, int opts, int omode){
+ TCHDB *hdb = tchdbnew();
+ if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+ if(!tchdbopen(hdb, path, HDBOWRITER | omode)){
+ printerr(hdb);
+ tchdbdel(hdb);
+ return 1;
+ }
+ bool err = false;
+ if(!tchdboptimize(hdb, bnum, apow, fpow, opts)){
+ printerr(hdb);
+ err = true;
+ }
+ if(!tchdbclose(hdb)){
+ if(!err) printerr(hdb);
+ err = true;
+ }
+ tchdbdel(hdb);
+ return err ? 1 : 0;
+}
+
+
+/* perform importtsv command */
+static int procimporttsv(const char *path, const char *file, int omode, bool sc){
+ TCHDB *hdb = tchdbnew();
+ if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+ FILE *ifp = file ? fopen(file, "rb") : stdin;
+ if(!ifp){
+ fprintf(stderr, "%s: could not open\n", file ? file : "(stdin)");
+ tchdbdel(hdb);
+ return 1;
+ }
+ if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | omode)){
+ printerr(hdb);
+ tchdbdel(hdb);
+ return 1;
+ }
+ bool err = false;
+ char *line;
+ int cnt = 0;
+ while(!err && (line = mygetline(ifp)) != NULL){
+ char *pv = strchr(line, '\t');
+ if(!pv) continue;
+ *pv = '\0';
+ if(sc) tcstrtolower(line);
+ if(!tchdbput2(hdb, line, pv + 1) && tchdbecode(hdb) != TCEKEEP){
+ printerr(hdb);
+ err = true;
+ }
+ free(line);
+ if(cnt > 0 && cnt % 100 == 0){
+ putchar('.');
+ fflush(stdout);
+ if(cnt % 5000 == 0) printf(" (%08d)\n", cnt);
+ }
+ cnt++;
+ }
+ printf(" (%08d)\n", cnt);
+ if(!tchdbclose(hdb)){
+ if(!err) printerr(hdb);
+ err = true;
+ }
+ tchdbdel(hdb);
+ if(ifp != stdin) fclose(ifp);
+ return err ? 1 : 0;
+}
+
+
+/* perform version command */
+static int procversion(void){
+ printf("Tokyo Cabinet version %s (%d:%s)\n", tcversion, _TC_LIBVER, _TC_FORMATVER);
+ printf("Copyright (C) 2006-2008 Mikio Hirabayashi\n");
+ return 0;
+}
+
+
+
+// END OF FILE
--- /dev/null
+/*************************************************************************************************
+ * The test cases of the hash database API
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include <tchdb.h>
+#include "myconf.h"
+
+#define RECBUFSIZ 32 // buffer for records
+
+typedef struct { // type of structure for write thread
+ TCHDB *hdb;
+ int rnum;
+ bool as;
+ bool rnd;
+ int id;
+} TARGWRITE;
+
+typedef struct { // type of structure for read thread
+ TCHDB *hdb;
+ int rnum;
+ bool wb;
+ bool rnd;
+ int id;
+} TARGREAD;
+
+typedef struct { // type of structure for remove thread
+ TCHDB *hdb;
+ int rnum;
+ bool rnd;
+ int id;
+} TARGREMOVE;
+
+typedef struct { // type of structure for wicked thread
+ TCHDB *hdb;
+ int rnum;
+ bool nc;
+ int id;
+ TCMAP *map;
+} TARGWICKED;
+
+typedef struct { // type of structure for typical thread
+ TCHDB *hdb;
+ int rnum;
+ bool nc;
+ int rratio;
+ int id;
+} TARGTYPICAL;
+
+
+/* global variables */
+const char *g_progname; // program name
+int g_dbgfd; // debugging output
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void iprintf(const char *format, ...);
+static void eprint(TCHDB *hdb, const char *func);
+static void mprint(TCHDB *hdb);
+static int myrand(int range);
+static int myrandnd(int range);
+static int runwrite(int argc, char **argv);
+static int runread(int argc, char **argv);
+static int runremove(int argc, char **argv);
+static int runwicked(int argc, char **argv);
+static int runtypical(int argc, char **argv);
+static int procwrite(const char *path, int tnum, int rnum, int bnum, int apow, int fpow,
+ int opts, int omode, bool as, bool rnd);
+static int procread(const char *path, int tnum, int rcnum, int omode, bool wb, bool rnd);
+static int procremove(const char *path, int tnum, int omode, bool rnd);
+static int procwicked(const char *path, int tnum, int rnum, int opts, int omode, bool nc);
+static int proctypical(const char *path, int tnum, int rnum, int bnum, int apow, int fpow,
+ int opts, int rcnum, int omode, bool nc, int rratio);
+static void *threadwrite(void *targ);
+static void *threadread(void *targ);
+static void *threadremove(void *targ);
+static void *threadwicked(void *targ);
+static void *threadtypical(void *targ);
+
+
+/* main routine */
+int main(int argc, char **argv){
+ g_progname = argv[0];
+ g_dbgfd = -1;
+ const char *ebuf = getenv("TCDBGFD");
+ if(ebuf) g_dbgfd = atoi(ebuf);
+ srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+ if(argc < 2) usage();
+ int rv = 0;
+ if(!strcmp(argv[1], "write")){
+ rv = runwrite(argc, argv);
+ } else if(!strcmp(argv[1], "read")){
+ rv = runread(argc, argv);
+ } else if(!strcmp(argv[1], "remove")){
+ rv = runremove(argc, argv);
+ } else if(!strcmp(argv[1], "wicked")){
+ rv = runwicked(argc, argv);
+ } else if(!strcmp(argv[1], "typical")){
+ rv = runtypical(argc, argv);
+ } else {
+ usage();
+ }
+ return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+ fprintf(stderr, "%s: test cases of the hash database API of Tokyo Cabinet\n", g_progname);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, " %s write [-tl] [-td|-tb] [-nl|-nb] [-as] [-rnd] path tnum rnum"
+ " [bnum [apow [fpow]]]\n", g_progname);
+ fprintf(stderr, " %s read [-rc num] [-nl|-nb] [-wb] [-rnd] path tnum\n", g_progname);
+ fprintf(stderr, " %s remove [-nl|-nb] [-wb] [-rnd] path tnum\n", g_progname);
+ fprintf(stderr, " %s wicked [-tl] [-td|-tb] [-nl|-nb] [-nc] path tnum rnum\n",
+ g_progname);
+ fprintf(stderr, " %s typical [-tl] [-td|-tb] [-rc num] [-nl|-nb] [-nc] [-rr num]"
+ " path tnum rnum [bnum [apow [fpow]]]\n", g_progname);
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+
+/* print formatted information string and flush the buffer */
+static void iprintf(const char *format, ...){
+ va_list ap;
+ va_start(ap, format);
+ vprintf(format, ap);
+ fflush(stdout);
+ va_end(ap);
+}
+
+
+/* print error message of hash database */
+static void eprint(TCHDB *hdb, const char *func){
+ const char *path = tchdbpath(hdb);
+ int ecode = tchdbecode(hdb);
+ fprintf(stderr, "%s: %s: %s: error: %d: %s\n",
+ g_progname, path ? path : "-", func, ecode, tchdberrmsg(ecode));
+}
+
+
+/* print members of hash database */
+static void mprint(TCHDB *hdb){
+ if(hdb->cnt_writerec < 0) return;
+ iprintf("bucket number: %lld\n", (long long)tchdbbnum(hdb));
+ iprintf("used bucket number: %lld\n", (long long)tchdbbnumused(hdb));
+ iprintf("cnt_writerec: %lld\n", (long long)hdb->cnt_writerec);
+ iprintf("cnt_reuserec: %lld\n", (long long)hdb->cnt_reuserec);
+ iprintf("cnt_moverec: %lld\n", (long long)hdb->cnt_moverec);
+ iprintf("cnt_readrec: %lld\n", (long long)hdb->cnt_readrec);
+ iprintf("cnt_searchfbp: %lld\n", (long long)hdb->cnt_searchfbp);
+ iprintf("cnt_insertfbp: %lld\n", (long long)hdb->cnt_insertfbp);
+ iprintf("cnt_splicefbp: %lld\n", (long long)hdb->cnt_splicefbp);
+ iprintf("cnt_dividefbp: %lld\n", (long long)hdb->cnt_dividefbp);
+ iprintf("cnt_mergefbp: %lld\n", (long long)hdb->cnt_mergefbp);
+ iprintf("cnt_reducefbp: %lld\n", (long long)hdb->cnt_reducefbp);
+ iprintf("cnt_appenddrp: %lld\n", (long long)hdb->cnt_appenddrp);
+ iprintf("cnt_deferdrp: %lld\n", (long long)hdb->cnt_deferdrp);
+ iprintf("cnt_flushdrp: %lld\n", (long long)hdb->cnt_flushdrp);
+ iprintf("cnt_adjrecc: %lld\n", (long long)hdb->cnt_adjrecc);
+}
+
+
+/* get a random number */
+static int myrand(int range){
+ return (int)((double)range * rand() / (RAND_MAX + 1.0));
+}
+
+
+/* get a random number based on normal distribution */
+static int myrandnd(int range){
+ int num = (int)tcdrandnd(range >> 1, range / 10);
+ return (num < 0 || num >= range) ? 0 : num;
+}
+
+
+/* parse arguments of write command */
+static int runwrite(int argc, char **argv){
+ char *path = NULL;
+ char *tstr = NULL;
+ char *rstr = NULL;
+ char *bstr = NULL;
+ char *astr = NULL;
+ char *fstr = NULL;
+ int opts = 0;
+ int omode = 0;
+ bool as = false;
+ bool rnd = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-tl")){
+ opts |= HDBTLARGE;
+ } else if(!strcmp(argv[i], "-td")){
+ opts |= HDBTDEFLATE;
+ } else if(!strcmp(argv[i], "-tb")){
+ opts |= HDBTTCBS;
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= HDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= HDBOLCKNB;
+ } else if(!strcmp(argv[i], "-as")){
+ as = true;
+ } else if(!strcmp(argv[i], "-rnd")){
+ rnd = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!tstr){
+ tstr = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else if(!bstr){
+ bstr = argv[i];
+ } else if(!astr){
+ astr = argv[i];
+ } else if(!fstr){
+ fstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !tstr || !rstr) usage();
+ int tnum = atoi(tstr);
+ int rnum = atoi(rstr);
+ if(tnum < 1 || rnum < 1) usage();
+ int bnum = bstr ? atoi(bstr) : -1;
+ int apow = astr ? atoi(astr) : -1;
+ int fpow = fstr ? atoi(fstr) : -1;
+ int rv = procwrite(path, tnum, rnum, bnum, apow, fpow, opts, omode, as, rnd);
+ return rv;
+}
+
+
+/* parse arguments of read command */
+static int runread(int argc, char **argv){
+ char *path = NULL;
+ char *tstr = NULL;
+ int rcnum = 0;
+ int omode = 0;
+ bool wb = false;
+ bool rnd = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-rc")){
+ if(++i >= argc) usage();
+ rcnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= HDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= HDBOLCKNB;
+ } else if(!strcmp(argv[i], "-wb")){
+ wb = true;
+ } else if(!strcmp(argv[i], "-rnd")){
+ rnd = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!tstr){
+ tstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !tstr) usage();
+ int tnum = atoi(tstr);
+ if(tnum < 1) usage();
+ int rv = procread(path, tnum, rcnum, omode, wb, rnd);
+ return rv;
+}
+
+
+/* parse arguments of remove command */
+static int runremove(int argc, char **argv){
+ char *path = NULL;
+ char *tstr = NULL;
+ int omode = 0;
+ bool rnd = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-nl")){
+ omode |= HDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= HDBOLCKNB;
+ } else if(!strcmp(argv[i], "-rnd")){
+ rnd = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!tstr){
+ tstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !tstr) usage();
+ int tnum = atoi(tstr);
+ if(tnum < 1) usage();
+ int rv = procremove(path, tnum, omode, rnd);
+ return rv;
+}
+
+
+/* parse arguments of wicked command */
+static int runwicked(int argc, char **argv){
+ char *path = NULL;
+ char *tstr = NULL;
+ char *rstr = NULL;
+ int opts = 0;
+ int omode = 0;
+ bool nc = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-tl")){
+ opts |= HDBTLARGE;
+ } else if(!strcmp(argv[i], "-td")){
+ opts |= HDBTDEFLATE;
+ } else if(!strcmp(argv[i], "-tb")){
+ opts |= HDBTTCBS;
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= HDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= HDBOLCKNB;
+ } else if(!strcmp(argv[i], "-nc")){
+ nc = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!tstr){
+ tstr = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !tstr || !rstr) usage();
+ int tnum = atoi(tstr);
+ int rnum = atoi(rstr);
+ if(tnum < 1 || rnum < 1) usage();
+ int rv = procwicked(path, tnum, rnum, opts, omode, nc);
+ return rv;
+}
+
+
+/* parse arguments of typical command */
+static int runtypical(int argc, char **argv){
+ char *path = NULL;
+ char *tstr = NULL;
+ char *rstr = NULL;
+ char *bstr = NULL;
+ char *astr = NULL;
+ char *fstr = NULL;
+ int opts = 0;
+ int rcnum = 0;
+ int omode = 0;
+ int rratio = -1;
+ bool nc = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-tl")){
+ opts |= HDBTLARGE;
+ } else if(!strcmp(argv[i], "-td")){
+ opts |= HDBTDEFLATE;
+ } else if(!strcmp(argv[i], "-tb")){
+ opts |= HDBTTCBS;
+ } else if(!strcmp(argv[i], "-rc")){
+ if(++i >= argc) usage();
+ rcnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= HDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= HDBOLCKNB;
+ } else if(!strcmp(argv[i], "-nc")){
+ nc = true;
+ } else if(!strcmp(argv[i], "-rr")){
+ if(++i >= argc) usage();
+ rratio = atoi(argv[i]);
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!tstr){
+ tstr = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else if(!bstr){
+ bstr = argv[i];
+ } else if(!astr){
+ astr = argv[i];
+ } else if(!fstr){
+ fstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !tstr || !rstr) usage();
+ int tnum = atoi(tstr);
+ int rnum = atoi(rstr);
+ if(tnum < 1 || rnum < 1) usage();
+ int bnum = bstr ? atoi(bstr) : -1;
+ int apow = astr ? atoi(astr) : -1;
+ int fpow = fstr ? atoi(fstr) : -1;
+ int rv = proctypical(path, tnum, rnum, bnum, apow, fpow, opts, rcnum, omode, nc, rratio);
+ return rv;
+}
+
+
+/* perform write command */
+static int procwrite(const char *path, int tnum, int rnum, int bnum, int apow, int fpow,
+ int opts, int omode, bool as, bool rnd){
+ iprintf("<Writing Test>\n path=%s tnum=%d rnum=%d bnum=%d apow=%d fpow=%d"
+ " opts=%d omode=%d as=%d rnd=%d\n\n",
+ path, tnum, rnum, bnum, apow, fpow, opts, omode, as, rnd);
+ bool err = false;
+ double stime = tctime();
+ TCHDB *hdb = tchdbnew();
+ if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+ if(!tchdbsetmutex(hdb)){
+ eprint(hdb, "tchdbsetmutex");
+ err = true;
+ }
+ if(!tchdbtune(hdb, bnum, apow, fpow, opts)){
+ eprint(hdb, "tchdbtune");
+ err = true;
+ }
+ if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){
+ eprint(hdb, "tchdbopen");
+ err = true;
+ }
+ TARGWRITE targs[tnum];
+ pthread_t threads[tnum];
+ if(tnum == 1){
+ targs[0].hdb = hdb;
+ targs[0].rnum = rnum;
+ targs[0].as = as;
+ targs[0].rnd = rnd;
+ targs[0].id = 0;
+ if(threadwrite(targs) != NULL) err = true;
+ } else {
+ for(int i = 0; i < tnum; i++){
+ targs[i].hdb = hdb;
+ targs[i].rnum = rnum;
+ targs[i].as = as;
+ targs[i].rnd = rnd;
+ targs[i].id = i;
+ if(pthread_create(threads + i, NULL, threadwrite, targs + i) != 0){
+ eprint(hdb, "pthread_create");
+ targs[i].id = -1;
+ err = true;
+ }
+ }
+ for(int i = 0; i < tnum; i++){
+ if(targs[i].id == -1) continue;
+ void *rv;
+ if(pthread_join(threads[i], &rv) != 0){
+ eprint(hdb, "pthread_join");
+ err = true;
+ } else if(rv){
+ err = true;
+ }
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+ iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+ mprint(hdb);
+ if(!tchdbclose(hdb)){
+ eprint(hdb, "tchdbclose");
+ err = true;
+ }
+ tchdbdel(hdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform read command */
+static int procread(const char *path, int tnum, int rcnum, int omode, bool wb, bool rnd){
+ iprintf("<Reading Test>\n path=%s tnum=%d rcnum=%d omode=%d wb=%d rnd=%d\n\n",
+ path, tnum, rcnum, omode, wb, rnd);
+ bool err = false;
+ double stime = tctime();
+ TCHDB *hdb = tchdbnew();
+ if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+ if(!tchdbsetmutex(hdb)){
+ eprint(hdb, "tchdbsetmutex");
+ err = true;
+ }
+ if(!tchdbsetcache(hdb, rcnum)){
+ eprint(hdb, "tchdbsetcache");
+ err = true;
+ }
+ if(!tchdbopen(hdb, path, HDBOREADER | omode)){
+ eprint(hdb, "tchdbopen");
+ err = true;
+ }
+ int rnum = tchdbrnum(hdb) / tnum;
+ TARGREAD targs[tnum];
+ pthread_t threads[tnum];
+ if(tnum == 1){
+ targs[0].hdb = hdb;
+ targs[0].rnum = rnum;
+ targs[0].wb = wb;
+ targs[0].rnd = rnd;
+ targs[0].id = 0;
+ if(threadread(targs) != NULL) err = true;
+ } else {
+ for(int i = 0; i < tnum; i++){
+ targs[i].hdb = hdb;
+ targs[i].rnum = rnum;
+ targs[i].wb = wb;
+ targs[i].rnd = rnd;
+ targs[i].id = i;
+ if(pthread_create(threads + i, NULL, threadread, targs + i) != 0){
+ eprint(hdb, "pthread_create");
+ targs[i].id = -1;
+ err = true;
+ }
+ }
+ for(int i = 0; i < tnum; i++){
+ if(targs[i].id == -1) continue;
+ void *rv;
+ if(pthread_join(threads[i], &rv) != 0){
+ eprint(hdb, "pthread_join");
+ err = true;
+ } else if(rv){
+ err = true;
+ }
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+ iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+ mprint(hdb);
+ if(!tchdbclose(hdb)){
+ eprint(hdb, "tchdbclose");
+ err = true;
+ }
+ tchdbdel(hdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform remove command */
+static int procremove(const char *path, int tnum, int omode, bool rnd){
+ iprintf("<Removing Test>\n path=%s tnum=%d omode=%d rnd=%d\n\n", path, tnum, omode, rnd);
+ bool err = false;
+ double stime = tctime();
+ TCHDB *hdb = tchdbnew();
+ if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+ if(!tchdbsetmutex(hdb)){
+ eprint(hdb, "tchdbsetmutex");
+ err = true;
+ }
+ if(!tchdbopen(hdb, path, HDBOWRITER | omode)){
+ eprint(hdb, "tchdbopen");
+ err = true;
+ }
+ int rnum = tchdbrnum(hdb) / tnum;
+ TARGREMOVE targs[tnum];
+ pthread_t threads[tnum];
+ if(tnum == 1){
+ targs[0].hdb = hdb;
+ targs[0].rnum = rnum;
+ targs[0].rnd = rnd;
+ targs[0].id = 0;
+ if(threadremove(targs) != NULL) err = true;
+ } else {
+ for(int i = 0; i < tnum; i++){
+ targs[i].hdb = hdb;
+ targs[i].rnum = rnum;
+ targs[i].rnd = rnd;
+ targs[i].id = i;
+ if(pthread_create(threads + i, NULL, threadremove, targs + i) != 0){
+ eprint(hdb, "pthread_create");
+ targs[i].id = -1;
+ err = true;
+ }
+ }
+ for(int i = 0; i < tnum; i++){
+ if(targs[i].id == -1) continue;
+ void *rv;
+ if(pthread_join(threads[i], &rv) != 0){
+ eprint(hdb, "pthread_join");
+ err = true;
+ } else if(rv){
+ err = true;
+ }
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+ iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+ mprint(hdb);
+ if(!tchdbclose(hdb)){
+ eprint(hdb, "tchdbclose");
+ err = true;
+ }
+ tchdbdel(hdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform wicked command */
+static int procwicked(const char *path, int tnum, int rnum, int opts, int omode, bool nc){
+ iprintf("<Writing Test>\n path=%s tnum=%d rnum=%d opts=%d omode=%d nc=%d\n\n",
+ path, tnum, rnum, opts, omode, nc);
+ bool err = false;
+ double stime = tctime();
+ TCHDB *hdb = tchdbnew();
+ if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+ if(!tchdbsetmutex(hdb)){
+ eprint(hdb, "tchdbsetmutex");
+ err = true;
+ }
+ if(!tchdbtune(hdb, rnum / 50, 2, -1, opts)){
+ eprint(hdb, "tchdbtune");
+ err = true;
+ }
+ if(!tchdbsetcache(hdb, rnum / 2)){
+ eprint(hdb, "tchdbsetcache");
+ err = true;
+ }
+ if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){
+ eprint(hdb, "tchdbopen");
+ err = true;
+ }
+ if(!tchdbiterinit(hdb)){
+ eprint(hdb, "tchdbiterinit");
+ err = true;
+ }
+ TARGWICKED targs[tnum];
+ pthread_t threads[tnum];
+ TCMAP *map = tcmapnew();
+ if(tnum == 1){
+ targs[0].hdb = hdb;
+ targs[0].rnum = rnum;
+ targs[0].nc = nc;
+ targs[0].id = 0;
+ targs[0].map = map;
+ if(threadwicked(targs) != NULL) err = true;
+ } else {
+ for(int i = 0; i < tnum; i++){
+ targs[i].hdb = hdb;
+ targs[i].rnum = rnum;
+ targs[i].nc = nc;
+ targs[i].id = i;
+ targs[i].map = map;
+ targs[i].map = map;
+ if(pthread_create(threads + i, NULL, threadwicked, targs + i) != 0){
+ eprint(hdb, "pthread_create");
+ targs[i].id = -1;
+ err = true;
+ }
+ }
+ for(int i = 0; i < tnum; i++){
+ if(targs[i].id == -1) continue;
+ void *rv;
+ if(pthread_join(threads[i], &rv) != 0){
+ eprint(hdb, "pthread_join");
+ err = true;
+ } else if(rv){
+ err = true;
+ }
+ }
+ }
+ if(!nc){
+ if(!tchdbsync(hdb)){
+ eprint(hdb, "tchdbsync");
+ err = true;
+ }
+ if(tchdbrnum(hdb) != tcmaprnum(map)){
+ eprint(hdb, "(validation)");
+ err = true;
+ }
+ int end = rnum * tnum;
+ for(int i = 1; i <= end && !err; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", i - 1);
+ int vsiz;
+ const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz);
+ int rsiz;
+ char *rbuf = tchdbget(hdb, kbuf, ksiz, &rsiz);
+ if(vbuf){
+ putchar('.');
+ if(!rbuf){
+ eprint(hdb, "tchdbget");
+ err = true;
+ } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+ eprint(hdb, "(validation)");
+ err = true;
+ }
+ } else {
+ putchar('*');
+ if(rbuf || tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "(validation)");
+ err = true;
+ }
+ }
+ free(rbuf);
+ if(i % 50 == 0) iprintf(" (%08d)\n", i);
+ }
+ if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
+ }
+ tcmapdel(map);
+ iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+ iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+ mprint(hdb);
+ if(!tchdbclose(hdb)){
+ eprint(hdb, "tchdbclose");
+ err = true;
+ }
+ tchdbdel(hdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform typical command */
+static int proctypical(const char *path, int tnum, int rnum, int bnum, int apow, int fpow,
+ int opts, int rcnum, int omode, bool nc, int rratio){
+ iprintf("<Typical Access Test>\n path=%s tnum=%d rnum=%d bnum=%d apow=%d fpow=%d"
+ " opts=%d rcnum=%d omode=%d nc=%d rratio=%d\n\n",
+ path, tnum, rnum, bnum, apow, fpow, opts, rcnum, omode, nc, rratio);
+ bool err = false;
+ double stime = tctime();
+ TCHDB *hdb = tchdbnew();
+ if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+ if(!tchdbsetmutex(hdb)){
+ eprint(hdb, "tchdbsetmutex");
+ err = true;
+ }
+ if(!tchdbtune(hdb, bnum, apow, fpow, opts)){
+ eprint(hdb, "tchdbtune");
+ err = true;
+ }
+ if(!tchdbsetcache(hdb, rcnum)){
+ eprint(hdb, "tchdbsetcache");
+ err = true;
+ }
+ if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){
+ eprint(hdb, "tchdbopen");
+ err = true;
+ }
+ TARGTYPICAL targs[tnum];
+ pthread_t threads[tnum];
+ if(tnum == 1){
+ targs[0].hdb = hdb;
+ targs[0].rnum = rnum;
+ targs[0].nc = nc;
+ targs[0].rratio = rratio;
+ targs[0].id = 0;
+ if(threadtypical(targs) != NULL) err = true;
+ } else {
+ for(int i = 0; i < tnum; i++){
+ targs[i].hdb = hdb;
+ targs[i].rnum = rnum;
+ targs[i].nc = nc;
+ targs[i].rratio= rratio;
+ targs[i].id = i;
+ if(pthread_create(threads + i, NULL, threadtypical, targs + i) != 0){
+ eprint(hdb, "pthread_create");
+ targs[i].id = -1;
+ err = true;
+ }
+ }
+ for(int i = 0; i < tnum; i++){
+ if(targs[i].id == -1) continue;
+ void *rv;
+ if(pthread_join(threads[i], &rv) != 0){
+ eprint(hdb, "pthread_join");
+ err = true;
+ } else if(rv){
+ err = true;
+ }
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+ iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+ mprint(hdb);
+ if(!tchdbclose(hdb)){
+ eprint(hdb, "tchdbclose");
+ err = true;
+ }
+ tchdbdel(hdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* thread the write function */
+static void *threadwrite(void *targ){
+ TCHDB *hdb = ((TARGWRITE *)targ)->hdb;
+ int rnum = ((TARGWRITE *)targ)->rnum;
+ bool as = ((TARGWRITE *)targ)->as;
+ bool rnd = ((TARGWRITE *)targ)->rnd;
+ int id = ((TARGWRITE *)targ)->id;
+ bool err = false;
+ int base = id * rnum;
+ for(int i = 1; i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len = sprintf(buf, "%08d", base + (rnd ? myrand(i) : i));
+ if(as){
+ if(!tchdbputasync(hdb, buf, len, buf, len)){
+ eprint(hdb, "tchdbput");
+ err = true;
+ break;
+ }
+ } else {
+ if(!tchdbput(hdb, buf, len, buf, len)){
+ eprint(hdb, "tchdbput");
+ err = true;
+ break;
+ }
+ }
+ if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ return err ? "error" : NULL;
+}
+
+
+/* thread the read function */
+static void *threadread(void *targ){
+ TCHDB *hdb = ((TARGREAD *)targ)->hdb;
+ int rnum = ((TARGREAD *)targ)->rnum;
+ bool wb = ((TARGREAD *)targ)->wb;
+ bool rnd = ((TARGREAD *)targ)->rnd;
+ int id = ((TARGREAD *)targ)->id;
+ bool err = false;
+ int base = id * rnum;
+ for(int i = 1; i <= rnum && !err; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrandnd(i) : i));
+ int vsiz;
+ if(wb){
+ char vbuf[RECBUFSIZ];
+ int vsiz = tchdbget3(hdb, kbuf, ksiz, vbuf, RECBUFSIZ);
+ if(vsiz < 0 && (!rnd || tchdbecode(hdb) != TCENOREC)){
+ eprint(hdb, "tchdbget3");
+ err = true;
+ }
+ } else {
+ char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
+ if(!vbuf && (!rnd || tchdbecode(hdb) != TCENOREC)){
+ eprint(hdb, "tchdbget");
+ err = true;
+ }
+ free(vbuf);
+ }
+ if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ return err ? "error" : NULL;
+}
+
+
+/* thread the remove function */
+static void *threadremove(void *targ){
+ TCHDB *hdb = ((TARGREMOVE *)targ)->hdb;
+ int rnum = ((TARGREMOVE *)targ)->rnum;
+ bool rnd = ((TARGREMOVE *)targ)->rnd;
+ int id = ((TARGREMOVE *)targ)->id;
+ bool err = false;
+ int base = id * rnum;
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrand(i + 1) : i));
+ if(!tchdbout(hdb, kbuf, ksiz) && (!rnd || tchdbecode(hdb) != TCENOREC)){
+ eprint(hdb, "tchdbout");
+ err = true;
+ break;
+ }
+ if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ return err ? "error" : NULL;
+}
+
+
+/* thread the wicked function */
+static void *threadwicked(void *targ){
+ TCHDB *hdb = ((TARGWICKED *)targ)->hdb;
+ int rnum = ((TARGWICKED *)targ)->rnum;
+ bool nc = ((TARGWICKED *)targ)->nc;
+ int id = ((TARGWICKED *)targ)->id;
+ TCMAP *map = ((TARGWICKED *)targ)->map;
+ bool err = false;
+ for(int i = 1; i <= rnum && !err; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", myrand(rnum * (id + 1)));
+ char vbuf[RECBUFSIZ];
+ int vsiz = myrand(RECBUFSIZ);
+ memset(vbuf, '*', vsiz);
+ vbuf[vsiz] = '\0';
+ char *rbuf;
+ if(!nc) tcglobalmutexlock();
+ switch(myrand(16)){
+ case 0:
+ if(id == 0) putchar('0');
+ if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(hdb, "tchdbput");
+ err = true;
+ }
+ if(!nc) tcmapput(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 1:
+ if(id == 0) putchar('1');
+ if(!tchdbput2(hdb, kbuf, vbuf)){
+ eprint(hdb, "tchdbput2");
+ err = true;
+ }
+ if(!nc) tcmapput2(map, kbuf, vbuf);
+ break;
+ case 2:
+ if(id == 0) putchar('2');
+ if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz) && tchdbecode(hdb) != TCEKEEP){
+ eprint(hdb, "tchdbputkeep");
+ err = true;
+ }
+ if(!nc) tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 3:
+ if(id == 0) putchar('3');
+ if(!tchdbputkeep2(hdb, kbuf, vbuf) && tchdbecode(hdb) != TCEKEEP){
+ eprint(hdb, "tchdbputkeep2");
+ err = true;
+ }
+ if(!nc) tcmapputkeep2(map, kbuf, vbuf);
+ break;
+ case 4:
+ if(id == 0) putchar('4');
+ if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(hdb, "tchdbputcat");
+ err = true;
+ }
+ if(!nc) tcmapputcat(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 5:
+ if(id == 0) putchar('5');
+ if(!tchdbputcat2(hdb, kbuf, vbuf)){
+ eprint(hdb, "tchdbputcat2");
+ err = true;
+ }
+ if(!nc) tcmapputcat2(map, kbuf, vbuf);
+ break;
+ case 6:
+ if(id == 0) putchar('6');
+ if(i > rnum / 4 * 3){
+ if(!tchdbputasync(hdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(hdb, "tchdbputasync");
+ err = true;
+ }
+ } else {
+ if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(hdb, "tchdbput");
+ err = true;
+ }
+ }
+ if(!nc) tcmapput(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 7:
+ if(id == 0) putchar('7');
+ if(i > rnum / 4 * 3){
+ if(!tchdbputasync2(hdb, kbuf, vbuf)){
+ eprint(hdb, "tchdbputasync2");
+ err = true;
+ }
+ } else {
+ if(!tchdbput2(hdb, kbuf, vbuf)){
+ eprint(hdb, "tchdbput2");
+ err = true;
+ }
+ }
+ if(!nc) tcmapput2(map, kbuf, vbuf);
+ break;
+ case 8:
+ if(id == 0) putchar('8');
+ if(myrand(10) == 0){
+ if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tchdbout");
+ err = true;
+ }
+ if(!nc) tcmapout(map, kbuf, ksiz);
+ }
+ break;
+ case 9:
+ if(id == 0) putchar('9');
+ if(myrand(10) == 0){
+ if(!tchdbout2(hdb, kbuf) && tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tchdbout2");
+ err = true;
+ }
+ if(!nc) tcmapout2(map, kbuf);
+ }
+ break;
+ case 10:
+ if(id == 0) putchar('A');
+ if(!(rbuf = tchdbget(hdb, kbuf, ksiz, &vsiz))){
+ if(tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tchdbget");
+ err = true;
+ }
+ rbuf = tcsprintf("[%d]", myrand(i + 1));
+ vsiz = strlen(rbuf);
+ }
+ vsiz += myrand(vsiz);
+ if(myrand(3) == 0) vsiz += PATH_MAX;
+ rbuf = tcrealloc(rbuf, vsiz + 1);
+ for(int j = 0; j < vsiz; j++){
+ rbuf[j] = myrand(0x100);
+ }
+ if(!tchdbput(hdb, kbuf, ksiz, rbuf, vsiz)){
+ eprint(hdb, "tchdbput");
+ err = true;
+ }
+ if(!nc) tcmapput(map, kbuf, ksiz, rbuf, vsiz);
+ free(rbuf);
+ break;
+ case 11:
+ if(id == 0) putchar('B');
+ if(!(rbuf = tchdbget(hdb, kbuf, ksiz, &vsiz)) && tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tchdbget");
+ err = true;
+ }
+ free(rbuf);
+ break;
+ case 12:
+ if(id == 0) putchar('C');
+ if(!(rbuf = tchdbget2(hdb, kbuf)) && tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tchdbget");
+ err = true;
+ }
+ free(rbuf);
+ break;
+ case 13:
+ if(id == 0) putchar('D');
+ if(myrand(1) == 0) vsiz = 1;
+ if((vsiz = tchdbget3(hdb, kbuf, ksiz, vbuf, vsiz)) < 0 && tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tchdbget3");
+ err = true;
+ }
+ break;
+ case 14:
+ if(id == 0) putchar('E');
+ if(myrand(rnum / 50) == 0){
+ if(!tchdbiterinit(hdb)){
+ eprint(hdb, "tchdbiterinit");
+ err = true;
+ }
+ }
+ TCXSTR *ikey = tcxstrnew();
+ TCXSTR *ival = tcxstrnew();
+ for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){
+ if(j % 3 == 0){
+ if(!tchdbiternext3(hdb, ikey, ival)){
+ int ecode = tchdbecode(hdb);
+ if(ecode != TCEINVALID && ecode != TCENOREC){
+ eprint(hdb, "tchdbiternext3");
+ err = true;
+ }
+ }
+ } else {
+ int iksiz;
+ char *ikbuf = tchdbiternext(hdb, &iksiz);
+ if(ikbuf){
+ free(ikbuf);
+ } else {
+ int ecode = tchdbecode(hdb);
+ if(ecode != TCEINVALID && ecode != TCENOREC){
+ eprint(hdb, "tchdbiternext");
+ err = true;
+ }
+ }
+ }
+ }
+ tcxstrdel(ival);
+ tcxstrdel(ikey);
+ break;
+ default:
+ if(id == 0) putchar('@');
+ if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+ break;
+ }
+ if(!nc) tcglobalmutexunlock();
+ if(id == 0){
+ if(i % 50 == 0) iprintf(" (%08d)\n", i);
+ if(id == 0 && i == rnum / 4){
+ if(!tchdboptimize(hdb, rnum / 50, -1, -1, -1)){
+ eprint(hdb, "tchdboptimize");
+ err = true;
+ }
+ if(!tchdbiterinit(hdb)){
+ eprint(hdb, "tchdbiterinit");
+ err = true;
+ }
+ }
+ }
+ }
+ return err ? "error" : NULL;
+}
+
+
+/* thread the typical function */
+static void *threadtypical(void *targ){
+ TCHDB *hdb = ((TARGTYPICAL *)targ)->hdb;
+ int rnum = ((TARGTYPICAL *)targ)->rnum;
+ bool nc = ((TARGTYPICAL *)targ)->nc;
+ int rratio = ((TARGTYPICAL *)targ)->rratio;
+ int id = ((TARGTYPICAL *)targ)->id;
+ bool err = false;
+ TCMAP *map = (!nc && id == 0) ? tcmapnew2(rnum + 1) : NULL;
+ int base = id * rnum;
+ int mrange = tclmax(50 + rratio, 100);
+ for(int i = 1; !err && i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len = sprintf(buf, "%08d", base + myrandnd(i));
+ int rnd = myrand(mrange);
+ if(rnd < 10){
+ if(!tchdbput(hdb, buf, len, buf, len)){
+ eprint(hdb, "tchdbput");
+ err = true;
+ }
+ if(map) tcmapput(map, buf, len, buf, len);
+ } else if(rnd < 15){
+ if(!tchdbputkeep(hdb, buf, len, buf, len) && tchdbecode(hdb) != TCEKEEP){
+ eprint(hdb, "tchdbputkeep");
+ err = true;
+ }
+ if(map) tcmapputkeep(map, buf, len, buf, len);
+ } else if(rnd < 20){
+ if(!tchdbputcat(hdb, buf, len, buf, len)){
+ eprint(hdb, "tchdbputcat");
+ err = true;
+ }
+ if(map) tcmapputcat(map, buf, len, buf, len);
+ } else if(rnd < 25){
+ if(i > rnum / 10 * 9){
+ if(!tchdbputasync(hdb, buf, len, buf, len)){
+ eprint(hdb, "tchdbputasync");
+ err = true;
+ }
+ } else {
+ if(!tchdbput(hdb, buf, len, buf, len)){
+ eprint(hdb, "tchdbput");
+ err = true;
+ }
+ }
+ if(map) tcmapput(map, buf, len, buf, len);
+ } else if(rnd < 30){
+ if(!tchdbout(hdb, buf, len) && tchdbecode(hdb) && tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tchdbout");
+ err = true;
+ }
+ if(map) tcmapout(map, buf, len);
+ } else if(rnd < 31){
+ if(myrand(10) == 0 && !tchdbiterinit(hdb) && tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tchdbiterinit");
+ err = true;
+ }
+ for(int j = 0; !err && j < 10; j++){
+ int ksiz;
+ char *kbuf = tchdbiternext(hdb, &ksiz);
+ if(kbuf){
+ free(kbuf);
+ } else if(tchdbecode(hdb) != TCEINVALID && tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tchdbiternext");
+ err = true;
+ }
+ }
+ } else {
+ int vsiz;
+ char *vbuf = tchdbget(hdb, buf, len, &vsiz);
+ if(vbuf){
+ if(map){
+ int msiz;
+ const char *mbuf = tcmapget(map, buf, len, &msiz);
+ if(msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){
+ eprint(hdb, "(validation)");
+ err = true;
+ }
+ }
+ free(vbuf);
+ } else {
+ if(tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tchdbget");
+ err = true;
+ }
+ if(map && tcmapget(map, buf, len, &vsiz)){
+ eprint(hdb, "(validation)");
+ err = true;
+ }
+ }
+ }
+ if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ if(map){
+ tcmapiterinit(map);
+ int ksiz;
+ const char *kbuf;
+ while(!err && (kbuf = tcmapiternext(map, &ksiz)) != NULL){
+ int vsiz;
+ char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
+ if(vbuf){
+ int msiz;
+ const char *mbuf = tcmapget(map, kbuf, ksiz, &msiz);
+ if(!mbuf || msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){
+ eprint(hdb, "(validation)");
+ err = true;
+ }
+ free(vbuf);
+ } else {
+ eprint(hdb, "(validation)");
+ err = true;
+ }
+ }
+ tcmapdel(map);
+ }
+ return err ? "error" : NULL;
+}
+
+
+
+// END OF FILE
--- /dev/null
+/*************************************************************************************************
+ * The test cases of the hash database API
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include <tchdb.h>
+#include "myconf.h"
+
+#define RECBUFSIZ 32 // buffer for records
+
+
+/* global variables */
+const char *g_progname; // program name
+int g_dbgfd; // debugging output
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void iprintf(const char *format, ...);
+static void eprint(TCHDB *hdb, const char *func);
+static void mprint(TCHDB *hdb);
+static int myrand(int range);
+static int runwrite(int argc, char **argv);
+static int runread(int argc, char **argv);
+static int runremove(int argc, char **argv);
+static int runrcat(int argc, char **argv);
+static int runmisc(int argc, char **argv);
+static int runwicked(int argc, char **argv);
+static int procwrite(const char *path, int rnum, int bnum, int apow, int fpow,
+ bool mt, int opts, int rcnum, int omode, bool as);
+static int procread(const char *path, bool mt, int rcnum, int omode, bool wb);
+static int procremove(const char *path, bool mt, int rcnum, int omode);
+static int procrcat(const char *path, int rnum, int bnum, int apow, int fpow,
+ bool mt, int opts, int rcnum, int omode, int pnum, bool rl);
+static int procmisc(const char *path, int rnum, bool mt, int opts, int omode);
+static int procwicked(const char *path, int rnum, bool mt, int opts, int omode);
+
+
+/* main routine */
+int main(int argc, char **argv){
+ g_progname = argv[0];
+ g_dbgfd = -1;
+ const char *ebuf = getenv("TCDBGFD");
+ if(ebuf) g_dbgfd = atoi(ebuf);
+ srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+ if(argc < 2) usage();
+ int rv = 0;
+ if(!strcmp(argv[1], "write")){
+ rv = runwrite(argc, argv);
+ } else if(!strcmp(argv[1], "read")){
+ rv = runread(argc, argv);
+ } else if(!strcmp(argv[1], "remove")){
+ rv = runremove(argc, argv);
+ } else if(!strcmp(argv[1], "rcat")){
+ rv = runrcat(argc, argv);
+ } else if(!strcmp(argv[1], "misc")){
+ rv = runmisc(argc, argv);
+ } else if(!strcmp(argv[1], "wicked")){
+ rv = runwicked(argc, argv);
+ } else {
+ usage();
+ }
+ return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+ fprintf(stderr, "%s: test cases of the hash database API of Tokyo Cabinet\n", g_progname);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, " %s write [-mt] [-tl] [-td|-tb] [-rc num] [-nl|-nb] [-as] path rnum"
+ " [bnum [apow [fpow]]]\n", g_progname);
+ fprintf(stderr, " %s read [-mt] [-rc num] [-nl|-nb] [-wb] path\n", g_progname);
+ fprintf(stderr, " %s remove [-mt] [-rc num] [-nl|-nb] path\n", g_progname);
+ fprintf(stderr, " %s rcat [-mt] [-rc num] [-tl] [-td|-tb] [-nl|-nb] [-pn num] [-rl]"
+ " path rnum [bnum [apow [fpow]]]\n", g_progname);
+ fprintf(stderr, " %s misc [-mt] [-tl] [-td|-tb] [-nl|-nb] path rnum\n", g_progname);
+ fprintf(stderr, " %s wicked [-mt] [-tl] [-td|-tb] [-nl|-nb] path rnum\n", g_progname);
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+
+/* print formatted information string and flush the buffer */
+static void iprintf(const char *format, ...){
+ va_list ap;
+ va_start(ap, format);
+ vprintf(format, ap);
+ fflush(stdout);
+ va_end(ap);
+}
+
+
+/* print error message of hash database */
+static void eprint(TCHDB *hdb, const char *func){
+ const char *path = tchdbpath(hdb);
+ int ecode = tchdbecode(hdb);
+ fprintf(stderr, "%s: %s: %s: error: %d: %s\n",
+ g_progname, path ? path : "-", func, ecode, tchdberrmsg(ecode));
+}
+
+
+/* print members of hash database */
+static void mprint(TCHDB *hdb){
+ if(hdb->cnt_writerec < 0) return;
+ iprintf("bucket number: %lld\n", (long long)tchdbbnum(hdb));
+ iprintf("used bucket number: %lld\n", (long long)tchdbbnumused(hdb));
+ iprintf("cnt_writerec: %lld\n", (long long)hdb->cnt_writerec);
+ iprintf("cnt_reuserec: %lld\n", (long long)hdb->cnt_reuserec);
+ iprintf("cnt_moverec: %lld\n", (long long)hdb->cnt_moverec);
+ iprintf("cnt_readrec: %lld\n", (long long)hdb->cnt_readrec);
+ iprintf("cnt_searchfbp: %lld\n", (long long)hdb->cnt_searchfbp);
+ iprintf("cnt_insertfbp: %lld\n", (long long)hdb->cnt_insertfbp);
+ iprintf("cnt_splicefbp: %lld\n", (long long)hdb->cnt_splicefbp);
+ iprintf("cnt_dividefbp: %lld\n", (long long)hdb->cnt_dividefbp);
+ iprintf("cnt_mergefbp: %lld\n", (long long)hdb->cnt_mergefbp);
+ iprintf("cnt_reducefbp: %lld\n", (long long)hdb->cnt_reducefbp);
+ iprintf("cnt_appenddrp: %lld\n", (long long)hdb->cnt_appenddrp);
+ iprintf("cnt_deferdrp: %lld\n", (long long)hdb->cnt_deferdrp);
+ iprintf("cnt_flushdrp: %lld\n", (long long)hdb->cnt_flushdrp);
+ iprintf("cnt_adjrecc: %lld\n", (long long)hdb->cnt_adjrecc);
+}
+
+
+/* get a random number */
+static int myrand(int range){
+ return (int)((double)range * rand() / (RAND_MAX + 1.0));
+}
+
+
+/* parse arguments of write command */
+static int runwrite(int argc, char **argv){
+ char *path = NULL;
+ char *rstr = NULL;
+ char *bstr = NULL;
+ char *astr = NULL;
+ char *fstr = NULL;
+ bool mt = false;
+ int opts = 0;
+ int rcnum = 0;
+ int omode = 0;
+ bool as = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-mt")){
+ mt = true;
+ } else if(!strcmp(argv[i], "-tl")){
+ opts |= HDBTLARGE;
+ } else if(!strcmp(argv[i], "-td")){
+ opts |= HDBTDEFLATE;
+ } else if(!strcmp(argv[i], "-tb")){
+ opts |= HDBTTCBS;
+ } else if(!strcmp(argv[i], "-rc")){
+ if(++i >= argc) usage();
+ rcnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= HDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= HDBOLCKNB;
+ } else if(!strcmp(argv[i], "-as")){
+ as = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else if(!bstr){
+ bstr = argv[i];
+ } else if(!astr){
+ astr = argv[i];
+ } else if(!fstr){
+ fstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !rstr) usage();
+ int rnum = atoi(rstr);
+ if(rnum < 1) usage();
+ int bnum = bstr ? atoi(bstr) : -1;
+ int apow = astr ? atoi(astr) : -1;
+ int fpow = fstr ? atoi(fstr) : -1;
+ int rv = procwrite(path, rnum, bnum, apow, fpow, mt, opts, rcnum, omode, as);
+ return rv;
+}
+
+
+/* parse arguments of read command */
+static int runread(int argc, char **argv){
+ char *path = NULL;
+ bool mt = false;
+ int rcnum = 0;
+ int omode = 0;
+ bool wb = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-mt")){
+ mt = true;
+ } else if(!strcmp(argv[i], "-rc")){
+ if(++i >= argc) usage();
+ rcnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= HDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= HDBOLCKNB;
+ } else if(!strcmp(argv[i], "-wb")){
+ wb = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path) usage();
+ int rv = procread(path, mt, rcnum, omode, wb);
+ return rv;
+}
+
+
+/* parse arguments of remove command */
+static int runremove(int argc, char **argv){
+ char *path = NULL;
+ bool mt = false;
+ int rcnum = 0;
+ int omode = 0;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-mt")){
+ mt = true;
+ } else if(!strcmp(argv[i], "-rc")){
+ if(++i >= argc) usage();
+ rcnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= HDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= HDBOLCKNB;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path) usage();
+ int rv = procremove(path, mt, rcnum, omode);
+ return rv;
+}
+
+
+/* parse arguments of rcat command */
+static int runrcat(int argc, char **argv){
+ char *path = NULL;
+ char *rstr = NULL;
+ char *bstr = NULL;
+ char *astr = NULL;
+ char *fstr = NULL;
+ bool mt = false;
+ int opts = 0;
+ int rcnum = 0;
+ int omode = 0;
+ int pnum = 0;
+ bool rl = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-mt")){
+ mt = true;
+ } else if(!strcmp(argv[i], "-tl")){
+ opts |= HDBTLARGE;
+ } else if(!strcmp(argv[i], "-td")){
+ opts |= HDBTDEFLATE;
+ } else if(!strcmp(argv[i], "-tb")){
+ opts |= HDBTTCBS;
+ } else if(!strcmp(argv[i], "-rc")){
+ if(++i >= argc) usage();
+ rcnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= HDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= HDBOLCKNB;
+ } else if(!strcmp(argv[i], "-pn")){
+ if(++i >= argc) usage();
+ pnum = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-rl")){
+ rl = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else if(!bstr){
+ bstr = argv[i];
+ } else if(!astr){
+ astr = argv[i];
+ } else if(!fstr){
+ fstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !rstr) usage();
+ int rnum = atoi(rstr);
+ if(rnum < 1) usage();
+ int bnum = bstr ? atoi(bstr) : -1;
+ int apow = astr ? atoi(astr) : -1;
+ int fpow = fstr ? atoi(fstr) : -1;
+ int rv = procrcat(path, rnum, bnum, apow, fpow, mt, opts, rcnum, omode, pnum, rl);
+ return rv;
+}
+
+
+/* parse arguments of misc command */
+static int runmisc(int argc, char **argv){
+ char *path = NULL;
+ char *rstr = NULL;
+ bool mt = false;
+ int opts = 0;
+ int omode = 0;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-mt")){
+ mt = true;
+ } else if(!strcmp(argv[i], "-tl")){
+ opts |= HDBTLARGE;
+ } else if(!strcmp(argv[i], "-td")){
+ opts |= HDBTDEFLATE;
+ } else if(!strcmp(argv[i], "-tb")){
+ opts |= HDBTTCBS;
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= HDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= HDBOLCKNB;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !rstr) usage();
+ int rnum = atoi(rstr);
+ if(rnum < 1) usage();
+ int rv = procmisc(path, rnum, mt, opts, omode);
+ return rv;
+}
+
+
+/* parse arguments of wicked command */
+static int runwicked(int argc, char **argv){
+ char *path = NULL;
+ char *rstr = NULL;
+ bool mt = false;
+ int opts = 0;
+ int omode = 0;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-mt")){
+ mt = true;
+ } else if(!strcmp(argv[i], "-tl")){
+ opts |= HDBTLARGE;
+ } else if(!strcmp(argv[i], "-td")){
+ opts |= HDBTDEFLATE;
+ } else if(!strcmp(argv[i], "-tb")){
+ opts |= HDBTTCBS;
+ } else if(!strcmp(argv[i], "-nl")){
+ omode |= HDBONOLCK;
+ } else if(!strcmp(argv[i], "-nb")){
+ omode |= HDBOLCKNB;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!path || !rstr) usage();
+ int rnum = atoi(rstr);
+ if(rnum < 1) usage();
+ int rv = procwicked(path, rnum, mt, opts, omode);
+ return rv;
+}
+
+
+/* perform write command */
+static int procwrite(const char *path, int rnum, int bnum, int apow, int fpow,
+ bool mt, int opts, int rcnum, int omode, bool as){
+ iprintf("<Writing Test>\n path=%s rnum=%d bnum=%d apow=%d fpow=%d mt=%d"
+ " opts=%d rcnum=%d omode=%d as=%d\n\n",
+ path, rnum, bnum, apow, fpow, mt, opts, rcnum, omode, as);
+ bool err = false;
+ double stime = tctime();
+ TCHDB *hdb = tchdbnew();
+ if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+ if(mt && !tchdbsetmutex(hdb)){
+ eprint(hdb, "tchdbsetmutex");
+ err = true;
+ }
+ if(!tchdbtune(hdb, bnum, apow, fpow, opts)){
+ eprint(hdb, "tchdbtune");
+ err = true;
+ }
+ if(!tchdbsetcache(hdb, rcnum)){
+ eprint(hdb, "tchdbsetcache");
+ err = true;
+ }
+ if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){
+ eprint(hdb, "tchdbopen");
+ err = true;
+ }
+ for(int i = 1; i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len = sprintf(buf, "%08d", i);
+ if(as){
+ if(!tchdbputasync(hdb, buf, len, buf, len)){
+ eprint(hdb, "tchdbput");
+ err = true;
+ break;
+ }
+ } else {
+ if(!tchdbput(hdb, buf, len, buf, len)){
+ eprint(hdb, "tchdbput");
+ err = true;
+ break;
+ }
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+ iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+ mprint(hdb);
+ if(!tchdbclose(hdb)){
+ eprint(hdb, "tchdbclose");
+ err = true;
+ }
+ tchdbdel(hdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform read command */
+static int procread(const char *path, bool mt, int rcnum, int omode, bool wb){
+ iprintf("<Reading Test>\n path=%s mt=%d rcnum=%d omode=%d wb=%d\n",
+ path, mt, rcnum, omode, wb);
+ bool err = false;
+ double stime = tctime();
+ TCHDB *hdb = tchdbnew();
+ if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+ if(mt && !tchdbsetmutex(hdb)){
+ eprint(hdb, "tchdbsetmutex");
+ err = true;
+ }
+ if(!tchdbsetcache(hdb, rcnum)){
+ eprint(hdb, "tchdbsetcache");
+ err = true;
+ }
+ if(!tchdbopen(hdb, path, HDBOREADER | omode)){
+ eprint(hdb, "tchdbopen");
+ err = true;
+ }
+ int rnum = tchdbrnum(hdb);
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%08d", i);
+ int vsiz;
+ if(wb){
+ char vbuf[RECBUFSIZ];
+ int vsiz = tchdbget3(hdb, kbuf, ksiz, vbuf, RECBUFSIZ);
+ if(vsiz < 0){
+ eprint(hdb, "tchdbget3");
+ err = true;
+ break;
+ }
+ } else {
+ char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
+ if(!vbuf){
+ eprint(hdb, "tchdbget");
+ err = true;
+ break;
+ }
+ free(vbuf);
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+ iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+ mprint(hdb);
+ if(!tchdbclose(hdb)){
+ eprint(hdb, "tchdbclose");
+ err = true;
+ }
+ tchdbdel(hdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform remove command */
+static int procremove(const char *path, bool mt, int rcnum, int omode){
+ iprintf("<Removing Test>\n path=%s mt=%d rcnum=%d omode=%d\n", path, mt, rcnum, omode);
+ bool err = false;
+ double stime = tctime();
+ TCHDB *hdb = tchdbnew();
+ if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+ if(mt && !tchdbsetmutex(hdb)){
+ eprint(hdb, "tchdbsetmutex");
+ err = true;
+ }
+ if(!tchdbsetcache(hdb, rcnum)){
+ eprint(hdb, "tchdbsetcache");
+ err = true;
+ }
+ if(!tchdbopen(hdb, path, HDBOWRITER | omode)){
+ eprint(hdb, "tchdbopen");
+ err = true;
+ }
+ int rnum = tchdbrnum(hdb);
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%08d", i);
+ if(!tchdbout(hdb, kbuf, ksiz)){
+ eprint(hdb, "tchdbout");
+ err = true;
+ break;
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+ iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+ mprint(hdb);
+ if(!tchdbclose(hdb)){
+ eprint(hdb, "tchdbclose");
+ err = true;
+ }
+ tchdbdel(hdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform rcat command */
+static int procrcat(const char *path, int rnum, int bnum, int apow, int fpow,
+ bool mt, int opts, int rcnum, int omode, int pnum, bool rl){
+ iprintf("<Random Concatenating Test>\n"
+ " path=%s rnum=%d bnum=%d apow=%d fpow=%d mt=%d opts=%d rcnum=%d"
+ " omode=%d pnum=%d rl=%d\n\n",
+ path, rnum, bnum, apow, fpow, mt, opts, rcnum, omode, pnum, rl);
+ if(pnum < 1) pnum = rnum;
+ bool err = false;
+ double stime = tctime();
+ TCHDB *hdb = tchdbnew();
+ if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+ if(mt && !tchdbsetmutex(hdb)){
+ eprint(hdb, "tchdbsetmutex");
+ err = true;
+ }
+ if(!tchdbtune(hdb, bnum, apow, fpow, opts)){
+ eprint(hdb, "tchdbtune");
+ err = true;
+ }
+ if(!tchdbsetcache(hdb, rcnum)){
+ eprint(hdb, "tchdbsetcache");
+ err = true;
+ }
+ if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){
+ eprint(hdb, "tchdbopen");
+ err = true;
+ }
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", myrand(pnum));
+ if(rl){
+ char vbuf[PATH_MAX];
+ int vsiz = myrand(PATH_MAX);
+ for(int j = 0; j < vsiz; j++){
+ vbuf[j] = myrand(0x100);
+ }
+ if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(hdb, "tchdbputcat");
+ err = true;
+ break;
+ }
+ } else {
+ if(!tchdbputcat(hdb, kbuf, ksiz, kbuf, ksiz)){
+ eprint(hdb, "tchdbputcat");
+ err = true;
+ break;
+ }
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+ iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+ mprint(hdb);
+ if(!tchdbclose(hdb)){
+ eprint(hdb, "tchdbclose");
+ err = true;
+ }
+ tchdbdel(hdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform misc command */
+static int procmisc(const char *path, int rnum, bool mt, int opts, int omode){
+ iprintf("<Miscellaneous Test>\n path=%s rnum=%d mt=%d opts=%d omode=%d\n\n",
+ path, rnum, mt, opts, omode);
+ bool err = false;
+ double stime = tctime();
+ TCHDB *hdb = tchdbnew();
+ if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+ if(mt && !tchdbsetmutex(hdb)){
+ eprint(hdb, "tchdbsetmutex");
+ err = true;
+ }
+ if(!tchdbtune(hdb, rnum / 50, 2, -1, opts)){
+ eprint(hdb, "tchdbtune");
+ err = true;
+ }
+ if(!tchdbsetcache(hdb, rnum / 10)){
+ eprint(hdb, "tchdbsetcache");
+ err = true;
+ }
+ if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){
+ eprint(hdb, "tchdbopen");
+ err = true;
+ }
+ iprintf("writing:\n");
+ for(int i = 1; i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len = sprintf(buf, "%08d", i);
+ if(i % 3 == 0){
+ if(!tchdbputkeep(hdb, buf, len, buf, len)){
+ eprint(hdb, "tchdbputkeep");
+ err = true;
+ break;
+ }
+ } else {
+ if(!tchdbputasync(hdb, buf, len, buf, len)){
+ eprint(hdb, "tchdbputasync");
+ err = true;
+ break;
+ }
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("reading:\n");
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%08d", i);
+ int vsiz;
+ char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
+ if(!vbuf){
+ eprint(hdb, "tchdbget");
+ err = true;
+ break;
+ } else if(vsiz != ksiz || memcmp(vbuf, kbuf, vsiz)){
+ eprint(hdb, "(validation)");
+ err = true;
+ free(vbuf);
+ break;
+ }
+ free(vbuf);
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ if(tchdbrnum(hdb) != rnum){
+ eprint(hdb, "(validation)");
+ err = true;
+ }
+ iprintf("random writing:\n");
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+ char vbuf[RECBUFSIZ];
+ int vsiz = myrand(RECBUFSIZ);
+ memset(vbuf, '*', vsiz);
+ if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(hdb, "tchdbput");
+ err = true;
+ break;
+ }
+ int rsiz;
+ char *rbuf = tchdbget(hdb, kbuf, ksiz, &rsiz);
+ if(!rbuf){
+ eprint(hdb, "tchdbget");
+ err = true;
+ break;
+ }
+ if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+ eprint(hdb, "(validation)");
+ err = true;
+ free(rbuf);
+ break;
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ free(rbuf);
+ }
+ iprintf("word writing:\n");
+ const char *words[] = {
+ "a", "A", "bb", "BB", "ccc", "CCC", "dddd", "DDDD", "eeeee", "EEEEEE",
+ "mikio", "hirabayashi", "tokyo", "cabinet", "hyper", "estraier", "19780211", "birth day",
+ "one", "first", "two", "second", "three", "third", "four", "fourth", "five", "fifth",
+ "_[1]_", "uno", "_[2]_", "dos", "_[3]_", "tres", "_[4]_", "cuatro", "_[5]_", "cinco",
+ "[\xe5\xb9\xb3\xe6\x9e\x97\xe5\xb9\xb9\xe9\x9b\x84]", "[\xe9\xa6\xac\xe9\xb9\xbf]", NULL
+ };
+ for(int i = 0; words[i] != NULL; i += 2){
+ const char *kbuf = words[i];
+ int ksiz = strlen(kbuf);
+ const char *vbuf = words[i+1];
+ int vsiz = strlen(vbuf);
+ if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(hdb, "tchdbputkeep");
+ err = true;
+ break;
+ }
+ if(rnum > 250) putchar('.');
+ }
+ if(rnum > 250) iprintf(" (%08d)\n", sizeof(words) / sizeof(*words));
+ iprintf("random erasing:\n");
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+ if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tchdbout");
+ err = true;
+ break;
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("writing:\n");
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "[%d]", i);
+ char vbuf[RECBUFSIZ];
+ int vsiz = i % RECBUFSIZ;
+ memset(vbuf, '*', vsiz);
+ if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(hdb, "tchdbputkeep");
+ err = true;
+ break;
+ }
+ if(vsiz < 1){
+ char tbuf[PATH_MAX];
+ for(int j = 0; j < PATH_MAX; j++){
+ tbuf[j] = myrand(0x100);
+ }
+ if(!tchdbput(hdb, kbuf, ksiz, tbuf, PATH_MAX)){
+ eprint(hdb, "tchdbput");
+ err = true;
+ break;
+ }
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("erasing:\n");
+ for(int i = 1; i <= rnum; i++){
+ if(i % 2 == 1){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "[%d]", i);
+ if(!tchdbout(hdb, kbuf, ksiz)){
+ eprint(hdb, "tchdbout");
+ err = true;
+ break;
+ }
+ if(tchdbout(hdb, kbuf, ksiz) || tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tchdbout");
+ err = true;
+ break;
+ }
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("random writing and reopening:\n");
+ for(int i = 1; i <= rnum; i++){
+ if(myrand(10) == 0){
+ int ksiz, vsiz;
+ char *kbuf, *vbuf;
+ ksiz = (myrand(5) == 0) ? myrand(UINT16_MAX) : myrand(RECBUFSIZ);
+ kbuf = tcmalloc(ksiz + 1);
+ memset(kbuf, '@', ksiz);
+ vsiz = (myrand(5) == 0) ? myrand(UINT16_MAX) : myrand(RECBUFSIZ);
+ vbuf = tcmalloc(vsiz + 1);
+ for(int j = 0; j < vsiz; j++){
+ vbuf[j] = myrand(256);
+ }
+ switch(myrand(4)){
+ case 0:
+ if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(hdb, "tchdbput");
+ err = true;
+ }
+ break;
+ case 1:
+ if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(hdb, "tchdbputcat");
+ err = true;
+ }
+ break;
+ case 2:
+ if(!tchdbputasync(hdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(hdb, "tchdbputasync");
+ err = true;
+ }
+ break;
+ case 3:
+ if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tchdbout");
+ err = true;
+ }
+ break;
+ }
+ free(vbuf);
+ free(kbuf);
+ } else {
+ char kbuf[RECBUFSIZ];
+ int ksiz = myrand(RECBUFSIZ);
+ memset(kbuf, '@', ksiz);
+ char vbuf[RECBUFSIZ];
+ int vsiz = myrand(RECBUFSIZ);
+ memset(vbuf, '@', vsiz);
+ if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(hdb, "tchdbputcat");
+ err = true;
+ break;
+ }
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ if(!tchdbclose(hdb)){
+ eprint(hdb, "tchdbclose");
+ err = true;
+ }
+ if(!tchdbopen(hdb, path, HDBOWRITER | omode)){
+ eprint(hdb, "tchdbopen");
+ err = true;
+ }
+ iprintf("checking:\n");
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "[%d]", i);
+ int vsiz;
+ char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
+ if(i % 2 == 0){
+ if(!vbuf){
+ eprint(hdb, "tchdbget");
+ err = true;
+ break;
+ }
+ if(vsiz != i % RECBUFSIZ && vsiz != PATH_MAX){
+ eprint(hdb, "(validation)");
+ err = true;
+ free(vbuf);
+ break;
+ }
+ } else {
+ if(vbuf || tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "(validation)");
+ err = true;
+ free(vbuf);
+ break;
+ }
+ }
+ free(vbuf);
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("writing:\n");
+ for(int i = 1; i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len = sprintf(buf, "%08d", i);
+ if(!tchdbput(hdb, buf, len, buf, len)){
+ eprint(hdb, "tchdbput");
+ err = true;
+ break;
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("reading:\n");
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%08d", i);
+ int vsiz;
+ char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
+ if(!vbuf){
+ eprint(hdb, "tchdbget");
+ err = true;
+ break;
+ } else if(vsiz != ksiz || memcmp(vbuf, kbuf, vsiz)){
+ eprint(hdb, "(validation)");
+ err = true;
+ free(vbuf);
+ break;
+ }
+ free(vbuf);
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("word checking:\n");
+ for(int i = 0; words[i] != NULL; i += 2){
+ const char *kbuf = words[i];
+ int ksiz = strlen(kbuf);
+ const char *vbuf = words[i+1];
+ int vsiz = strlen(vbuf);
+ int rsiz;
+ char *rbuf = tchdbget(hdb, kbuf, ksiz, &rsiz);
+ if(!rbuf){
+ eprint(hdb, "tchdbget");
+ err = true;
+ break;
+ } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+ eprint(hdb, "(validation)");
+ err = true;
+ free(rbuf);
+ break;
+ }
+ free(rbuf);
+ if(rnum > 250) putchar('.');
+ }
+ if(rnum > 250) iprintf(" (%08d)\n", sizeof(words) / sizeof(*words));
+ iprintf("iterator checking:\n");
+ if(!tchdbiterinit(hdb)){
+ eprint(hdb, "tchdbiterinit");
+ err = true;
+ }
+ char *kbuf;
+ int ksiz;
+ int inum = 0;
+ for(int i = 1; (kbuf = tchdbiternext(hdb, &ksiz)) != NULL; i++, inum++){
+ int vsiz;
+ char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz);
+ if(!vbuf){
+ eprint(hdb, "tchdbget");
+ err = true;
+ free(kbuf);
+ break;
+ }
+ free(vbuf);
+ free(kbuf);
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ if(rnum > 250) iprintf(" (%08d)\n", inum);
+ if(tchdbecode(hdb) != TCENOREC || inum != tchdbrnum(hdb)){
+ eprint(hdb, "(validation)");
+ err = true;
+ }
+ iprintf("iteration updating:\n");
+ if(!tchdbiterinit(hdb)){
+ eprint(hdb, "tchdbiterinit");
+ err = true;
+ }
+ inum = 0;
+ for(int i = 1; (kbuf = tchdbiternext(hdb, &ksiz)) != NULL; i++, inum++){
+ if(myrand(2) == 0){
+ if(!tchdbputcat(hdb, kbuf, ksiz, "0123456789", 10)){
+ eprint(hdb, "tchdbputcat");
+ err = true;
+ free(kbuf);
+ break;
+ }
+ } else {
+ if(!tchdbout(hdb, kbuf, ksiz)){
+ eprint(hdb, "tchdbout");
+ err = true;
+ free(kbuf);
+ break;
+ }
+ }
+ free(kbuf);
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ if(rnum > 250) iprintf(" (%08d)\n", inum);
+ if(tchdbecode(hdb) != TCENOREC || inum < tchdbrnum(hdb)){
+ eprint(hdb, "(validation)");
+ err = true;
+ }
+ if(!tchdbsync(hdb)){
+ eprint(hdb, "tchdbsync");
+ err = true;
+ }
+ if(!tchdbvanish(hdb)){
+ eprint(hdb, "tchdbvanish");
+ err = true;
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+ iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+ mprint(hdb);
+ if(!tchdbclose(hdb)){
+ eprint(hdb, "tchdbclose");
+ err = true;
+ }
+ tchdbdel(hdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform wicked command */
+static int procwicked(const char *path, int rnum, bool mt, int opts, int omode){
+ iprintf("<Wicked Writing Test>\n path=%s rnum=%d mt=%d opts=%d omode=%d\n\n",
+ path, rnum, mt, opts, omode);
+ bool err = false;
+ double stime = tctime();
+ TCHDB *hdb = tchdbnew();
+ if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd);
+ if(mt && !tchdbsetmutex(hdb)){
+ eprint(hdb, "tchdbsetmutex");
+ err = true;
+ }
+ if(!tchdbtune(hdb, rnum / 50, 2, -1, opts)){
+ eprint(hdb, "tchdbtune");
+ err = true;
+ }
+ if(!tchdbsetcache(hdb, rnum / 10)){
+ eprint(hdb, "tchdbsetcache");
+ err = true;
+ }
+ if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){
+ eprint(hdb, "tchdbopen");
+ err = true;
+ }
+ if(!tchdbiterinit(hdb)){
+ eprint(hdb, "tchdbiterinit");
+ err = true;
+ }
+ TCMAP *map = tcmapnew2(rnum / 5);
+ for(int i = 1; i <= rnum && !err; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", myrand(rnum));
+ char vbuf[RECBUFSIZ];
+ int vsiz = myrand(RECBUFSIZ);
+ memset(vbuf, '*', vsiz);
+ vbuf[vsiz] = '\0';
+ char *rbuf;
+ switch(myrand(16)){
+ case 0:
+ putchar('0');
+ if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(hdb, "tchdbput");
+ err = true;
+ }
+ tcmapput(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 1:
+ putchar('1');
+ if(!tchdbput2(hdb, kbuf, vbuf)){
+ eprint(hdb, "tchdbput2");
+ err = true;
+ }
+ tcmapput2(map, kbuf, vbuf);
+ break;
+ case 2:
+ putchar('2');
+ if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz) && tchdbecode(hdb) != TCEKEEP){
+ eprint(hdb, "tchdbputkeep");
+ err = true;
+ }
+ tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 3:
+ putchar('3');
+ if(!tchdbputkeep2(hdb, kbuf, vbuf) && tchdbecode(hdb) != TCEKEEP){
+ eprint(hdb, "tchdbputkeep2");
+ err = true;
+ }
+ tcmapputkeep2(map, kbuf, vbuf);
+ break;
+ case 4:
+ putchar('4');
+ if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(hdb, "tchdbputcat");
+ err = true;
+ }
+ tcmapputcat(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 5:
+ putchar('5');
+ if(!tchdbputcat2(hdb, kbuf, vbuf)){
+ eprint(hdb, "tchdbputcat2");
+ err = true;
+ }
+ tcmapputcat2(map, kbuf, vbuf);
+ break;
+ case 6:
+ putchar('6');
+ if(!tchdbputasync(hdb, kbuf, ksiz, vbuf, vsiz)){
+ eprint(hdb, "tchdbputasync");
+ err = true;
+ }
+ tcmapput(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 7:
+ putchar('7');
+ if(!tchdbputasync2(hdb, kbuf, vbuf)){
+ eprint(hdb, "tchdbputasync2");
+ err = true;
+ }
+ tcmapput2(map, kbuf, vbuf);
+ break;
+ case 8:
+ putchar('8');
+ if(myrand(10) == 0){
+ if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tchdbout");
+ err = true;
+ }
+ tcmapout(map, kbuf, ksiz);
+ }
+ break;
+ case 9:
+ putchar('9');
+ if(myrand(10) == 0){
+ if(!tchdbout2(hdb, kbuf) && tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tchdbout2");
+ err = true;
+ }
+ tcmapout2(map, kbuf);
+ }
+ break;
+ case 10:
+ putchar('A');
+ if(!(rbuf = tchdbget(hdb, kbuf, ksiz, &vsiz))){
+ if(tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tchdbget");
+ err = true;
+ }
+ rbuf = tcsprintf("[%d]", myrand(i + 1));
+ vsiz = strlen(rbuf);
+ }
+ vsiz += myrand(vsiz);
+ if(myrand(3) == 0) vsiz += PATH_MAX;
+ rbuf = tcrealloc(rbuf, vsiz + 1);
+ for(int j = 0; j < vsiz; j++){
+ rbuf[j] = myrand(0x100);
+ }
+ if(!tchdbput(hdb, kbuf, ksiz, rbuf, vsiz)){
+ eprint(hdb, "tchdbput");
+ err = true;
+ }
+ tcmapput(map, kbuf, ksiz, rbuf, vsiz);
+ free(rbuf);
+ break;
+ case 11:
+ putchar('B');
+ if(!(rbuf = tchdbget(hdb, kbuf, ksiz, &vsiz)) && tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tchdbget");
+ err = true;
+ }
+ free(rbuf);
+ break;
+ case 12:
+ putchar('C');
+ if(!(rbuf = tchdbget2(hdb, kbuf)) && tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tchdbget");
+ err = true;
+ }
+ free(rbuf);
+ break;
+ case 13:
+ putchar('D');
+ if(myrand(1) == 0) vsiz = 1;
+ if((vsiz = tchdbget3(hdb, kbuf, ksiz, vbuf, vsiz)) < 0 && tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tchdbget");
+ err = true;
+ }
+ break;
+ case 14:
+ putchar('E');
+ if(myrand(rnum / 50) == 0){
+ if(!tchdbiterinit(hdb)){
+ eprint(hdb, "tchdbiterinit");
+ err = true;
+ }
+ }
+ TCXSTR *ikey = tcxstrnew();
+ TCXSTR *ival = tcxstrnew();
+ for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){
+ if(j % 3 == 0){
+ if(tchdbiternext3(hdb, ikey, ival)){
+ if(tcxstrsize(ival) != tchdbvsiz(hdb, tcxstrptr(ikey), tcxstrsize(ikey))){
+ eprint(hdb, "(validation)");
+ err = true;
+ }
+ } else {
+ int ecode = tchdbecode(hdb);
+ if(ecode != TCEINVALID && ecode != TCENOREC){
+ eprint(hdb, "tchdbiternext3");
+ err = true;
+ }
+ }
+ } else {
+ int iksiz;
+ char *ikbuf = tchdbiternext(hdb, &iksiz);
+ if(ikbuf){
+ free(ikbuf);
+ } else {
+ int ecode = tchdbecode(hdb);
+ if(ecode != TCEINVALID && ecode != TCENOREC){
+ eprint(hdb, "tchdbiternext");
+ err = true;
+ }
+ }
+ }
+ }
+ tcxstrdel(ival);
+ tcxstrdel(ikey);
+ break;
+ default:
+ putchar('@');
+ if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+ if(myrand(rnum / 16 + 1) == 0){
+ int cnt = myrand(30);
+ for(int j = 0; j < rnum && !err; j++){
+ ksiz = sprintf(kbuf, "%d", i + j);
+ if(tchdbout(hdb, kbuf, ksiz)){
+ cnt--;
+ } else if(tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "tcbdbout");
+ err = true;
+ }
+ tcmapout(map, kbuf, ksiz);
+ if(cnt < 0) break;
+ }
+ }
+ break;
+ }
+ if(i % 50 == 0) iprintf(" (%08d)\n", i);
+ if(i == rnum / 2){
+ if(!tchdbclose(hdb)){
+ eprint(hdb, "tchdbclose");
+ err = true;
+ }
+ if(!tchdbopen(hdb, path, HDBOWRITER | omode)){
+ eprint(hdb, "tchdbopen");
+ err = true;
+ }
+ } else if(i == rnum / 4){
+ char *npath = tcsprintf("%s-tmp", path);
+ if(!tchdbcopy(hdb, npath)){
+ eprint(hdb, "tchdbcopy");
+ err = true;
+ }
+ TCHDB *nhdb = tchdbnew();
+ if(!tchdbopen(nhdb, npath, HDBOREADER | omode)){
+ eprint(nhdb, "tchdbopen");
+ err = true;
+ }
+ tchdbdel(nhdb);
+ unlink(npath);
+ free(npath);
+ if(!tchdboptimize(hdb, rnum / 50, -1, -1, -1)){
+ eprint(hdb, "tchdboptimize");
+ err = true;
+ }
+ if(!tchdbiterinit(hdb)){
+ eprint(hdb, "tchdbiterinit");
+ err = true;
+ }
+ }
+ }
+ if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
+ if(!tchdbsync(hdb)){
+ eprint(hdb, "tchdbsync");
+ err = true;
+ }
+ if(tchdbrnum(hdb) != tcmaprnum(map)){
+ eprint(hdb, "(validation)");
+ err = true;
+ }
+ for(int i = 1; i <= rnum && !err; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", i - 1);
+ int vsiz;
+ const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz);
+ int rsiz;
+ char *rbuf = tchdbget(hdb, kbuf, ksiz, &rsiz);
+ if(vbuf){
+ putchar('.');
+ if(!rbuf){
+ eprint(hdb, "tchdbget");
+ err = true;
+ } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+ eprint(hdb, "(validation)");
+ err = true;
+ }
+ } else {
+ putchar('*');
+ if(rbuf || tchdbecode(hdb) != TCENOREC){
+ eprint(hdb, "(validation)");
+ err = true;
+ }
+ }
+ free(rbuf);
+ if(i % 50 == 0) iprintf(" (%08d)\n", i);
+ }
+ if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
+ tcmapiterinit(map);
+ int ksiz;
+ const char *kbuf;
+ for(int i = 1; (kbuf = tcmapiternext(map, &ksiz)) != NULL; i++){
+ putchar('+');
+ int vsiz;
+ const char *vbuf = tcmapiterval(kbuf, &vsiz);
+ int rsiz;
+ char *rbuf = tchdbget(hdb, kbuf, ksiz, &rsiz);
+ if(!rbuf){
+ eprint(hdb, "tchdbget");
+ err = true;
+ } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){
+ eprint(hdb, "(validation)");
+ err = true;
+ }
+ free(rbuf);
+ if(!tchdbout(hdb, kbuf, ksiz)){
+ eprint(hdb, "tchdbout");
+ err = true;
+ }
+ if(i % 50 == 0) iprintf(" (%08d)\n", i);
+ }
+ int mrnum = tcmaprnum(map);
+ if(mrnum % 50 > 0) iprintf(" (%08d)\n", mrnum);
+ if(tchdbrnum(hdb) != 0){
+ eprint(hdb, "(validation)");
+ err = true;
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb));
+ iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb));
+ mprint(hdb);
+ tcmapdel(map);
+ if(!tchdbclose(hdb)){
+ eprint(hdb, "tchdbclose");
+ err = true;
+ }
+ tchdbdel(hdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+
+// END OF FILE
--- /dev/null
+/*************************************************************************************************
+ * Popular encoders and decoders of the utility API
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include "myconf.h"
+
+
+/* global variables */
+const char *g_progname; // program name
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void eprintf(const char *format, ...);
+static int runurl(int argc, char **argv);
+static int runbase(int argc, char **argv);
+static int runquote(int argc, char **argv);
+static int runmime(int argc, char **argv);
+static int runpack(int argc, char **argv);
+static int runtcbs(int argc, char **argv);
+static int runzlib(int argc, char **argv);
+static int runxml(int argc, char **argv);
+static int runucs(int argc, char **argv);
+static int rundate(int argc, char **argv);
+static int runconf(int argc, char **argv);
+static int procurl(const char *ibuf, int isiz, bool dec, bool br, const char *base);
+static int procbase(const char *ibuf, int isiz, bool dec);
+static int procquote(const char *ibuf, int isiz, bool dec);
+static int procmime(const char *ibuf, int isiz, bool dec, const char *ename, bool qb, bool on);
+static int procpack(const char *ibuf, int isiz, bool dec, bool bwt);
+static int proctcbs(const char *ibuf, int isiz, bool dec);
+static int proczlib(const char *ibuf, int isiz, bool dec, bool gz);
+static int procxml(const char *ibuf, int isiz, bool dec, bool br);
+static int procucs(const char *ibuf, int isiz, bool dec);
+static int procdate(const char *str, int jl, bool wf, bool rf);
+static int procconf(int mode);
+
+
+/* main routine */
+int main(int argc, char **argv){
+ g_progname = argv[0];
+ if(argc < 2) usage();
+ int rv = 0;
+ if(!strcmp(argv[1], "url")){
+ rv = runurl(argc, argv);
+ } else if(!strcmp(argv[1], "base")){
+ rv = runbase(argc, argv);
+ } else if(!strcmp(argv[1], "quote")){
+ rv = runquote(argc, argv);
+ } else if(!strcmp(argv[1], "mime")){
+ rv = runmime(argc, argv);
+ } else if(!strcmp(argv[1], "pack")){
+ rv = runpack(argc, argv);
+ } else if(!strcmp(argv[1], "tcbs")){
+ rv = runtcbs(argc, argv);
+ } else if(!strcmp(argv[1], "zlib")){
+ rv = runzlib(argc, argv);
+ } else if(!strcmp(argv[1], "xml")){
+ rv = runxml(argc, argv);
+ } else if(!strcmp(argv[1], "ucs")){
+ rv = runucs(argc, argv);
+ } else if(!strcmp(argv[1], "date")){
+ rv = rundate(argc, argv);
+ } else if(!strcmp(argv[1], "conf")){
+ rv = runconf(argc, argv);
+ } else {
+ usage();
+ }
+ return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+ fprintf(stderr, "%s: popular encoders and decoders of Tokyo Cabinet\n", g_progname);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, " %s url [-d] [-br] [-rs base] [file]\n", g_progname);
+ fprintf(stderr, " %s base [-d] [file]\n", g_progname);
+ fprintf(stderr, " %s quote [-d] [file]\n", g_progname);
+ fprintf(stderr, " %s mime [-d] [-en name] [-q] [file]\n", g_progname);
+ fprintf(stderr, " %s pack [-d] [-bwt] [file]\n", g_progname);
+ fprintf(stderr, " %s tcbs [-d] [file]\n", g_progname);
+ fprintf(stderr, " %s zlib [-d] [-gz] [file]\n", g_progname);
+ fprintf(stderr, " %s xml [-d] [-br] [file]\n", g_progname);
+ fprintf(stderr, " %s ucs [-d] [file]\n", g_progname);
+ fprintf(stderr, " %s date [-ds str] [-jl num] [-wf] [-rf]\n", g_progname);
+ fprintf(stderr, " %s conf [-v|-i|-l|-p]\n", g_progname);
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+
+/* print formatted error string */
+static void eprintf(const char *format, ...){
+ va_list ap;
+ va_start(ap, format);
+ fprintf(stderr, "%s: ", g_progname);
+ vfprintf(stderr, format, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+
+/* parse arguments of url command */
+static int runurl(int argc, char **argv){
+ char *path = NULL;
+ bool dec = false;
+ bool br = false;
+ char *base = NULL;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-d")){
+ dec = true;
+ } else if(!strcmp(argv[i], "-br")){
+ br = true;
+ } else if(!strcmp(argv[i], "-rs")){
+ if(++i >= argc) usage();
+ base = argv[i];
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else {
+ usage();
+ }
+ }
+ char *ibuf;
+ int isiz;
+ if(path && path[0] == '@'){
+ isiz = strlen(path) - 1;
+ ibuf = tcmemdup(path + 1, isiz);
+ } else {
+ ibuf = tcreadfile(path, -1, &isiz);
+ }
+ if(!ibuf){
+ eprintf("%s: cannot open", path ? path : "(stdin)");
+ return 1;
+ }
+ int rv = procurl(ibuf, isiz, dec, br, base);
+ if(path && path[0] == '@' && !br) printf("\n");
+ free(ibuf);
+ return rv;
+}
+
+
+/* parse arguments of base command */
+static int runbase(int argc, char **argv){
+ char *path = NULL;
+ bool dec = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-d")){
+ dec = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else {
+ usage();
+ }
+ }
+ char *ibuf;
+ int isiz;
+ if(path && path[0] == '@'){
+ isiz = strlen(path) - 1;
+ ibuf = tcmemdup(path + 1, isiz);
+ } else {
+ ibuf = tcreadfile(path, -1, &isiz);
+ }
+ if(!ibuf){
+ eprintf("%s: cannot open", path ? path : "(stdin)");
+ return 1;
+ }
+ int rv = procbase(ibuf, isiz, dec);
+ if(path && path[0] == '@') printf("\n");
+ free(ibuf);
+ return rv;
+}
+
+
+/* parse arguments of quote command */
+static int runquote(int argc, char **argv){
+ char *path = NULL;
+ bool dec = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-d")){
+ dec = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else {
+ usage();
+ }
+ }
+ char *ibuf;
+ int isiz;
+ if(path && path[0] == '@'){
+ isiz = strlen(path) - 1;
+ ibuf = tcmemdup(path + 1, isiz);
+ } else {
+ ibuf = tcreadfile(path, -1, &isiz);
+ }
+ if(!ibuf){
+ eprintf("%s: cannot open", path ? path : "(stdin)");
+ return 1;
+ }
+ int rv = procquote(ibuf, isiz, dec);
+ if(path && path[0] == '@') printf("\n");
+ free(ibuf);
+ return rv;
+}
+
+
+/* parse arguments of mime command */
+static int runmime(int argc, char **argv){
+ char *path = NULL;
+ bool dec = false;
+ char *ename = NULL;
+ bool qb = false;
+ bool on = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-d")){
+ dec = true;
+ } else if(!strcmp(argv[i], "-en")){
+ if(++i >= argc) usage();
+ ename = argv[i];
+ } else if(!strcmp(argv[i], "-q")){
+ qb = true;
+ } else if(!strcmp(argv[i], "-on")){
+ on = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else {
+ usage();
+ }
+ }
+ char *ibuf;
+ int isiz;
+ if(path && path[0] == '@'){
+ isiz = strlen(path) - 1;
+ ibuf = tcmemdup(path + 1, isiz);
+ } else {
+ ibuf = tcreadfile(path, -1, &isiz);
+ }
+ if(!ibuf){
+ eprintf("%s: cannot open", path ? path : "(stdin)");
+ return 1;
+ }
+ if(!ename) ename = "UTF-8";
+ int rv = procmime(ibuf, isiz, dec, ename, qb, on);
+ if(path && path[0] == '@') printf("\n");
+ free(ibuf);
+ return rv;
+}
+
+
+/* parse arguments of pack command */
+static int runpack(int argc, char **argv){
+ char *path = NULL;
+ bool dec = false;
+ bool bwt = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-d")){
+ dec = true;
+ } else if(!strcmp(argv[i], "-bwt")){
+ bwt = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else {
+ usage();
+ }
+ }
+ char *ibuf;
+ int isiz;
+ if(path && path[0] == '@'){
+ isiz = strlen(path) - 1;
+ ibuf = tcmemdup(path + 1, isiz);
+ } else {
+ ibuf = tcreadfile(path, -1, &isiz);
+ }
+ if(!ibuf){
+ eprintf("%s: cannot open", path ? path : "(stdin)");
+ return 1;
+ }
+ int rv = procpack(ibuf, isiz, dec, bwt);
+ if(path && path[0] == '@') printf("\n");
+ free(ibuf);
+ return rv;
+}
+
+
+/* parse arguments of tcbs command */
+static int runtcbs(int argc, char **argv){
+ char *path = NULL;
+ bool dec = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-d")){
+ dec = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else {
+ usage();
+ }
+ }
+ char *ibuf;
+ int isiz;
+ if(path && path[0] == '@'){
+ isiz = strlen(path) - 1;
+ ibuf = tcmemdup(path + 1, isiz);
+ } else {
+ ibuf = tcreadfile(path, -1, &isiz);
+ }
+ if(!ibuf){
+ eprintf("%s: cannot open", path ? path : "(stdin)");
+ return 1;
+ }
+ int rv = proctcbs(ibuf, isiz, dec);
+ if(path && path[0] == '@') printf("\n");
+ free(ibuf);
+ return rv;
+}
+
+
+/* parse arguments of zlib command */
+static int runzlib(int argc, char **argv){
+ char *path = NULL;
+ bool dec = false;
+ bool gz = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-d")){
+ dec = true;
+ } else if(!strcmp(argv[i], "-gz")){
+ gz = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else {
+ usage();
+ }
+ }
+ char *ibuf;
+ int isiz;
+ if(path && path[0] == '@'){
+ isiz = strlen(path) - 1;
+ ibuf = tcmemdup(path + 1, isiz);
+ } else {
+ ibuf = tcreadfile(path, -1, &isiz);
+ }
+ if(!ibuf){
+ eprintf("%s: cannot open", path ? path : "(stdin)");
+ return 1;
+ }
+ int rv = proczlib(ibuf, isiz, dec, gz);
+ if(path && path[0] == '@') printf("\n");
+ free(ibuf);
+ return rv;
+}
+
+
+/* parse arguments of xml command */
+static int runxml(int argc, char **argv){
+ char *path = NULL;
+ bool dec = false;
+ bool br = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-d")){
+ dec = true;
+ } else if(!strcmp(argv[i], "-br")){
+ br = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else {
+ usage();
+ }
+ }
+ char *ibuf;
+ int isiz;
+ if(path && path[0] == '@'){
+ isiz = strlen(path) - 1;
+ ibuf = tcmemdup(path + 1, isiz);
+ } else {
+ ibuf = tcreadfile(path, -1, &isiz);
+ }
+ if(!ibuf){
+ eprintf("%s: cannot open", path ? path : "(stdin)");
+ return 1;
+ }
+ int rv = procxml(ibuf, isiz, dec, br);
+ if(path && path[0] == '@') printf("\n");
+ free(ibuf);
+ return rv;
+}
+
+
+/* parse arguments of ucs command */
+static int runucs(int argc, char **argv){
+ char *path = NULL;
+ bool dec = false;
+ for(int i = 2; i < argc; i++){
+ if(!path && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-d")){
+ dec = true;
+ } else {
+ usage();
+ }
+ } else if(!path){
+ path = argv[i];
+ } else {
+ usage();
+ }
+ }
+ char *ibuf;
+ int isiz;
+ if(path && path[0] == '@'){
+ isiz = strlen(path) - 1;
+ ibuf = tcmemdup(path + 1, isiz);
+ } else {
+ ibuf = tcreadfile(path, -1, &isiz);
+ }
+ if(!ibuf){
+ eprintf("%s: cannot open", path ? path : "(stdin)");
+ return 1;
+ }
+ int rv = procucs(ibuf, isiz, dec);
+ if(path && path[0] == '@') printf("\n");
+ free(ibuf);
+ return rv;
+}
+
+
+/* parse arguments of date command */
+static int rundate(int argc, char **argv){
+ char *str = NULL;
+ int jl = INT_MAX;
+ bool wf = false;
+ bool rf = false;
+ for(int i = 2; i < argc; i++){
+ if(argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-ds")){
+ if(++i >= argc) usage();
+ str = argv[i];
+ } else if(!strcmp(argv[i], "-jl")){
+ if(++i >= argc) usage();
+ jl = atoi(argv[i]);
+ } else if(!strcmp(argv[i], "-wf")){
+ wf = true;
+ } else if(!strcmp(argv[i], "-rf")){
+ rf = true;
+ } else {
+ usage();
+ }
+ } else {
+ usage();
+ }
+ }
+ int rv = procdate(str, jl, wf, rf);
+ return rv;
+}
+
+
+/* parse arguments of conf command */
+static int runconf(int argc, char **argv){
+ int mode = 0;
+ for(int i = 2; i < argc; i++){
+ if(argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-v")){
+ mode = 'v';
+ } else if(!strcmp(argv[i], "-i")){
+ mode = 'i';
+ } else if(!strcmp(argv[i], "-l")){
+ mode = 'l';
+ } else if(!strcmp(argv[i], "-p")){
+ mode = 'p';
+ } else {
+ usage();
+ }
+ } else {
+ usage();
+ }
+ }
+ int rv = procconf(mode);
+ return rv;
+}
+
+
+/* perform url command */
+static int procurl(const char *ibuf, int isiz, bool dec, bool br, const char *base){
+ if(base){
+ char *obuf = tcurlresolve(base, ibuf);
+ printf("%s", obuf);
+ free(obuf);
+ } else if(br){
+ TCMAP *elems = tcurlbreak(ibuf);
+ const char *elem;
+ if((elem = tcmapget2(elems, "self")) != NULL) printf("self: %s\n", elem);
+ if((elem = tcmapget2(elems, "scheme")) != NULL) printf("scheme: %s\n", elem);
+ if((elem = tcmapget2(elems, "host")) != NULL) printf("host: %s\n", elem);
+ if((elem = tcmapget2(elems, "port")) != NULL) printf("port: %s\n", elem);
+ if((elem = tcmapget2(elems, "authority")) != NULL) printf("authority: %s\n", elem);
+ if((elem = tcmapget2(elems, "path")) != NULL) printf("path: %s\n", elem);
+ if((elem = tcmapget2(elems, "file")) != NULL) printf("file: %s\n", elem);
+ if((elem = tcmapget2(elems, "query")) != NULL) printf("query: %s\n", elem);
+ if((elem = tcmapget2(elems, "fragment")) != NULL) printf("fragment: %s\n", elem);
+ tcmapdel(elems);
+ } else if(dec){
+ int osiz;
+ char *obuf = tcurldecode(ibuf, &osiz);
+ fwrite(obuf, osiz, 1, stdout);
+ free(obuf);
+ } else {
+ char *obuf = tcurlencode(ibuf, isiz);
+ fwrite(obuf, strlen(obuf), 1, stdout);
+ free(obuf);
+ }
+ return 0;
+}
+
+
+/* perform base command */
+static int procbase(const char *ibuf, int isiz, bool dec){
+ if(dec){
+ int osiz;
+ char *obuf = tcbasedecode(ibuf, &osiz);
+ fwrite(obuf, osiz, 1, stdout);
+ free(obuf);
+ } else {
+ char *obuf = tcbaseencode(ibuf, isiz);
+ fwrite(obuf, strlen(obuf), 1, stdout);
+ free(obuf);
+ }
+ return 0;
+}
+
+
+/* perform quote command */
+static int procquote(const char *ibuf, int isiz, bool dec){
+ if(dec){
+ int osiz;
+ char *obuf = tcquotedecode(ibuf, &osiz);
+ fwrite(obuf, osiz, 1, stdout);
+ free(obuf);
+ } else {
+ char *obuf = tcquoteencode(ibuf, isiz);
+ fwrite(obuf, strlen(obuf), 1, stdout);
+ free(obuf);
+ }
+ return 0;
+}
+
+
+/* perform mime command */
+static int procmime(const char *ibuf, int isiz, bool dec, const char *ename, bool qb, bool on){
+ if(dec){
+ char ebuf[32];
+ char *obuf = tcmimedecode(ibuf, ebuf);
+ if(on){
+ fwrite(ebuf, strlen(ebuf), 1, stdout);
+ } else {
+ fwrite(obuf, strlen(obuf), 1, stdout);
+ }
+ free(obuf);
+ } else {
+ char *obuf = tcmimeencode(ibuf, ename, !qb);
+ fwrite(obuf, strlen(obuf), 1, stdout);
+ free(obuf);
+ }
+ return 0;
+}
+
+
+/* perform pack command */
+static int procpack(const char *ibuf, int isiz, bool dec, bool bwt){
+ if(dec){
+ int osiz;
+ char *obuf = tcpackdecode(ibuf, isiz, &osiz);
+ if(bwt && osiz > 0){
+ int idx, step;
+ TCREADVNUMBUF(obuf, idx, step);
+ char *tbuf = tcbwtdecode(obuf + step, osiz - step, idx);
+ fwrite(tbuf, osiz - step, 1, stdout);
+ free(tbuf);
+ } else {
+ fwrite(obuf, osiz, 1, stdout);
+ }
+ free(obuf);
+ } else {
+ char *tbuf = NULL;
+ if(bwt){
+ int idx;
+ tbuf = tcbwtencode(ibuf, isiz, &idx);
+ char vnumbuf[sizeof(int)+1];
+ int step;
+ TCSETVNUMBUF(step, vnumbuf, idx);
+ tbuf = tcrealloc(tbuf, isiz + step + 1);
+ memmove(tbuf + step, tbuf, isiz);
+ memcpy(tbuf, vnumbuf, step);
+ isiz += step;
+ ibuf = tbuf;
+ }
+ int osiz;
+ char *obuf = tcpackencode(ibuf, isiz, &osiz);
+ fwrite(obuf, osiz, 1, stdout);
+ free(obuf);
+ free(tbuf);
+ }
+ return 0;
+}
+
+
+/* perform tcbs command */
+static int proctcbs(const char *ibuf, int isiz, bool dec){
+ if(dec){
+ int osiz;
+ char *obuf = tcbsdecode(ibuf, isiz, &osiz);
+ fwrite(obuf, osiz, 1, stdout);
+ free(obuf);
+ } else {
+ int osiz;
+ char *obuf = tcbsencode(ibuf, isiz, &osiz);
+ fwrite(obuf, osiz, 1, stdout);
+ free(obuf);
+ }
+ return 0;
+}
+
+
+/* perform zlib command */
+static int proczlib(const char *ibuf, int isiz, bool dec, bool gz){
+ if(dec){
+ int osiz;
+ char *obuf = gz ? tcgzipdecode(ibuf, isiz, &osiz) : tcinflate(ibuf, isiz, &osiz);
+ if(obuf){
+ fwrite(obuf, osiz, 1, stdout);
+ free(obuf);
+ } else {
+ eprintf("inflate failure");
+ }
+ } else {
+ int osiz;
+ char *obuf = gz ? tcgzipencode(ibuf, isiz, &osiz) : tcdeflate(ibuf, isiz, &osiz);
+ if(obuf){
+ fwrite(obuf, osiz, 1, stdout);
+ free(obuf);
+ } else {
+ eprintf("deflate failure");
+ }
+ }
+ return 0;
+}
+
+
+/* perform xml command */
+static int procxml(const char *ibuf, int isiz, bool dec, bool br){
+ if(br){
+ TCLIST *elems = tcxmlbreak(ibuf);
+ for(int i = 0; i < tclistnum(elems); i++){
+ int esiz;
+ const char *elem = tclistval(elems, i, &esiz);
+ char *estr = tcmemdup(elem, esiz);
+ tcstrsubchr(estr, "\t\n\r", " ");
+ tcstrtrim(estr);
+ if(*estr != '\0'){
+ if(*elem == '<'){
+ if(tcstrfwm(estr, "<!--")){
+ printf("COMMENT\t%s\n", estr);
+ } else if(tcstrfwm(estr, "<![CDATA[")){
+ printf("CDATA\t%s\n", estr);
+ } else if(tcstrfwm(estr, "<!")){
+ printf("DECL\t%s\n", estr);
+ } else if(tcstrfwm(estr, "<?")){
+ printf("XMLDECL\t%s\n", estr);
+ } else {
+ TCMAP *attrs = tcxmlattrs(estr);
+ tcmapiterinit(attrs);
+ const char *name;
+ if((name = tcmapget2(attrs, "")) != NULL){
+ if(tcstrfwm(estr, "</")){
+ printf("END");
+ } else if(tcstrbwm(estr, "/>")){
+ printf("EMPTY");
+ } else {
+ printf("BEGIN");
+ }
+ printf("\t%s", name);
+ while((name = tcmapiternext2(attrs)) != NULL){
+ if(*name == '\0') continue;
+ printf("\t%s\t%s", name, tcmapiterval2(name));
+ }
+ printf("\n");
+ }
+ tcmapdel(attrs);
+ }
+ } else {
+ char *dstr = tcxmlunescape(estr);
+ printf("TEXT\t%s\n", dstr);
+ free(dstr);
+ }
+ }
+ free(estr);
+ }
+ tclistdel(elems);
+ } else if(dec){
+ char *obuf = tcxmlunescape(ibuf);
+ fwrite(obuf, strlen(obuf), 1, stdout);
+ free(obuf);
+ } else {
+ char *obuf = tcxmlescape(ibuf);
+ fwrite(obuf, strlen(obuf), 1, stdout);
+ free(obuf);
+ }
+ return 0;
+}
+
+
+/* perform ucs command */
+static int procucs(const char *ibuf, int isiz, bool dec){
+ if(dec){
+ uint16_t *ary = tcmalloc(isiz + 1);
+ int anum = 0;
+ for(int i = 0; i < isiz; i += 2){
+ ary[anum++] = (((unsigned char *)ibuf)[i] << 8) + ((unsigned char *)ibuf)[i+1];
+ }
+ char *str = tcmalloc(isiz * 3 + 1);
+ tcstrucstoutf(ary, anum, str);
+ printf("%s", str);
+ free(str);
+ free(ary);
+ } else {
+ uint16_t *ary = tcmalloc(isiz * sizeof(uint16_t) + 1);
+ int anum;
+ tcstrutftoucs(ibuf, ary, &anum);
+ for(int i = 0; i < anum; i++){
+ int c = ary[i];
+ putchar(c >> 8);
+ putchar(c & 0xff);
+ }
+ free(ary);
+ }
+ return 0;
+}
+
+
+/* perform date command */
+static int procdate(const char *str, int jl, bool wf, bool rf){
+ int64_t t = str ? tcstrmktime(str) : time(NULL);
+ if(wf){
+ char buf[48];
+ tcdatestrwww(t, jl, buf);
+ printf("%s\n", buf);
+ } else if(rf){
+ char buf[48];
+ tcdatestrhttp(t, jl, buf);
+ printf("%s\n", buf);
+ } else {
+ printf("%lld\n", (long long int)t);
+ }
+ return 0;
+}
+
+
+/* perform conf command */
+static int procconf(int mode){
+ switch(mode){
+ case 'v':
+ printf("%s\n", tcversion);
+ break;
+ case 'i':
+ printf("%s\n", _TC_APPINC);
+ break;
+ case 'l':
+ printf("%s\n", _TC_APPLIBS);
+ break;
+ case 'p':
+ printf("%s\n", _TC_BINDIR);
+ break;
+ default:
+ printf("myconf(version): %s\n", tcversion);
+ printf("myconf(libver): %d\n", _TC_LIBVER);
+ printf("myconf(formatver): %s\n", _TC_FORMATVER);
+ printf("myconf(prefix): %s\n", _TC_PREFIX);
+ printf("myconf(includedir): %s\n", _TC_INCLUDEDIR);
+ printf("myconf(libdir): %s\n", _TC_LIBDIR);
+ printf("myconf(bindir): %s\n", _TC_BINDIR);
+ printf("myconf(libexecdir): %s\n", _TC_LIBEXECDIR);
+ printf("myconf(appinc): %s\n", _TC_APPINC);
+ printf("myconf(applibs): %s\n", _TC_APPLIBS);
+ printf("myconf(bigend): %d\n", TCBIGEND);
+ printf("myconf(usezlib): %d\n", TCUSEZLIB);
+ printf("sizeof(bool): %d\n", sizeof(bool));
+ printf("sizeof(char): %d\n", sizeof(char));
+ printf("sizeof(short): %d\n", sizeof(short));
+ printf("sizeof(int): %d\n", sizeof(int));
+ printf("sizeof(long): %d\n", sizeof(long));
+ printf("sizeof(long long): %d\n", sizeof(long long));
+ printf("sizeof(float): %d\n", sizeof(float));
+ printf("sizeof(double): %d\n", sizeof(double));
+ printf("sizeof(long double): %d\n", sizeof(long double));
+ printf("sizeof(size_t): %d\n", sizeof(size_t));
+ printf("sizeof(time_t): %d\n", sizeof(time_t));
+ printf("sizeof(off_t): %d\n", sizeof(off_t));
+ printf("sizeof(ino_t): %d\n", sizeof(ino_t));
+ printf("sizeof(intptr_t): %d\n", sizeof(intptr_t));
+ printf("sizeof(void *): %d\n", sizeof(void *));
+ printf("macro(CHAR_MAX): %llu\n", (unsigned long long)CHAR_MAX);
+ printf("macro(SHRT_MAX): %llu\n", (unsigned long long)SHRT_MAX);
+ printf("macro(INT_MAX): %llu\n", (unsigned long long)INT_MAX);
+ printf("macro(LONG_MAX): %llu\n", (unsigned long long)LONG_MAX);
+ printf("macro(LLONG_MAX): %llu\n", (unsigned long long)LLONG_MAX);
+ printf("macro(PATH_MAX): %llu\n", (unsigned long long)PATH_MAX);
+ printf("macro(RAND_MAX): %llu\n", (unsigned long long)RAND_MAX);
+ printf("sysconf(_SC_CLK_TCK): %ld\n", sysconf(_SC_CLK_TCK));
+ printf("sysconf(_SC_OPEN_MAX): %ld\n", sysconf(_SC_OPEN_MAX));
+ printf("sysconf(_SC_PAGESIZE): %ld\n", sysconf(_SC_PAGESIZE));
+ struct stat sbuf;
+ if(stat(MYCDIRSTR, &sbuf) == 0){
+ printf("stat(st_uid): %d\n", (int)sbuf.st_uid);
+ printf("stat(st_gid): %d\n", (int)sbuf.st_gid);
+ printf("stat(st_blksize): %d\n", (int)sbuf.st_blksize);
+ }
+ }
+ return 0;
+}
+
+
+
+// END OF FILE
--- /dev/null
+/*************************************************************************************************
+ * The test cases of the on-memory database API
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include "myconf.h"
+
+#define RECBUFSIZ 32 // buffer for records
+
+typedef struct { // type of structure for combo thread
+ TCMDB *mdb;
+ int rnum;
+ bool rnd;
+ int id;
+} TARGCOMBO;
+
+typedef struct { // type of structure for typical thread
+ TCMDB *mdb;
+ int rnum;
+ bool nc;
+ int rratio;
+ int id;
+} TARGTYPICAL;
+
+
+/* global variables */
+const char *g_progname; // program name
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void iprintf(const char *format, ...);
+static void eprint(const char *func);
+static int myrand(int range);
+static int myrandnd(int range);
+static int runcombo(int argc, char **argv);
+static int runtypical(int argc, char **argv);
+static int proccombo(int tnum, int rnum, int bnum, bool rnd);
+static int proctypical(int tnum, int rnum, int bnum, bool nc, int rratio);
+static void *threadwrite(void *targ);
+static void *threadread(void *targ);
+static void *threadremove(void *targ);
+static void *threadtypical(void *targ);
+
+
+/* main routine */
+int main(int argc, char **argv){
+ g_progname = argv[0];
+ srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+ if(argc < 2) usage();
+ int rv = 0;
+ if(!strcmp(argv[1], "combo")){
+ rv = runcombo(argc, argv);
+ } else if(!strcmp(argv[1], "typical")){
+ rv = runtypical(argc, argv);
+ } else {
+ usage();
+ }
+ return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+ fprintf(stderr, "%s: test cases of the on-memory database API of Tokyo Cabinet\n", g_progname);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, " %s combo [-rnd] tnum rnum [bnum]\n", g_progname);
+ fprintf(stderr, " %s typical [-nc] [-rr num] tnum rnum [bnum]\n", g_progname);
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+
+/* print formatted information string and flush the buffer */
+static void iprintf(const char *format, ...){
+ va_list ap;
+ va_start(ap, format);
+ vprintf(format, ap);
+ fflush(stdout);
+ va_end(ap);
+}
+
+
+/* print error message of on-memory database */
+static void eprint(const char *func){
+ fprintf(stderr, "%s: %s: error\n", g_progname, func);
+}
+
+
+/* get a random number */
+static int myrand(int range){
+ return (int)((double)range * rand() / (RAND_MAX + 1.0));
+}
+
+
+/* get a random number based on normal distribution */
+static int myrandnd(int range){
+ int num = (int)tcdrandnd(range >> 1, range / 10);
+ return (num < 0 || num >= range) ? 0 : num;
+}
+
+
+/* parse arguments of combo command */
+static int runcombo(int argc, char **argv){
+ char *tstr = NULL;
+ char *rstr = NULL;
+ char *bstr = NULL;
+ bool rnd = false;
+ for(int i = 2; i < argc; i++){
+ if(!tstr && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-rnd")){
+ rnd = true;
+ } else {
+ usage();
+ }
+ } else if(!tstr){
+ tstr = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else if(!bstr){
+ bstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!tstr || !rstr) usage();
+ int tnum = atoi(tstr);
+ int rnum = atoi(rstr);
+ if(tnum < 1 || rnum < 1) usage();
+ int bnum = bstr ? atoi(bstr) : -1;
+ int rv = proccombo(tnum, rnum, bnum, rnd);
+ return rv;
+}
+
+
+/* parse arguments of typical command */
+static int runtypical(int argc, char **argv){
+ char *tstr = NULL;
+ char *rstr = NULL;
+ char *bstr = NULL;
+ int rratio = -1;
+ bool nc = false;
+ for(int i = 2; i < argc; i++){
+ if(!tstr && argv[i][0] == '-'){
+ if(!strcmp(argv[i], "-nc")){
+ nc = true;
+ } else if(!strcmp(argv[i], "-rr")){
+ if(++i >= argc) usage();
+ rratio = atoi(argv[i]);
+ } else {
+ usage();
+ }
+ } else if(!tstr){
+ tstr = argv[i];
+ } else if(!rstr){
+ rstr = argv[i];
+ } else if(!bstr){
+ bstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!tstr || !rstr) usage();
+ int tnum = atoi(tstr);
+ int rnum = atoi(rstr);
+ if(tnum < 1 || rnum < 1) usage();
+ int bnum = bstr ? atoi(bstr) : -1;
+ int rv = proctypical(tnum, rnum, bnum, nc, rratio);
+ return rv;
+}
+
+
+/* perform combo command */
+static int proccombo(int tnum, int rnum, int bnum, bool rnd){
+ iprintf("<Combination Test>\n tnum=%d rnum=%d bnum=%d rnd=%d\n\n", tnum, rnum, bnum, rnd);
+ bool err = false;
+ double stime = tctime();
+ TCMDB *mdb = (bnum > 0) ? tcmdbnew2(bnum) : tcmdbnew();
+ TARGCOMBO targs[tnum];
+ pthread_t threads[tnum];
+ if(tnum == 1){
+ targs[0].mdb = mdb;
+ targs[0].rnum = rnum;
+ targs[0].rnd = rnd;
+ targs[0].id = 0;
+ if(threadwrite(targs) != NULL) err = true;
+ } else {
+ for(int i = 0; i < tnum; i++){
+ targs[i].mdb = mdb;
+ targs[i].rnum = rnum;
+ targs[i].rnd = rnd;
+ targs[i].id = i;
+ if(pthread_create(threads + i, NULL, threadwrite, targs + i) != 0){
+ eprint("pthread_create");
+ targs[i].id = -1;
+ err = true;
+ }
+ }
+ for(int i = 0; i < tnum; i++){
+ if(targs[i].id == -1) continue;
+ void *rv;
+ if(pthread_join(threads[i], &rv) != 0){
+ eprint("pthread_join");
+ err = true;
+ } else if(rv){
+ err = true;
+ }
+ }
+ }
+ if(tnum == 1){
+ targs[0].mdb = mdb;
+ targs[0].rnum = rnum;
+ targs[0].rnd = rnd;
+ targs[0].id = 0;
+ if(threadread(targs) != NULL) err = true;
+ } else {
+ for(int i = 0; i < tnum; i++){
+ targs[i].mdb = mdb;
+ targs[i].rnum = rnum;
+ targs[i].rnd = rnd;
+ targs[i].id = i;
+ if(pthread_create(threads + i, NULL, threadread, targs + i) != 0){
+ eprint("pthread_create");
+ targs[i].id = -1;
+ err = true;
+ }
+ }
+ for(int i = 0; i < tnum; i++){
+ if(targs[i].id == -1) continue;
+ void *rv;
+ if(pthread_join(threads[i], &rv) != 0){
+ eprint("pthread_join");
+ err = true;
+ } else if(rv){
+ err = true;
+ }
+ }
+ }
+ if(tnum == 1){
+ targs[0].mdb = mdb;
+ targs[0].rnum = rnum;
+ targs[0].rnd = rnd;
+ targs[0].id = 0;
+ if(threadremove(targs) != NULL) err = true;
+ } else {
+ for(int i = 0; i < tnum; i++){
+ targs[i].mdb = mdb;
+ targs[i].rnum = rnum;
+ targs[i].rnd = rnd;
+ targs[i].id = i;
+ if(pthread_create(threads + i, NULL, threadremove, targs + i) != 0){
+ eprint("pthread_create");
+ targs[i].id = -1;
+ err = true;
+ }
+ }
+ for(int i = 0; i < tnum; i++){
+ if(targs[i].id == -1) continue;
+ void *rv;
+ if(pthread_join(threads[i], &rv) != 0){
+ eprint("pthread_join");
+ err = true;
+ } else if(rv){
+ err = true;
+ }
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tcmdbrnum(mdb));
+ tcmdbdel(mdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* perform typical command */
+static int proctypical(int tnum, int rnum, int bnum, bool nc, int rratio){
+ iprintf("<Typical Access Test>\n tnum=%d rnum=%d bnum=%d nc=%d rratio=%d\n\n",
+ tnum, rnum, bnum, nc, rratio);
+ bool err = false;
+ double stime = tctime();
+ TCMDB *mdb = (bnum > 0) ? tcmdbnew2(bnum) : tcmdbnew();
+ TARGTYPICAL targs[tnum];
+ pthread_t threads[tnum];
+ if(tnum == 1){
+ targs[0].mdb = mdb;
+ targs[0].rnum = rnum;
+ targs[0].nc = nc;
+ targs[0].rratio = rratio;
+ targs[0].id = 0;
+ if(threadtypical(targs) != NULL) err = true;
+ } else {
+ for(int i = 0; i < tnum; i++){
+ targs[i].mdb = mdb;
+ targs[i].rnum = rnum;
+ targs[i].nc = nc;
+ targs[i].rratio= rratio;
+ targs[i].id = i;
+ if(pthread_create(threads + i, NULL, threadtypical, targs + i) != 0){
+ eprint("pthread_create");
+ targs[i].id = -1;
+ err = true;
+ }
+ }
+ for(int i = 0; i < tnum; i++){
+ if(targs[i].id == -1) continue;
+ void *rv;
+ if(pthread_join(threads[i], &rv) != 0){
+ eprint("pthread_join");
+ err = true;
+ } else if(rv){
+ err = true;
+ }
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tcmdbrnum(mdb));
+ tcmdbdel(mdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("%s\n\n", err ? "error" : "ok");
+ return err ? 1 : 0;
+}
+
+
+/* thread the write function */
+static void *threadwrite(void *targ){
+ TCMDB *mdb = ((TARGCOMBO *)targ)->mdb;
+ int rnum = ((TARGCOMBO *)targ)->rnum;
+ bool rnd = ((TARGCOMBO *)targ)->rnd;
+ int id = ((TARGCOMBO *)targ)->id;
+ double stime = tctime();
+ if(id == 0) iprintf("writing:\n");
+ int base = id * rnum;
+ for(int i = 1; i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len = sprintf(buf, "%08d", base + (rnd ? myrand(i) : i));
+ tcmdbput(mdb, buf, len, buf, len);
+ if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ if(id == 0) iprintf("time: %.3f\n", tctime() - stime);
+ return NULL;
+}
+
+
+/* thread the read function */
+static void *threadread(void *targ){
+ TCMDB *mdb = ((TARGCOMBO *)targ)->mdb;
+ int rnum = ((TARGCOMBO *)targ)->rnum;
+ bool rnd = ((TARGCOMBO *)targ)->rnd;
+ int id = ((TARGCOMBO *)targ)->id;
+ double stime = tctime();
+ if(id == 0) iprintf("reading:\n");
+ int base = id * rnum;
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrand(i) : i));
+ int vsiz;
+ char *vbuf = tcmdbget(mdb, kbuf, ksiz, &vsiz);
+ if(vbuf) free(vbuf);
+ if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ if(id == 0) iprintf("time: %.3f\n", tctime() - stime);
+ return NULL;
+}
+
+
+/* thread the remove function */
+static void *threadremove(void *targ){
+ TCMDB *mdb = ((TARGCOMBO *)targ)->mdb;
+ int rnum = ((TARGCOMBO *)targ)->rnum;
+ bool rnd = ((TARGCOMBO *)targ)->rnd;
+ int id = ((TARGCOMBO *)targ)->id;
+ double stime = tctime();
+ if(id == 0) iprintf("removing:\n");
+ int base = id * rnum;
+ for(int i = 1; i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len = sprintf(buf, "%08d", base + (rnd ? myrand(i) : i));
+ tcmdbout(mdb, buf, len);
+ if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ if(id == 0) iprintf("time: %.3f\n", tctime() - stime);
+ return NULL;
+}
+
+
+/* thread the typical function */
+static void *threadtypical(void *targ){
+ TCMDB *mdb = ((TARGTYPICAL *)targ)->mdb;
+ int rnum = ((TARGTYPICAL *)targ)->rnum;
+ bool nc = ((TARGTYPICAL *)targ)->nc;
+ int rratio = ((TARGTYPICAL *)targ)->rratio;
+ int id = ((TARGTYPICAL *)targ)->id;
+ bool err = false;
+ TCMAP *map = (!nc && id == 0) ? tcmapnew2(rnum + 1) : NULL;
+ int base = id * rnum;
+ int mrange = tclmax(50 + rratio, 100);
+ for(int i = 1; !err && i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len = sprintf(buf, "%08d", base + myrandnd(i));
+ int rnd = myrand(mrange);
+ if(rnd < 10){
+ tcmdbput(mdb, buf, len, buf, len);
+ if(map) tcmapput(map, buf, len, buf, len);
+ } else if(rnd < 15){
+ tcmdbputkeep(mdb, buf, len, buf, len);
+ if(map) tcmapputkeep(map, buf, len, buf, len);
+ } else if(rnd < 20){
+ tcmdbputcat(mdb, buf, len, buf, len);
+ if(map) tcmapputcat(map, buf, len, buf, len);
+ } else if(rnd < 30){
+ tcmdbout(mdb, buf, len);
+ if(map) tcmapout(map, buf, len);
+ } else if(rnd < 31){
+ if(myrand(10) == 0) tcmdbiterinit(mdb);
+ for(int j = 0; !err && j < 10; j++){
+ int ksiz;
+ char *kbuf = tcmdbiternext(mdb, &ksiz);
+ if(kbuf) free(kbuf);
+ }
+ } else {
+ int vsiz;
+ char *vbuf = tcmdbget(mdb, buf, len, &vsiz);
+ if(vbuf){
+ if(map){
+ int msiz;
+ const char *mbuf = tcmapget(map, buf, len, &msiz);
+ if(msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){
+ eprint("(validation)");
+ err = true;
+ }
+ }
+ free(vbuf);
+ } else {
+ if(map && tcmapget(map, buf, len, &vsiz)){
+ eprint("(validation)");
+ err = true;
+ }
+ }
+ }
+ if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ if(map){
+ tcmapiterinit(map);
+ int ksiz;
+ const char *kbuf;
+ while(!err && (kbuf = tcmapiternext(map, &ksiz)) != NULL){
+ int vsiz;
+ char *vbuf = tcmdbget(mdb, kbuf, ksiz, &vsiz);
+ if(vbuf){
+ int msiz;
+ const char *mbuf = tcmapget(map, kbuf, ksiz, &msiz);
+ if(!mbuf || msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){
+ eprint("(validation)");
+ err = true;
+ }
+ free(vbuf);
+ } else {
+ eprint("(validation)");
+ err = true;
+ }
+ }
+ tcmapdel(map);
+ }
+ return err ? "error" : NULL;
+}
+
+
+
+// END OF FILE
--- /dev/null
+/*************************************************************************************************
+ * The test cases of the utility API
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include <tcutil.h>
+#include "myconf.h"
+
+#define RECBUFSIZ 32 // buffer for records
+
+
+/* global variables */
+const char *g_progname; // program name
+
+
+/* function prototypes */
+int main(int argc, char **argv);
+static void usage(void);
+static void iprintf(const char *format, ...);
+static int myrand(int range);
+static int runxstr(int argc, char **argv);
+static int runlist(int argc, char **argv);
+static int runmap(int argc, char **argv);
+static int runmdb(int argc, char **argv);
+static int runmisc(int argc, char **argv);
+static int runwicked(int argc, char **argv);
+static int procxstr(int rnum);
+static int proclist(int rnum, int anum);
+static int procmap(int rnum, int bnum);
+static int procmdb(int rnum, int bnum);
+static int procmisc(int rnum);
+static int procwicked(int rnum);
+
+
+/* main routine */
+int main(int argc, char **argv){
+ g_progname = argv[0];
+ srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+ if(argc < 2) usage();
+ int rv = 0;
+ if(!strcmp(argv[1], "xstr")){
+ rv = runxstr(argc, argv);
+ } else if(!strcmp(argv[1], "list")){
+ rv = runlist(argc, argv);
+ } else if(!strcmp(argv[1], "map")){
+ rv = runmap(argc, argv);
+ } else if(!strcmp(argv[1], "mdb")){
+ rv = runmdb(argc, argv);
+ } else if(!strcmp(argv[1], "misc")){
+ rv = runmisc(argc, argv);
+ } else if(!strcmp(argv[1], "wicked")){
+ rv = runwicked(argc, argv);
+ } else {
+ usage();
+ }
+ return rv;
+}
+
+
+/* print the usage and exit */
+static void usage(void){
+ fprintf(stderr, "%s: test cases of the utility API of Tokyo Cabinet\n", g_progname);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, " %s xstr rnum\n", g_progname);
+ fprintf(stderr, " %s list rnum [anum]\n", g_progname);
+ fprintf(stderr, " %s map rnum [bnum]\n", g_progname);
+ fprintf(stderr, " %s mdb rnum [bnum]\n", g_progname);
+ fprintf(stderr, " %s misc rnum\n", g_progname);
+ fprintf(stderr, " %s wicked rnum\n", g_progname);
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+
+/* print formatted information string and flush the buffer */
+static void iprintf(const char *format, ...){
+ va_list ap;
+ va_start(ap, format);
+ vprintf(format, ap);
+ fflush(stdout);
+ va_end(ap);
+}
+
+
+/* get a random number */
+static int myrand(int range){
+ return (int)((double)range * rand() / (RAND_MAX + 1.0));
+}
+
+
+/* parse arguments of xstr command */
+static int runxstr(int argc, char **argv){
+ char *rstr = NULL;
+ for(int i = 2; i < argc; i++){
+ if(!rstr && argv[i][0] == '-'){
+ usage();
+ } else if(!rstr){
+ rstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!rstr) usage();
+ int rnum = atoi(rstr);
+ if(rnum < 1) usage();
+ int rv = procxstr(rnum);
+ return rv;
+}
+
+
+/* parse arguments of list command */
+static int runlist(int argc, char **argv){
+ char *rstr = NULL;
+ char *astr = NULL;
+ for(int i = 2; i < argc; i++){
+ if(!rstr && argv[i][0] == '-'){
+ usage();
+ } else if(!rstr){
+ rstr = argv[i];
+ } else if(!astr){
+ astr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!rstr) usage();
+ int rnum = atoi(rstr);
+ if(rnum < 1) usage();
+ int anum = astr ? atoi(astr) : -1;
+ int rv = proclist(rnum, anum);
+ return rv;
+}
+
+
+/* parse arguments of map command */
+static int runmap(int argc, char **argv){
+ char *rstr = NULL;
+ char *bstr = NULL;
+ for(int i = 2; i < argc; i++){
+ if(!rstr && argv[i][0] == '-'){
+ usage();
+ } else if(!rstr){
+ rstr = argv[i];
+ } else if(!bstr){
+ bstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!rstr) usage();
+ int rnum = atoi(rstr);
+ if(rnum < 1) usage();
+ int bnum = bstr ? atoi(bstr) : -1;
+ int rv = procmap(rnum, bnum);
+ return rv;
+}
+
+
+/* parse arguments of mdb command */
+static int runmdb(int argc, char **argv){
+ char *rstr = NULL;
+ char *bstr = NULL;
+ for(int i = 2; i < argc; i++){
+ if(!rstr && argv[i][0] == '-'){
+ usage();
+ } else if(!rstr){
+ rstr = argv[i];
+ } else if(!bstr){
+ bstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!rstr) usage();
+ int rnum = atoi(rstr);
+ if(rnum < 1) usage();
+ int bnum = bstr ? atoi(bstr) : -1;
+ int rv = procmdb(rnum, bnum);
+ return rv;
+}
+
+
+/* parse arguments of misc command */
+static int runmisc(int argc, char **argv){
+ char *rstr = NULL;
+ for(int i = 2; i < argc; i++){
+ if(!rstr && argv[i][0] == '-'){
+ usage();
+ } else if(!rstr){
+ rstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!rstr) usage();
+ int rnum = atoi(rstr);
+ if(rnum < 1) usage();
+ int rv = procmisc(rnum);
+ return rv;
+}
+
+
+/* parse arguments of wicked command */
+static int runwicked(int argc, char **argv){
+ char *rstr = NULL;
+ for(int i = 2; i < argc; i++){
+ if(!rstr && argv[i][0] == '-'){
+ usage();
+ } else if(!rstr){
+ rstr = argv[i];
+ } else {
+ usage();
+ }
+ }
+ if(!rstr) usage();
+ int rnum = atoi(rstr);
+ if(rnum < 1) usage();
+ int rv = procwicked(rnum);
+ return rv;
+}
+
+
+/* perform xstr command */
+static int procxstr(int rnum){
+ iprintf("<Extensible String Writing Test>\n rnum=%d\n\n", rnum);
+ double stime = tctime();
+ TCXSTR *xstr = tcxstrnew();
+ for(int i = 1; i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len = sprintf(buf, "%08d", i);
+ tcxstrcat(xstr, buf, len);
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ tcxstrdel(xstr);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("ok\n\n");
+ return 0;
+}
+
+
+/* perform list command */
+static int proclist(int rnum, int anum){
+ iprintf("<List Writing Test>\n rnum=%d anum=%d\n\n", rnum, anum);
+ double stime = tctime();
+ TCLIST *list = (anum > 0) ? tclistnew2(anum) : tclistnew();
+ for(int i = 1; i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len = sprintf(buf, "%08d", i);
+ tclistpush(list, buf, len);
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ tclistdel(list);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("ok\n\n");
+ return 0;
+}
+
+
+/* perform map command */
+static int procmap(int rnum, int bnum){
+ iprintf("<Map Writing Test>\n rnum=%d\n\n", rnum);
+ double stime = tctime();
+ TCMAP *map = (bnum > 0) ? tcmapnew2(bnum) : tcmapnew();
+ for(int i = 1; i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len = sprintf(buf, "%08d", i);
+ tcmapput(map, buf, len, buf, len);
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ tcmapdel(map);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("ok\n\n");
+ return 0;
+}
+
+
+/* perform mdb command */
+static int procmdb(int rnum, int bnum){
+ iprintf("<On-memory Database Writing Test>\n rnum=%d\n\n", rnum);
+ double stime = tctime();
+ TCMDB *mdb = (bnum > 0) ? tcmdbnew2(bnum) : tcmdbnew();
+ for(int i = 1; i <= rnum; i++){
+ char buf[RECBUFSIZ];
+ int len = sprintf(buf, "%08d", i);
+ tcmdbput(mdb, buf, len, buf, len);
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("record number: %llu\n", (unsigned long long)tcmdbrnum(mdb));
+ iprintf("size: %llu\n", (unsigned long long)tcmdbmsiz(mdb));
+ tcmdbdel(mdb);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("ok\n\n");
+ return 0;
+}
+
+
+/* perform misc command */
+static int procmisc(int rnum){
+ iprintf("<Miscellaneous Test>\n rnum=%d\n\n", rnum);
+ double stime = tctime();
+ bool err = false;
+ for(int i = 1; i <= rnum && !err; i++){
+ const char *str = "5%2+3-1=4 \"Yes/No\" <a&b>";
+ int slen = strlen(str);
+ char *buf, *dec;
+ int bsiz, dsiz, jl;
+ time_t date, ddate;
+ TCXSTR *xstr;
+ TCLIST *list;
+ TCMAP *map;
+ buf = tcmemdup(str, slen);
+ xstr = tcxstrfrommalloc(buf, slen);
+ buf = tcxstrtomalloc(xstr);
+ if(strcmp(buf, str)) err = true;
+ free(buf);
+ if(tclmax(1, 2) != 2) err = true;
+ if(tclmin(1, 2) != 1) err = true;
+ tclrand();
+ if(tcdrand() >= 1.0) err = true;
+ tcdrandnd(50, 10);
+ if(tcstricmp("abc", "ABC") != 0) err = true;
+ if(!tcstrfwm("abc", "ab") || !tcstrifwm("abc", "AB")) err = true;
+ if(!tcstrbwm("abc", "bc") || !tcstribwm("abc", "BC")) err = true;
+ if(tcstrdist("abcde", "abdfgh") != 4 || tcstrdist("abdfgh", "abcde") != 4) err = true;
+ if(tcstrdistutf("abcde", "abdfgh") != 4 || tcstrdistutf("abdfgh", "abcde") != 4) err = true;
+ buf = tcmemdup("abcde", 5);
+ tcstrtoupper(buf);
+ if(strcmp(buf, "ABCDE")) err = true;
+ tcstrtolower(buf);
+ if(strcmp(buf, "abcde")) err = true;
+ free(buf);
+ buf = tcmemdup(" ab cd ", 10);
+ tcstrtrim(buf);
+ if(strcmp(buf, "ab cd")) err = true;
+ tcstrsqzspc(buf);
+ if(strcmp(buf, "ab cd")) err = true;
+ tcstrsubchr(buf, "cd", "C");
+ if(strcmp(buf, "ab C")) err = true;
+ if(tcstrcntutf(buf) != 4) err = true;
+ tcstrcututf(buf, 2);
+ if(strcmp(buf, "ab")) err = true;
+ free(buf);
+ if(i % 10 == 1){
+ int anum = myrand(30);
+ uint16_t ary[anum+1];
+ for(int j = 0; j < anum; j++){
+ ary[j] = myrand(65535) + 1;
+ }
+ char ustr[anum*3+1];
+ tcstrucstoutf(ary, anum, ustr);
+ uint16_t dary[anum+1];
+ int danum;
+ tcstrutftoucs(ustr, dary, &danum);
+ if(danum != anum){
+ err = true;
+ } else {
+ for(int j = 0; j < danum; j++){
+ if(dary[j] != dary[j]) err = true;
+ }
+ }
+ list = tcstrsplit(",a,b..c,d,", ",.");
+ if(tclistnum(list) != 7) err = true;
+ buf = tcstrjoin(list, ':');
+ if(strcmp(buf, ":a:b::c:d:")) err = true;
+ free(buf);
+ tclistdel(list);
+ if(!tcregexmatch("ABCDEFGHI", "*(b)c[d-f]*g(h)")) err = true;
+ buf = tcregexreplace("ABCDEFGHI", "*(b)c[d-f]*g(h)", "[\\1][\\2][&]");
+ if(strcmp(buf, "A[B][H][BCDEFGH]I")) err = true;
+ free(buf);
+ }
+ buf = tcmalloc(48);
+ date = myrand(INT_MAX - 1000000);
+ jl = 3600 * (myrand(23) - 11);
+ tcdatestrwww(date, jl, buf);
+ ddate = tcstrmktime(buf);
+ if(ddate != date) err = true;
+ tcdatestrhttp(date, jl, buf);
+ ddate = tcstrmktime(buf);
+ if(ddate != date) err = true;
+ free(buf);
+ if(i % 100){
+ map = tcmapnew();
+ for(int j = 0; j < 10; j++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", myrand(10));
+ tcmapaddint(map, kbuf, ksiz, 1);
+ const char *vbuf = tcmapget2(map, kbuf);
+ if(*(int *)vbuf < 1) err = true;
+ }
+ tcmapdel(map);
+ }
+ buf = tcurlencode(str, slen);
+ if(strcmp(buf, "5%252%2B3-1%3D4%20%22Yes%2FNo%22%20%3Ca%26b%3E")) err = true;
+ dec = tcurldecode(buf, &dsiz);
+ if(dsiz != slen || strcmp(dec, str)) err = true;
+ free(dec);
+ free(buf);
+ if(i % 10 == 1){
+ map = tcurlbreak("http://mikio:oikim@estraier.net:1978/foo/bar/baz.cgi?ab=cd&ef=jkl#quux");
+ const char *elem;
+ if(!(elem = tcmapget2(map, "self")) ||
+ strcmp(elem, "http://mikio:oikim@estraier.net:1978/foo/bar/baz.cgi?ab=cd&ef=jkl#quux"))
+ err = true;
+ if(!(elem = tcmapget2(map, "scheme")) || strcmp(elem, "http")) err = true;
+ if(!(elem = tcmapget2(map, "host")) || strcmp(elem, "estraier.net")) err = true;
+ if(!(elem = tcmapget2(map, "port")) || strcmp(elem, "1978")) err = true;
+ if(!(elem = tcmapget2(map, "authority")) || strcmp(elem, "mikio:oikim")) err = true;
+ if(!(elem = tcmapget2(map, "path")) || strcmp(elem, "/foo/bar/baz.cgi")) err = true;
+ if(!(elem = tcmapget2(map, "file")) || strcmp(elem, "baz.cgi")) err = true;
+ if(!(elem = tcmapget2(map, "query")) || strcmp(elem, "ab=cd&ef=jkl")) err = true;
+ if(!(elem = tcmapget2(map, "fragment")) || strcmp(elem, "quux")) err = true;
+ tcmapdel(map);
+ buf = tcurlresolve("http://a:b@c.d:1/e/f/g.h?i=j#k", "http://A:B@C.D:1/E/F/G.H?I=J#K");
+ if(strcmp(buf, "http://A:B@c.d:1/E/F/G.H?I=J#K")) err = true;
+ free(buf);
+ buf = tcurlresolve("http://a:b@c.d:1/e/f/g.h?i=j#k", "/E/F/G.H?I=J#K");
+ if(strcmp(buf, "http://a:b@c.d:1/E/F/G.H?I=J#K")) err = true;
+ free(buf);
+ buf = tcurlresolve("http://a:b@c.d:1/e/f/g.h?i=j#k", "G.H?I=J#K");
+ if(strcmp(buf, "http://a:b@c.d:1/e/f/G.H?I=J#K")) err = true;
+ free(buf);
+ buf = tcurlresolve("http://a:b@c.d:1/e/f/g.h?i=j#k", "?I=J#K");
+ if(strcmp(buf, "http://a:b@c.d:1/e/f/g.h?I=J#K")) err = true;
+ free(buf);
+ buf = tcurlresolve("http://a:b@c.d:1/e/f/g.h?i=j#k", "#K");
+ if(strcmp(buf, "http://a:b@c.d:1/e/f/g.h?i=j#K")) err = true;
+ free(buf);
+ }
+ buf = tcbaseencode(str, slen);
+ if(strcmp(buf, "NSUyKzMtMT00ICJZZXMvTm8iIDxhJmI+")) err = true;
+ dec = tcbasedecode(buf, &dsiz);
+ if(dsiz != slen || strcmp(dec, str)) err = true;
+ free(dec);
+ free(buf);
+ buf = tcquoteencode(str, slen);
+ if(strcmp(buf, "5%2+3-1=3D4 \"Yes/No\" <a&b>")) err = true;
+ dec = tcquotedecode(buf, &dsiz);
+ if(dsiz != slen || strcmp(dec, str)) err = true;
+ free(dec);
+ free(buf);
+ buf = tcmimeencode(str, "UTF-8", true);
+ if(strcmp(buf, "=?UTF-8?B?NSUyKzMtMT00ICJZZXMvTm8iIDxhJmI+?=")) err = true;
+ char encname[32];
+ dec = tcmimedecode(buf, encname);
+ if(strcmp(dec, str) || strcmp(encname, "UTF-8")) err = true;
+ free(dec);
+ free(buf);
+ buf = tcpackencode(str, slen, &bsiz);
+ dec = tcpackdecode(buf, bsiz, &dsiz);
+ if(dsiz != slen || strcmp(dec, str)) err = true;
+ free(dec);
+ free(buf);
+ buf = tcbsencode(str, slen, &bsiz);
+ dec = tcbsdecode(buf, bsiz, &dsiz);
+ if(dsiz != slen || strcmp(dec, str)) err = true;
+ free(dec);
+ free(buf);
+ int idx;
+ buf = tcbwtencode(str, slen, &idx);
+ if(memcmp(buf, "4\"o 5a23s-%+=> 1b/\"<&YNe", slen) || idx != 13) err = true;
+ dec = tcbwtdecode(buf, slen, idx);
+ if(memcmp(dec, str, slen)) err = true;
+ free(dec);
+ free(buf);
+ if(_tc_deflate){
+ if((buf = tcdeflate(str, slen, &bsiz)) != NULL){
+ if((dec = tcinflate(buf, bsiz, &dsiz)) != NULL){
+ if(slen != dsiz || memcmp(str, dec, dsiz)) err = true;
+ free(dec);
+ } else {
+ err = true;
+ }
+ free(buf);
+ } else {
+ err = true;
+ }
+ if((buf = tcgzipencode(str, slen, &bsiz)) != NULL){
+ if((dec = tcgzipdecode(buf, bsiz, &dsiz)) != NULL){
+ if(slen != dsiz || memcmp(str, dec, dsiz)) err = true;
+ free(dec);
+ } else {
+ err = true;
+ }
+ free(buf);
+ } else {
+ err = true;
+ }
+ if(tcgetcrc("hoge", 4) % 10000 != 7034) err = true;
+ }
+ int anum = myrand(50)+1;
+ unsigned int ary[anum];
+ for(int j = 0; j < anum; j++){
+ ary[j] = myrand(INT_MAX);
+ }
+ buf = tcberencode(ary, anum, &bsiz);
+ int dnum;
+ unsigned int *dary = tcberdecode(buf, bsiz, &dnum);
+ if(anum != dnum || memcmp(ary, dary, sizeof(*dary) * dnum)) err = true;
+ free(dary);
+ free(buf);
+ buf = tcxmlescape(str);
+ if(strcmp(buf, "5%2+3-1=4 "Yes/No" <a&b>")) err = true;
+ dec = tcxmlunescape(buf);
+ if(strcmp(dec, str)) err = true;
+ free(dec);
+ free(buf);
+ if(i % 10 == 1){
+ list = tcxmlbreak("<abc de=\"foo&\" gh='<bar>'>xyz<br>\na<!--<mikio>--></abc>");
+ for(int j = 0; j < tclistnum(list); j++){
+ const char *elem = tclistval2(list, j);
+ TCMAP *attrs = tcxmlattrs(elem);
+ tcmapdel(attrs);
+ }
+ tclistdel(list);
+ }
+ if(i % 10 == 1){
+ for(int16_t j = 1; j <= 0x2000; j *= 2){
+ for(int16_t num = j - 1; num <= j + 1; num++){
+ int16_t nnum = TCHTOIS(num);
+ if(num != TCITOHS(nnum)) err = true;
+ }
+ }
+ for(int32_t j = 1; j <= 0x20000000; j *= 2){
+ for(int32_t num = j - 1; num <= j + 1; num++){
+ int32_t nnum = TCHTOIL(num);
+ if(num != TCITOHL(nnum)) err = true;
+ char buf[TCNUMBUFSIZ];
+ int step, nstep;
+ TCSETVNUMBUF(step, buf, num);
+ TCREADVNUMBUF(buf, nnum, nstep);
+ if(num != nnum || step != nstep) err = true;
+ }
+ }
+ for(int64_t j = 1; j <= 0x2000000000000000; j *= 2){
+ for(int64_t num = j - 1; num <= j + 1; num++){
+ int64_t nnum = TCHTOILL(num);
+ if(num != TCITOHLL(nnum)) err = true;
+ char buf[TCNUMBUFSIZ];
+ int step, nstep;
+ TCSETVNUMBUF64(step, buf, num);
+ TCREADVNUMBUF64(buf, nnum, nstep);
+ if(num != nnum || step != nstep) err = true;
+ }
+ }
+ char *bitmap = TCBITMAPNEW(100);
+ for(int j = 0; j < 100; j++){
+ if(j % 3 == 0) TCBITMAPON(bitmap, j);
+ if(j % 5 == 0) TCBITMAPOFF(bitmap, j);
+ }
+ for(int j = 0; j < 100; j++){
+ if(j % 5 == 0){
+ if(TCBITMAPCHECK(bitmap, j)) err = true;
+ } else if(j % 3 == 0){
+ if(!TCBITMAPCHECK(bitmap, j)) err = true;
+ }
+ }
+ TCBITMAPDEL(bitmap);
+ buf = tcmalloc(i / 8 + 2);
+ TCBITSTRM strm;
+ TCBITSTRMINITW(strm, buf);
+ for(int j = 0; j < i; j++){
+ int sign = j % 3 == 0 || j % 7 == 0;
+ TCBITSTRMCAT(strm, sign);
+ }
+ TCBITSTRMSETEND(strm);
+ int bnum = TCBITSTRMNUM(strm);
+ if(bnum != i) err = true;
+ TCBITSTRMINITR(strm, buf, bsiz);
+ for(int j = 0; j < i; j++){
+ int sign;
+ TCBITSTRMREAD(strm, sign);
+ if(sign != (j % 3 == 0 || j % 7 == 0)) err = true;
+ }
+ free(buf);
+ }
+ if(rnum > 250 && i % (rnum / 250) == 0){
+ putchar('.');
+ fflush(stdout);
+ if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i);
+ }
+ }
+ iprintf("time: %.3f\n", tctime() - stime);
+ if(err){
+ iprintf("error\n\n");
+ return 1;
+ }
+ iprintf("ok\n\n");
+ return 0;
+}
+
+
+/* perform wicked command */
+static int procwicked(int rnum){
+ iprintf("<Wicked Writing Test>\n rnum=%d\n\n", rnum);
+ double stime = tctime();
+ TCMPOOL *mpool = tcmpoolglobal();
+ TCXSTR *xstr = myrand(2) > 0 ? tcxstrnew() : tcxstrnew2("hello world");
+ tcmpoolputxstr(mpool, xstr);
+ TCLIST *list = myrand(2) > 0 ? tclistnew() : tclistnew2(myrand(rnum) + rnum / 2);
+ tcmpoolputlist(mpool, list);
+ TCMAP *map = myrand(2) > 0 ? tcmapnew() : tcmapnew2(myrand(rnum) + rnum / 2);
+ tcmpoolputmap(mpool, map);
+ TCMDB *mdb = myrand(2) > 0 ? tcmdbnew() : tcmdbnew2(myrand(rnum) + rnum / 2);
+ tcmpoolput(mpool, mdb, (void (*)(void*))tcmdbdel);
+ for(int i = 1; i <= rnum; i++){
+ char kbuf[RECBUFSIZ];
+ int ksiz = sprintf(kbuf, "%d", myrand(i));
+ char vbuf[RECBUFSIZ];
+ int vsiz = sprintf(vbuf, "%d", myrand(i));
+ char *tmp;
+ switch(myrand(69)){
+ case 0:
+ putchar('0');
+ tcxstrcat(xstr, kbuf, ksiz);
+ break;
+ case 1:
+ putchar('1');
+ tcxstrcat2(xstr, kbuf);
+ break;
+ case 2:
+ putchar('2');
+ if(myrand(rnum / 100 + 1) == 0) tcxstrclear(xstr);
+ break;
+ case 3:
+ putchar('3');
+ tcxstrprintf(xstr, "[%s:%d]", kbuf, i);
+ break;
+ case 4:
+ putchar('4');
+ tclistpush(list, kbuf, ksiz);
+ break;
+ case 5:
+ putchar('5');
+ tclistpush2(list, kbuf);
+ break;
+ case 6:
+ putchar('6');
+ tmp = tcmemdup(kbuf, ksiz);
+ tclistpushmalloc(list, tmp, strlen(tmp));
+ break;
+ case 7:
+ putchar('7');
+ if(myrand(10) == 0) free(tclistpop(list, &ksiz));
+ break;
+ case 8:
+ putchar('8');
+ if(myrand(10) == 0) free(tclistpop2(list));
+ break;
+ case 9:
+ putchar('9');
+ tclistunshift(list, kbuf, ksiz);
+ break;
+ case 10:
+ putchar('A');
+ tclistunshift2(list, kbuf);
+ break;
+ case 11:
+ putchar('B');
+ if(myrand(10) == 0) free(tclistshift(list, &ksiz));
+ break;
+ case 12:
+ putchar('C');
+ if(myrand(10) == 0) free(tclistshift2(list));
+ break;
+ case 13:
+ putchar('D');
+ tclistinsert(list, i / 10, kbuf, ksiz);
+ break;
+ case 14:
+ putchar('E');
+ tclistinsert2(list, i / 10, kbuf);
+ break;
+ case 15:
+ putchar('F');
+ if(myrand(10) == 0) free(tclistremove(list, i / 10, &ksiz));
+ break;
+ case 16:
+ putchar('G');
+ if(myrand(10) == 0) free(tclistremove2(list, i / 10));
+ break;
+ case 17:
+ putchar('H');
+ tclistover(list, i / 10, kbuf, ksiz);
+ break;
+ case 18:
+ putchar('I');
+ tclistover2(list, i / 10, kbuf);
+ break;
+ case 19:
+ putchar('J');
+ if(myrand(rnum / 1000 + 1) == 0) tclistsort(list);
+ break;
+ case 20:
+ putchar('K');
+ if(myrand(rnum / 1000 + 1) == 0) tclistsortci(list);
+ break;
+ case 21:
+ putchar('L');
+ if(myrand(rnum / 1000 + 1) == 0) tclistlsearch(list, kbuf, ksiz);
+ break;
+ case 22:
+ putchar('M');
+ if(myrand(rnum / 1000 + 1) == 0) tclistbsearch(list, kbuf, ksiz);
+ break;
+ case 23:
+ putchar('N');
+ if(myrand(rnum / 100 + 1) == 0) tclistclear(list);
+ break;
+ case 24:
+ putchar('O');
+ if(myrand(rnum / 100 + 1) == 0){
+ int dsiz;
+ char *dbuf = tclistdump(list, &dsiz);
+ tclistdel(tclistload(dbuf, dsiz));
+ free(dbuf);
+ }
+ break;
+ case 25:
+ putchar('P');
+ if(myrand(100) == 0){
+ if(myrand(2) == 0){
+ for(int j = 0; j < tclistnum(list); j++){
+ int rsiz;
+ tclistval(list, j, &rsiz);
+ }
+ } else {
+ for(int j = 0; j < tclistnum(list); j++){
+ tclistval2(list, j);
+ }
+ }
+ }
+ break;
+ case 26:
+ putchar('Q');
+ tcmapput(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 27:
+ putchar('R');
+ tcmapput2(map, kbuf, vbuf);
+ break;
+ case 28:
+ putchar('S');
+ tcmapput3(map, kbuf, ksiz, vbuf, vsiz, vbuf, vsiz);
+ break;
+ case 29:
+ putchar('T');
+ tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 30:
+ putchar('U');
+ tcmapputkeep2(map, kbuf, vbuf);
+ break;
+ case 31:
+ putchar('V');
+ tcmapputcat(map, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 32:
+ putchar('W');
+ tcmapputcat2(map, kbuf, vbuf);
+ break;
+ case 33:
+ putchar('X');
+ if(myrand(10) == 0) tcmapout(map, kbuf, ksiz);
+ break;
+ case 34:
+ putchar('Y');
+ if(myrand(10) == 0) tcmapout2(map, kbuf);
+ break;
+ case 35:
+ putchar('Z');
+ tcmapget3(map, kbuf, ksiz, &vsiz);
+ break;
+ case 36:
+ putchar('a');
+ tcmapmove(map, kbuf, ksiz, true);
+ break;
+ case 37:
+ putchar('b');
+ tcmapmove(map, kbuf, ksiz, false);
+ break;
+ case 38:
+ putchar('c');
+ tcmapmove2(map, kbuf, true);
+ break;
+ case 39:
+ putchar('d');
+ if(myrand(100) == 0) tcmapiterinit(map);
+ break;
+ case 40:
+ putchar('e');
+ tcmapiternext(map, &vsiz);
+ break;
+ case 41:
+ putchar('f');
+ tcmapiternext2(map);
+ break;
+ case 42:
+ putchar('g');
+ if(myrand(100) == 0){
+ if(myrand(2) == 0){
+ tclistdel(tcmapkeys(map));
+ } else {
+ tclistdel(tcmapvals(map));
+ }
+ }
+ break;
+ case 43:
+ putchar('h');
+ if(myrand(rnum / 100 + 1) == 0) tcmapclear(map);
+ break;
+ case 44:
+ putchar('i');
+ if(myrand(20) == 0) tcmapcutfront(map, myrand(10));
+ break;
+ case 45:
+ putchar('j');
+ if(myrand(rnum / 100 + 1) == 0){
+ int dsiz;
+ char *dbuf = tcmapdump(map, &dsiz);
+ free(tcmaploadone(dbuf, dsiz, kbuf, ksiz, &vsiz));
+ tcmapdel(tcmapload(dbuf, dsiz));
+ free(dbuf);
+ }
+ break;
+ case 46:
+ putchar('k');
+ tcmdbput(mdb, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 47:
+ putchar('l');
+ tcmdbput2(mdb, kbuf, vbuf);
+ break;
+ case 48:
+ putchar('m');
+ tcmdbputkeep(mdb, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 49:
+ putchar('n');
+ tcmdbputkeep2(mdb, kbuf, vbuf);
+ break;
+ case 50:
+ putchar('o');
+ tcmdbputcat(mdb, kbuf, ksiz, vbuf, vsiz);
+ break;
+ case 51:
+ putchar('p');
+ tcmdbputcat2(mdb, kbuf, vbuf);
+ break;
+ case 52:
+ putchar('q');
+ if(myrand(10) == 0) tcmdbout(mdb, kbuf, ksiz);
+ break;
+ case 53:
+ putchar('r');
+ if(myrand(10) == 0) tcmdbout2(mdb, kbuf);
+ break;
+ case 54:
+ putchar('s');
+ free(tcmdbget(mdb, kbuf, ksiz, &vsiz));
+ break;
+ case 55:
+ putchar('t');
+ free(tcmdbget3(mdb, kbuf, ksiz, &vsiz));
+ break;
+ case 56:
+ putchar('u');
+ if(myrand(100) == 0) tcmdbiterinit(mdb);
+ break;
+ case 57:
+ putchar('v');
+ free(tcmdbiternext(mdb, &vsiz));
+ break;
+ case 58:
+ putchar('w');
+ tmp = tcmdbiternext2(mdb);
+ free(tmp);
+ break;
+ case 59:
+ putchar('x');
+ if(myrand(rnum / 100 + 1) == 0) tcmdbvanish(mdb);
+ break;
+ case 60:
+ putchar('y');
+ if(myrand(200) == 0) tcmdbcutfront(mdb, myrand(100));
+ break;
+ case 61:
+ putchar('+');
+ if(myrand(100) == 0) tcmpoolmalloc(mpool, 1);
+ break;
+ case 62:
+ putchar('+');
+ if(myrand(100) == 0) tcmpoolxstrnew(mpool);
+ break;
+ case 63:
+ putchar('+');
+ if(myrand(100) == 0) tcmpoollistnew(mpool);
+ break;
+ case 64:
+ putchar('+');
+ if(myrand(100) == 0) tcmpoolmapnew(mpool);
+ break;
+ default:
+ putchar('@');
+ if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX);
+ break;
+ }
+ if(i % 50 == 0) iprintf(" (%08d)\n", i);
+ }
+ if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum);
+ iprintf("time: %.3f\n", tctime() - stime);
+ iprintf("ok\n\n");
+ return 0;
+}
+
+
+
+// END OF FILE
--- /dev/null
+/*************************************************************************************************
+ * The utility API of Tokyo Cabinet
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#include "tcutil.h"
+#include "myconf.h"
+
+
+/*************************************************************************************************
+ * basic utilities
+ *************************************************************************************************/
+
+
+/* String containing the version information. */
+const char *tcversion = _TC_VERSION;
+
+
+/* Call back function for handling a fatal error. */
+void (*tcfatalfunc)(const char *message) = NULL;
+
+
+/* Allocate a region on memory. */
+void *tcmalloc(size_t size){
+ assert(size > 0 && size < INT_MAX);
+ char *p = malloc(size);
+ if(!p) tcmyfatal("out of memory");
+ return p;
+}
+
+
+/* Allocate a nullified region on memory. */
+void *tccalloc(size_t nmemb, size_t size){
+ assert(nmemb > 0 && size < INT_MAX && size > 0 && size < INT_MAX);
+ char *p = calloc(nmemb, size);
+ if(!p) tcmyfatal("out of memory");
+ return p;
+}
+
+
+/* Re-allocate a region on memory. */
+void *tcrealloc(void *ptr, size_t size){
+ assert(size >= 0 && size < INT_MAX);
+ char *p = realloc(ptr, size);
+ if(!p) tcmyfatal("out of memory");
+ return p;
+}
+
+
+/* Duplicate a region on memory. */
+void *tcmemdup(const void *ptr, size_t size){
+ assert(ptr && size >= 0);
+ char *p;
+ TCMALLOC(p, size + 1);
+ memcpy(p, ptr, size);
+ p[size] = '\0';
+ return p;
+}
+
+
+/* Duplicate a string on memory. */
+char *tcstrdup(const void *str){
+ assert(str);
+ int size = strlen(str);
+ char *p;
+ TCMALLOC(p, size + 1);
+ memcpy(p, str, size);
+ p[size] = '\0';
+ return p;
+}
+
+
+/* Free a region on memory. */
+void tcfree(void *ptr){
+ free(ptr);
+}
+
+
+
+/*************************************************************************************************
+ * extensible string
+ *************************************************************************************************/
+
+
+#define TCXSTRUNIT 12 // allocation unit size of an extensible string
+
+
+/* private function prototypes */
+static void tcvxstrprintf(TCXSTR *xstr, const char *format, va_list ap);
+
+
+/* Create an extensible string object. */
+TCXSTR *tcxstrnew(void){
+ TCXSTR *xstr;
+ TCMALLOC(xstr, sizeof(*xstr));
+ TCMALLOC(xstr->ptr, TCXSTRUNIT);
+ xstr->size = 0;
+ xstr->asize = TCXSTRUNIT;
+ xstr->ptr[0] = '\0';
+ return xstr;
+}
+
+
+/* Create an extensible string object from a character string. */
+TCXSTR *tcxstrnew2(const char *str){
+ assert(str);
+ TCXSTR *xstr;
+ TCMALLOC(xstr, sizeof(*xstr));
+ int size = strlen(str);
+ int asize = tclmax(size + 1, TCXSTRUNIT);
+ TCMALLOC(xstr->ptr, asize);
+ xstr->size = size;
+ xstr->asize = asize;
+ memcpy(xstr->ptr, str, size + 1);
+ return xstr;
+}
+
+
+/* Create an extensible string object with the initial allocation size. */
+TCXSTR *tcxstrnew3(int asiz){
+ assert(asiz >= 0);
+ asiz = tclmax(asiz, TCXSTRUNIT);
+ TCXSTR *xstr;
+ TCMALLOC(xstr, sizeof(*xstr));
+ TCMALLOC(xstr->ptr, asiz);
+ xstr->size = 0;
+ xstr->asize = asiz;
+ xstr->ptr[0] = '\0';
+ return xstr;
+}
+
+
+/* Copy an extensible string object. */
+TCXSTR *tcxstrdup(const TCXSTR *xstr){
+ assert(xstr);
+ TCXSTR *nxstr;
+ TCMALLOC(nxstr, sizeof(*nxstr));
+ int asize = tclmax(xstr->size + 1, TCXSTRUNIT);
+ TCMALLOC(nxstr->ptr, asize);
+ nxstr->size = xstr->size;
+ nxstr->asize = asize;
+ memcpy(nxstr->ptr, xstr->ptr, xstr->size + 1);
+ return nxstr;
+}
+
+
+/* Delete an extensible string object. */
+void tcxstrdel(TCXSTR *xstr){
+ assert(xstr);
+ free(xstr->ptr);
+ free(xstr);
+}
+
+
+/* Concatenate a region to the end of an extensible string object. */
+void tcxstrcat(TCXSTR *xstr, const void *ptr, int size){
+ assert(xstr && ptr && size >= 0);
+ int nsize = xstr->size + size + 1;
+ if(xstr->asize < nsize){
+ while(xstr->asize < nsize){
+ xstr->asize *= 2;
+ if(xstr->asize < nsize) xstr->asize = nsize;
+ }
+ TCREALLOC(xstr->ptr, xstr->ptr, xstr->asize);
+ }
+ memcpy(xstr->ptr + xstr->size, ptr, size);
+ xstr->size += size;
+ xstr->ptr[xstr->size] = '\0';
+}
+
+
+/* Concatenate a character string to the end of an extensible string object. */
+void tcxstrcat2(TCXSTR *xstr, const char *str){
+ assert(xstr && str);
+ int size = strlen(str);
+ int nsize = xstr->size + size + 1;
+ if(xstr->asize < nsize){
+ while(xstr->asize < nsize){
+ xstr->asize *= 2;
+ if(xstr->asize < nsize) xstr->asize = nsize;
+ }
+ TCREALLOC(xstr->ptr, xstr->ptr, xstr->asize);
+ }
+ memcpy(xstr->ptr + xstr->size, str, size + 1);
+ xstr->size += size;
+}
+
+
+/* Get the pointer of the region of an extensible string object. */
+const void *tcxstrptr(const TCXSTR *xstr){
+ assert(xstr);
+ return xstr->ptr;
+}
+
+
+/* Get the size of the region of an extensible string object. */
+int tcxstrsize(const TCXSTR *xstr){
+ assert(xstr);
+ return xstr->size;
+}
+
+
+/* Clear an extensible string object. */
+void tcxstrclear(TCXSTR *xstr){
+ assert(xstr);
+ xstr->size = 0;
+ xstr->ptr[0] = '\0';
+}
+
+
+/* Convert an extensible string object into a usual allocated region. */
+void *tcxstrtomalloc(TCXSTR *xstr){
+ assert(xstr);
+ char *ptr;
+ ptr = xstr->ptr;
+ free(xstr);
+ return ptr;
+}
+
+
+/* Create an extensible string object from an allocated region. */
+TCXSTR *tcxstrfrommalloc(void *ptr, int size){
+ TCXSTR *xstr;
+ TCMALLOC(xstr, sizeof(*xstr));
+ TCREALLOC(xstr->ptr, ptr, size + 1);
+ xstr->ptr[size] = '\0';
+ xstr->size = size;
+ xstr->asize = size;
+ return xstr;
+}
+
+
+/* Perform formatted output into an extensible string object. */
+void tcxstrprintf(TCXSTR *xstr, const char *format, ...){
+ assert(xstr && format);
+ va_list ap;
+ va_start(ap, format);
+ tcvxstrprintf(xstr, format, ap);
+ va_end(ap);
+}
+
+
+/* Allocate a formatted string on memory. */
+char *tcsprintf(const char *format, ...){
+ assert(format);
+ TCXSTR *xstr = tcxstrnew();
+ va_list ap;
+ va_start(ap, format);
+ tcvxstrprintf(xstr, format, ap);
+ va_end(ap);
+ return tcxstrtomalloc(xstr);
+}
+
+
+/* Perform formatted output into an extensible string object. */
+static void tcvxstrprintf(TCXSTR *xstr, const char *format, va_list ap){
+ assert(xstr && format);
+ while(*format != '\0'){
+ if(*format == '%'){
+ char cbuf[TCNUMBUFSIZ];
+ cbuf[0] = '%';
+ int cblen = 1;
+ int lnum = 0;
+ format++;
+ while(strchr("0123456789 .+-hlLz", *format) && *format != '\0' &&
+ cblen < TCNUMBUFSIZ - 1){
+ if(*format == 'l' || *format == 'L') lnum++;
+ cbuf[cblen++] = *(format++);
+ }
+ cbuf[cblen++] = *format;
+ cbuf[cblen] = '\0';
+ int tlen;
+ char *tmp, tbuf[TCNUMBUFSIZ*2];
+ switch(*format){
+ case 's':
+ tmp = va_arg(ap, char *);
+ if(!tmp) tmp = "(null)";
+ tcxstrcat2(xstr, tmp);
+ break;
+ case 'd':
+ if(lnum >= 2){
+ tlen = sprintf(tbuf, cbuf, va_arg(ap, long long));
+ } else if(lnum >= 1){
+ tlen = sprintf(tbuf, cbuf, va_arg(ap, long));
+ } else {
+ tlen = sprintf(tbuf, cbuf, va_arg(ap, int));
+ }
+ TCXSTRCAT(xstr, tbuf, tlen);
+ break;
+ case 'o': case 'u': case 'x': case 'X': case 'c':
+ if(lnum >= 2){
+ tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned long long));
+ } else if(lnum >= 1){
+ tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned long));
+ } else {
+ tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned int));
+ }
+ TCXSTRCAT(xstr, tbuf, tlen);
+ break;
+ case 'e': case 'E': case 'f': case 'g': case 'G':
+ if(lnum >= 1){
+ tlen = sprintf(tbuf, cbuf, va_arg(ap, long double));
+ } else {
+ tlen = sprintf(tbuf, cbuf, va_arg(ap, double));
+ }
+ TCXSTRCAT(xstr, tbuf, tlen);
+ break;
+ case '@':
+ tmp = va_arg(ap, char *);
+ if(!tmp) tmp = "(null)";
+ while(*tmp){
+ switch(*tmp){
+ case '&': TCXSTRCAT(xstr, "&", 5); break;
+ case '<': TCXSTRCAT(xstr, "<", 4); break;
+ case '>': TCXSTRCAT(xstr, ">", 4); break;
+ case '"': TCXSTRCAT(xstr, """, 6); break;
+ default:
+ if(!((*tmp >= 0 && *tmp <= 0x8) || (*tmp >= 0x0e && *tmp <= 0x1f)))
+ TCXSTRCAT(xstr, tmp, 1);
+ break;
+ }
+ tmp++;
+ }
+ break;
+ case '?':
+ tmp = va_arg(ap, char *);
+ if(!tmp) tmp = "(null)";
+ while(*tmp){
+ unsigned char c = *(unsigned char *)tmp;
+ if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
+ (c >= '0' && c <= '9') || (c != '\0' && strchr("_-.", c))){
+ TCXSTRCAT(xstr, tmp, 1);
+ } else {
+ tlen = sprintf(tbuf, "%%%02X", c);
+ TCXSTRCAT(xstr, tbuf, tlen);
+ }
+ tmp++;
+ }
+ break;
+ case '%':
+ TCXSTRCAT(xstr, "%", 1);
+ break;
+ }
+ } else {
+ TCXSTRCAT(xstr, format, 1);
+ }
+ format++;
+ }
+}
+
+
+
+/*************************************************************************************************
+ * array list
+ *************************************************************************************************/
+
+
+#define TCLISTUNIT 64 // allocation unit number of a list handle
+
+
+/* private function prototypes */
+static int tclistelemcmp(const void *a, const void *b);
+static int tclistelemcmpci(const void *a, const void *b);
+
+
+/* Create a list object. */
+TCLIST *tclistnew(void){
+ TCLIST *list;
+ TCMALLOC(list, sizeof(*list));
+ list->anum = TCLISTUNIT;
+ TCMALLOC(list->array, sizeof(list->array[0]) * list->anum);
+ list->start = 0;
+ list->num = 0;
+ return list;
+}
+
+
+/* Create a list object. */
+TCLIST *tclistnew2(int anum){
+ TCLIST *list;
+ TCMALLOC(list, sizeof(*list));
+ if(anum < 1) anum = 1;
+ list->anum = anum;
+ TCMALLOC(list->array, sizeof(list->array[0]) * list->anum);
+ list->start = 0;
+ list->num = 0;
+ return list;
+}
+
+
+/* Copy a list object. */
+TCLIST *tclistdup(const TCLIST *list){
+ assert(list);
+ int num = list->num;
+ if(num < 1) tclistnew();
+ const TCLISTDATUM *array = list->array + list->start;
+ TCLIST *nlist;
+ TCMALLOC(nlist, sizeof(*nlist));
+ TCLISTDATUM *narray;
+ TCMALLOC(narray, sizeof(list->array[0]) * tclmax(num, 1));
+ for(int i = 0; i < num; i++){
+ int size = array[i].size;
+ TCMALLOC(narray[i].ptr, tclmax(size + 1, TCXSTRUNIT));
+ memcpy(narray[i].ptr, array[i].ptr, size + 1);
+ narray[i].size = array[i].size;
+ }
+ nlist->anum = num;
+ nlist->array = narray;
+ nlist->start = 0;
+ nlist->num = num;
+ return nlist;
+}
+
+
+/* Delete a list object. */
+void tclistdel(TCLIST *list){
+ assert(list);
+ TCLISTDATUM *array = list->array;
+ int end = list->start + list->num;
+ for(int i = list->start; i < end; i++){
+ free(array[i].ptr);
+ }
+ free(list->array);
+ free(list);
+}
+
+
+/* Get the number of elements of a list object. */
+int tclistnum(const TCLIST *list){
+ assert(list);
+ return list->num;
+}
+
+
+/* Get the pointer to the region of an element of a list object. */
+const void *tclistval(const TCLIST *list, int index, int *sp){
+ assert(list && index >= 0 && sp);
+ if(index >= list->num) return NULL;
+ index += list->start;
+ *sp = list->array[index].size;
+ return list->array[index].ptr;
+}
+
+
+/* Get the string of an element of a list object. */
+const char *tclistval2(const TCLIST *list, int index){
+ assert(list && index >= 0);
+ if(index >= list->num) return NULL;
+ index += list->start;
+ return list->array[index].ptr;
+}
+
+
+/* Add an element at the end of a list object. */
+void tclistpush(TCLIST *list, const void *ptr, int size){
+ assert(list && ptr && size >= 0);
+ int index = list->start + list->num;
+ if(index >= list->anum){
+ list->anum += list->num + 1;
+ TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
+ }
+ TCLISTDATUM *array = list->array;
+ TCMALLOC(array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
+ memcpy(array[index].ptr, ptr, size);
+ array[index].ptr[size] = '\0';
+ array[index].size = size;
+ list->num++;
+}
+
+
+/* Add a string element at the end of a list object. */
+void tclistpush2(TCLIST *list, const char *str){
+ assert(list && str);
+ int index = list->start + list->num;
+ if(index >= list->anum){
+ list->anum += list->num + 1;
+ TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
+ }
+ int size = strlen(str);
+ TCLISTDATUM *array = list->array;
+ TCMALLOC(array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
+ memcpy(array[index].ptr, str, size + 1);
+ array[index].size = size;
+ list->num++;
+}
+
+
+/* Add an allocated element at the end of a list object. */
+void tclistpushmalloc(TCLIST *list, void *ptr, int size){
+ assert(list && ptr && size >= 0);
+ int index = list->start + list->num;
+ if(index >= list->anum){
+ list->anum += list->num + 1;
+ TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
+ }
+ TCLISTDATUM *array = list->array;
+ TCREALLOC(array[index].ptr, ptr, size + 1);
+ array[index].ptr[size] = '\0';
+ array[index].size = size;
+ list->num++;
+}
+
+
+/* Remove an element of the end of a list object. */
+void *tclistpop(TCLIST *list, int *sp){
+ assert(list && sp);
+ if(list->num < 1) return NULL;
+ int index = list->start + list->num - 1;
+ list->num--;
+ *sp = list->array[index].size;
+ return list->array[index].ptr;
+}
+
+
+/* Remove a string element of the end of a list object. */
+char *tclistpop2(TCLIST *list){
+ assert(list);
+ if(list->num < 1) return NULL;
+ int index = list->start + list->num - 1;
+ list->num--;
+ return list->array[index].ptr;
+}
+
+
+/* Add an element at the top of a list object. */
+void tclistunshift(TCLIST *list, const void *ptr, int size){
+ assert(list && ptr && size >= 0);
+ if(list->start < 1){
+ if(list->start + list->num >= list->anum){
+ list->anum += list->num + 1;
+ TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
+ }
+ list->start = list->anum - list->num;
+ memmove(list->array + list->start, list->array, list->num * sizeof(list->array[0]));
+ }
+ int index = list->start - 1;
+ TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
+ memcpy(list->array[index].ptr, ptr, size);
+ list->array[index].ptr[size] = '\0';
+ list->array[index].size = size;
+ list->start--;
+ list->num++;
+}
+
+
+/* Add a string element at the top of a list object. */
+void tclistunshift2(TCLIST *list, const char *str){
+ assert(list && str);
+ if(list->start < 1){
+ if(list->start + list->num >= list->anum){
+ list->anum += list->num + 1;
+ TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
+ }
+ list->start = list->anum - list->num;
+ memmove(list->array + list->start, list->array, list->num * sizeof(list->array[0]));
+ }
+ int index = list->start - 1;
+ int size = strlen(str);
+ TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
+ memcpy(list->array[index].ptr, str, size + 1);
+ list->array[index].size = size;
+ list->start--;
+ list->num++;
+}
+
+
+/* Remove an element of the top of a list object. */
+void *tclistshift(TCLIST *list, int *sp){
+ assert(list && sp);
+ if(list->num < 1) return NULL;
+ int index = list->start;
+ list->start++;
+ list->num--;
+ *sp = list->array[index].size;
+ void *rv = list->array[index].ptr;
+ if((list->start & 0xff) == 0 && list->start > (list->num >> 1)){
+ memmove(list->array, list->array + list->start, list->num * sizeof(list->array[0]));
+ list->start = 0;
+ }
+ return rv;
+}
+
+
+/* Remove a string element of the top of a list object. */
+char *tclistshift2(TCLIST *list){
+ assert(list);
+ if(list->num < 1) return NULL;
+ int index = list->start;
+ list->start++;
+ list->num--;
+ void *rv = list->array[index].ptr;
+ if((list->start & 0xff) == 0 && list->start > (list->num >> 1)){
+ memmove(list->array, list->array + list->start, list->num * sizeof(list->array[0]));
+ list->start = 0;
+ }
+ return rv;
+}
+
+
+/* Add an element at the specified location of a list object. */
+void tclistinsert(TCLIST *list, int index, const void *ptr, int size){
+ assert(list && index >= 0 && ptr && size >= 0);
+ if(index > list->num) return;
+ index += list->start;
+ if(list->start + list->num >= list->anum){
+ list->anum += list->num + 1;
+ TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
+ }
+ memmove(list->array + index + 1, list->array + index,
+ sizeof(list->array[0]) * (list->start + list->num - index));
+ TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
+ memcpy(list->array[index].ptr, ptr, size);
+ list->array[index].ptr[size] = '\0';
+ list->array[index].size = size;
+ list->num++;
+}
+
+
+/* Add a string element at the specified location of a list object. */
+void tclistinsert2(TCLIST *list, int index, const char *str){
+ assert(list && index >= 0 && str);
+ if(index > list->num) return;
+ index += list->start;
+ if(list->start + list->num >= list->anum){
+ list->anum += list->num + 1;
+ TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0]));
+ }
+ memmove(list->array + index + 1, list->array + index,
+ sizeof(list->array[0]) * (list->start + list->num - index));
+ int size = strlen(str);
+ TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT));
+ memcpy(list->array[index].ptr, str, size);
+ list->array[index].ptr[size] = '\0';
+ list->array[index].size = size;
+ list->num++;
+}
+
+
+/* Remove an element at the specified location of a list object. */
+void *tclistremove(TCLIST *list, int index, int *sp){
+ assert(list && index >= 0 && sp);
+ if(index >= list->num) return NULL;
+ index += list->start;
+ char *ptr = list->array[index].ptr;
+ *sp = list->array[index].size;
+ list->num--;
+ memmove(list->array + index, list->array + index + 1,
+ sizeof(list->array[0]) * (list->start + list->num - index));
+ return ptr;
+}
+
+
+/* Remove a string element at the specified location of a list object. */
+char *tclistremove2(TCLIST *list, int index){
+ assert(list && index >= 0);
+ if(index >= list->num) return NULL;
+ index += list->start;
+ char *ptr = list->array[index].ptr;
+ list->num--;
+ memmove(list->array + index, list->array + index + 1,
+ sizeof(list->array[0]) * (list->start + list->num - index));
+ return ptr;
+}
+
+
+/* Overwrite an element at the specified location of a list object. */
+void tclistover(TCLIST *list, int index, const void *ptr, int size){
+ assert(list && index >= 0 && ptr && size >= 0);
+ if(index >= list->num) return;
+ index += list->start;
+ if(size > list->array[index].size)
+ TCREALLOC(list->array[index].ptr, list->array[index].ptr, size + 1);
+ memcpy(list->array[index].ptr, ptr, size);
+ list->array[index].size = size;
+ list->array[index].ptr[size] = '\0';
+}
+
+
+/* Overwrite a string element at the specified location of a list object. */
+void tclistover2(TCLIST *list, int index, const char *str){
+ assert(list && index >= 0 && str);
+ if(index >= list->num) return;
+ index += list->start;
+ int size = strlen(str);
+ if(size > list->array[index].size)
+ TCREALLOC(list->array[index].ptr, list->array[index].ptr, size + 1);
+ memcpy(list->array[index].ptr, str, size + 1);
+ list->array[index].size = size;
+}
+
+
+/* Sort elements of a list object in lexical order. */
+void tclistsort(TCLIST *list){
+ assert(list);
+ qsort(list->array + list->start, list->num, sizeof(list->array[0]), tclistelemcmp);
+}
+
+
+/* Sort elements of a list object in case-insensitive lexical order. */
+void tclistsortci(TCLIST *list){
+ assert(list);
+ qsort(list->array + list->start, list->num, sizeof(list->array[0]), tclistelemcmpci);
+}
+
+
+/* Sort elements of a list object by an arbitrary comparison function. */
+void tclistsortex(TCLIST *list, int (*cmp)(const TCLISTDATUM *, const TCLISTDATUM *)){
+ assert(list && cmp);
+ qsort(list->array + list->start, list->num, sizeof(list->array[0]),
+ (int (*)(const void *, const void *))cmp);
+}
+
+
+/* Search a list object for an element using liner search. */
+int tclistlsearch(const TCLIST *list, const void *ptr, int size){
+ assert(list && ptr && size >= 0);
+ int end = list->start + list->num;
+ for(int i = list->start; i < end; i++){
+ if(list->array[i].size == size && !memcmp(list->array[i].ptr, ptr, size))
+ return i - list->start;
+ }
+ return -1;
+}
+
+
+/* Search a list object for an element using binary search. */
+int tclistbsearch(const TCLIST *list, const void *ptr, int size){
+ assert(list && ptr && size >= 0);
+ TCLISTDATUM key;
+ key.ptr = (char *)ptr;
+ key.size = size;
+ TCLISTDATUM *res = bsearch(&key, list->array + list->start,
+ list->num, sizeof(list->array[0]), tclistelemcmp);
+ return res ? res - list->array - list->start : -1;
+}
+
+
+/* Clear a list object. */
+void tclistclear(TCLIST *list){
+ assert(list);
+ TCLISTDATUM *array = list->array;
+ int end = list->start + list->num;
+ for(int i = list->start; i < end; i++){
+ free(array[i].ptr);
+ }
+ list->start = 0;
+ list->num = 0;
+}
+
+
+/* Serialize a list object into a byte array. */
+void *tclistdump(const TCLIST *list, int *sp){
+ assert(list && sp);
+ const TCLISTDATUM *array = list->array;
+ int end = list->start + list->num;
+ int tsiz = 0;
+ for(int i = list->start; i < end; i++){
+ tsiz += array[i].size + sizeof(int);
+ }
+ char *buf;
+ TCMALLOC(buf, tsiz + 1);
+ char *wp = buf;
+ for(int i = list->start; i < end; i++){
+ int step;
+ TCSETVNUMBUF(step, wp, array[i].size);
+ wp += step;
+ memcpy(wp, array[i].ptr, array[i].size);
+ wp += array[i].size;
+ }
+ *sp = wp - buf;
+ return buf;
+}
+
+
+/* Create a list object from a serialized byte array. */
+TCLIST *tclistload(const void *ptr, int size){
+ assert(ptr && size >= 0);
+ TCLIST *list;
+ TCMALLOC(list, sizeof(*list));
+ int anum = size / sizeof(int) + 1;
+ TCLISTDATUM *array;
+ TCMALLOC(array, sizeof(array[0]) * anum);
+ int num = 0;
+ const char *rp = ptr;
+ const char *ep = (char *)ptr + size;
+ while(rp < ep){
+ int step, vsiz;
+ TCREADVNUMBUF(rp, vsiz, step);
+ rp += step;
+ if(num >= anum){
+ anum *= 2;
+ TCREALLOC(array, array, anum * sizeof(array[0]));
+ }
+ TCMALLOC(array[num].ptr, tclmax(vsiz + 1, TCXSTRUNIT));
+ memcpy(array[num].ptr, rp, vsiz);
+ array[num].ptr[vsiz] = '\0';
+ array[num].size = vsiz;
+ num++;
+ rp += vsiz;
+ }
+ list->anum = anum;
+ list->array = array;
+ list->start = 0;
+ list->num = num;
+ return list;
+}
+
+
+/* Compare two list elements in lexical order.
+ `a' specifies the pointer to one element.
+ `b' specifies the pointer to the other element.
+ The return value is positive if the former is big, negative if the latter is big, 0 if both
+ are equivalent. */
+static int tclistelemcmp(const void *a, const void *b){
+ assert(a && b);
+ unsigned char *ao = (unsigned char *)((TCLISTDATUM *)a)->ptr;
+ unsigned char *bo = (unsigned char *)((TCLISTDATUM *)b)->ptr;
+ int size = (((TCLISTDATUM *)a)->size < ((TCLISTDATUM *)b)->size) ?
+ ((TCLISTDATUM *)a)->size : ((TCLISTDATUM *)b)->size;
+ for(int i = 0; i < size; i++){
+ if(ao[i] > bo[i]) return 1;
+ if(ao[i] < bo[i]) return -1;
+ }
+ return ((TCLISTDATUM *)a)->size - ((TCLISTDATUM *)b)->size;
+}
+
+
+/* Compare two list elements in case-insensitive lexical order..
+ `a' specifies the pointer to one element.
+ `b' specifies the pointer to the other element.
+ The return value is positive if the former is big, negative if the latter is big, 0 if both
+ are equivalent. */
+static int tclistelemcmpci(const void *a, const void *b){
+ assert(a && b);
+ TCLISTDATUM *ap = (TCLISTDATUM *)a;
+ TCLISTDATUM *bp = (TCLISTDATUM *)b;
+ unsigned char *ao = (unsigned char *)ap->ptr;
+ unsigned char *bo = (unsigned char *)bp->ptr;
+ int size = (ap->size < bp->size) ? ap->size : bp->size;
+ for(int i = 0; i < size; i++){
+ int ac = ao[i];
+ bool ab = false;
+ if(ac >= 'A' && ac <= 'Z'){
+ ac += 'a' - 'A';
+ ab = true;
+ }
+ int bc = bo[i];
+ bool bb = false;
+ if(bc >= 'A' && bc <= 'Z'){
+ bc += 'a' - 'A';
+ bb = true;
+ }
+ if(ac > bc) return 1;
+ if(ac < bc) return -1;
+ if(!ab && bb) return 1;
+ if(ab && !bb) return -1;
+ }
+ return ap->size - bp->size;
+}
+
+
+
+/*************************************************************************************************
+ * hash map
+ *************************************************************************************************/
+
+
+#define TCMAPBNUM 4093 // allocation unit number of a map
+#define TCMAPCSUNIT 52 // small allocation unit size of map concatenation
+#define TCMAPCBUNIT 252 // big allocation unit size of map concatenation
+
+/* get the first hash value */
+#define TCMAPHASH1(TC_res, TC_kbuf, TC_ksiz) \
+ do { \
+ const unsigned char *_TC_p = (const unsigned char *)(TC_kbuf); \
+ int _TC_ksiz = TC_ksiz; \
+ for((TC_res) = 19780211; _TC_ksiz--;){ \
+ (TC_res) = ((TC_res) << 5) + ((TC_res) << 2) + (TC_res) + *(_TC_p)++; \
+ } \
+ } while(false)
+
+/* get the second hash value */
+#define TCMAPHASH2(TC_res, TC_kbuf, TC_ksiz) \
+ do { \
+ const unsigned char *_TC_p = (const unsigned char *)(TC_kbuf) + TC_ksiz - 1; \
+ int _TC_ksiz = TC_ksiz; \
+ for((TC_res) = 0x13579bdf; _TC_ksiz--;){ \
+ (TC_res) = ((TC_res) << 5) - (TC_res) + *(_TC_p)--; \
+ } \
+ } while(false)
+
+/* get the size of padding bytes for pointer alignment */
+#define TCALIGNPAD(TC_hsiz) \
+ (((TC_hsiz | ~-(int)sizeof(void *)) + 1) - TC_hsiz)
+
+/* compare two keys */
+#define TCKEYCMP(TC_abuf, TC_asiz, TC_bbuf, TC_bsiz) \
+ ((TC_asiz > TC_bsiz) ? 1 : (TC_asiz < TC_bsiz) ? -1 : memcmp(TC_abuf, TC_bbuf, TC_asiz))
+
+
+/* Create a map object. */
+TCMAP *tcmapnew(void){
+ return tcmapnew2(TCMAPBNUM);
+}
+
+
+/* Create a map object with specifying the number of the buckets. */
+TCMAP *tcmapnew2(uint32_t bnum){
+ if(bnum < 1) bnum = 1;
+ TCMAP *map;
+ TCMALLOC(map, sizeof(*map));
+ TCMAPREC **buckets;
+ TCCALLOC(buckets, bnum, sizeof(map->buckets[0]));
+ map->buckets = buckets;
+ map->first = NULL;
+ map->last = NULL;
+ map->cur = NULL;
+ map->bnum = bnum;
+ map->rnum = 0;
+ map->msiz = 0;
+ return map;
+}
+
+
+/* Copy a map object. */
+TCMAP *tcmapdup(const TCMAP *map){
+ assert(map);
+ TCMAP *nmap = tcmapnew2(tclmax(tclmax(map->bnum, map->rnum), TCMAPBNUM));
+ TCMAPREC *cur = map->cur;
+ const char *kbuf;
+ int ksiz;
+ ((TCMAP *)map)->cur = map->first;
+ while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){
+ int vsiz;
+ const char *vbuf = tcmapiterval(kbuf, &vsiz);
+ tcmapputkeep(nmap, kbuf, ksiz, vbuf, vsiz);
+ }
+ ((TCMAP *)map)->cur = cur;
+ return nmap;
+}
+
+
+/* Close a map object. */
+void tcmapdel(TCMAP *map){
+ assert(map);
+ TCMAPREC *rec = map->first;
+ while(rec){
+ TCMAPREC *next = rec->next;
+ free(rec);
+ rec = next;
+ }
+ free(map->buckets);
+ free(map);
+}
+
+
+/* Store a record into a map object. */
+void tcmapput(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+ assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ unsigned int hash;
+ TCMAPHASH1(hash, kbuf, ksiz);
+ int bidx = hash % map->bnum;
+ TCMAPREC *rec = map->buckets[bidx];
+ TCMAPREC **entp = map->buckets + bidx;
+ TCMAPHASH2(hash, kbuf, ksiz);
+ while(rec){
+ if(hash > rec->hash){
+ entp = &(rec->left);
+ rec = rec->left;
+ } else if(hash < rec->hash){
+ entp = &(rec->right);
+ rec = rec->right;
+ } else {
+ char *dbuf = (char *)rec + sizeof(*rec);
+ int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
+ if(kcmp < 0){
+ entp = &(rec->left);
+ rec = rec->left;
+ } else if(kcmp > 0){
+ entp = &(rec->right);
+ rec = rec->right;
+ } else {
+ map->msiz += vsiz - rec->vsiz;
+ int psiz = TCALIGNPAD(ksiz);
+ if(vsiz > rec->vsiz){
+ TCMAPREC *old = rec;
+ TCREALLOC(rec, rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
+ if(rec != old){
+ if(map->first == old) map->first = rec;
+ if(map->last == old) map->last = rec;
+ if(map->cur == old) map->cur = rec;
+ if(*entp == old) *entp = rec;
+ if(rec->prev) rec->prev->next = rec;
+ if(rec->next) rec->next->prev = rec;
+ dbuf = (char *)rec + sizeof(*rec);
+ }
+ }
+ memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
+ dbuf[ksiz+psiz+vsiz] = '\0';
+ rec->vsiz = vsiz;
+ return;
+ }
+ }
+ }
+ int psiz = TCALIGNPAD(ksiz);
+ map->msiz += ksiz + vsiz;
+ TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
+ char *dbuf = (char *)rec + sizeof(*rec);
+ memcpy(dbuf, kbuf, ksiz);
+ dbuf[ksiz] = '\0';
+ rec->ksiz = ksiz;
+ memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
+ dbuf[ksiz+psiz+vsiz] = '\0';
+ rec->vsiz = vsiz;
+ rec->hash = hash;
+ rec->left = NULL;
+ rec->right = NULL;
+ rec->prev = map->last;
+ rec->next = NULL;
+ *entp = rec;
+ if(!map->first) map->first = rec;
+ if(map->last) map->last->next = rec;
+ map->last = rec;
+ map->rnum++;
+}
+
+
+/* Store a string record into a map object. */
+void tcmapput2(TCMAP *map, const char *kstr, const char *vstr){
+ assert(map && kstr && vstr);
+ tcmapput(map, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Store a record of the value of two regions into a map object. */
+void tcmapput3(TCMAP *map, const char *kbuf, int ksiz,
+ const void *fvbuf, int fvsiz, const char *lvbuf, int lvsiz){
+ assert(map && kbuf && ksiz >= 0 && fvbuf && fvsiz >= 0 && lvbuf && lvsiz >= 0);
+ unsigned int hash;
+ TCMAPHASH1(hash, kbuf, ksiz);
+ int bidx = hash % map->bnum;
+ TCMAPREC *rec = map->buckets[bidx];
+ TCMAPREC **entp = map->buckets + bidx;
+ TCMAPHASH2(hash, kbuf, ksiz);
+ while(rec){
+ if(hash > rec->hash){
+ entp = &(rec->left);
+ rec = rec->left;
+ } else if(hash < rec->hash){
+ entp = &(rec->right);
+ rec = rec->right;
+ } else {
+ char *dbuf = (char *)rec + sizeof(*rec);
+ int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
+ if(kcmp < 0){
+ entp = &(rec->left);
+ rec = rec->left;
+ } else if(kcmp > 0){
+ entp = &(rec->right);
+ rec = rec->right;
+ } else {
+ int vsiz = fvsiz + lvsiz;
+ map->msiz += vsiz - rec->vsiz;
+ int psiz = TCALIGNPAD(ksiz);
+ ksiz += psiz;
+ if(vsiz > rec->vsiz){
+ TCMAPREC *old = rec;
+ TCREALLOC(rec, rec, sizeof(*rec) + ksiz + vsiz + 1);
+ if(rec != old){
+ if(map->first == old) map->first = rec;
+ if(map->last == old) map->last = rec;
+ if(map->cur == old) map->cur = rec;
+ if(*entp == old) *entp = rec;
+ if(rec->prev) rec->prev->next = rec;
+ if(rec->next) rec->next->prev = rec;
+ dbuf = (char *)rec + sizeof(*rec);
+ }
+ }
+ memcpy(dbuf + ksiz, fvbuf, fvsiz);
+ memcpy(dbuf + ksiz + fvsiz, lvbuf, lvsiz);
+ dbuf[ksiz+vsiz] = '\0';
+ rec->vsiz = vsiz;
+ return;
+ }
+ }
+ }
+ int vsiz = fvsiz + lvsiz;
+ int psiz = TCALIGNPAD(ksiz);
+ map->msiz += ksiz + vsiz;
+ TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
+ char *dbuf = (char *)rec + sizeof(*rec);
+ memcpy(dbuf, kbuf, ksiz);
+ dbuf[ksiz] = '\0';
+ rec->ksiz = ksiz;
+ ksiz += psiz;
+ memcpy(dbuf + ksiz, fvbuf, fvsiz);
+ memcpy(dbuf + ksiz + fvsiz, lvbuf, lvsiz);
+ dbuf[ksiz+vsiz] = '\0';
+ rec->vsiz = vsiz;
+ rec->hash = hash;
+ rec->left = NULL;
+ rec->right = NULL;
+ rec->prev = map->last;
+ rec->next = NULL;
+ *entp = rec;
+ if(!map->first) map->first = rec;
+ if(map->last) map->last->next = rec;
+ map->last = rec;
+ map->rnum++;
+}
+
+
+/* Store a new record into a map object. */
+bool tcmapputkeep(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+ assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ unsigned int hash;
+ TCMAPHASH1(hash, kbuf, ksiz);
+ int bidx = hash % map->bnum;
+ TCMAPREC *rec = map->buckets[bidx];
+ TCMAPREC **entp = map->buckets + bidx;
+ TCMAPHASH2(hash, kbuf, ksiz);
+ while(rec){
+ if(hash > rec->hash){
+ entp = &(rec->left);
+ rec = rec->left;
+ } else if(hash < rec->hash){
+ entp = &(rec->right);
+ rec = rec->right;
+ } else {
+ char *dbuf = (char *)rec + sizeof(*rec);
+ int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
+ if(kcmp < 0){
+ entp = &(rec->left);
+ rec = rec->left;
+ } else if(kcmp > 0){
+ entp = &(rec->right);
+ rec = rec->right;
+ } else {
+ return false;
+ }
+ }
+ }
+ int psiz = TCALIGNPAD(ksiz);
+ map->msiz += ksiz + vsiz;
+ TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1);
+ char *dbuf = (char *)rec + sizeof(*rec);
+ memcpy(dbuf, kbuf, ksiz);
+ dbuf[ksiz] = '\0';
+ rec->ksiz = ksiz;
+ memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
+ dbuf[ksiz+psiz+vsiz] = '\0';
+ rec->vsiz = vsiz;
+ rec->hash = hash;
+ rec->left = NULL;
+ rec->right = NULL;
+ rec->prev = map->last;
+ rec->next = NULL;
+ *entp = rec;
+ if(!map->first) map->first = rec;
+ if(map->last) map->last->next = rec;
+ map->last = rec;
+ map->rnum++;
+ return true;
+}
+
+
+/* Store a new string record into a map object.
+ `map' specifies the map object.
+ `kstr' specifies the string of the key.
+ `vstr' specifies the string of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, this function has no effect. */
+bool tcmapputkeep2(TCMAP *map, const char *kstr, const char *vstr){
+ assert(map && kstr && vstr);
+ return tcmapputkeep(map, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Concatenate a value at the end of the value of the existing record in a map object. */
+void tcmapputcat(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+ assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ unsigned int hash;
+ TCMAPHASH1(hash, kbuf, ksiz);
+ int bidx = hash % map->bnum;
+ TCMAPREC *rec = map->buckets[bidx];
+ TCMAPREC **entp = map->buckets + bidx;
+ TCMAPHASH2(hash, kbuf, ksiz);
+ while(rec){
+ if(hash > rec->hash){
+ entp = &(rec->left);
+ rec = rec->left;
+ } else if(hash < rec->hash){
+ entp = &(rec->right);
+ rec = rec->right;
+ } else {
+ char *dbuf = (char *)rec + sizeof(*rec);
+ int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
+ if(kcmp < 0){
+ entp = &(rec->left);
+ rec = rec->left;
+ } else if(kcmp > 0){
+ entp = &(rec->right);
+ rec = rec->right;
+ } else {
+ map->msiz += vsiz;
+ int psiz = TCALIGNPAD(ksiz);
+ int asiz = sizeof(*rec) + ksiz + psiz + rec->vsiz + vsiz + 1;
+ int unit = (asiz <= TCMAPCSUNIT) ? TCMAPCSUNIT : TCMAPCBUNIT;
+ asiz = (asiz - 1) + unit - (asiz - 1) % unit;
+ TCMAPREC *old = rec;
+ TCREALLOC(rec, rec, asiz);
+ if(rec != old){
+ if(map->first == old) map->first = rec;
+ if(map->last == old) map->last = rec;
+ if(map->cur == old) map->cur = rec;
+ if(*entp == old) *entp = rec;
+ if(rec->prev) rec->prev->next = rec;
+ if(rec->next) rec->next->prev = rec;
+ dbuf = (char *)rec + sizeof(*rec);
+ }
+ memcpy(dbuf + ksiz + psiz + rec->vsiz, vbuf, vsiz);
+ dbuf[ksiz+psiz+rec->vsiz+vsiz] = '\0';
+ rec->vsiz += vsiz;
+ return;
+ }
+ }
+ }
+ int psiz = TCALIGNPAD(ksiz);
+ int asiz = sizeof(*rec) + ksiz + psiz + vsiz + 1;
+ int unit = (asiz <= TCMAPCSUNIT) ? TCMAPCSUNIT : TCMAPCBUNIT;
+ asiz = (asiz - 1) + unit - (asiz - 1) % unit;
+ map->msiz += ksiz + vsiz;
+ TCMALLOC(rec, asiz);
+ char *dbuf = (char *)rec + sizeof(*rec);
+ memcpy(dbuf, kbuf, ksiz);
+ dbuf[ksiz] = '\0';
+ rec->ksiz = ksiz;
+ memcpy(dbuf + ksiz + psiz, vbuf, vsiz);
+ dbuf[ksiz+psiz+vsiz] = '\0';
+ rec->vsiz = vsiz;
+ rec->hash = hash;
+ rec->left = NULL;
+ rec->right = NULL;
+ rec->prev = map->last;
+ rec->next = NULL;
+ *entp = rec;
+ if(!map->first) map->first = rec;
+ if(map->last) map->last->next = rec;
+ map->last = rec;
+ map->rnum++;
+}
+
+
+/* Concatenate a string value at the end of the value of the existing record in a map object. */
+void tcmapputcat2(TCMAP *map, const char *kstr, const char *vstr){
+ assert(map && kstr && vstr);
+ tcmapputcat(map, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Remove a record of a map object. */
+bool tcmapout(TCMAP *map, const void *kbuf, int ksiz){
+ assert(map && kbuf && ksiz >= 0);
+ unsigned int hash;
+ TCMAPHASH1(hash, kbuf, ksiz);
+ int bidx = hash % map->bnum;
+ TCMAPREC *rec = map->buckets[bidx];
+ TCMAPREC **entp = map->buckets + bidx;
+ TCMAPHASH2(hash, kbuf, ksiz);
+ while(rec){
+ if(hash > rec->hash){
+ entp = &(rec->left);
+ rec = rec->left;
+ } else if(hash < rec->hash){
+ entp = &(rec->right);
+ rec = rec->right;
+ } else {
+ char *dbuf = (char *)rec + sizeof(*rec);
+ int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
+ if(kcmp < 0){
+ entp = &(rec->left);
+ rec = rec->left;
+ } else if(kcmp > 0){
+ entp = &(rec->right);
+ rec = rec->right;
+ } else {
+ map->rnum--;
+ map->msiz -= rec->ksiz + rec->vsiz;
+ if(rec->prev) rec->prev->next = rec->next;
+ if(rec->next) rec->next->prev = rec->prev;
+ if(rec == map->first) map->first = rec->next;
+ if(rec == map->last) map->last = rec->prev;
+ if(rec == map->cur) map->cur = rec->next;
+ if(rec->left && !rec->right){
+ *entp = rec->left;
+ } else if(!rec->left && rec->right){
+ *entp = rec->right;
+ } else if(!rec->left && !rec->left){
+ *entp = NULL;
+ } else {
+ *entp = rec->left;
+ TCMAPREC *tmp = *entp;
+ while(tmp->right){
+ tmp = tmp->right;
+ }
+ tmp->right = rec->right;
+ }
+ free(rec);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+/* Remove a string record of a map object. */
+bool tcmapout2(TCMAP *map, const char *kstr){
+ assert(map && kstr);
+ return tcmapout(map, kstr, strlen(kstr));
+}
+
+
+/* Retrieve a record in a map object. */
+const void *tcmapget(const TCMAP *map, const void *kbuf, int ksiz, int *sp){
+ assert(map && kbuf && ksiz >= 0 && sp);
+ unsigned int hash;
+ TCMAPHASH1(hash, kbuf, ksiz);
+ TCMAPREC *rec = map->buckets[hash%map->bnum];
+ TCMAPHASH2(hash, kbuf, ksiz);
+ while(rec){
+ if(hash > rec->hash){
+ rec = rec->left;
+ } else if(hash < rec->hash){
+ rec = rec->right;
+ } else {
+ char *dbuf = (char *)rec + sizeof(*rec);
+ int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
+ if(kcmp < 0){
+ rec = rec->left;
+ } else if(kcmp > 0){
+ rec = rec->right;
+ } else {
+ *sp = rec->vsiz;
+ return dbuf + rec->ksiz + TCALIGNPAD(rec->ksiz);
+ }
+ }
+ }
+ return NULL;
+}
+
+
+/* Retrieve a string record in a map object. */
+const char *tcmapget2(const TCMAP *map, const char *kstr){
+ assert(map && kstr);
+ int ksiz = strlen(kstr);
+ unsigned int hash;
+ TCMAPHASH1(hash, kstr, ksiz);
+ TCMAPREC *rec = map->buckets[hash%map->bnum];
+ TCMAPHASH2(hash, kstr, ksiz);
+ while(rec){
+ if(hash > rec->hash){
+ rec = rec->left;
+ } else if(hash < rec->hash){
+ rec = rec->right;
+ } else {
+ char *dbuf = (char *)rec + sizeof(*rec);
+ int kcmp = TCKEYCMP(kstr, ksiz, dbuf, rec->ksiz);
+ if(kcmp < 0){
+ rec = rec->left;
+ } else if(kcmp > 0){
+ rec = rec->right;
+ } else {
+ return dbuf + rec->ksiz + TCALIGNPAD(rec->ksiz);
+ }
+ }
+ }
+ return NULL;
+}
+
+
+/* Retrieve a semivolatile record in a map object. */
+const void *tcmapget3(TCMAP *map, const void *kbuf, int ksiz, int *sp){
+ assert(map && kbuf && ksiz >= 0 && sp);
+ unsigned int hash;
+ TCMAPHASH1(hash, kbuf, ksiz);
+ TCMAPREC *rec = map->buckets[hash%map->bnum];
+ TCMAPHASH2(hash, kbuf, ksiz);
+ while(rec){
+ if(hash > rec->hash){
+ rec = rec->left;
+ } else if(hash < rec->hash){
+ rec = rec->right;
+ } else {
+ char *dbuf = (char *)rec + sizeof(*rec);
+ int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
+ if(kcmp < 0){
+ rec = rec->left;
+ } else if(kcmp > 0){
+ rec = rec->right;
+ } else {
+ if(map->last != rec){
+ if(map->first == rec) map->first = rec->next;
+ if(rec->prev) rec->prev->next = rec->next;
+ if(rec->next) rec->next->prev = rec->prev;
+ rec->prev = map->last;
+ rec->next = NULL;
+ map->last->next = rec;
+ map->last = rec;
+ }
+ *sp = rec->vsiz;
+ return dbuf + rec->ksiz + TCALIGNPAD(rec->ksiz);
+ }
+ }
+ }
+ return NULL;
+}
+
+
+/* Move a record to the edge of a map object. */
+bool tcmapmove(TCMAP *map, const void *kbuf, int ksiz, bool head){
+ assert(map && kbuf && ksiz >= 0);
+ unsigned int hash;
+ TCMAPHASH1(hash, kbuf, ksiz);
+ TCMAPREC *rec = map->buckets[hash%map->bnum];
+ TCMAPHASH2(hash, kbuf, ksiz);
+ while(rec){
+ if(hash > rec->hash){
+ rec = rec->left;
+ } else if(hash < rec->hash){
+ rec = rec->right;
+ } else {
+ char *dbuf = (char *)rec + sizeof(*rec);
+ int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
+ if(kcmp < 0){
+ rec = rec->left;
+ } else if(kcmp > 0){
+ rec = rec->right;
+ } else {
+ if(head){
+ if(map->first == rec) return true;
+ if(map->last == rec) map->last = rec->prev;
+ if(rec->prev) rec->prev->next = rec->next;
+ if(rec->next) rec->next->prev = rec->prev;
+ rec->prev = NULL;
+ rec->next = map->first;
+ map->first->prev = rec;
+ map->first = rec;
+ } else {
+ if(map->last == rec) return true;
+ if(map->first == rec) map->first = rec->next;
+ if(rec->prev) rec->prev->next = rec->next;
+ if(rec->next) rec->next->prev = rec->prev;
+ rec->prev = map->last;
+ rec->next = NULL;
+ map->last->next = rec;
+ map->last = rec;
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+/* Move a string record to the edge of a map object. */
+bool tcmapmove2(TCMAP *map, const char *kstr, bool head){
+ assert(map && kstr);
+ return tcmapmove(map, kstr, strlen(kstr), head);
+}
+
+
+/* Initialize the iterator of a map object. */
+void tcmapiterinit(TCMAP *map){
+ assert(map);
+ map->cur = map->first;
+}
+
+
+/* Get the next key of the iterator of a map object. */
+const void *tcmapiternext(TCMAP *map, int *sp){
+ assert(map && sp);
+ TCMAPREC *rec;
+ if(!map->cur) return NULL;
+ rec = map->cur;
+ map->cur = rec->next;
+ *sp = rec->ksiz;
+ return (char *)rec + sizeof(*rec);
+}
+
+
+/* Get the next key string of the iterator of a map object. */
+const char *tcmapiternext2(TCMAP *map){
+ assert(map);
+ TCMAPREC *rec;
+ if(!map->cur) return NULL;
+ rec = map->cur;
+ map->cur = rec->next;
+ return (char *)rec + sizeof(*rec);
+}
+
+
+/* Get the value bound to the key fetched from the iterator of a map object. */
+const void *tcmapiterval(const void *kbuf, int *sp){
+ assert(kbuf && sp);
+ TCMAPREC *rec = (TCMAPREC *)((char *)kbuf - sizeof(*rec));
+ *sp = rec->vsiz;
+ return (char *)kbuf + rec->ksiz + TCALIGNPAD(rec->ksiz);
+}
+
+
+/* Get the value string bound to the key fetched from the iterator of a map object. */
+const char *tcmapiterval2(const char *kstr){
+ assert(kstr);
+ TCMAPREC *rec = (TCMAPREC *)(kstr - sizeof(*rec));
+ return kstr + rec->ksiz + TCALIGNPAD(rec->ksiz);
+}
+
+
+/* Get the number of records stored in a map object. */
+uint64_t tcmaprnum(const TCMAP *map){
+ assert(map);
+ return map->rnum;
+}
+
+
+/* Get the total size of memory used in a map object. */
+uint64_t tcmapmsiz(const TCMAP *map){
+ assert(map);
+ return map->msiz + map->rnum * (sizeof(*map->first) + sizeof(void *)) +
+ map->bnum * sizeof(void *);
+}
+
+
+/* Create a list object containing all keys in a map object. */
+TCLIST *tcmapkeys(const TCMAP *map){
+ assert(map);
+ TCLIST *list = tclistnew2(map->rnum);
+ TCMAPREC *cur = map->cur;
+ const char *kbuf;
+ int ksiz;
+ ((TCMAP *)map)->cur = map->first;
+ while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){
+ TCLISTPUSH(list, kbuf, ksiz);
+ }
+ ((TCMAP *)map)->cur = cur;
+ return list;
+}
+
+
+/* Create a list object containing all values in a map object. */
+TCLIST *tcmapvals(const TCMAP *map){
+ assert(map);
+ TCLIST *list = tclistnew2(map->rnum);
+ TCMAPREC *cur = map->cur;
+ const char *kbuf;
+ int ksiz;
+ ((TCMAP *)map)->cur = map->first;
+ while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){
+ int vsiz;
+ const char *vbuf = tcmapiterval(kbuf, &vsiz);
+ TCLISTPUSH(list, vbuf, vsiz);
+ }
+ ((TCMAP *)map)->cur = cur;
+ return list;
+}
+
+
+/* Add an integer to a record in a map object. */
+void tcmapaddint(TCMAP *map, const char *kbuf, int ksiz, int num){
+ assert(map && kbuf && ksiz >= 0);
+ unsigned int hash;
+ TCMAPHASH1(hash, kbuf, ksiz);
+ int bidx = hash % map->bnum;
+ TCMAPREC *rec = map->buckets[bidx];
+ TCMAPREC **entp = map->buckets + bidx;
+ TCMAPHASH2(hash, kbuf, ksiz);
+ while(rec){
+ if(hash > rec->hash){
+ entp = &(rec->left);
+ rec = rec->left;
+ } else if(hash < rec->hash){
+ entp = &(rec->right);
+ rec = rec->right;
+ } else {
+ char *dbuf = (char *)rec + sizeof(*rec);
+ int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rec->ksiz);
+ if(kcmp < 0){
+ entp = &(rec->left);
+ rec = rec->left;
+ } else if(kcmp > 0){
+ entp = &(rec->right);
+ rec = rec->right;
+ } else {
+ int psiz = TCALIGNPAD(ksiz);
+ *(int *)(dbuf + ksiz + psiz) += num;
+ return;
+ }
+ }
+ }
+ int psiz = TCALIGNPAD(ksiz);
+ TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + sizeof(num) + 1);
+ char *dbuf = (char *)rec + sizeof(*rec);
+ memcpy(dbuf, kbuf, ksiz);
+ dbuf[ksiz] = '\0';
+ rec->ksiz = ksiz;
+ memcpy(dbuf + ksiz + psiz, &num, sizeof(num));
+ dbuf[ksiz+psiz+sizeof(num)] = '\0';
+ rec->vsiz = sizeof(num);
+ rec->hash = hash;
+ rec->left = NULL;
+ rec->right = NULL;
+ rec->prev = map->last;
+ rec->next = NULL;
+ *entp = rec;
+ if(!map->first) map->first = rec;
+ if(map->last) map->last->next = rec;
+ map->last = rec;
+ map->rnum++;
+}
+
+
+/* Clear a map object. */
+void tcmapclear(TCMAP *map){
+ assert(map);
+ TCMAPREC *rec = map->first;
+ while(rec){
+ TCMAPREC *next = rec->next;
+ free(rec);
+ rec = next;
+ }
+ TCMAPREC **buckets = map->buckets;
+ int bnum = map->bnum;
+ for(int i = 0; i < bnum; i++){
+ buckets[i] = NULL;
+ }
+ map->first = NULL;
+ map->last = NULL;
+ map->cur = NULL;
+ map->rnum = 0;
+ map->msiz = 0;
+}
+
+
+/* Remove front records of a map object. */
+void tcmapcutfront(TCMAP *map, int num){
+ assert(map && num >= 0);
+ tcmapiterinit(map);
+ while(num-- > 0){
+ int ksiz;
+ const char *kbuf = tcmapiternext(map, &ksiz);
+ if(!kbuf) break;
+ tcmapout(map, kbuf, ksiz);
+ }
+}
+
+
+/* Serialize a map object into a byte array. */
+void *tcmapdump(const TCMAP *map, int *sp){
+ assert(map && sp);
+ TCMAPREC *cur = map->cur;
+ int tsiz = 0;
+ const char *kbuf, *vbuf;
+ int ksiz, vsiz;
+ ((TCMAP *)map)->cur = map->first;
+ while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){
+ vbuf = tcmapiterval(kbuf, &vsiz);
+ tsiz += ksiz + vsiz + sizeof(int) * 2;
+ }
+ char *buf;
+ TCMALLOC(buf, tsiz + 1);
+ char *wp = buf;
+ ((TCMAP *)map)->cur = map->first;
+ while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){
+ vbuf = tcmapiterval(kbuf, &vsiz);
+ int step;
+ TCSETVNUMBUF(step, wp, ksiz);
+ wp += step;
+ memcpy(wp, kbuf, ksiz);
+ wp += ksiz;
+ TCSETVNUMBUF(step, wp, vsiz);
+ wp += step;
+ memcpy(wp, vbuf, vsiz);
+ wp += vsiz;
+ }
+ ((TCMAP *)map)->cur = cur;
+ *sp = wp - buf;
+ return buf;
+}
+
+
+/* Create a map object from a serialized byte array. */
+TCMAP *tcmapload(const void *ptr, int size){
+ assert(ptr && size >= 0);
+ TCMAP *map = tcmapnew();
+ const char *rp = ptr;
+ const char *ep = (char *)ptr + size;
+ while(rp < ep){
+ int step, ksiz, vsiz;
+ TCREADVNUMBUF(rp, ksiz, step);
+ rp += step;
+ const char *kbuf = rp;
+ rp += ksiz;
+ TCREADVNUMBUF(rp, vsiz, step);
+ rp += step;
+ tcmapputkeep(map, kbuf, ksiz, rp, vsiz);
+ rp += vsiz;
+ }
+ return map;
+}
+
+
+/* Extract a map record from a serialized byte array. */
+void *tcmaploadone(const void *ptr, int size, const void *kbuf, int ksiz, int *sp){
+ assert(ptr && size >= 0 && kbuf && ksiz >= 0 && sp);
+ const char *rp = ptr;
+ const char *ep = (char *)ptr + size;
+ while(rp < ep){
+ int step, rsiz;
+ TCREADVNUMBUF(rp, rsiz, step);
+ rp += step;
+ if(rsiz == ksiz && !memcmp(kbuf, rp, rsiz)){
+ rp += rsiz;
+ TCREADVNUMBUF(rp, rsiz, step);
+ rp += step;
+ *sp = rsiz;
+ char *rv;
+ TCMEMDUP(rv, rp, rsiz);
+ return rv;
+ }
+ rp += rsiz;
+ TCREADVNUMBUF(rp, rsiz, step);
+ rp += step;
+ rp += rsiz;
+ }
+ return NULL;
+}
+
+
+
+/*************************************************************************************************
+ * on-memory database
+ *************************************************************************************************/
+
+
+#define TCMDBMNUM 8 // number of internal maps
+#define TCMDBBNUM 65536 // allocation unit number of a map
+
+/* get the first hash value */
+#define TCMDBHASH(TC_res, TC_kbuf, TC_ksiz) \
+ do { \
+ const unsigned char *_TC_p = (const unsigned char *)(TC_kbuf) + TC_ksiz - 1; \
+ int _TC_ksiz = TC_ksiz; \
+ for((TC_res) = 0x20071123; _TC_ksiz--;){ \
+ (TC_res) = ((TC_res) << 5) + (TC_res) + *(_TC_p)--; \
+ } \
+ (TC_res) &= TCMDBMNUM - 1; \
+ } while(false)
+
+
+/* Create an on-memory database object. */
+TCMDB *tcmdbnew(void){
+ return tcmdbnew2(TCMDBBNUM);
+}
+
+
+/* Create an on-memory database with specifying the number of the buckets. */
+TCMDB *tcmdbnew2(uint32_t bnum){
+ TCMDB *mdb;
+ if(bnum < 1) bnum = TCMDBBNUM;
+ bnum = bnum / TCMDBMNUM + 17;
+ TCMALLOC(mdb, sizeof(*mdb));
+ TCMALLOC(mdb->mmtxs, sizeof(pthread_rwlock_t) * TCMDBMNUM);
+ TCMALLOC(mdb->imtx, sizeof(pthread_mutex_t));
+ TCMALLOC(mdb->maps, sizeof(TCMAP *) * TCMDBMNUM);
+ if(pthread_mutex_init(mdb->imtx, NULL) != 0) tcmyfatal("mutex error");
+ for(int i = 0; i < TCMDBMNUM; i++){
+ if(pthread_rwlock_init((pthread_rwlock_t *)mdb->mmtxs + i, NULL) != 0)
+ tcmyfatal("rwlock error");
+ mdb->maps[i] = tcmapnew2(bnum);
+ }
+ mdb->iter = -1;
+ return mdb;
+}
+
+
+/* Delete an on-memory database object. */
+void tcmdbdel(TCMDB *mdb){
+ assert(mdb);
+ for(int i = TCMDBMNUM - 1; i >= 0; i--){
+ tcmapdel(mdb->maps[i]);
+ pthread_rwlock_destroy((pthread_rwlock_t *)mdb->mmtxs + i);
+ }
+ pthread_mutex_destroy(mdb->imtx);
+ free(mdb->maps);
+ free(mdb->imtx);
+ free(mdb->mmtxs);
+ free(mdb);
+}
+
+
+/* Store a record into an on-memory database. */
+void tcmdbput(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+ assert(mdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ unsigned int mi;
+ TCMDBHASH(mi, kbuf, ksiz);
+ if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return;
+ tcmapput(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz);
+ pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+}
+
+
+/* Store a string record into an on-memory database. */
+void tcmdbput2(TCMDB *mdb, const char *kstr, const char *vstr){
+ assert(mdb && kstr && vstr);
+ tcmdbput(mdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Store a record of the value of two regions into an on-memory database object. */
+void tcmdbput3(TCMDB *mdb, const char *kbuf, int ksiz,
+ const void *fvbuf, int fvsiz, const char *lvbuf, int lvsiz){
+ assert(mdb && kbuf && ksiz >= 0 && fvbuf && fvsiz >= 0 && lvbuf && lvsiz >= 0);
+ unsigned int mi;
+ TCMDBHASH(mi, kbuf, ksiz);
+ if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return;
+ tcmapput3(mdb->maps[mi], kbuf, ksiz, fvbuf, fvsiz, lvbuf, lvsiz);
+ pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+}
+
+
+/* Store a new record into an on-memory database. */
+bool tcmdbputkeep(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+ assert(mdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ unsigned int mi;
+ TCMDBHASH(mi, kbuf, ksiz);
+ if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return false;
+ bool rv = tcmapputkeep(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz);
+ pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+ return rv;
+}
+
+
+/* Store a new string record into an on-memory database. */
+bool tcmdbputkeep2(TCMDB *mdb, const char *kstr, const char *vstr){
+ assert(mdb && kstr && vstr);
+ return tcmdbputkeep(mdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Concatenate a value at the end of the value of the existing record in an on-memory database. */
+void tcmdbputcat(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
+ assert(mdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
+ unsigned int mi;
+ TCMDBHASH(mi, kbuf, ksiz);
+ if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return;
+ tcmapputcat(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz);
+ pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+}
+
+
+/* Concatenate a string at the end of the value of the existing record in an on-memory database. */
+void tcmdbputcat2(TCMDB *mdb, const char *kstr, const char *vstr){
+ assert(mdb && kstr && vstr);
+ tcmdbputcat(mdb, kstr, strlen(kstr), vstr, strlen(vstr));
+}
+
+
+/* Remove a record of an on-memory database. */
+bool tcmdbout(TCMDB *mdb, const void *kbuf, int ksiz){
+ assert(mdb && kbuf && ksiz >= 0);
+ unsigned int mi;
+ TCMDBHASH(mi, kbuf, ksiz);
+ if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return false;
+ bool rv = tcmapout(mdb->maps[mi], kbuf, ksiz);
+ pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+ return rv;
+}
+
+
+/* Remove a string record of an on-memory database. */
+bool tcmdbout2(TCMDB *mdb, const char *kstr){
+ assert(mdb && kstr);
+ return tcmdbout(mdb, kstr, strlen(kstr));
+}
+
+
+/* Retrieve a record in an on-memory database. */
+void *tcmdbget(TCMDB *mdb, const void *kbuf, int ksiz, int *sp){
+ assert(mdb && kbuf && ksiz >= 0 && sp);
+ unsigned int mi;
+ TCMDBHASH(mi, kbuf, ksiz);
+ if(pthread_rwlock_rdlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return NULL;
+ int vsiz;
+ const char *vbuf = tcmapget(mdb->maps[mi], kbuf, ksiz, &vsiz);
+ char *rv;
+ if(vbuf){
+ TCMEMDUP(rv, vbuf, vsiz);
+ *sp = vsiz;
+ } else {
+ rv = NULL;
+ }
+ pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+ return rv;
+}
+
+
+/* Retrieve a string record in an on-memory database. */
+char *tcmdbget2(TCMDB *mdb, const char *kstr){
+ assert(mdb && kstr);
+ int vsiz;
+ return tcmdbget(mdb, kstr, strlen(kstr), &vsiz);
+}
+
+
+/* Retrieve a record and move it astern in an on-memory database. */
+void *tcmdbget3(TCMDB *mdb, const void *kbuf, int ksiz, int *sp){
+ assert(mdb && kbuf && ksiz >= 0 && sp);
+ unsigned int mi;
+ TCMDBHASH(mi, kbuf, ksiz);
+ if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return NULL;
+ int vsiz;
+ const char *vbuf = tcmapget3(mdb->maps[mi], kbuf, ksiz, &vsiz);
+ char *rv;
+ if(vbuf){
+ TCMEMDUP(rv, vbuf, vsiz);
+ *sp = vsiz;
+ } else {
+ rv = NULL;
+ }
+ pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+ return rv;
+}
+
+
+/* Get the size of the value of a record in an on-memory database object. */
+int tcmdbvsiz(TCMDB *mdb, const void *kbuf, int ksiz){
+ assert(mdb && kbuf && ksiz >= 0);
+ unsigned int mi;
+ TCMDBHASH(mi, kbuf, ksiz);
+ if(pthread_rwlock_rdlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return -1;
+ int vsiz;
+ const char *vbuf = tcmapget(mdb->maps[mi], kbuf, ksiz, &vsiz);
+ if(!vbuf) vsiz = -1;
+ pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+ return vsiz;
+}
+
+
+/* Get the size of the value of a string record in an on-memory database object. */
+int tcmdbvsiz2(TCMDB *mdb, const char *kstr){
+ assert(mdb && kstr);
+ return tcmdbvsiz(mdb, kstr, strlen(kstr));
+}
+
+
+/* Initialize the iterator of an on-memory database. */
+void tcmdbiterinit(TCMDB *mdb){
+ assert(mdb);
+ if(pthread_mutex_lock(mdb->imtx) != 0) return;
+ for(int i = 0; i < TCMDBMNUM; i++){
+ tcmapiterinit(mdb->maps[i]);
+ }
+ mdb->iter = 0;
+ pthread_mutex_unlock(mdb->imtx);
+}
+
+
+/* Get the next key of the iterator of an on-memory database. */
+void *tcmdbiternext(TCMDB *mdb, int *sp){
+ assert(mdb && sp);
+ if(pthread_mutex_lock(mdb->imtx) != 0) return NULL;
+ if(mdb->iter < 0 || mdb->iter >= TCMDBMNUM){
+ pthread_mutex_unlock(mdb->imtx);
+ return NULL;
+ }
+ int mi = mdb->iter;
+ if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0){
+ pthread_mutex_unlock(mdb->imtx);
+ return NULL;
+ }
+ int ksiz;
+ const char *kbuf;
+ while(!(kbuf = tcmapiternext(mdb->maps[mi], &ksiz)) && mi < TCMDBMNUM - 1){
+ pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+ mi = ++mdb->iter;
+ if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0){
+ pthread_mutex_unlock(mdb->imtx);
+ return NULL;
+ }
+ }
+ char *rv;
+ if(kbuf){
+ TCMEMDUP(rv, kbuf, ksiz);
+ *sp = ksiz;
+ } else {
+ rv = NULL;
+ }
+ pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi);
+ pthread_mutex_unlock(mdb->imtx);
+ return rv;
+}
+
+
+/* Get the next key string of the iterator of an on-memory database. */
+char *tcmdbiternext2(TCMDB *mdb){
+ assert(mdb);
+ int ksiz;
+ return tcmdbiternext(mdb, &ksiz);
+}
+
+
+/* Get forward matching keys in an on-memory database object. */
+TCLIST *tcmdbfwmkeys(TCMDB *mdb, const void *pbuf, int psiz, int max){
+ assert(mdb && pbuf && psiz >= 0);
+ TCLIST* keys = tclistnew();
+ if(pthread_mutex_lock(mdb->imtx) != 0) return keys;
+ if(max < 0) max = INT_MAX;
+ for(int i = 0; i < TCMDBMNUM && TCLISTNUM(keys) < max; i++){
+ if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + i) == 0){
+ TCMAP *map = mdb->maps[i];
+ TCMAPREC *cur = map->cur;
+ tcmapiterinit(map);
+ int ksiz;
+ const char *kbuf;
+ while(TCLISTNUM(keys) < max && (kbuf = tcmapiternext(map, &ksiz)) != NULL){
+ if(ksiz >= psiz && !memcmp(kbuf, pbuf, psiz)) tclistpush(keys, kbuf, ksiz);
+ }
+ map->cur = cur;
+ pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i);
+ }
+ }
+ pthread_mutex_unlock(mdb->imtx);
+ return keys;
+}
+
+
+/* Get forward matching string keys in an on-memory database object. */
+TCLIST *tcmdbfwmkeys2(TCMDB *mdb, const char *pstr, int max){
+ assert(mdb && pstr);
+ return tcmdbfwmkeys(mdb, pstr, strlen(pstr), max);
+}
+
+
+/* Get the number of records stored in an on-memory database. */
+uint64_t tcmdbrnum(TCMDB *mdb){
+ assert(mdb);
+ uint64_t rnum = 0;
+ for(int i = 0; i < TCMDBMNUM; i++){
+ rnum += tcmaprnum(mdb->maps[i]);
+ }
+ return rnum;
+}
+
+
+/* Get the total size of memory used in an on-memory database object. */
+uint64_t tcmdbmsiz(TCMDB *mdb){
+ assert(mdb);
+ uint64_t msiz = 0;
+ for(int i = 0; i < TCMDBMNUM; i++){
+ msiz += tcmapmsiz(mdb->maps[i]);
+ }
+ return msiz;
+}
+
+
+/* Clear an on-memory database object. */
+void tcmdbvanish(TCMDB *mdb){
+ assert(mdb);
+ for(int i = 0; i < TCMDBMNUM; i++){
+ if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + i) == 0){
+ tcmapclear(mdb->maps[i]);
+ pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i);
+ }
+ }
+}
+
+
+/* Remove front records of a map object. */
+void tcmdbcutfront(TCMDB *mdb, int num){
+ assert(mdb && num >= 0);
+ num = num / TCMDBMNUM + 1;
+ for(int i = 0; i < TCMDBMNUM; i++){
+ if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + i) == 0){
+ tcmapcutfront(mdb->maps[i], num);
+ pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i);
+ }
+ }
+}
+
+
+
+/*************************************************************************************************
+ * memory pool
+ *************************************************************************************************/
+
+
+#define TCMPOOLUNIT 128 // allocation unit size of memory pool elements
+
+
+/* Global memory pool object. */
+TCMPOOL *tcglobalmemorypool = NULL;
+
+
+/* private function prototypes */
+static void tcmpooldelglobal(void);
+
+
+/* Create a memory pool object. */
+TCMPOOL *tcmpoolnew(void){
+ TCMPOOL *mpool;
+ TCMALLOC(mpool, sizeof(*mpool));
+ TCMALLOC(mpool->mutex, sizeof(pthread_mutex_t));
+ if(pthread_mutex_init(mpool->mutex, NULL) != 0) tcmyfatal("locking failed");
+ mpool->anum = TCMPOOLUNIT;
+ TCMALLOC(mpool->elems, sizeof(mpool->elems[0]) * mpool->anum);
+ mpool->num = 0;
+ return mpool;
+}
+
+
+/* Delete a memory pool object. */
+void tcmpooldel(TCMPOOL *mpool){
+ assert(mpool);
+ TCMPELEM *elems = mpool->elems;
+ for(int i = mpool->num - 1; i >= 0; i--){
+ elems[i].del(elems[i].ptr);
+ }
+ free(elems);
+ pthread_mutex_destroy(mpool->mutex);
+ free(mpool->mutex);
+ free(mpool);
+}
+
+
+/* Relegate an arbitrary object to a memory pool object. */
+void tcmpoolput(TCMPOOL *mpool, void *ptr, void (*del)(void *)){
+ assert(mpool && ptr && del);
+ if(pthread_mutex_lock(mpool->mutex) != 0) tcmyfatal("locking failed");
+ int num = mpool->num;
+ if(num >= mpool->anum){
+ mpool->anum *= 2;
+ TCREALLOC(mpool->elems, mpool->elems, mpool->anum * sizeof(mpool->elems[0]));
+ }
+ mpool->elems[num].ptr = ptr;
+ mpool->elems[num].del = del;
+ mpool->num++;
+ pthread_mutex_unlock(mpool->mutex);
+}
+
+
+/* Relegate an allocated region to a memory pool object. */
+void tcmpoolputptr(TCMPOOL *mpool, void *ptr){
+ assert(mpool && ptr);
+ tcmpoolput(mpool, ptr, (void (*)(void *))free);
+}
+
+
+/* Relegate an extensible string object to a memory pool object. */
+void tcmpoolputxstr(TCMPOOL *mpool, TCXSTR *xstr){
+ assert(mpool && xstr);
+ tcmpoolput(mpool, xstr, (void (*)(void *))tcxstrdel);
+}
+
+
+/* Relegate a list object to a memory pool object. */
+void tcmpoolputlist(TCMPOOL *mpool, TCLIST *list){
+ assert(mpool && list);
+ tcmpoolput(mpool, list, (void (*)(void *))tclistdel);
+}
+
+
+/* Relegate a map object to a memory pool object. */
+void tcmpoolputmap(TCMPOOL *mpool, TCMAP *map){
+ assert(mpool && map);
+ tcmpoolput(mpool, map, (void (*)(void *))tcmapdel);
+}
+
+
+/* Allocate a region relegated to a memory pool object. */
+void *tcmpoolmalloc(TCMPOOL *mpool, size_t size){
+ assert(mpool && size > 0);
+ void *ptr;
+ TCMALLOC(ptr, size);
+ tcmpoolput(mpool, ptr, (void (*)(void *))free);
+ return ptr;
+}
+
+
+/* Create an extensible string object relegated to a memory pool object. */
+TCXSTR *tcmpoolxstrnew(TCMPOOL *mpool){
+ assert(mpool);
+ TCXSTR *xstr = tcxstrnew();
+ tcmpoolput(mpool, xstr, (void (*)(void *))tcxstrdel);
+ return xstr;
+}
+
+
+/* Create a list object relegated to a memory pool object. */
+TCLIST *tcmpoollistnew(TCMPOOL *mpool){
+ assert(mpool);
+ TCLIST *list = tclistnew();
+ tcmpoolput(mpool, list, (void (*)(void *))tclistdel);
+ return list;
+}
+
+
+/* Create a map object relegated to a memory pool object. */
+TCMAP *tcmpoolmapnew(TCMPOOL *mpool){
+ assert(mpool);
+ TCMAP *map = tcmapnew();
+ tcmpoolput(mpool, map, (void (*)(void *))tcmapdel);
+ return map;
+}
+
+
+/* Get the global memory pool object. */
+TCMPOOL *tcmpoolglobal(void){
+ if(tcglobalmemorypool) return tcglobalmemorypool;
+ tcglobalmemorypool = tcmpoolnew();
+ atexit(tcmpooldelglobal);
+ return tcglobalmemorypool;
+}
+
+
+/* Detete global memory pool object. */
+static void tcmpooldelglobal(void){
+ if(tcglobalmemorypool) tcmpooldel(tcglobalmemorypool);
+}
+
+
+
+/*************************************************************************************************
+ * miscellaneous utilities
+ *************************************************************************************************/
+
+
+#define TCRANDDEV "/dev/urandom" // path of the random device file
+#define TCDISTBUFSIZ 16384 // size of an distance buffer
+
+
+/* File descriptor of random number generator. */
+int tcrandomdevfd = -1;
+
+
+/* private function prototypes */
+static void tcrandomfdclose(void);
+static time_t tcmkgmtime(struct tm *tm);
+
+
+/* Get the larger value of two integers. */
+long tclmax(long a, long b){
+ return (a > b) ? a : b;
+}
+
+
+/* Get the lesser value of two integers. */
+long tclmin(long a, long b){
+ return (a < b) ? a : b;
+}
+
+
+/* Get a random number as long integer based on uniform distribution. */
+unsigned long tclrand(void){
+ static unsigned int cnt = 0;
+ static unsigned int seed = 0;
+ static unsigned long mask = 0;
+ static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+ if((cnt & 0xff) == 0 && pthread_mutex_lock(&mutex) == 0){
+ if(cnt++ == 0) seed = time(NULL);
+ if(tcrandomdevfd == -1 && (tcrandomdevfd = open(TCRANDDEV, O_RDONLY, 00644)) != -1)
+ atexit(tcrandomfdclose);
+ if(tcrandomdevfd != -1) read(tcrandomdevfd, &mask, sizeof(mask));
+ pthread_mutex_unlock(&mutex);
+ }
+ return (mask ^ cnt++) ^ (unsigned long)rand_r(&seed);
+}
+
+
+/* Get a random number as double decimal based on uniform distribution. */
+double tcdrand(void){
+ return tclrand() / (ULONG_MAX + 0.01);
+}
+
+
+/* Get a random number as double decimal based on normal distribution. */
+double tcdrandnd(double avg, double sd){
+ assert(sd >= 0.0);
+ return sqrt(-2.0 * log(tcdrand())) * cos(2 * 3.141592653589793 * tcdrand()) * sd + avg;
+}
+
+
+/* Compare two strings with case insensitive evaluation. */
+int tcstricmp(const char *astr, const char *bstr){
+ assert(astr && bstr);
+ while(*astr != '\0'){
+ if(*bstr == '\0') return 1;
+ int ac = (*astr >= 'A' && *astr <= 'Z') ? *astr + ('a' - 'A') : *(unsigned char *)astr;
+ int bc = (*bstr >= 'A' && *bstr <= 'Z') ? *bstr + ('a' - 'A') : *(unsigned char *)bstr;
+ if(ac != bc) return ac - bc;
+ astr++;
+ bstr++;
+ }
+ return (*bstr == '\0') ? 0 : -1;
+}
+
+
+/* Check whether a string begins with a key. */
+bool tcstrfwm(const char *str, const char *key){
+ assert(str && key);
+ while(*key != '\0'){
+ if(*str != *key || *str == '\0') return false;
+ key++;
+ str++;
+ }
+ return true;
+}
+
+
+/* Check whether a string begins with a key with case insensitive evaluation. */
+bool tcstrifwm(const char *str, const char *key){
+ assert(str && key);
+ while(*key != '\0'){
+ if(*str == '\0') return false;
+ int sc = *str;
+ if(sc >= 'A' && sc <= 'Z') sc += 'a' - 'A';
+ int kc = *key;
+ if(kc >= 'A' && kc <= 'Z') kc += 'a' - 'A';
+ if(sc != kc) return false;
+ key++;
+ str++;
+ }
+ return true;
+}
+
+
+/* Check whether a string ends with a key. */
+bool tcstrbwm(const char *str, const char *key){
+ assert(str && key);
+ int slen = strlen(str);
+ int klen = strlen(key);
+ for(int i = 1; i <= klen; i++){
+ if(i > slen || str[slen-i] != key[klen-i]) return false;
+ }
+ return true;
+}
+
+
+/* Check whether a string ends with a key with case insensitive evaluation. */
+bool tcstribwm(const char *str, const char *key){
+ assert(str && key);
+ int slen = strlen(str);
+ int klen = strlen(key);
+ for(int i = 1; i <= klen; i++){
+ if(i > slen) return false;
+ int sc = str[slen-i];
+ if(sc >= 'A' && sc <= 'Z') sc += 'a' - 'A';
+ int kc = key[klen-i];
+ if(kc >= 'A' && kc <= 'Z') kc += 'a' - 'A';
+ if(sc != kc) return false;
+ }
+ return true;
+}
+
+
+/* Calculate the edit distance of two strings. */
+int tcstrdist(const char *astr, const char *bstr){
+ assert(astr && bstr);
+ int alen = strlen(astr);
+ int blen = strlen(bstr);
+ int dsiz = blen + 1;
+ int tbuf[TCDISTBUFSIZ];
+ int *tbl;
+ if((alen + 1) * dsiz < TCDISTBUFSIZ){
+ tbl = tbuf;
+ } else {
+ TCMALLOC(tbl, (alen + 1) * dsiz * sizeof(*tbl));
+ }
+ for(int i = 0; i <= alen; i++){
+ tbl[i*dsiz] = i;
+ }
+ for(int i = 1; i <= blen; i++){
+ tbl[i] = i;
+ }
+ astr--;
+ bstr--;
+ for(int i = 1; i <= alen; i++){
+ for(int j = 1; j <= blen; j++){
+ int ac = tbl[(i-1)*dsiz+j] + 1;
+ int bc = tbl[i*dsiz+j-1] + 1;
+ int cc = tbl[(i-1)*dsiz+j-1] + (astr[i] != bstr[j]);
+ ac = ac < bc ? ac : bc;
+ tbl[i*dsiz+j] = ac < cc ? ac : cc;
+ }
+ }
+ int rv = tbl[alen*dsiz+blen];
+ if(tbl != tbuf) free(tbl);
+ return rv;
+}
+
+
+/* Calculate the edit distance of two UTF-8 strings. */
+int tcstrdistutf(const char *astr, const char *bstr){
+ assert(astr && bstr);
+ int alen = strlen(astr);
+ uint16_t abuf[TCDISTBUFSIZ];
+ uint16_t *aary;
+ if(alen < TCDISTBUFSIZ){
+ aary = abuf;
+ } else {
+ TCMALLOC(aary, alen * sizeof(*aary));
+ }
+ tcstrutftoucs(astr, aary, &alen);
+ int blen = strlen(bstr);
+ uint16_t bbuf[TCDISTBUFSIZ];
+ uint16_t *bary;
+ if(blen < TCDISTBUFSIZ){
+ bary = bbuf;
+ } else {
+ TCMALLOC(bary, blen * sizeof(*bary));
+ }
+ tcstrutftoucs(bstr, bary, &blen);
+ int dsiz = blen + 1;
+ int tbuf[TCDISTBUFSIZ];
+ int *tbl;
+ if((alen + 1) * dsiz < TCDISTBUFSIZ){
+ tbl = tbuf;
+ } else {
+ TCMALLOC(tbl, (alen + 1) * dsiz * sizeof(*tbl));
+ }
+ for(int i = 0; i <= alen; i++){
+ tbl[i*dsiz] = i;
+ }
+ for(int i = 1; i <= blen; i++){
+ tbl[i] = i;
+ }
+ aary--;
+ bary--;
+ for(int i = 1; i <= alen; i++){
+ for(int j = 1; j <= blen; j++){
+ int ac = tbl[(i-1)*dsiz+j] + 1;
+ int bc = tbl[i*dsiz+j-1] + 1;
+ int cc = tbl[(i-1)*dsiz+j-1] + (aary[i] != bary[j]);
+ ac = ac < bc ? ac : bc;
+ tbl[i*dsiz+j] = ac < cc ? ac : cc;
+ }
+ }
+ aary++;
+ bary++;
+ int rv = tbl[alen*dsiz+blen];
+ if(tbl != tbuf) free(tbl);
+ if(bary != bbuf) free(bary);
+ if(aary != abuf) free(aary);
+ return rv;
+}
+
+
+/* Convert the letters of a string into upper case. */
+char *tcstrtoupper(char *str){
+ assert(str);
+ char *wp = str;
+ while(*wp != '\0'){
+ if(*wp >= 'a' && *wp <= 'z') *wp -= 'a' - 'A';
+ wp++;
+ }
+ return str;
+}
+
+
+/* Convert the letters of a string into lower case. */
+char *tcstrtolower(char *str){
+ assert(str);
+ char *wp = str;
+ while(*wp != '\0'){
+ if(*wp >= 'A' && *wp <= 'Z') *wp += 'a' - 'A';
+ wp++;
+ }
+ return str;
+}
+
+
+/* Cut space characters at head or tail of a string. */
+char *tcstrtrim(char *str){
+ assert(str);
+ const char *rp = str;
+ char *wp = str;
+ bool head = true;
+ while(*rp != '\0'){
+ if(*rp > '\0' && *rp <= ' '){
+ if(!head) *(wp++) = *rp;
+ } else {
+ *(wp++) = *rp;
+ head = false;
+ }
+ rp++;
+ }
+ *wp = '\0';
+ while(wp > str && wp[-1] > '\0' && wp[-1] <= ' '){
+ *(--wp) = '\0';
+ }
+ return str;
+}
+
+
+/* Squeeze space characters in a string and trim it. */
+char *tcstrsqzspc(char *str){
+ assert(str);
+ char *rp = str;
+ char *wp = str;
+ bool spc = true;
+ while(*rp != '\0'){
+ if(*rp > 0 && *rp <= ' '){
+ if(!spc) *(wp++) = *rp;
+ spc = true;
+ } else {
+ *(wp++) = *rp;
+ spc = false;
+ }
+ rp++;
+ }
+ *wp = '\0';
+ for(wp--; wp >= str; wp--){
+ if(*wp > 0 && *wp <= ' '){
+ *wp = '\0';
+ } else {
+ break;
+ }
+ }
+ return str;
+}
+
+
+/* Substitute characters in a string. */
+char *tcstrsubchr(char *str, const char *rstr, const char *sstr){
+ assert(str && rstr && sstr);
+ int slen = strlen(sstr);
+ char *wp = str;
+ for(int i = 0; str[i] != '\0'; i++){
+ const char *p = strchr(rstr, str[i]);
+ if(p){
+ int idx = p - rstr;
+ if(idx < slen) *(wp++) = sstr[idx];
+ } else {
+ *(wp++) = str[i];
+ }
+ }
+ *wp = '\0';
+ return str;
+}
+
+
+/* Count the number of characters in a string of UTF-8. */
+int tcstrcntutf(const char *str){
+ assert(str);
+ const unsigned char *rp = (unsigned char *)str;
+ int cnt = 0;
+ while(*rp != '\0'){
+ if((*rp & 0x80) == 0x00 || (*rp & 0xe0) == 0xc0 ||
+ (*rp & 0xf0) == 0xe0 || (*rp & 0xf8) == 0xf0) cnt++;
+ rp++;
+ }
+ return cnt;
+}
+
+
+/* Cut a string of UTF-8 at the specified number of characters. */
+char *tcstrcututf(char *str, int num){
+ assert(str && num >= 0);
+ unsigned char *wp = (unsigned char *)str;
+ int cnt = 0;
+ while(*wp != '\0'){
+ if((*wp & 0x80) == 0x00 || (*wp & 0xe0) == 0xc0 ||
+ (*wp & 0xf0) == 0xe0 || (*wp & 0xf8) == 0xf0){
+ cnt++;
+ if(cnt > num){
+ *wp = '\0';
+ break;
+ }
+ }
+ wp++;
+ }
+ return str;
+}
+
+
+/* Convert a UTF-8 string into a UCS-2 array. */
+void tcstrutftoucs(const char *str, uint16_t *ary, int *np){
+ assert(str && ary && np);
+ const unsigned char *rp = (unsigned char *)str;
+ unsigned int wi = 0;
+ while(*rp != '\0'){
+ int c = *(unsigned char *)rp;
+ if(c < 0x80){
+ ary[wi++] = c;
+ } else if(c < 0xe0){
+ if(rp[1] >= 0x80){
+ ary[wi++] = ((rp[0] & 0x1f) << 6) | (rp[1] & 0x3f);
+ rp++;
+ }
+ } else if(c < 0xf0){
+ if(rp[1] >= 0x80 && rp[2] >= 0x80){
+ ary[wi++] = ((rp[0] & 0xf) << 12) | ((rp[1] & 0x3f) << 6) | (rp[2] & 0x3f);
+ rp += 2;
+ }
+ }
+ rp++;
+ }
+ *np = wi;
+}
+
+
+/* Convert a UCS-2 array into a UTF-8 string. */
+void tcstrucstoutf(const uint16_t *ary, int num, char *str){
+ assert(ary && num >= 0 && str);
+ unsigned char *wp = (unsigned char *)str;
+ for(int i = 0; i < num; i++){
+ unsigned int c = ary[i];
+ if(c < 0x80){
+ *(wp++) = c;
+ } else if(c < 0x800){
+ *(wp++) = 0xc0 | (c >> 6);
+ *(wp++) = 0x80 | (c & 0x3f);
+ } else {
+ *(wp++) = 0xe0 | (c >> 12);
+ *(wp++) = 0x80 | ((c & 0xfff) >> 6);
+ *(wp++) = 0x80 | (c & 0x3f);
+ }
+ }
+ *wp = '\0';
+}
+
+
+/* Create a list object by splitting a string. */
+TCLIST *tcstrsplit(const char *str, const char *delims){
+ assert(str && delims);
+ TCLIST *list = tclistnew();
+ while(true){
+ const char *sp = str;
+ while(*str != '\0' && !strchr(delims, *str)){
+ str++;
+ }
+ TCLISTPUSH(list, sp, str - sp);
+ if(*str == '\0') break;
+ str++;
+ }
+ return list;
+}
+
+
+/* Create a string by joining all elements of a list object. */
+char *tcstrjoin(TCLIST *list, char delim){
+ assert(list);
+ int num = TCLISTNUM(list);
+ int size = num + 1;
+ for(int i = 0; i < num; i++){
+ size += TCLISTVALNUM(list, i);
+ }
+ char *buf;
+ TCMALLOC(buf, size);
+ char *wp = buf;
+ for(int i = 0; i < num; i++){
+ if(i > 0) *(wp++) = delim;
+ int vsiz;
+ const char *vbuf = tclistval(list, i, &vsiz);
+ memcpy(wp, vbuf, vsiz);
+ wp += vsiz;
+ }
+ *wp = '\0';
+ return buf;
+}
+
+
+/* Check whether a string matches a regular expression. */
+bool tcregexmatch(const char *str, const char *regex){
+ assert(str && regex);
+ int options = REG_EXTENDED | REG_NOSUB;
+ if(*regex == '*'){
+ options |= REG_ICASE;
+ regex++;
+ }
+ regex_t rbuf;
+ if(regcomp(&rbuf, regex, options) != 0) return false;
+ bool rv = regexec(&rbuf, str, 0, NULL, 0) == 0;
+ regfree(&rbuf);
+ return rv;
+}
+
+
+/* Replace each substring matching a regular expression string. */
+char *tcregexreplace(const char *str, const char *regex, const char *alt){
+ assert(str && regex && alt);
+ int options = REG_EXTENDED;
+ if(*regex == '*'){
+ options |= REG_ICASE;
+ regex++;
+ }
+ regex_t rbuf;
+ if(regex[0] == '\0' || regcomp(&rbuf, regex, options) != 0) return tcstrdup(str);
+ regmatch_t subs[256];
+ if(regexec(&rbuf, str, 32, subs, 0) != 0){
+ regfree(&rbuf);
+ return tcstrdup(str);
+ }
+ const char *sp = str;
+ TCXSTR *xstr = tcxstrnew();
+ bool first = true;
+ while(sp[0] != '\0' && regexec(&rbuf, sp, 10, subs, first ? 0 : REG_NOTBOL) == 0){
+ first = false;
+ if(subs[0].rm_so == -1) break;
+ tcxstrcat(xstr, sp, subs[0].rm_so);
+ for(const char *rp = alt; *rp != '\0'; rp++){
+ if(*rp == '\\'){
+ if(rp[1] >= '0' && rp[1] <= '9'){
+ int num = rp[1] - '0';
+ if(subs[num].rm_so != -1 && subs[num].rm_eo != -1)
+ tcxstrcat(xstr, sp + subs[num].rm_so, subs[num].rm_eo - subs[num].rm_so);
+ ++rp;
+ } else if(rp[1] != '\0'){
+ tcxstrcat(xstr, ++rp, 1);
+ }
+ } else if(*rp == '&'){
+ tcxstrcat(xstr, sp + subs[0].rm_so, subs[0].rm_eo - subs[0].rm_so);
+ } else {
+ tcxstrcat(xstr, rp, 1);
+ }
+ }
+ sp += subs[0].rm_eo;
+ if(subs[0].rm_eo < 1) break;
+ }
+ tcxstrcat2(xstr, sp);
+ regfree(&rbuf);
+ return tcxstrtomalloc(xstr);
+}
+
+
+/* Get the time of day in seconds. */
+double tctime(void){
+ struct timeval tv;
+ if(gettimeofday(&tv, NULL) == -1) return 0.0;
+ return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
+}
+
+
+/* Get the Gregorian calendar of a time. */
+void tccalendar(int64_t t, int jl, int *yearp, int *monp, int *dayp,
+ int *hourp, int *minp, int *secp){
+ if(t == INT64_MAX || jl == INT_MAX){
+ struct timeval tv;
+ struct timezone tz;
+ if(gettimeofday(&tv, &tz) == 0){
+ if(t == INT64_MAX) t = tv.tv_sec;
+ if(jl == INT_MAX) jl = tz.tz_minuteswest * -60;
+ } else {
+ if(yearp) *yearp = 0;
+ if(monp) *monp = 0;
+ if(dayp) *dayp = 0;
+ if(hourp) *hourp = 0;
+ if(minp) *minp = 0;
+ if(secp) *secp = 0;
+ }
+ }
+ time_t tt = (time_t)t + jl;
+ struct tm ts;
+ if(!gmtime_r(&tt, &ts)){
+ if(yearp) *yearp = 0;
+ if(monp) *monp = 0;
+ if(dayp) *dayp = 0;
+ if(hourp) *hourp = 0;
+ if(minp) *minp = 0;
+ if(secp) *secp = 0;
+ }
+ if(yearp) *yearp = ts.tm_year + 1900;
+ if(monp) *monp = ts.tm_mon + 1;
+ if(dayp) *dayp = ts.tm_mday;
+ if(hourp) *hourp = ts.tm_hour;
+ if(minp) *minp = ts.tm_min;
+ if(secp) *secp = ts.tm_sec;
+}
+
+
+/* Format a date as a string in W3CDTF. */
+void tcdatestrwww(int64_t t, int jl, char *buf){
+ assert(buf);
+ if(t == INT64_MAX || jl == INT_MAX){
+ struct timeval tv;
+ struct timezone tz;
+ if(gettimeofday(&tv, &tz) == 0){
+ if(t == INT64_MAX) t = tv.tv_sec;
+ if(jl == INT_MAX) jl = tz.tz_minuteswest * -60;
+ } else {
+ if(t == INT64_MAX) t = time(NULL);
+ if(jl == INT_MAX) jl = 0;
+ }
+ }
+ time_t tt = (time_t)t + jl;
+ struct tm ts;
+ if(!gmtime_r(&tt, &ts)) memset(&ts, 0, sizeof(ts));
+ ts.tm_year += 1900;
+ ts.tm_mon += 1;
+ jl /= 60;
+ char tzone[16];
+ if(jl == 0){
+ sprintf(tzone, "Z");
+ } else if(jl < 0){
+ jl *= -1;
+ sprintf(tzone, "-%02d:%02d", jl / 60, jl % 60);
+ } else {
+ sprintf(tzone, "+%02d:%02d", jl / 60, jl % 60);
+ }
+ sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d%s",
+ ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, tzone);
+}
+
+
+/* Format a date as a string in RFC 1123 format. */
+void tcdatestrhttp(int64_t t, int jl, char *buf){
+ assert(buf);
+ if(t == INT64_MAX || jl == INT_MAX){
+ struct timeval tv;
+ struct timezone tz;
+ if(gettimeofday(&tv, &tz) == 0){
+ if(t == INT64_MAX) t = tv.tv_sec;
+ if(jl == INT_MAX) jl = tz.tz_minuteswest * -60;
+ } else {
+ if(t == INT64_MAX) t = time(NULL);
+ if(jl == INT_MAX) jl = 0;
+ }
+ }
+ time_t tt = (time_t)t + jl;
+ struct tm ts;
+ if(!gmtime_r(&tt, &ts)) memset(&ts, 0, sizeof(ts));
+ ts.tm_year += 1900;
+ ts.tm_mon += 1;
+ jl /= 60;
+ char *wp = buf;
+ switch(tcdayofweek(ts.tm_year, ts.tm_mon, ts.tm_mday)){
+ case 0: wp += sprintf(wp, "Sun, "); break;
+ case 1: wp += sprintf(wp, "Mon, "); break;
+ case 2: wp += sprintf(wp, "Tue, "); break;
+ case 3: wp += sprintf(wp, "Wed, "); break;
+ case 4: wp += sprintf(wp, "Thu, "); break;
+ case 5: wp += sprintf(wp, "Fri, "); break;
+ case 6: wp += sprintf(wp, "Sat, "); break;
+ }
+ wp += sprintf(wp, "%02d ", ts.tm_mday);
+ switch(ts.tm_mon){
+ case 1: wp += sprintf(wp, "Jan "); break;
+ case 2: wp += sprintf(wp, "Feb "); break;
+ case 3: wp += sprintf(wp, "Mar "); break;
+ case 4: wp += sprintf(wp, "Apr "); break;
+ case 5: wp += sprintf(wp, "May "); break;
+ case 6: wp += sprintf(wp, "Jun "); break;
+ case 7: wp += sprintf(wp, "Jul "); break;
+ case 8: wp += sprintf(wp, "Aug "); break;
+ case 9: wp += sprintf(wp, "Sep "); break;
+ case 10: wp += sprintf(wp, "Oct "); break;
+ case 11: wp += sprintf(wp, "Nov "); break;
+ case 12: wp += sprintf(wp, "Dec "); break;
+ }
+ wp += sprintf(wp, "%04d %02d:%02d:%02d ", ts.tm_year, ts.tm_hour, ts.tm_min, ts.tm_sec);
+ if(jl == 0){
+ sprintf(wp, "GMT");
+ } else if(jl < 0){
+ jl *= -1;
+ sprintf(wp, "-%02d%02d", jl / 60, jl % 60);
+ } else {
+ sprintf(wp, "+%02d%02d", jl / 60, jl % 60);
+ }
+}
+
+
+/* Get the time value of a date string. */
+int64_t tcstrmktime(const char *str){
+ assert(str);
+ while(*str > '\0' && *str <= ' '){
+ str++;
+ }
+ if(*str == '\0') return 0;
+ if(str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
+ return (int64_t)strtoll(str + 2, NULL, 16);
+ struct tm ts;
+ memset(&ts, 0, sizeof(ts));
+ ts.tm_year = 70;
+ ts.tm_mon = 0;
+ ts.tm_mday = 1;
+ ts.tm_hour = 0;
+ ts.tm_min = 0;
+ ts.tm_sec = 0;
+ ts.tm_isdst = 0;
+ int len = strlen(str);
+ char *pv;
+ time_t t = (time_t)strtoll(str, &pv, 10);
+ if(*(signed char *)pv >= '\0' && *pv <= ' '){
+ while(*pv > '\0' && *pv <= ' '){
+ pv++;
+ }
+ if(*pv == '\0') return (int64_t)t;
+ }
+ if((pv[0] == 's' || pv[0] == 'S') && ((signed char *)pv)[1] >= '\0' && pv[1] <= ' ')
+ return (int64_t)t;
+ if((pv[0] == 'm' || pv[0] == 'M') && ((signed char *)pv)[1] >= '\0' && pv[1] <= ' ')
+ return (int64_t)t * 60;
+ if((pv[0] == 'h' || pv[0] == 'H') && ((signed char *)pv)[1] >= '\0' && pv[1] <= ' ')
+ return (int64_t)t * 60 * 60;
+ if((pv[0] == 'd' || pv[0] == 'D') && ((signed char *)pv)[1] >= '\0' && pv[1] <= ' ')
+ return (int64_t)t * 60 * 60 * 24;
+ if(len > 4 && str[4] == '-'){
+ ts.tm_year = atoi(str) - 1900;
+ if((pv = strchr(str, '-')) != NULL && pv - str == 4){
+ char *rp = pv + 1;
+ ts.tm_mon = atoi(rp) - 1;
+ if((pv = strchr(rp, '-')) != NULL && pv - str == 7){
+ rp = pv + 1;
+ ts.tm_mday = atoi(rp);
+ if((pv = strchr(rp, 'T')) != NULL && pv - str == 10){
+ rp = pv + 1;
+ ts.tm_hour = atoi(rp);
+ if((pv = strchr(rp, ':')) != NULL && pv - str == 13){
+ rp = pv + 1;
+ ts.tm_min = atoi(rp);
+ }
+ if((pv = strchr(rp, ':')) != NULL && pv - str == 16){
+ rp = pv + 1;
+ ts.tm_sec = atoi(rp);
+ }
+ if((pv = strchr(rp, '.')) != NULL && pv - str >= 19) rp = pv + 1;
+ strtol(rp, &pv, 10);
+ if((*pv == '+' || *pv == '-') && strlen(pv) >= 6 && pv[3] == ':')
+ ts.tm_sec -= (atoi(pv + 1) * 3600 + atoi(pv + 4) * 60) * (pv[0] == '+' ? 1 : -1);
+ }
+ }
+ }
+ return (int64_t)tcmkgmtime(&ts);
+ }
+ if(len > 4 && str[4] == '/'){
+ ts.tm_year = atoi(str) - 1900;
+ if((pv = strchr(str, '/')) != NULL && pv - str == 4){
+ char *rp = pv + 1;
+ ts.tm_mon = atoi(rp) - 1;
+ if((pv = strchr(rp, '/')) != NULL && pv - str == 7){
+ rp = pv + 1;
+ ts.tm_mday = atoi(rp);
+ if((pv = strchr(rp, ' ')) != NULL && pv - str == 10){
+ rp = pv + 1;
+ ts.tm_hour = atoi(rp);
+ if((pv = strchr(rp, ':')) != NULL && pv - str == 13){
+ rp = pv + 1;
+ ts.tm_min = atoi(rp);
+ }
+ if((pv = strchr(rp, ':')) != NULL && pv - str == 16){
+ rp = pv + 1;
+ ts.tm_sec = atoi(rp);
+ }
+ if((pv = strchr(rp, '.')) != NULL && pv - str >= 19) rp = pv + 1;
+ strtol(rp, &pv, 10);
+ if((*pv == '+' || *pv == '-') && strlen(pv) >= 6 && pv[3] == ':')
+ ts.tm_sec -= (atoi(pv + 1) * 3600 + atoi(pv + 4) * 60) * (pv[0] == '+' ? 1 : -1);
+ }
+ }
+ }
+ return (int64_t)tcmkgmtime(&ts);
+ }
+ const char *crp = str;
+ if(len >= 4 && str[3] == ',') crp = str + 4;
+ while(*crp == ' '){
+ crp++;
+ }
+ ts.tm_mday = atoi(crp);
+ while((*crp >= '0' && *crp <= '9') || *crp == ' '){
+ crp++;
+ }
+ if(tcstrifwm(crp, "Jan")){
+ ts.tm_mon = 0;
+ } else if(tcstrifwm(crp, "Feb")){
+ ts.tm_mon = 1;
+ } else if(tcstrifwm(crp, "Mar")){
+ ts.tm_mon = 2;
+ } else if(tcstrifwm(crp, "Apr")){
+ ts.tm_mon = 3;
+ } else if(tcstrifwm(crp, "May")){
+ ts.tm_mon = 4;
+ } else if(tcstrifwm(crp, "Jun")){
+ ts.tm_mon = 5;
+ } else if(tcstrifwm(crp, "Jul")){
+ ts.tm_mon = 6;
+ } else if(tcstrifwm(crp, "Aug")){
+ ts.tm_mon = 7;
+ } else if(tcstrifwm(crp, "Sep")){
+ ts.tm_mon = 8;
+ } else if(tcstrifwm(crp, "Oct")){
+ ts.tm_mon = 9;
+ } else if(tcstrifwm(crp, "Nov")){
+ ts.tm_mon = 10;
+ } else if(tcstrifwm(crp, "Dec")){
+ ts.tm_mon = 11;
+ } else {
+ ts.tm_mon = -1;
+ }
+ if(ts.tm_mon >= 0) crp += 3;
+ while(*crp == ' '){
+ crp++;
+ }
+ ts.tm_year = atoi(crp);
+ if(ts.tm_year >= 1969) ts.tm_year -= 1900;
+ while(*crp >= '0' && *crp <= '9'){
+ crp++;
+ }
+ while(*crp == ' '){
+ crp++;
+ }
+ if(ts.tm_mday > 0 && ts.tm_mon >= 0 && ts.tm_year >= 0){
+ int clen = strlen(crp);
+ if(clen >= 8 && crp[2] == ':' && crp[5] == ':'){
+ ts.tm_hour = atoi(crp + 0);
+ ts.tm_min = atoi(crp + 3);
+ ts.tm_sec = atoi(crp + 6);
+ if(clen >= 14 && crp[8] == ' ' && (crp[9] == '+' || crp[9] == '-')){
+ ts.tm_sec -= ((crp[10] - '0') * 36000 + (crp[11] - '0') * 3600 +
+ (crp[12] - '0') * 600 + (crp[13] - '0') * 60) * (crp[9] == '+' ? 1 : -1);
+ } else if(clen > 9){
+ if(!strcmp(crp + 9, "JST")){
+ ts.tm_sec -= 9 * 3600;
+ } else if(!strcmp(crp + 9, "CCT")){
+ ts.tm_sec -= 8 * 3600;
+ } else if(!strcmp(crp + 9, "KST")){
+ ts.tm_sec -= 9 * 3600;
+ } else if(!strcmp(crp + 9, "EDT")){
+ ts.tm_sec -= -4 * 3600;
+ } else if(!strcmp(crp + 9, "EST")){
+ ts.tm_sec -= -5 * 3600;
+ } else if(!strcmp(crp + 9, "CDT")){
+ ts.tm_sec -= -5 * 3600;
+ } else if(!strcmp(crp + 9, "CST")){
+ ts.tm_sec -= -6 * 3600;
+ } else if(!strcmp(crp + 9, "MDT")){
+ ts.tm_sec -= -6 * 3600;
+ } else if(!strcmp(crp + 9, "MST")){
+ ts.tm_sec -= -7 * 3600;
+ } else if(!strcmp(crp + 9, "PDT")){
+ ts.tm_sec -= -7 * 3600;
+ } else if(!strcmp(crp + 9, "PST")){
+ ts.tm_sec -= -8 * 3600;
+ } else if(!strcmp(crp + 9, "HDT")){
+ ts.tm_sec -= -9 * 3600;
+ } else if(!strcmp(crp + 9, "HST")){
+ ts.tm_sec -= -10 * 3600;
+ }
+ }
+ }
+ return (int64_t)tcmkgmtime(&ts);
+ }
+ return 0;
+}
+
+
+/* Get the day of week of a date. */
+int tcdayofweek(int year, int mon, int day){
+ if(mon < 3){
+ year--;
+ mon += 12;
+ }
+ return (day + ((8 + (13 * mon)) / 5) + (year + (year / 4) - (year / 100) + (year / 400))) % 7;
+}
+
+
+/* Close the random number generator. */
+static void tcrandomfdclose(void){
+ close(tcrandomdevfd);
+}
+
+
+/* Make the GMT from a time structure.
+ `tm' specifies the pointer to the time structure.
+ The return value is the GMT. */
+static time_t tcmkgmtime(struct tm *tm){
+#if defined(_SYS_LINUX_)
+ assert(tm);
+ return timegm(tm);
+#else
+ assert(tm);
+ static int jl = INT_MAX;
+ if(jl == INT_MAX){
+ struct timeval tv;
+ struct timezone tz;
+ if(gettimeofday(&tv, &tz) == 0){
+ jl = tz.tz_minuteswest * -60;
+ } else {
+ jl = 0;
+ }
+ }
+ tm->tm_sec += jl;
+ return mktime(tm);
+#endif
+}
+
+
+
+/*************************************************************************************************
+ * filesystem utilities
+ *************************************************************************************************/
+
+
+#define TCFILEMODE 00644 // permission of a creating file
+#define TCIOBUFSIZ 16384 // size of an I/O buffer
+
+
+/* Get the canonicalized absolute path of a file. */
+char *tcrealpath(const char *path){
+ assert(path);
+ char buf[PATH_MAX];
+ if(!realpath(path, buf)) return NULL;
+ return tcstrdup(buf);
+}
+
+
+/* Read whole data of a file. */
+void *tcreadfile(const char *path, int limit, int *sp){
+ int fd = path ? open(path, O_RDONLY, TCFILEMODE) : 0;
+ if(fd == -1) return NULL;
+ if(fd == 0){
+ TCXSTR *xstr = tcxstrnew();
+ char buf[TCIOBUFSIZ];
+ limit = limit > 0 ? limit : INT_MAX;
+ int rsiz;
+ while((rsiz = read(fd, buf, tclmin(TCIOBUFSIZ, limit))) > 0){
+ TCXSTRCAT(xstr, buf, rsiz);
+ limit -= rsiz;
+ }
+ if(sp) *sp = TCXSTRSIZE(xstr);
+ return tcxstrtomalloc(xstr);
+ }
+ struct stat sbuf;
+ if(fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)){
+ close(fd);
+ return NULL;
+ }
+ limit = limit > 0 ? tclmin((int)sbuf.st_size, limit) : sbuf.st_size;
+ char *buf;
+ TCMALLOC(buf, sbuf.st_size + 1);
+ char *wp = buf;
+ int rsiz;
+ while((rsiz = read(fd, wp, limit - (wp - buf))) > 0){
+ wp += rsiz;
+ }
+ *wp = '\0';
+ close(fd);
+ if(sp) *sp = wp - buf;
+ return buf;
+}
+
+
+/* Read every line of a file. */
+TCLIST *tcreadfilelines(const char *path){
+ int fd = path ? open(path, O_RDONLY, TCFILEMODE) : 0;
+ if(fd == -1) return NULL;
+ TCLIST *list = tclistnew();
+ TCXSTR *xstr = tcxstrnew();
+ char buf[TCIOBUFSIZ];
+ int rsiz;
+ while((rsiz = read(fd, buf, TCIOBUFSIZ)) > 0){
+ for(int i = 0; i < rsiz; i++){
+ switch(buf[i]){
+ case '\r':
+ break;
+ case '\n':
+ TCLISTPUSH(list, TCXSTRPTR(xstr), TCXSTRSIZE(xstr));
+ tcxstrclear(xstr);
+ break;
+ default:
+ TCXSTRCAT(xstr, buf + i, 1);
+ break;
+ }
+ }
+ }
+ TCLISTPUSH(list, TCXSTRPTR(xstr), TCXSTRSIZE(xstr));
+ tcxstrdel(xstr);
+ if(path) close(fd);
+ return list;
+}
+
+
+/* Write data into a file. */
+bool tcwritefile(const char *path, const void *ptr, int size){
+ assert(ptr && size >= 0);
+ int fd = 1;
+ if(path && (fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, TCFILEMODE)) == -1) return false;
+ bool err = false;
+ if(!tcwrite(fd, ptr, size)) err = true;
+ if(close(fd) == -1) err = true;
+ return !err;
+}
+
+
+/* Copy a file. */
+bool tccopyfile(const char *src, const char *dest){
+ int ifd = open(src, O_RDONLY, TCFILEMODE);
+ if(ifd == -1) return false;
+ int ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, TCFILEMODE);
+ if(ofd == -1){
+ close(ifd);
+ return false;
+ }
+ bool err = false;
+ while(true){
+ char buf[TCIOBUFSIZ];
+ int size = read(ifd, buf, TCIOBUFSIZ);
+ if(size > 0){
+ if(!tcwrite(ofd, buf, size)){
+ err = true;
+ break;
+ }
+ } else if(size == -1){
+ if(errno != EINTR){
+ err = true;
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ if(close(ofd) == -1) err = true;
+ if(close(ifd) == -1) err = true;
+ return !err;
+}
+
+
+/* Read names of files in a directory. */
+TCLIST *tcreaddir(const char *path){
+ assert(path);
+ DIR *DD;
+ struct dirent *dp;
+ if(!(DD = opendir(path))) return NULL;
+ TCLIST *list = tclistnew();
+ while((dp = readdir(DD)) != NULL){
+ if(!strcmp(dp->d_name, MYCDIRSTR) || !strcmp(dp->d_name, MYPDIRSTR)) continue;
+ TCLISTPUSH(list, dp->d_name, strlen(dp->d_name));
+ }
+ closedir(DD);
+ return list;
+}
+
+
+/* Expand a pattern into a list of matched paths. */
+TCLIST *tcglobpat(const char *pattern){
+ assert(pattern);
+ TCLIST *list = tclistnew();
+ glob_t gbuf;
+ memset(&gbuf, 0, sizeof(gbuf));
+ if(glob(pattern, GLOB_ERR | GLOB_NOSORT, NULL, &gbuf) == 0){
+ for(int i = 0; i < gbuf.gl_pathc; i++){
+ tclistpush2(list, gbuf.gl_pathv[i]);
+ }
+ globfree(&gbuf);
+ }
+ return list;
+}
+
+
+/* Remove a file or a directory and its sub ones recursively. */
+bool tcremovelink(const char *path){
+ assert(path);
+ struct stat sbuf;
+ if(lstat(path, &sbuf) == -1) return false;
+ if(unlink(path) == 0) return true;
+ TCLIST *list;
+ if(!S_ISDIR(sbuf.st_mode) || !(list = tcreaddir(path))) return false;
+ bool tail = path[0] != '\0' && path[strlen(path)-1] == MYPATHCHR;
+ for(int i = 0; i < TCLISTNUM(list); i++){
+ const char *elem = TCLISTVALPTR(list, i);
+ if(!strcmp(MYCDIRSTR, elem) || !strcmp(MYPDIRSTR, elem)) continue;
+ char *cpath;
+ if(tail){
+ cpath = tcsprintf("%s%s", path, elem);
+ } else {
+ cpath = tcsprintf("%s%c%s", path, MYPATHCHR, elem);
+ }
+ tcremovelink(cpath);
+ free(cpath);
+ }
+ tclistdel(list);
+ return rmdir(path) == 0 ? true : false;
+}
+
+
+/* Write data into a file. */
+bool tcwrite(int fd, const void *buf, size_t size){
+ assert(fd >= 0 && buf && size >= 0);
+ const char *rp = buf;
+ do {
+ int wb = write(fd, rp, size);
+ switch(wb){
+ case -1: if(errno != EINTR) return false;
+ case 0: break;
+ default:
+ rp += wb;
+ size -= wb;
+ break;
+ }
+ } while(size > 0);
+ return true;
+}
+
+
+/* Read data from a file. */
+bool tcread(int fd, void *buf, size_t size){
+ assert(fd >= 0 && buf && size >= 0);
+ char *wp = buf;
+ do {
+ int rb = read(fd, wp, size);
+ switch(rb){
+ case -1: if(errno != EINTR) return false;
+ case 0: return size < 1;
+ default:
+ wp += rb;
+ size -= rb;
+ }
+ } while(size > 0);
+ return true;
+}
+
+
+/* Lock a file. */
+bool tclock(int fd, bool ex, bool nb){
+ assert(fd >= 0);
+ struct flock lock;
+ memset(&lock, 0, sizeof(struct flock));
+ lock.l_type = ex ? F_WRLCK : F_RDLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+ lock.l_pid = 0;
+ while(fcntl(fd, nb ? F_SETLK : F_SETLKW, &lock) == -1){
+ if(errno != EINTR) return false;
+ }
+ return true;
+}
+
+
+
+/*************************************************************************************************
+ * encoding utilities
+ *************************************************************************************************/
+
+
+#define TCURLELBNUM 31 // bucket number of URL elements
+#define TCENCBUFSIZ 32 // size of a buffer for encoding name
+#define TCXMLATBNUM 31 // bucket number of XML attributes
+
+
+/* Encode a serial object with URL encoding. */
+char *tcurlencode(const char *ptr, int size){
+ assert(ptr && size >= 0);
+ char *buf;
+ TCMALLOC(buf, size * 3 + 1);
+ char *wp = buf;
+ for(int i = 0; i < size; i++){
+ int c = ((unsigned char *)ptr)[i];
+ if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
+ (c >= '0' && c <= '9') || (c != '\0' && strchr("_-.!~*'()", c))){
+ *(wp++) = c;
+ } else {
+ wp += sprintf(wp, "%%%02X", c);
+ }
+ }
+ *wp = '\0';
+ return buf;
+}
+
+
+/* Decode a string encoded with URL encoding. */
+char *tcurldecode(const char *str, int *sp){
+ assert(str && sp);
+ char *buf = tcstrdup(str);
+ char *wp = buf;
+ while(*str != '\0'){
+ if(*str == '%'){
+ str++;
+ if(((str[0] >= '0' && str[0] <= '9') || (str[0] >= 'A' && str[0] <= 'F') ||
+ (str[0] >= 'a' && str[0] <= 'f')) &&
+ ((str[1] >= '0' && str[1] <= '9') || (str[1] >= 'A' && str[1] <= 'F') ||
+ (str[1] >= 'a' && str[1] <= 'f'))){
+ unsigned char c = *str;
+ if(c >= 'A' && c <= 'Z') c += 'a' - 'A';
+ if(c >= 'a' && c <= 'z'){
+ *wp = c - 'a' + 10;
+ } else {
+ *wp = c - '0';
+ }
+ *wp *= 0x10;
+ str++;
+ c = *str;
+ if(c >= 'A' && c <= 'Z') c += 'a' - 'A';
+ if(c >= 'a' && c <= 'z'){
+ *wp += c - 'a' + 10;
+ } else {
+ *wp += c - '0';
+ }
+ str++;
+ wp++;
+ } else {
+ break;
+ }
+ } else if(*str == '+'){
+ *wp = ' ';
+ str++;
+ wp++;
+ } else {
+ *wp = *str;
+ str++;
+ wp++;
+ }
+ }
+ *wp = '\0';
+ *sp = wp - buf;
+ return buf;
+}
+
+
+/* Break up a URL into elements. */
+TCMAP *tcurlbreak(const char *str){
+ assert(str);
+ TCMAP *map = tcmapnew2(TCURLELBNUM);
+ char *tmp = tcstrdup(str);
+ const char *rp = tcstrtrim(tmp);
+ tcmapput2(map, "self", rp);
+ bool serv = false;
+ if(tcstrifwm(rp, "http://")){
+ tcmapput2(map, "scheme", "http");
+ rp += 7;
+ serv = true;
+ } else if(tcstrifwm(rp, "https://")){
+ tcmapput2(map, "scheme", "https");
+ rp += 8;
+ serv = true;
+ } else if(tcstrifwm(rp, "ftp://")){
+ tcmapput2(map, "scheme", "ftp");
+ rp += 6;
+ serv = true;
+ } else if(tcstrifwm(rp, "sftp://")){
+ tcmapput2(map, "scheme", "sftp");
+ rp += 7;
+ serv = true;
+ } else if(tcstrifwm(rp, "ftps://")){
+ tcmapput2(map, "scheme", "ftps");
+ rp += 7;
+ serv = true;
+ } else if(tcstrifwm(rp, "tftp://")){
+ tcmapput2(map, "scheme", "tftp");
+ rp += 7;
+ serv = true;
+ } else if(tcstrifwm(rp, "ldap://")){
+ tcmapput2(map, "scheme", "ldap");
+ rp += 7;
+ serv = true;
+ } else if(tcstrifwm(rp, "ldaps://")){
+ tcmapput2(map, "scheme", "ldaps");
+ rp += 8;
+ serv = true;
+ } else if(tcstrifwm(rp, "file://")){
+ tcmapput2(map, "scheme", "file");
+ rp += 7;
+ serv = true;
+ }
+ char *ep;
+ if((ep = strchr(rp, '#')) != NULL){
+ tcmapput2(map, "fragment", ep + 1);
+ *ep = '\0';
+ }
+ if((ep = strchr(rp, '?')) != NULL){
+ tcmapput2(map, "query", ep + 1);
+ *ep = '\0';
+ }
+ if(serv){
+ if((ep = strchr(rp, '/')) != NULL){
+ tcmapput2(map, "path", ep);
+ *ep = '\0';
+ } else {
+ tcmapput2(map, "path", "/");
+ }
+ if((ep = strchr(rp, '@')) != NULL){
+ *ep = '\0';
+ if(rp[0] != '\0') tcmapput2(map, "authority", rp);
+ rp = ep + 1;
+ }
+ if((ep = strchr(rp, ':')) != NULL){
+ if(ep[1] != '\0') tcmapput2(map, "port", ep + 1);
+ *ep = '\0';
+ }
+ if(rp[0] != '\0') tcmapput2(map, "host", rp);
+ } else {
+ tcmapput2(map, "path", rp);
+ }
+ free(tmp);
+ if((rp = tcmapget2(map, "path")) != NULL){
+ if((ep = strrchr(rp, '/')) != NULL){
+ if(ep[1] != '\0') tcmapput2(map, "file", ep + 1);
+ } else {
+ tcmapput2(map, "file", rp);
+ }
+ }
+ if((rp = tcmapget2(map, "file")) != NULL && (!strcmp(rp, ".") || !strcmp(rp, "..")))
+ tcmapout2(map, "file");
+ return map;
+}
+
+
+/* Resolve a relative URL with an absolute URL. */
+char *tcurlresolve(const char *base, const char *target){
+ assert(base && target);
+ const char *vbuf, *path;
+ char *tmp, *wp, *enc;
+ while(*base > '\0' && *base <= ' '){
+ base++;
+ }
+ while(*target > '\0' && *target <= ' '){
+ target++;
+ }
+ if(*target == '\0') target = base;
+ TCXSTR *rbuf = tcxstrnew();
+ TCMAP *telems = tcurlbreak(target);
+ int port = 80;
+ TCMAP *belems = tcurlbreak(tcmapget2(telems, "scheme") ? target : base);
+ if((vbuf = tcmapget2(belems, "scheme")) != NULL){
+ tcxstrcat2(rbuf, vbuf);
+ TCXSTRCAT(rbuf, "://", 3);
+ if(!tcstricmp(vbuf, "https")){
+ port = 443;
+ } else if(!tcstricmp(vbuf, "ftp")){
+ port = 21;
+ } else if(!tcstricmp(vbuf, "sftp")){
+ port = 115;
+ } else if(!tcstricmp(vbuf, "ftps")){
+ port = 22;
+ } else if(!tcstricmp(vbuf, "tftp")){
+ port = 69;
+ } else if(!tcstricmp(vbuf, "ldap")){
+ port = 389;
+ } else if(!tcstricmp(vbuf, "ldaps")){
+ port = 636;
+ }
+ } else {
+ tcxstrcat2(rbuf, "http://");
+ }
+ int vsiz;
+ if((vbuf = tcmapget2(belems, "authority")) != NULL){
+ if((wp = strchr(vbuf, ':')) != NULL){
+ *wp = '\0';
+ tmp = tcurldecode(vbuf, &vsiz);
+ enc = tcurlencode(tmp, vsiz);
+ tcxstrcat2(rbuf, enc);
+ free(enc);
+ free(tmp);
+ TCXSTRCAT(rbuf, ":", 1);
+ wp++;
+ tmp = tcurldecode(wp, &vsiz);
+ enc = tcurlencode(tmp, vsiz);
+ tcxstrcat2(rbuf, enc);
+ free(enc);
+ free(tmp);
+ } else {
+ tmp = tcurldecode(vbuf, &vsiz);
+ enc = tcurlencode(tmp, vsiz);
+ tcxstrcat2(rbuf, enc);
+ free(enc);
+ free(tmp);
+ }
+ TCXSTRCAT(rbuf, "@", 1);
+ }
+ if((vbuf = tcmapget2(belems, "host")) != NULL){
+ tmp = tcurldecode(vbuf, &vsiz);
+ tcstrtolower(tmp);
+ enc = tcurlencode(tmp, vsiz);
+ tcxstrcat2(rbuf, enc);
+ free(enc);
+ free(tmp);
+ } else {
+ TCXSTRCAT(rbuf, "localhost", 9);
+ }
+ int num;
+ char numbuf[TCNUMBUFSIZ];
+ if((vbuf = tcmapget2(belems, "port")) != NULL && (num = atoi(vbuf)) != port && num > 0){
+ sprintf(numbuf, ":%d", num);
+ tcxstrcat2(rbuf, numbuf);
+ }
+ if(!(path = tcmapget2(telems, "path"))) path = "/";
+ if(path[0] == '\0' && (vbuf = tcmapget2(belems, "path")) != NULL) path = vbuf;
+ if(path[0] == '\0') path = "/";
+ TCLIST *bpaths = tclistnew();
+ TCLIST *opaths;
+ if(path[0] != '/' && (vbuf = tcmapget2(belems, "path")) != NULL){
+ opaths = tcstrsplit(vbuf, "/");
+ } else {
+ opaths = tcstrsplit("/", "/");
+ }
+ free(tclistpop2(opaths));
+ for(int i = 0; i < TCLISTNUM(opaths); i++){
+ vbuf = tclistval(opaths, i, &vsiz);
+ if(vsiz < 1 || !strcmp(vbuf, ".")) continue;
+ if(!strcmp(vbuf, "..")){
+ free(tclistpop2(bpaths));
+ } else {
+ TCLISTPUSH(bpaths, vbuf, vsiz);
+ }
+ }
+ tclistdel(opaths);
+ opaths = tcstrsplit(path, "/");
+ for(int i = 0; i < TCLISTNUM(opaths); i++){
+ vbuf = tclistval(opaths, i, &vsiz);
+ if(vsiz < 1 || !strcmp(vbuf, ".")) continue;
+ if(!strcmp(vbuf, "..")){
+ free(tclistpop2(bpaths));
+ } else {
+ TCLISTPUSH(bpaths, vbuf, vsiz);
+ }
+ }
+ tclistdel(opaths);
+ for(int i = 0; i < TCLISTNUM(bpaths); i++){
+ vbuf = TCLISTVALPTR(bpaths, i);
+ if(strchr(vbuf, '%')){
+ tmp = tcurldecode(vbuf, &vsiz);
+ } else {
+ tmp = tcstrdup(vbuf);
+ }
+ enc = tcurlencode(tmp, strlen(tmp));
+ TCXSTRCAT(rbuf, "/", 1);
+ tcxstrcat2(rbuf, enc);
+ free(enc);
+ free(tmp);
+ }
+ if(tcstrbwm(path, "/")) TCXSTRCAT(rbuf, "/", 1);
+ tclistdel(bpaths);
+ if((vbuf = tcmapget2(telems, "query")) != NULL ||
+ (*target == '#' && (vbuf = tcmapget2(belems, "query")) != NULL)){
+ TCXSTRCAT(rbuf, "?", 1);
+ TCLIST *qelems = tcstrsplit(vbuf, "&;");
+ for(int i = 0; i < TCLISTNUM(qelems); i++){
+ vbuf = TCLISTVALPTR(qelems, i);
+ if(i > 0) TCXSTRCAT(rbuf, "&", 1);
+ if((wp = strchr(vbuf, '=')) != NULL){
+ *wp = '\0';
+ tmp = tcurldecode(vbuf, &vsiz);
+ enc = tcurlencode(tmp, vsiz);
+ tcxstrcat2(rbuf, enc);
+ free(enc);
+ free(tmp);
+ TCXSTRCAT(rbuf, "=", 1);
+ wp++;
+ tmp = tcurldecode(wp, &vsiz);
+ enc = tcurlencode(tmp, strlen(tmp));
+ tcxstrcat2(rbuf, enc);
+ free(enc);
+ free(tmp);
+ } else {
+ tmp = tcurldecode(vbuf, &vsiz);
+ enc = tcurlencode(tmp, vsiz);
+ tcxstrcat2(rbuf, enc);
+ free(enc);
+ free(tmp);
+ }
+ }
+ tclistdel(qelems);
+ }
+ if((vbuf = tcmapget2(telems, "fragment")) != NULL){
+ tmp = tcurldecode(vbuf, &vsiz);
+ enc = tcurlencode(tmp, vsiz);
+ TCXSTRCAT(rbuf, "#", 1);
+ tcxstrcat2(rbuf, enc);
+ free(enc);
+ free(tmp);
+ }
+ tcmapdel(belems);
+ tcmapdel(telems);
+ return tcxstrtomalloc(rbuf);
+}
+
+
+/* Encode a serial object with Base64 encoding. */
+char *tcbaseencode(const char *ptr, int size){
+ assert(ptr && size >= 0);
+ char *tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ const unsigned char *obj = (const unsigned char *)ptr;
+ char *buf;
+ TCMALLOC(buf, 4 * (size + 2) / 3 + 1);
+ char *wp = buf;
+ for(int i = 0; i < size; i += 3){
+ switch(size - i){
+ case 1:
+ *wp++ = tbl[obj[0] >> 2];
+ *wp++ = tbl[(obj[0] & 3) << 4];
+ *wp++ = '=';
+ *wp++ = '=';
+ break;
+ case 2:
+ *wp++ = tbl[obj[0] >> 2];
+ *wp++ = tbl[((obj[0] & 3) << 4) + (obj[1] >> 4)];
+ *wp++ = tbl[(obj[1] & 0xf) << 2];
+ *wp++ = '=';
+ break;
+ default:
+ *wp++ = tbl[obj[0] >> 2];
+ *wp++ = tbl[((obj[0] & 3) << 4) + (obj[1] >> 4)];
+ *wp++ = tbl[((obj[1] & 0xf) << 2) + (obj[2] >> 6)];
+ *wp++ = tbl[obj[2] & 0x3f];
+ break;
+ }
+ obj += 3;
+ }
+ *wp = '\0';
+ return buf;
+}
+
+
+/* Decode a string encoded with Base64 encoding. */
+char *tcbasedecode(const char *str, int *sp){
+ assert(str && sp);
+ int cnt = 0;
+ int bpos = 0;
+ int eqcnt = 0;
+ int len = strlen(str);
+ unsigned char *obj;
+ TCMALLOC(obj, len + 4);
+ unsigned char *wp = obj;
+ while(bpos < len && eqcnt == 0){
+ int bits = 0;
+ int i;
+ for(i = 0; bpos < len && i < 4; bpos++){
+ if(str[bpos] >= 'A' && str[bpos] <= 'Z'){
+ bits = (bits << 6) | (str[bpos] - 'A');
+ i++;
+ } else if(str[bpos] >= 'a' && str[bpos] <= 'z'){
+ bits = (bits << 6) | (str[bpos] - 'a' + 26);
+ i++;
+ } else if(str[bpos] >= '0' && str[bpos] <= '9'){
+ bits = (bits << 6) | (str[bpos] - '0' + 52);
+ i++;
+ } else if(str[bpos] == '+'){
+ bits = (bits << 6) | 62;
+ i++;
+ } else if(str[bpos] == '/'){
+ bits = (bits << 6) | 63;
+ i++;
+ } else if(str[bpos] == '='){
+ bits <<= 6;
+ i++;
+ eqcnt++;
+ }
+ }
+ if(i == 0 && bpos >= len) continue;
+ switch(eqcnt){
+ case 0:
+ *wp++ = (bits >> 16) & 0xff;
+ *wp++ = (bits >> 8) & 0xff;
+ *wp++ = bits & 0xff;
+ cnt += 3;
+ break;
+ case 1:
+ *wp++ = (bits >> 16) & 0xff;
+ *wp++ = (bits >> 8) & 0xff;
+ cnt += 2;
+ break;
+ case 2:
+ *wp++ = (bits >> 16) & 0xff;
+ cnt += 1;
+ break;
+ }
+ }
+ obj[cnt] = '\0';
+ *sp = cnt;
+ return (char *)obj;
+}
+
+
+/* Encode a serial object with Quoted-printable encoding. */
+char *tcquoteencode(const char *ptr, int size){
+ assert(ptr && size >= 0);
+ const unsigned char *rp = (const unsigned char *)ptr;
+ char *buf;
+ TCMALLOC(buf, size * 3 + 1);
+ char *wp = buf;
+ int cols = 0;
+ for(int i = 0; i < size; i++){
+ if(rp[i] == '=' || (rp[i] < 0x20 && rp[i] != '\r' && rp[i] != '\n' && rp[i] != '\t') ||
+ rp[i] > 0x7e){
+ wp += sprintf(wp, "=%02X", rp[i]);
+ cols += 3;
+ } else {
+ *(wp++) = rp[i];
+ cols++;
+ }
+ }
+ *wp = '\0';
+ return buf;
+}
+
+
+/* Decode a string encoded with Quoted-printable encoding. */
+char *tcquotedecode(const char *str, int *sp){
+ assert(str && sp);
+ char *buf;
+ TCMALLOC(buf, strlen(str) + 1);
+ char *wp = buf;
+ for(; *str != '\0'; str++){
+ if(*str == '='){
+ str++;
+ if(*str == '\0'){
+ break;
+ } else if(str[0] == '\r' && str[1] == '\n'){
+ str++;
+ } else if(str[0] != '\n' && str[0] != '\r'){
+ if(*str >= 'A' && *str <= 'Z'){
+ *wp = (*str - 'A' + 10) * 16;
+ } else if(*str >= 'a' && *str <= 'z'){
+ *wp = (*str - 'a' + 10) * 16;
+ } else {
+ *wp = (*str - '0') * 16;
+ }
+ str++;
+ if(*str == '\0') break;
+ if(*str >= 'A' && *str <= 'Z'){
+ *wp += *str - 'A' + 10;
+ } else if(*str >= 'a' && *str <= 'z'){
+ *wp += *str - 'a' + 10;
+ } else {
+ *wp += *str - '0';
+ }
+ wp++;
+ }
+ } else {
+ *wp = *str;
+ wp++;
+ }
+ }
+ *wp = '\0';
+ *sp = wp - buf;
+ return buf;
+}
+
+
+/* Encode a string with MIME encoding. */
+char *tcmimeencode(const char *str, const char *encname, bool base){
+ assert(str && encname);
+ int len = strlen(str);
+ char *buf;
+ TCMALLOC(buf, len * 3 + strlen(encname) + 16);
+ char *wp = buf;
+ wp += sprintf(wp, "=?%s?%c?", encname, base ? 'B' : 'Q');
+ char *enc = base ? tcbaseencode(str, len) : tcquoteencode(str, len);
+ wp += sprintf(wp, "%s?=", enc);
+ free(enc);
+ return buf;
+}
+
+
+/* Decode a string encoded with MIME encoding. */
+char *tcmimedecode(const char *str, char *enp){
+ assert(str);
+ if(enp) sprintf(enp, "US-ASCII");
+ char *buf;
+ TCMALLOC(buf, strlen(str) + 1);
+ char *wp = buf;
+ while(*str != '\0'){
+ if(tcstrfwm(str, "=?")){
+ str += 2;
+ const char *pv = str;
+ const char *ep = strchr(str, '?');
+ if(!ep) continue;
+ if(enp && ep - pv < TCENCBUFSIZ){
+ memcpy(enp, pv, ep - pv);
+ enp[ep-pv] = '\0';
+ }
+ pv = ep + 1;
+ bool quoted = (*pv == 'Q' || *pv == 'q');
+ if(*pv != '\0') pv++;
+ if(*pv != '\0') pv++;
+ if(!(ep = strchr(pv, '?'))) continue;
+ char *tmp;
+ TCMEMDUP(tmp, pv, ep - pv);
+ int len;
+ char *dec = quoted ? tcquotedecode(tmp, &len) : tcbasedecode(tmp, &len);
+ wp += sprintf(wp, "%s", dec);
+ free(dec);
+ free(tmp);
+ str = ep + 1;
+ if(*str != '\0') str++;
+ } else {
+ *(wp++) = *str;
+ str++;
+ }
+ }
+ *wp = '\0';
+ return buf;
+}
+
+
+/* Compress a serial object with Packbits encoding. */
+char *tcpackencode(const char *ptr, int size, int *sp){
+ assert(ptr && size >= 0 && sp);
+ char *buf;
+ TCMALLOC(buf, size * 2 + 1);
+ char *wp = buf;
+ const char *end = ptr + size;
+ while(ptr < end){
+ char *sp = wp;
+ const char *rp = ptr + 1;
+ int step = 1;
+ while(rp < end && step < 0x7f && *rp == *ptr){
+ step++;
+ rp++;
+ }
+ if(step <= 1 && rp < end){
+ wp = sp + 1;
+ *(wp++) = *ptr;
+ while(rp < end && step < 0x7f && *rp != *(rp - 1)){
+ *(wp++) = *rp;
+ step++;
+ rp++;
+ }
+ if(rp < end && *(rp - 1) == *rp){
+ wp--;
+ rp--;
+ step--;
+ }
+ *sp = step == 1 ? 1 : -step;
+ } else {
+ *(wp++) = step;
+ *(wp++) = *ptr;
+ }
+ ptr += step;
+ }
+ *sp = wp - buf;
+ return buf;
+}
+
+
+/* Decompress a serial object compressed with Packbits encoding. */
+char *tcpackdecode(const char *ptr, int size, int *sp){
+ assert(ptr && size >= 0 && sp);
+ int asiz = size * 3;
+ char *buf;
+ TCMALLOC(buf, asiz + 1);
+ int wi = 0;
+ const char *end = ptr + size;
+ while(ptr < end){
+ int step = abs(*ptr);
+ if(wi + step >= asiz){
+ asiz = asiz * 2 + step;
+ TCREALLOC(buf, buf, asiz + 1);
+ }
+ if(*(ptr++) >= 0){
+ memset(buf + wi, *ptr, step);
+ ptr++;
+ } else {
+ step = tclmin(step, end - ptr);
+ memcpy(buf + wi, ptr, step);
+ ptr += step;
+ }
+ wi += step;
+ }
+ buf[wi] = '\0';
+ *sp = wi;
+ return buf;
+}
+
+
+/* Compress a serial object with Deflate encoding. */
+char *tcdeflate(const char *ptr, int size, int *sp){
+ assert(ptr && sp);
+ if(!_tc_deflate) return NULL;
+ return _tc_deflate(ptr, size, sp, _TCZMZLIB);
+}
+
+
+/* Decompress a serial object compressed with Deflate encoding. */
+char *tcinflate(const char *ptr, int size, int *sp){
+ assert(ptr && size >= 0);
+ if(!_tc_inflate) return NULL;
+ return _tc_inflate(ptr, size, sp, _TCZMZLIB);
+}
+
+
+/* Compress a serial object with GZIP encoding. */
+char *tcgzipencode(const char *ptr, int size, int *sp){
+ assert(ptr && sp);
+ if(!_tc_deflate) return NULL;
+ return _tc_deflate(ptr, size, sp, _TCZMGZIP);
+}
+
+
+/* Decompress a serial object compressed with GZIP encoding. */
+char *tcgzipdecode(const char *ptr, int size, int *sp){
+ assert(ptr && size >= 0);
+ if(!_tc_inflate) return NULL;
+ return _tc_inflate(ptr, size, sp, _TCZMGZIP);
+}
+
+
+/* Get the CRC32 checksum of a serial object. */
+unsigned int tcgetcrc(const char *ptr, int size){
+ assert(ptr && size >= 0);
+ if(!_tc_getcrc) return 0;
+ return _tc_getcrc(ptr, size);
+}
+
+
+/* Encode an array of nonnegative integers with BER encoding. */
+char *tcberencode(const unsigned int *ary, int anum, int *sp){
+ assert(ary && anum >= 0 && sp);
+ char *buf;
+ TCMALLOC(buf, anum * (sizeof(int) + 1) + 1);
+ char *wp = buf;
+ for(int i = 0; i < anum; i++){
+ unsigned int num = ary[i];
+ if(num < (1 << 7)){
+ *(wp++) = num;
+ } else if(num < (1 << 14)){
+ *(wp++) = (num >> 7) | 0x80;
+ *(wp++) = num & 0x7f;
+ } else if(num < (1 << 21)){
+ *(wp++) = (num >> 14) | 0x80;
+ *(wp++) = ((num >> 7) & 0x7f) | 0x80;
+ *(wp++) = num & 0x7f;
+ } else if(num < (1 << 28)){
+ *(wp++) = (num >> 21) | 0x80;
+ *(wp++) = ((num >> 14) & 0x7f) | 0x80;
+ *(wp++) = ((num >> 7) & 0x7f) | 0x80;
+ *(wp++) = num & 0x7f;
+ } else {
+ *(wp++) = (num >> 28) | 0x80;
+ *(wp++) = ((num >> 21) & 0x7f) | 0x80;
+ *(wp++) = ((num >> 14) & 0x7f) | 0x80;
+ *(wp++) = ((num >> 7) & 0x7f) | 0x80;
+ *(wp++) = num & 0x7f;
+ }
+ }
+ *sp = wp - buf;
+ return buf;
+}
+
+
+/* Decode a serial object encoded with BER encoding. */
+unsigned int *tcberdecode(const char *ptr, int size, int *np){
+ assert(ptr && size >= 0 && np);
+ unsigned int *buf;
+ TCMALLOC(buf, size * sizeof(*buf) + 1);
+ unsigned int *wp = buf;
+ while(size > 0){
+ unsigned int num = 0;
+ int c;
+ do {
+ c = *(unsigned char *)ptr;
+ num = num * 0x80 + (c & 0x7f);
+ ptr++;
+ size--;
+ } while(c >= 0x80 && size > 0);
+ *(wp++) = num;
+ }
+ *np = wp - buf;
+ return buf;
+}
+
+
+/* Escape meta characters in a string with the entity references of XML. */
+char *tcxmlescape(const char *str){
+ assert(str);
+ const char *rp = str;
+ int bsiz = 0;
+ while(*rp != '\0'){
+ switch(*rp){
+ case '&':
+ bsiz += 5;
+ break;
+ case '<':
+ bsiz += 4;
+ break;
+ case '>':
+ bsiz += 4;
+ break;
+ case '"':
+ bsiz += 6;
+ break;
+ default:
+ bsiz++;
+ break;
+ }
+ rp++;
+ }
+ char *buf;
+ TCMALLOC(buf, bsiz + 1);
+ char *wp = buf;
+ while(*str != '\0'){
+ switch(*str){
+ case '&':
+ memcpy(wp, "&", 5);
+ wp += 5;
+ break;
+ case '<':
+ memcpy(wp, "<", 4);
+ wp += 4;
+ break;
+ case '>':
+ memcpy(wp, ">", 4);
+ wp += 4;
+ break;
+ case '"':
+ memcpy(wp, """, 6);
+ wp += 6;
+ break;
+ default:
+ *(wp++) = *str;
+ break;
+ }
+ str++;
+ }
+ *wp = '\0';
+ return buf;
+}
+
+
+/* Unescape entity references in a string of XML. */
+char *tcxmlunescape(const char *str){
+ assert(str);
+ char *buf;
+ TCMALLOC(buf, strlen(str) + 1);
+ char *wp = buf;
+ while(*str != '\0'){
+ if(*str == '&'){
+ if(tcstrfwm(str, "&")){
+ *(wp++) = '&';
+ str += 5;
+ } else if(tcstrfwm(str, "<")){
+ *(wp++) = '<';
+ str += 4;
+ } else if(tcstrfwm(str, ">")){
+ *(wp++) = '>';
+ str += 4;
+ } else if(tcstrfwm(str, """)){
+ *(wp++) = '"';
+ str += 6;
+ } else {
+ *(wp++) = *(str++);
+ }
+ } else {
+ *(wp++) = *(str++);
+ }
+ }
+ *wp = '\0';
+ return buf;
+}
+
+
+/* Split an XML string into tags and text sections. */
+TCLIST *tcxmlbreak(const char *str){
+ assert(str);
+ TCLIST *list = tclistnew();
+ int i = 0;
+ int pv = 0;
+ bool tag = false;
+ char *ep;
+ while(true){
+ if(str[i] == '\0'){
+ if(i > pv) TCLISTPUSH(list, str + pv, i - pv);
+ break;
+ } else if(!tag && str[i] == '<'){
+ if(str[i+1] == '!' && str[i+2] == '-' && str[i+3] == '-'){
+ if(i > pv) TCLISTPUSH(list, str + pv, i - pv);
+ if((ep = strstr(str + i, "-->")) != NULL){
+ TCLISTPUSH(list, str + i, ep - str - i + 3);
+ i = ep - str + 2;
+ pv = i + 1;
+ }
+ } else if(str[i+1] == '!' && str[i+2] == '[' && tcstrifwm(str + i, "<![CDATA[")){
+ if(i > pv) TCLISTPUSH(list, str + pv, i - pv);
+ if((ep = strstr(str + i, "]]>")) != NULL){
+ i += 9;
+ TCXSTR *xstr = tcxstrnew();
+ while(str + i < ep){
+ if(str[i] == '&'){
+ TCXSTRCAT(xstr, "&", 5);
+ } else if(str[i] == '<'){
+ TCXSTRCAT(xstr, "<", 4);
+ } else if(str[i] == '>'){
+ TCXSTRCAT(xstr, ">", 4);
+ } else {
+ TCXSTRCAT(xstr, str + i, 1);
+ }
+ i++;
+ }
+ if(TCXSTRSIZE(xstr) > 0) TCLISTPUSH(list, TCXSTRPTR(xstr), TCXSTRSIZE(xstr));
+ tcxstrdel(xstr);
+ i = ep - str + 2;
+ pv = i + 1;
+ }
+ } else {
+ if(i > pv) TCLISTPUSH(list, str + pv, i - pv);
+ tag = true;
+ pv = i;
+ }
+ } else if(tag && str[i] == '>'){
+ if(i > pv) TCLISTPUSH(list, str + pv, i - pv + 1);
+ tag = false;
+ pv = i + 1;
+ }
+ i++;
+ }
+ return list;
+}
+
+
+/* Get the map of attributes of an XML tag. */
+TCMAP *tcxmlattrs(const char *str){
+ assert(str);
+ TCMAP *map = tcmapnew2(TCXMLATBNUM);
+ const unsigned char *rp = (unsigned char *)str;
+ while(*rp == '<' || *rp == '/' || *rp == '?' || *rp == '!' || *rp == ' '){
+ rp++;
+ }
+ const unsigned char *key = rp;
+ while(*rp > 0x20 && *rp != '/' && *rp != '>'){
+ rp++;
+ }
+ tcmapputkeep(map, "", 0, (char *)key, rp - key);
+ while(*rp != '\0'){
+ while(*rp != '\0' && (*rp <= 0x20 || *rp == '/' || *rp == '?' || *rp == '>')){
+ rp++;
+ }
+ key = rp;
+ while(*rp > 0x20 && *rp != '/' && *rp != '>' && *rp != '='){
+ rp++;
+ }
+ int ksiz = rp - key;
+ while(*rp != '\0' && (*rp == '=' || *rp <= 0x20)){
+ rp++;
+ }
+ const unsigned char *val;
+ int vsiz;
+ if(*rp == '"'){
+ rp++;
+ val = rp;
+ while(*rp != '\0' && *rp != '"'){
+ rp++;
+ }
+ vsiz = rp - val;
+ } else if(*rp == '\''){
+ rp++;
+ val = rp;
+ while(*rp != '\0' && *rp != '\''){
+ rp++;
+ }
+ vsiz = rp - val;
+ } else {
+ val = rp;
+ while(*rp > 0x20 && *rp != '"' && *rp != '\'' && *rp != '>'){
+ rp++;
+ }
+ vsiz = rp - val;
+ }
+ if(*rp != '\0') rp++;
+ if(ksiz > 0){
+ char *copy;
+ TCMEMDUP(copy, val, vsiz);
+ char *raw = tcxmlunescape(copy);
+ tcmapputkeep(map, (char *)key, ksiz, raw, strlen(raw));
+ free(raw);
+ free(copy);
+ }
+ }
+ return map;
+}
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+#define TCBSENCUNIT 8192 // unit size of TCBS encoding
+#define TCBWTCNTMIN 64 // minimum element number of counting sort
+#define TCBWTCNTLV 4 // maximum recursion level of counting sort
+#define TCBWTBUFNUM 16384 // number of elements of BWT buffer
+
+typedef struct { // type of structure for a BWT character
+ int fchr; // character code of the first character
+ int tchr; // character code of the last character
+} TCBWTREC;
+
+
+/* private function prototypes */
+static void tcglobalmutexinit(void);
+static void tcglobalmutexdestroy(void);
+static void tcbwtsortstrcount(const char **arrays, int anum, int len, int level);
+static void tcbwtsortstrinsert(const char **arrays, int anum, int len, int skip);
+static void tcbwtsortstrheap(const char **arrays, int anum, int len, int skip);
+static void tcbwtsortchrcount(unsigned char *str, int len);
+static void tcbwtsortchrinsert(unsigned char *str, int len);
+static void tcbwtsortreccount(TCBWTREC *arrays, int anum);
+static void tcbwtsortrecinsert(TCBWTREC *array, int anum);
+static int tcbwtsearchrec(TCBWTREC *array, int anum, int tchr);
+static void tcmtfencode(char *ptr, int size);
+static void tcmtfdecode(char *ptr, int size);
+static int tcgammaencode(const char *ptr, int size, char *obuf);
+static int tcgammadecode(const char *ptr, int size, char *obuf);
+
+
+/* Show error message on the standard error output and exit. */
+void *tcmyfatal(const char *message){
+ assert(message);
+ if(tcfatalfunc){
+ tcfatalfunc(message);
+ } else {
+ fprintf(stderr, "fatal error: %s\n", message);
+ }
+ exit(1);
+ return NULL;
+}
+
+
+/* Global mutex object. */
+static pthread_rwlock_t tcglobalmutex;
+static pthread_once_t tcglobalmutexonce = PTHREAD_ONCE_INIT;
+
+
+/* Lock the global mutex object. */
+bool tcglobalmutexlock(void){
+ pthread_once(&tcglobalmutexonce, tcglobalmutexinit);
+ return pthread_rwlock_wrlock(&tcglobalmutex) == 0;
+}
+
+
+/* Lock the global mutex object by shared locking. */
+bool tcglobalmutexlockshared(void){
+ pthread_once(&tcglobalmutexonce, tcglobalmutexinit);
+ return pthread_rwlock_rdlock(&tcglobalmutex) == 0;
+}
+
+
+/* Unlock the global mutex object. */
+bool tcglobalmutexunlock(void){
+ return pthread_rwlock_unlock(&tcglobalmutex) == 0;
+}
+
+
+/* Compress a serial object with TCBS encoding. */
+char *tcbsencode(const char *ptr, int size, int *sp){
+ assert(ptr && size >= 0 && sp);
+ char *result;
+ TCMALLOC(result, (size * 7) / 3 + (size / TCBSENCUNIT + 1) * sizeof(uint16_t) +
+ TCBSENCUNIT * 2 + 0x200);
+ char *pv = result + size + 0x100;
+ char *wp = pv;
+ char *tp = pv + size + 0x100;
+ const char *end = ptr + size;
+ while(ptr < end){
+ int usiz = tclmin(TCBSENCUNIT, end - ptr);
+ memcpy(tp, ptr, usiz);
+ memcpy(tp + usiz, ptr, usiz);
+ char *sp = wp;
+ uint16_t idx = 0;
+ wp += sizeof(idx);
+ const char *arrays[usiz+1];
+ for(int i = 0; i < usiz; i++){
+ arrays[i] = tp + i;
+ }
+ const char *fp = arrays[0];
+ if(usiz >= TCBWTCNTMIN){
+ tcbwtsortstrcount(arrays, usiz, usiz, 0);
+ } else if(usiz > 1){
+ tcbwtsortstrinsert(arrays, usiz, usiz, 0);
+ }
+ for(int i = 0; i < usiz; i++){
+ int tidx = arrays[i] - fp;
+ if(tidx == 0){
+ idx = i;
+ *(wp++) = ptr[usiz-1];
+ } else {
+ *(wp++) = ptr[tidx-1];
+ }
+ }
+ idx = TCHTOIS(idx);
+ memcpy(sp, &idx, sizeof(idx));
+ ptr += TCBSENCUNIT;
+ }
+ size = wp - pv;
+ tcmtfencode(pv, size);
+ int nsiz = tcgammaencode(pv, size, result);
+ *sp = nsiz;
+ return result;
+}
+
+
+/* Decompress a serial object compressed with TCBS encoding. */
+char *tcbsdecode(const char *ptr, int size, int *sp){
+ char *result;
+ TCMALLOC(result, size * 9 + 0x200);
+ char *wp = result + size + 0x100;
+ int nsiz = tcgammadecode(ptr, size, wp);
+ tcmtfdecode(wp, nsiz);
+ ptr = wp;
+ wp = result;
+ const char *end = ptr + nsiz;
+ while(ptr < end){
+ uint16_t idx;
+ memcpy(&idx, ptr, sizeof(idx));
+ idx = TCITOHS(idx);
+ ptr += sizeof(idx);
+ int usiz = tclmin(TCBSENCUNIT, end - ptr);
+ if(idx >= usiz) idx = 0;
+ char rbuf[usiz+1];
+ memcpy(rbuf, ptr, usiz);
+ if(usiz >= TCBWTCNTMIN){
+ tcbwtsortchrcount((unsigned char *)rbuf, usiz);
+ } else if(usiz > 0){
+ tcbwtsortchrinsert((unsigned char *)rbuf, usiz);
+ }
+ int fnums[0x100], tnums[0x100];
+ memset(fnums, 0, sizeof(fnums));
+ memset(tnums, 0, sizeof(tnums));
+ TCBWTREC array[usiz+1];
+ TCBWTREC *rp = array;
+ for(int i = 0; i < usiz; i++){
+ int fc = *(unsigned char *)(rbuf + i);
+ rp->fchr = (fc << 23) + fnums[fc]++;
+ int tc = *(unsigned char *)(ptr + i);
+ rp->tchr = (tc << 23) + tnums[tc]++;
+ rp++;
+ }
+ unsigned int fchr = array[idx].fchr;
+ if(usiz >= TCBWTCNTMIN){
+ tcbwtsortreccount(array, usiz);
+ } else if(usiz > 1){
+ tcbwtsortrecinsert(array, usiz);
+ }
+ for(int i = 0; i < usiz; i++){
+ if(array[i].fchr == fchr){
+ idx = i;
+ break;
+ }
+ }
+ for(int i = 0; i < usiz; i++){
+ *(wp++) = array[idx].fchr >> 23;
+ idx = tcbwtsearchrec(array, usiz, array[idx].fchr);
+ }
+ ptr += usiz;
+ }
+ *wp = '\0';
+ *sp = wp - result;
+ return result;
+}
+
+
+/* Encode a serial object with BWT encoding. */
+char *tcbwtencode(const char *ptr, int size, int *idxp){
+ assert(ptr && size >= 0 && idxp);
+ if(size < 1){
+ *idxp = 0;
+ char *rv;
+ TCMEMDUP(rv, "", 0);
+ return rv;
+ }
+ char *result;
+ TCMALLOC(result, size * 3 + 1);
+ char *tp = result + size + 1;
+ memcpy(tp, ptr, size);
+ memcpy(tp + size, ptr, size);
+ const char *abuf[TCBWTBUFNUM];
+ const char **arrays = abuf;
+ if(size > TCBWTBUFNUM) TCMALLOC(arrays, sizeof(*arrays) * size);
+ for(int i = 0; i < size; i++){
+ arrays[i] = tp + i;
+ }
+ const char *fp = arrays[0];
+ if(size >= TCBWTCNTMIN){
+ tcbwtsortstrcount(arrays, size, size, -1);
+ } else if(size > 1){
+ tcbwtsortstrinsert(arrays, size, size, 0);
+ }
+ for(int i = 0; i < size; i++){
+ int idx = arrays[i] - fp;
+ if(idx == 0){
+ *idxp = i;
+ result[i] = ptr[size-1];
+ } else {
+ result[i] = ptr[idx-1];
+ }
+ }
+ if(arrays != abuf) free(arrays);
+ result[size] = '\0';
+ return result;
+}
+
+
+/* Decode a serial object encoded with BWT encoding. */
+char *tcbwtdecode(const char *ptr, int size, int idx){
+ assert(ptr && size >= 0);
+ if(size < 1 || idx < 0){
+ char *rv;
+ TCMEMDUP(rv, "", 0);
+ return rv;
+ }
+ if(idx >= size) idx = 0;
+ char *result;
+ TCMALLOC(result, size + 1);
+ memcpy(result, ptr, size);
+ if(size >= TCBWTCNTMIN){
+ tcbwtsortchrcount((unsigned char *)result, size);
+ } else {
+ tcbwtsortchrinsert((unsigned char *)result, size);
+ }
+ int fnums[0x100], tnums[0x100];
+ memset(fnums, 0, sizeof(fnums));
+ memset(tnums, 0, sizeof(tnums));
+ TCBWTREC abuf[TCBWTBUFNUM];
+ TCBWTREC *array = abuf;
+ if(size > TCBWTBUFNUM) TCMALLOC(array, sizeof(*array) * size);
+ TCBWTREC *rp = array;
+ for(int i = 0; i < size; i++){
+ int fc = *(unsigned char *)(result + i);
+ rp->fchr = (fc << 23) + fnums[fc]++;
+ int tc = *(unsigned char *)(ptr + i);
+ rp->tchr = (tc << 23) + tnums[tc]++;
+ rp++;
+ }
+ unsigned int fchr = array[idx].fchr;
+ if(size >= TCBWTCNTMIN){
+ tcbwtsortreccount(array, size);
+ } else if(size > 1){
+ tcbwtsortrecinsert(array, size);
+ }
+ for(int i = 0; i < size; i++){
+ if(array[i].fchr == fchr){
+ idx = i;
+ break;
+ }
+ }
+ char *wp = result;
+ for(int i = 0; i < size; i++){
+ *(wp++) = array[idx].fchr >> 23;
+ idx = tcbwtsearchrec(array, size, array[idx].fchr);
+ }
+ *wp = '\0';
+ if(array != abuf) free(array);
+ return result;
+}
+
+
+/* Initialize the global mutex object */
+static void tcglobalmutexinit(void){
+ if(!TCUSEPTHREAD) memset(&tcglobalmutex, 0, sizeof(tcglobalmutex));
+ if(pthread_rwlock_init(&tcglobalmutex, NULL) != 0) tcmyfatal("rwlock error");
+ atexit(tcglobalmutexdestroy);
+}
+
+
+/* Destroy the global mutex object */
+static void tcglobalmutexdestroy(void){
+ pthread_rwlock_destroy(&tcglobalmutex);
+}
+
+
+/* Sort BWT string arrays by dicrionary order by counting sort.
+ `array' specifies an array of string arrays.
+ `anum' specifies the number of the array.
+ `len' specifies the size of each string.
+ `level' specifies the level of recursion. */
+static void tcbwtsortstrcount(const char **arrays, int anum, int len, int level){
+ assert(arrays && anum >= 0 && len >= 0);
+ const char *nbuf[TCBWTBUFNUM];
+ const char **narrays = nbuf;
+ if(anum > TCBWTBUFNUM) TCMALLOC(narrays, sizeof(*narrays) * anum);
+ int count[0x100], accum[0x100];
+ memset(count, 0, sizeof(count));
+ int skip = level < 0 ? 0 : level;
+ for(int i = 0; i < anum; i++){
+ count[((unsigned char *)arrays[i])[skip]]++;
+ }
+ memcpy(accum, count, sizeof(count));
+ for(int i = 1; i < 0x100; i++){
+ accum[i] = accum[i-1] + accum[i];
+ }
+ for(int i = 0; i < anum; i++){
+ narrays[--accum[((unsigned char *)arrays[i])[skip]]] = arrays[i];
+ }
+ int off = 0;
+ if(level >= 0 && level < TCBWTCNTLV){
+ for(int i = 0; i < 0x100; i++){
+ int c = count[i];
+ if(c > 1){
+ if(c >= TCBWTCNTMIN){
+ tcbwtsortstrcount(narrays + off, c, len, level + 1);
+ } else {
+ tcbwtsortstrinsert(narrays + off, c, len, skip + 1);
+ }
+ }
+ off += c;
+ }
+ } else {
+ for(int i = 0; i < 0x100; i++){
+ int c = count[i];
+ if(c > 1){
+ if(c >= TCBWTCNTMIN){
+ tcbwtsortstrheap(narrays + off, c, len, skip + 1);
+ } else {
+ tcbwtsortstrinsert(narrays + off, c, len, skip + 1);
+ }
+ }
+ off += c;
+ }
+ }
+ memcpy(arrays, narrays, anum * sizeof(*narrays));
+ if(narrays != nbuf) free(narrays);
+}
+
+
+/* Sort BWT string arrays by dicrionary order by insertion sort.
+ `array' specifies an array of string arrays.
+ `anum' specifies the number of the array.
+ `len' specifies the size of each string.
+ `skip' specifies the number of skipped bytes. */
+static void tcbwtsortstrinsert(const char **arrays, int anum, int len, int skip){
+ assert(arrays && anum >= 0 && len >= 0);
+ for(int i = 1; i < anum; i++){
+ int cmp = 0;
+ const unsigned char *ap = (unsigned char *)arrays[i-1];
+ const unsigned char *bp = (unsigned char *)arrays[i];
+ for(int j = skip; j < len; j++){
+ if(ap[j] != bp[j]){
+ cmp = ap[j] - bp[j];
+ break;
+ }
+ }
+ if(cmp > 0){
+ const char *swap = arrays[i];
+ int j;
+ for(j = i; j > 0; j--){
+ int cmp = 0;
+ const unsigned char *ap = (unsigned char *)arrays[j-1];
+ const unsigned char *bp = (unsigned char *)swap;
+ for(int k = skip; k < len; k++){
+ if(ap[k] != bp[k]){
+ cmp = ap[k] - bp[k];
+ break;
+ }
+ }
+ if(cmp < 0) break;
+ arrays[j] = arrays[j-1];
+ }
+ arrays[j] = swap;
+ }
+ }
+}
+
+
+/* Sort BWT string arrays by dicrionary order by heap sort.
+ `array' specifies an array of string arrays.
+ `anum' specifies the number of the array.
+ `len' specifies the size of each string.
+ `skip' specifies the number of skipped bytes. */
+static void tcbwtsortstrheap(const char **arrays, int anum, int len, int skip){
+ assert(arrays && anum >= 0 && len >= 0);
+ anum--;
+ int bottom = (anum >> 1) + 1;
+ int top = anum;
+ while(bottom > 0){
+ bottom--;
+ int mybot = bottom;
+ int i = mybot << 1;
+ while(i <= top){
+ if(i < top){
+ int cmp = 0;
+ const unsigned char *ap = (unsigned char *)arrays[i+1];
+ const unsigned char *bp = (unsigned char *)arrays[i];
+ for(int j = skip; j < len; j++){
+ if(ap[j] != bp[j]){
+ cmp = ap[j] - bp[j];
+ break;
+ }
+ }
+ if(cmp > 0) i++;
+ }
+ int cmp = 0;
+ const unsigned char *ap = (unsigned char *)arrays[mybot];
+ const unsigned char *bp = (unsigned char *)arrays[i];
+ for(int j = skip; j < len; j++){
+ if(ap[j] != bp[j]){
+ cmp = ap[j] - bp[j];
+ break;
+ }
+ }
+ if(cmp >= 0) break;
+ const char *swap = arrays[mybot];
+ arrays[mybot] = arrays[i];
+ arrays[i] = swap;
+ mybot = i;
+ i = mybot << 1;
+ }
+ }
+ while(top > 0){
+ const char *swap = arrays[0];
+ arrays[0] = arrays[top];
+ arrays[top] = swap;
+ top--;
+ int mybot = bottom;
+ int i = mybot << 1;
+ while(i <= top){
+ if(i < top){
+ int cmp = 0;
+ const unsigned char *ap = (unsigned char *)arrays[i+1];
+ const unsigned char *bp = (unsigned char *)arrays[i];
+ for(int j = 0; j < len; j++){
+ if(ap[j] != bp[j]){
+ cmp = ap[j] - bp[j];
+ break;
+ }
+ }
+ if(cmp > 0) i++;
+ }
+ int cmp = 0;
+ const unsigned char *ap = (unsigned char *)arrays[mybot];
+ const unsigned char *bp = (unsigned char *)arrays[i];
+ for(int j = 0; j < len; j++){
+ if(ap[j] != bp[j]){
+ cmp = ap[j] - bp[j];
+ break;
+ }
+ }
+ if(cmp >= 0) break;
+ swap = arrays[mybot];
+ arrays[mybot] = arrays[i];
+ arrays[i] = swap;
+ mybot = i;
+ i = mybot << 1;
+ }
+ }
+}
+
+
+/* Sort BWT characters by code number by counting sort.
+ `str' specifies a string.
+ `len' specifies the length of the string. */
+static void tcbwtsortchrcount(unsigned char *str, int len){
+ assert(str && len >= 0);
+ int cnt[0x100];
+ memset(cnt, 0, sizeof(cnt));
+ for(int i = 0; i < len; i++){
+ cnt[str[i]]++;
+ }
+ int pos = 0;
+ for(int i = 0; i < 0x100; i++){
+ memset(str + pos, i, cnt[i]);
+ pos += cnt[i];
+ }
+}
+
+
+/* Sort BWT characters by code number by insertion sort.
+ `str' specifies a string.
+ `len' specifies the length of the string. */
+static void tcbwtsortchrinsert(unsigned char *str, int len){
+ assert(str && len >= 0);
+ for(int i = 1; i < len; i++){
+ if(str[i-1] - str[i] > 0){
+ unsigned char swap = str[i];
+ int j;
+ for(j = i; j > 0; j--){
+ if(str[j-1] - swap < 0) break;
+ str[j] = str[j-1];
+ }
+ str[j] = swap;
+ }
+ }
+}
+
+
+/* Sort BWT records by code number by counting sort.
+ `array' specifies an array of records.
+ `anum' specifies the number of the array. */
+static void tcbwtsortreccount(TCBWTREC *array, int anum){
+ assert(array && anum >= 0);
+ TCBWTREC nbuf[TCBWTBUFNUM];
+ TCBWTREC *narray = nbuf;
+ if(anum > TCBWTBUFNUM) TCMALLOC(narray, sizeof(*narray) * anum);
+ int count[0x100], accum[0x100];
+ memset(count, 0, sizeof(count));
+ for(int i = 0; i < anum; i++){
+ count[array[i].tchr>>23]++;
+ }
+ memcpy(accum, count, sizeof(count));
+ for(int i = 1; i < 0x100; i++){
+ accum[i] = accum[i-1] + accum[i];
+ }
+ for(int i = 0; i < 0x100; i++){
+ accum[i] -= count[i];
+ }
+ for(int i = 0; i < anum; i++){
+ narray[accum[array[i].tchr>>23]++] = array[i];
+ }
+ memcpy(array, narray, anum * sizeof(*narray));
+ if(narray != nbuf) free(narray);
+}
+
+
+/* Sort BWT records by code number by insertion sort.
+ `array' specifies an array of records..
+ `anum' specifies the number of the array. */
+static void tcbwtsortrecinsert(TCBWTREC *array, int anum){
+ assert(array && anum >= 0);
+ for(int i = 1; i < anum; i++){
+ if(array[i-1].tchr - array[i].tchr > 0){
+ TCBWTREC swap = array[i];
+ int j;
+ for(j = i; j > 0; j--){
+ if(array[j-1].tchr - swap.tchr < 0) break;
+ array[j] = array[j-1];
+ }
+ array[j] = swap;
+ }
+ }
+}
+
+
+/* Search the element of BWT records.
+ `array' specifies an array of records.
+ `anum' specifies the number of the array.
+ `tchr' specifies the last code number. */
+static int tcbwtsearchrec(TCBWTREC *array, int anum, int tchr){
+ assert(array && anum >= 0);
+ int bottom = 0;
+ int top = anum;
+ int mid;
+ do {
+ mid = (bottom + top) >> 1;
+ if(array[mid].tchr == tchr){
+ return mid;
+ } else if(array[mid].tchr < tchr){
+ bottom = mid + 1;
+ if(bottom >= anum) break;
+ } else {
+ top = mid - 1;
+ }
+ } while(bottom <= top);
+ return -1;
+}
+
+
+/* Initialization table for MTF encoder. */
+const unsigned char tcmtftable[] = {
+ 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
+ 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
+ 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
+ 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
+ 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
+ 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
+ 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
+ 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
+ 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
+ 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
+ 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
+ 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
+ 0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
+ 0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
+ 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
+ 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
+};
+
+
+/* Encode a region with MTF encoding.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region. */
+static void tcmtfencode(char *ptr, int size){
+ unsigned char table1[0x100], table2[0x100], *table, *another;
+ assert(ptr && size >= 0);
+ memcpy(table1, tcmtftable, sizeof(tcmtftable));
+ table = table1;
+ another = table2;
+ const char *end = ptr + size;
+ char *wp = ptr;
+ while(ptr < end){
+ unsigned char c = *ptr;
+ unsigned char *tp = table;
+ unsigned char *tend = table + 0x100;
+ while(tp < tend && *tp != c){
+ tp++;
+ }
+ int idx = tp - table;
+ *(wp++) = idx;
+ if(idx > 0){
+ memcpy(another, &c, 1);
+ memcpy(another + 1, table, idx);
+ memcpy(another + 1 + idx, table + idx + 1, 255 - idx);
+ unsigned char *swap = table;
+ table = another;
+ another = swap;
+ }
+ ptr++;
+ }
+}
+
+
+/* Decode a region compressed with MTF encoding.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region. */
+static void tcmtfdecode(char *ptr, int size){
+ assert(ptr && size >= 0);
+ unsigned char table1[0x100], table2[0x100], *table, *another;
+ assert(ptr && size >= 0);
+ memcpy(table1, tcmtftable, sizeof(tcmtftable));
+ table = table1;
+ another = table2;
+ const char *end = ptr + size;
+ char *wp = ptr;
+ while(ptr < end){
+ int idx = *(unsigned char *)ptr;
+ unsigned char c = table[idx];
+ *(wp++) = c;
+ if(idx > 0){
+ memcpy(another, &c, 1);
+ memcpy(another + 1, table, idx);
+ memcpy(another + 1 + idx, table + idx + 1, 255 - idx);
+ unsigned char *swap = table;
+ table = another;
+ another = swap;
+ }
+ ptr++;
+ }
+}
+
+
+/* Encode a region with Elias gamma encoding.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region.
+ `ptr' specifies the pointer to the output buffer. */
+static int tcgammaencode(const char *ptr, int size, char *obuf){
+ assert(ptr && size >= 0 && obuf);
+ TCBITSTRM strm;
+ TCBITSTRMINITW(strm, obuf);
+ const char *end = ptr + size;
+ while(ptr < end){
+ unsigned int c = *(unsigned char *)ptr;
+ if(!c){
+ TCBITSTRMCAT(strm, 1);
+ } else {
+ c++;
+ int plen = 8;
+ while(plen > 0 && !(c & (1 << plen))){
+ plen--;
+ }
+ int jlen = plen;
+ while(jlen-- > 0){
+ TCBITSTRMCAT(strm, 0);
+ }
+ while(plen >= 0){
+ int sign = (c & (1 << plen)) > 0;
+ TCBITSTRMCAT(strm, sign);
+ plen--;
+ }
+ }
+ ptr++;
+ }
+ TCBITSTRMSETEND(strm);
+ return TCBITSTRMSIZE(strm);
+}
+
+
+/* Decode a region compressed with Elias gamma encoding.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region.
+ `ptr' specifies the pointer to the output buffer. */
+static int tcgammadecode(const char *ptr, int size, char *obuf){
+ assert(ptr && size >= 0 && obuf);
+ char *wp = obuf;
+ TCBITSTRM strm;
+ TCBITSTRMINITR(strm, ptr, size);
+ int bnum = TCBITSTRMNUM(strm);
+ while(bnum > 0){
+ int sign;
+ TCBITSTRMREAD(strm, sign);
+ bnum--;
+ if(sign){
+ *(wp++) = 0;
+ } else {
+ int plen = 1;
+ while(bnum > 0){
+ TCBITSTRMREAD(strm, sign);
+ bnum--;
+ if(sign) break;
+ plen++;
+ }
+ unsigned int c = 1;
+ while(bnum > 0 && plen-- > 0){
+ TCBITSTRMREAD(strm, sign);
+ bnum--;
+ c = (c << 1) + (sign > 0);
+ }
+ *(wp++) = c - 1;
+ }
+ }
+ return wp - obuf;;
+}
+
+
+
+// END OF FILE
--- /dev/null
+/*************************************************************************************************
+ * The utility API of Tokyo Cabinet
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+#ifndef _TCUTIL_H /* duplication check */
+#define _TCUTIL_H
+
+#if defined(__cplusplus)
+#define __TCUTIL_CLINKAGEBEGIN extern "C" {
+#define __TCUTIL_CLINKAGEEND }
+#else
+#define __TCUTIL_CLINKAGEBEGIN
+#define __TCUTIL_CLINKAGEEND
+#endif
+__TCUTIL_CLINKAGEBEGIN
+
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <time.h>
+
+
+
+/*************************************************************************************************
+ * basic utilities
+ *************************************************************************************************/
+
+
+/* String containing the version information. */
+extern const char *tcversion;
+
+
+/* Pointer to the call back function for handling a fatal error.
+ The argument specifies the error message.
+ The initial value of this variable is `NULL'. If the value is `NULL', the default function is
+ called when a fatal error occurs. A fatal error occurs when memory allocation is failed. */
+extern void (*tcfatalfunc)(const char *);
+
+
+/* Allocate a region on memory.
+ `size' specifies the size of the region.
+ The return value is the pointer to the allocated region.
+ This function handles failure of memory allocation implicitly. Because the region of the
+ return value is allocated with the `malloc' call, it should be released with the `free' call
+ when it is no longer in use. */
+void *tcmalloc(size_t size);
+
+
+/* Allocate a nullified region on memory.
+ `nmemb' specifies the number of elements.
+ `size' specifies the size of each element.
+ The return value is the pointer to the allocated nullified region.
+ This function handles failure of memory allocation implicitly. Because the region of the
+ return value is allocated with the `calloc' call, it should be released with the `free' call
+ when it is no longer in use. */
+void *tccalloc(size_t nmemb, size_t size);
+
+
+/* Re-allocate a region on memory.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region.
+ The return value is the pointer to the re-allocated region.
+ This function handles failure of memory allocation implicitly. Because the region of the
+ return value is allocated with the `realloc' call, it should be released with the `free' call
+ when it is no longer in use. */
+void *tcrealloc(void *ptr, size_t size);
+
+
+/* Duplicate a region on memory.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region.
+ The return value is the pointer to the allocated region of the duplicate.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when
+ it is no longer in use. */
+void *tcmemdup(const void *ptr, size_t size);
+
+
+/* Duplicate a string on memory.
+ `str' specifies the string.
+ The return value is the allocated string equivalent to the specified string.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. */
+char *tcstrdup(const void *str);
+
+
+/* Free a region on memory.
+ `ptr' specifies the pointer to the region. If it is `NULL', this function has no effect.
+ Although this function is just a wrapper of `free' call, this is useful in applications using
+ another package of the `malloc' series. */
+void tcfree(void *ptr);
+
+
+
+/*************************************************************************************************
+ * extensible string
+ *************************************************************************************************/
+
+
+typedef struct { /* type of structure for an extensible string object */
+ char *ptr; /* pointer to the region */
+ int size; /* size of the region */
+ int asize; /* size of the allocated region */
+} TCXSTR;
+
+
+/* Create an extensible string object.
+ The return value is the new extensible string object. */
+TCXSTR *tcxstrnew(void);
+
+
+/* Create an extensible string object from a character string.
+ `str' specifies the string of the initial content.
+ The return value is the new extensible string object containing the specified string. */
+TCXSTR *tcxstrnew2(const char *str);
+
+
+/* Create an extensible string object with the initial allocation size.
+ `asiz' specifies the initial allocation size.
+ The return value is the new extensible string object. */
+TCXSTR *tcxstrnew3(int asiz);
+
+
+/* Copy an extensible string object.
+ `xstr' specifies the extensible string object.
+ The return value is the new extensible string object equivalent to the specified object. */
+TCXSTR *tcxstrdup(const TCXSTR *xstr);
+
+
+/* Delete an extensible string object.
+ `xstr' specifies the extensible string object.
+ Note that the deleted object and its derivatives can not be used anymore. */
+void tcxstrdel(TCXSTR *xstr);
+
+
+/* Concatenate a region to the end of an extensible string object.
+ `xstr' specifies the extensible string object.
+ `ptr' specifies the pointer to the region to be appended.
+ `size' specifies the size of the region. */
+void tcxstrcat(TCXSTR *xstr, const void *ptr, int size);
+
+
+/* Concatenate a character string to the end of an extensible string object.
+ `xstr' specifies the extensible string object.
+ `str' specifies the string to be appended. */
+void tcxstrcat2(TCXSTR *xstr, const char *str);
+
+
+/* Get the pointer of the region of an extensible string object.
+ `xstr' specifies the extensible string object.
+ The return value is the pointer of the region of the object.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. */
+const void *tcxstrptr(const TCXSTR *xstr);
+
+
+/* Get the size of the region of an extensible string object.
+ `xstr' specifies the extensible string object.
+ The return value is the size of the region of the object. */
+int tcxstrsize(const TCXSTR *xstr);
+
+
+/* Clear an extensible string object.
+ `xstr' specifies the extensible string object.
+ The internal buffer of the object is cleared and the size is set zero. */
+void tcxstrclear(TCXSTR *xstr);
+
+
+/* Convert an extensible string object into a usual allocated region.
+ `xstr' specifies the extensible string object.
+ The return value is the pointer to the allocated region of the object.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when it
+ is no longer in use. Because the region of the original object is deleted, it should not be
+ deleted again. */
+void *tcxstrtomalloc(TCXSTR *xstr);
+
+
+/* Create an extensible string object from an allocated region.
+ `ptr' specifies the pointer to the region allocated with `malloc' call.
+ `size' specifies the size of the region.
+ The return value is the new extensible string object wrapping the specified region.
+ Note that the specified region is released when the object is deleted. */
+TCXSTR *tcxstrfrommalloc(void *ptr, int size);
+
+
+/* Perform formatted output into an extensible string object.
+ `xstr' specifies the extensible string object.
+ `format' specifies the printf-like format string.
+ The conversion character `%' can be used with such flag characters as `s', `d', `o', `u',
+ `x', `X', `c', `e', `E', `f', `g', `G', `@', `?', `%'. `@' works as with `s' but escapes meta
+ characters of XML. `?' works as with `s' but escapes meta characters of URL. The other
+ conversion character work as with each original.
+ The other arguments are used according to the format string. */
+void tcxstrprintf(TCXSTR *xstr, const char *format, ...);
+
+
+/* Allocate a formatted string on memory.
+ `format' specifies the printf-like format string.
+ The conversion character `%' can be used with such flag characters as `s', `d', `o', `u',
+ `x', `X', `c', `e', `E', `f', `g', `G', `@', `?', `%'. `@' works as with `s' but escapes meta
+ characters of XML. `?' works as with `s' but escapes meta characters of URL. The other
+ conversion character work as with each original.
+ The other arguments are used according to the format string.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. */
+char *tcsprintf(const char *format, ...);
+
+
+
+/*************************************************************************************************
+ * array list
+ *************************************************************************************************/
+
+
+typedef struct { /* type of structure for an element of a list */
+ char *ptr; /* pointer to the region */
+ int size; /* size of the effective region */
+} TCLISTDATUM;
+
+typedef struct { /* type of structure for an array list */
+ TCLISTDATUM *array; /* array of data */
+ int anum; /* number of the elements of the array */
+ int start; /* start index of used elements */
+ int num; /* number of used elements */
+} TCLIST;
+
+
+/* Create a list object.
+ The return value is the new list object. */
+TCLIST *tclistnew(void);
+
+
+/* Create a list object with expecting the number of elements.
+ `anum' specifies the number of elements expected to be stored in the list.
+ The return value is the new list object. */
+TCLIST *tclistnew2(int anum);
+
+
+/* Copy a list object.
+ `list' specifies the list object.
+ The return value is the new list object equivalent to the specified object. */
+TCLIST *tclistdup(const TCLIST *list);
+
+
+/* Delete a list object.
+ `list' specifies the list object.
+ Note that the deleted object and its derivatives can not be used anymore. */
+void tclistdel(TCLIST *list);
+
+
+/* Get the number of elements of a list object.
+ `list' specifies the list object.
+ The return value is the number of elements of the list. */
+int tclistnum(const TCLIST *list);
+
+
+/* Get the pointer to the region of an element of a list object.
+ `list' specifies the list object.
+ `index' specifies the index of the element.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ The return value is the pointer to the region of the value.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. If `index' is equal to or more than
+ the number of elements, the return value is `NULL'. */
+const void *tclistval(const TCLIST *list, int index, int *sp);
+
+
+/* Get the string of an element of a list object.
+ `list' specifies the list object.
+ `index' specifies the index of the element.
+ The return value is the string of the value.
+ If `index' is equal to or more than the number of elements, the return value is `NULL'. */
+const char *tclistval2(const TCLIST *list, int index);
+
+
+/* Add an element at the end of a list object.
+ `list' specifies the list object.
+ `ptr' specifies the pointer to the region of the new element.
+ `size' specifies the size of the region. */
+void tclistpush(TCLIST *list, const void *ptr, int size);
+
+
+/* Add a string element at the end of a list object.
+ `list' specifies the list object.
+ `str' specifies the string of the new element. */
+void tclistpush2(TCLIST *list, const char *str);
+
+
+/* Add an allocated element at the end of a list object.
+ `list' specifies the list object.
+ `ptr' specifies the pointer to the region allocated with `malloc' call.
+ `size' specifies the size of the region.
+ Note that the specified region is released when the object is deleted. */
+void tclistpushmalloc(TCLIST *list, void *ptr, int size);
+
+
+/* Remove an element of the end of a list object.
+ `list' specifies the list object.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ The return value is the pointer to the region of the removed element.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when it
+ is no longer in use. If the list is empty, the return value is `NULL'. */
+void *tclistpop(TCLIST *list, int *sp);
+
+
+/* Remove a string element of the end of a list object.
+ `list' specifies the list object.
+ The return value is the string of the removed element.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. If the list is empty, the return
+ value is `NULL'. */
+char *tclistpop2(TCLIST *list);
+
+
+/* Add an element at the top of a list object.
+ `list' specifies the list object.
+ `ptr' specifies the pointer to the region of the new element.
+ `size' specifies the size of the region. */
+void tclistunshift(TCLIST *list, const void *ptr, int size);
+
+
+/* Add a string element at the top of a list object.
+ `list' specifies the list object.
+ `str' specifies the string of the new element. */
+void tclistunshift2(TCLIST *list, const char *str);
+
+
+/* Remove an element of the top of a list object.
+ `list' specifies the list object.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ The return value is the pointer to the region of the removed element.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when it
+ is no longer in use. If the list is empty, the return value is `NULL'. */
+void *tclistshift(TCLIST *list, int *sp);
+
+
+/* Remove a string element of the top of a list object.
+ `list' specifies the list object.
+ The return value is the string of the removed element.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. If the list is empty, the return
+ value is `NULL'. */
+char *tclistshift2(TCLIST *list);
+
+
+/* Add an element at the specified location of a list object.
+ `list' specifies the list object.
+ `index' specifies the index of the new element.
+ `ptr' specifies the pointer to the region of the new element.
+ `size' specifies the size of the region.
+ If `index' is equal to or more than the number of elements, this function has no effect. */
+void tclistinsert(TCLIST *list, int index, const void *ptr, int size);
+
+
+/* Add a string element at the specified location of a list object.
+ `list' specifies the list object.
+ `index' specifies the index of the new element.
+ `str' specifies the string of the new element.
+ If `index' is equal to or more than the number of elements, this function has no effect. */
+void tclistinsert2(TCLIST *list, int index, const char *str);
+
+
+/* Remove an element at the specified location of a list object.
+ `list' specifies the list object.
+ `index' specifies the index of the element to be removed.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ The return value is the pointer to the region of the removed element.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when it
+ is no longer in use. If `index' is equal to or more than the number of elements, no element
+ is removed and the return value is `NULL'. */
+void *tclistremove(TCLIST *list, int index, int *sp);
+
+
+/* Remove a string element at the specified location of a list object.
+ `list' specifies the list object.
+ `index' specifies the index of the element to be removed.
+ The return value is the string of the removed element.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. If `index' is equal to or more
+ than the number of elements, no element is removed and the return value is `NULL'. */
+char *tclistremove2(TCLIST *list, int index);
+
+
+/* Overwrite an element at the specified location of a list object.
+ `list' specifies the list object.
+ `index' specifies the index of the element to be overwritten.
+ `ptr' specifies the pointer to the region of the new content.
+ `size' specifies the size of the new content.
+ If `index' is equal to or more than the number of elements, this function has no effect. */
+void tclistover(TCLIST *list, int index, const void *ptr, int size);
+
+
+/* Overwrite a string element at the specified location of a list object.
+ `list' specifies the list object.
+ `index' specifies the index of the element to be overwritten.
+ `str' specifies the string of the new content.
+ If `index' is equal to or more than the number of elements, this function has no effect. */
+void tclistover2(TCLIST *list, int index, const char *str);
+
+
+/* Sort elements of a list object in lexical order.
+ `list' specifies the list object. */
+void tclistsort(TCLIST *list);
+
+
+/* Sort elements of a list object in case-insensitive lexical order.
+ `list' specifies the list object. */
+void tclistsortci(TCLIST *list);
+
+
+/* Sort elements of a list object by an arbitrary comparison function.
+ `list' specifies the list object.
+ `cmp' specifies the pointer to the comparison function. The structure TCLISTDATUM has the
+ member "ptr" which is the pointer to the region of the element, and the member "size" which is
+ the size of the region. */
+void tclistsortex(TCLIST *list, int (*cmp)(const TCLISTDATUM *, const TCLISTDATUM *));
+
+
+/* Search a list object for an element using liner search.
+ `list' specifies the list object.
+ `ptr' specifies the pointer to the region of the key.
+ `size' specifies the size of the region.
+ The return value is the index of a corresponding element or -1 if there is no corresponding
+ element.
+ If two or more elements correspond, the former returns. */
+int tclistlsearch(const TCLIST *list, const void *ptr, int size);
+
+
+/* Search a list object for an element using binary search.
+ `list' specifies the list object. It should be sorted in lexical order.
+ `ptr' specifies the pointer to the region of the key.
+ `size' specifies the size of the region.
+ The return value is the index of a corresponding element or -1 if there is no corresponding
+ element.
+ If two or more elements correspond, which returns is not defined. */
+int tclistbsearch(const TCLIST *list, const void *ptr, int size);
+
+
+/* Clear a list object.
+ `list' specifies the list object.
+ All elements are removed. */
+void tclistclear(TCLIST *list);
+
+
+/* Serialize a list object into a byte array.
+ `list' specifies the list object.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ The return value is the pointer to the region of the result serial region.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. */
+void *tclistdump(const TCLIST *list, int *sp);
+
+
+/* Create a list object from a serialized byte array.
+ `ptr' specifies the pointer to the region of serialized byte array.
+ `size' specifies the size of the region.
+ The return value is a new list object.
+ Because the object of the return value is created with the function `tclistnew', it should
+ be deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tclistload(const void *ptr, int size);
+
+
+
+/*************************************************************************************************
+ * hash map
+ *************************************************************************************************/
+
+
+typedef struct _TCMAPREC { /* type of structure for an element of a map */
+ int ksiz; /* size of the region of the key */
+ int vsiz; /* size of the region of the value */
+ unsigned int hash; /* second hash value */
+ struct _TCMAPREC *left; /* pointer to the left child */
+ struct _TCMAPREC *right; /* pointer to the right child */
+ struct _TCMAPREC *prev; /* pointer to the previous element */
+ struct _TCMAPREC *next; /* pointer to the next element */
+} TCMAPREC;
+
+typedef struct { /* type of structure for a map */
+ TCMAPREC **buckets; /* bucket array */
+ TCMAPREC *first; /* pointer to the first element */
+ TCMAPREC *last; /* pointer to the last element */
+ TCMAPREC *cur; /* pointer to the current element */
+ uint32_t bnum; /* number of buckets */
+ uint64_t rnum; /* number of records */
+ uint64_t msiz; /* total size of records */
+} TCMAP;
+
+
+/* Create a map object.
+ The return value is the new map object. */
+TCMAP *tcmapnew(void);
+
+
+/* Create a map object with specifying the number of the buckets.
+ `bnum' specifies the number of the buckets.
+ The return value is the new map object. */
+TCMAP *tcmapnew2(uint32_t bnum);
+
+
+/* Copy a map object.
+ `map' specifies the map object.
+ The return value is the new map object equivalent to the specified object. */
+TCMAP *tcmapdup(const TCMAP *map);
+
+
+/* Delete a map object.
+ `map' specifies the map object.
+ Note that the deleted object and its derivatives can not be used anymore. */
+void tcmapdel(TCMAP *map);
+
+
+/* Store a record into a map object.
+ `map' specifies the map object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If a record with the same key exists in the map, it is overwritten. */
+void tcmapput(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a string record into a map object.
+ `map' specifies the map object.
+ `kstr' specifies the string of the key.
+ `vstr' specifies the string of the value.
+ If a record with the same key exists in the map, it is overwritten. */
+void tcmapput2(TCMAP *map, const char *kstr, const char *vstr);
+
+
+/* Store a record of the value of two regions into a map object.
+ `map' specifies the map object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `fvbuf' specifies the pointer to the former region of the value.
+ `fvsiz' specifies the size of the former region of the value.
+ `lvbuf' specifies the pointer to the latter region of the value.
+ `lvsiz' specifies the size of the latter region of the value.
+ If a record with the same key exists in the map, it is overwritten. */
+void tcmapput3(TCMAP *map, const char *kbuf, int ksiz,
+ const void *fvbuf, int fvsiz, const char *lvbuf, int lvsiz);
+
+
+/* Store a new record into a map object.
+ `map' specifies the map object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the map, this function has no effect. */
+bool tcmapputkeep(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a new string record into a map object.
+ `map' specifies the map object.
+ `kstr' specifies the string of the key.
+ `vstr' specifies the string of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the map, this function has no effect. */
+bool tcmapputkeep2(TCMAP *map, const char *kstr, const char *vstr);
+
+
+/* Concatenate a value at the end of the value of the existing record in a map object.
+ `map' specifies the map object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If there is no corresponding record, a new record is created. */
+void tcmapputcat(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Concatenate a string value at the end of the value of the existing record in a map object.
+ `map' specifies the map object.
+ `kstr' specifies the string of the key.
+ `vstr' specifies the string of the value.
+ If there is no corresponding record, a new record is created. */
+void tcmapputcat2(TCMAP *map, const char *kstr, const char *vstr);
+
+
+/* Remove a record of a map object.
+ `map' specifies the map object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is true. False is returned when no record corresponds to
+ the specified key. */
+bool tcmapout(TCMAP *map, const void *kbuf, int ksiz);
+
+
+/* Remove a string record of a map object.
+ `map' specifies the map object.
+ `kstr' specifies the string of the key.
+ If successful, the return value is true. False is returned when no record corresponds to
+ the specified key. */
+bool tcmapout2(TCMAP *map, const char *kstr);
+
+
+/* Retrieve a record in a map object.
+ `map' specifies the map object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the value of the
+ corresponding record. `NULL' is returned when no record corresponds.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. */
+const void *tcmapget(const TCMAP *map, const void *kbuf, int ksiz, int *sp);
+
+
+/* Retrieve a string record in a map object.
+ `map' specifies the map object.
+ `kstr' specifies the string of the key.
+ If successful, the return value is the string of the value of the corresponding record.
+ `NULL' is returned when no record corresponds. */
+const char *tcmapget2(const TCMAP *map, const char *kstr);
+
+
+/* Retrieve a semivolatile record in a map object.
+ `map' specifies the map object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the value of the
+ corresponding record. `NULL' is returned when no record corresponds.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. The internal region of the returned
+ record is moved to the tail so that the record will survive for a time under LRU cache
+ algorithm removing records from the head. */
+const void *tcmapget3(TCMAP *map, const void *kbuf, int ksiz, int *sp);
+
+
+/* Move a record to the edge of a map object.
+ `map' specifies the map object.
+ `kbuf' specifies the pointer to the region of a key.
+ `ksiz' specifies the size of the region of the key.
+ `head' specifies the destination which is head if it is true or tail if else.
+ If successful, the return value is true. False is returned when no record corresponds to
+ the specified key. */
+bool tcmapmove(TCMAP *map, const void *kbuf, int ksiz, bool head);
+
+
+/* Move a string record to the edge of a map object.
+ `map' specifies the map object.
+ `kstr' specifies the string of a key.
+ `head' specifies the destination which is head if it is true or tail if else.
+ If successful, the return value is true. False is returned when no record corresponds to
+ the specified key. */
+bool tcmapmove2(TCMAP *map, const char *kstr, bool head);
+
+
+/* Initialize the iterator of a map object.
+ `map' specifies the map object.
+ The iterator is used in order to access the key of every record stored in the map object. */
+void tcmapiterinit(TCMAP *map);
+
+
+/* Get the next key of the iterator of a map object.
+ `map' specifies the map object.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the next key, else, it is
+ `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string.
+ The order of iteration is assured to be the same as the stored order. */
+const void *tcmapiternext(TCMAP *map, int *sp);
+
+
+/* Get the next key string of the iterator of a map object.
+ `map' specifies the map object.
+ If successful, the return value is the pointer to the region of the next key, else, it is
+ `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+ The order of iteration is assured to be the same as the stored order. */
+const char *tcmapiternext2(TCMAP *map);
+
+
+/* Get the value bound to the key fetched from the iterator of a map object.
+ `kbuf' specifies the pointer to the region of the iteration key.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ The return value is the pointer to the region of the value of the corresponding record.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. */
+const void *tcmapiterval(const void *kbuf, int *sp);
+
+
+/* Get the value string bound to the key fetched from the iterator of a map object.
+ `kstr' specifies the string of the iteration key.
+ The return value is the pointer to the region of the value of the corresponding record. */
+const char *tcmapiterval2(const char *kstr);
+
+
+/* Get the number of records stored in a map object.
+ `map' specifies the map object.
+ The return value is the number of the records stored in the map object. */
+uint64_t tcmaprnum(const TCMAP *map);
+
+
+/* Get the total size of memory used in a map object.
+ `map' specifies the map object.
+ The return value is the total size of memory used in a map object. */
+uint64_t tcmapmsiz(const TCMAP *map);
+
+
+/* Create a list object containing all keys in a map object.
+ `map' specifies the map object.
+ The return value is the new list object containing all keys in the map object.
+ Because the object of the return value is created with the function `tclistnew', it should
+ be deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcmapkeys(const TCMAP *map);
+
+
+/* Create a list object containing all values in a map object.
+ `map' specifies the map object.
+ The return value is the new list object containing all values in the map object.
+ Because the object of the return value is created with the function `tclistnew', it should
+ be deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcmapvals(const TCMAP *map);
+
+
+/* Add an integer to a record in a map object.
+ `map' specifies the map object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `num' specifies the additional value.
+ If the corresponding record exists, the value is treated as a integer and is added to. If no
+ record corresponds, a new record of the additional value is stored. */
+void tcmapaddint(TCMAP *map, const char *kbuf, int ksiz, int num);
+
+
+/* Clear a map object.
+ `map' specifies the map object.
+ All records are removed. */
+void tcmapclear(TCMAP *map);
+
+
+/* Remove front records of a map object.
+ `map' specifies the map object.
+ `num' specifies the number of records to be removed.
+ If successful, the return value is true. False is returned when no record corresponds to
+ the specified key. */
+void tcmapcutfront(TCMAP *map, int num);
+
+
+/* Serialize a map object into a byte array.
+ `map' specifies the map object.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ The return value is the pointer to the region of the result serial region.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. */
+void *tcmapdump(const TCMAP *map, int *sp);
+
+
+/* Create a map object from a serialized byte array.
+ `ptr' specifies the pointer to the region of serialized byte array.
+ `size' specifies the size of the region.
+ The return value is a new map object.
+ Because the object of the return value is created with the function `tcmapnew', it should be
+ deleted with the function `tcmapdel' when it is no longer in use. */
+TCMAP *tcmapload(const void *ptr, int size);
+
+
+/* Extract a map record from a serialized byte array.
+ `ptr' specifies the pointer to the region of serialized byte array.
+ `size' specifies the size of the region.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the value of the
+ corresponding record. `NULL' is returned when no record corresponds.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. */
+void *tcmaploadone(const void *ptr, int size, const void *kbuf, int ksiz, int *sp);
+
+
+
+/*************************************************************************************************
+ * on-memory database
+ *************************************************************************************************/
+
+
+typedef struct { /* type of structure for a on-memory database */
+ void **mmtxs; /* mutexes for method */
+ void *imtx; /* mutex for iterator */
+ TCMAP **maps; /* internal map object */
+ int iter; /* index of maps for the iterator */
+} TCMDB;
+
+
+/* Create an on-memory database object.
+ The return value is the new on-memory database object.
+ The object can be shared by plural threads because of the internal mutex. */
+TCMDB *tcmdbnew(void);
+
+
+/* Create an on-memory database object with specifying the number of the buckets.
+ `bnum' specifies the number of the buckets.
+ The return value is the new on-memory database object.
+ The object can be shared by plural threads because of the internal mutex. */
+TCMDB *tcmdbnew2(uint32_t bnum);
+
+
+/* Delete an on-memory database object.
+ `mdb' specifies the on-memory database object. */
+void tcmdbdel(TCMDB *mdb);
+
+
+/* Store a record into an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If a record with the same key exists in the database, it is overwritten. */
+void tcmdbput(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a string record into an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ `kstr' specifies the string of the key.
+ `vstr' specifies the string of the value.
+ If a record with the same key exists in the database, it is overwritten. */
+void tcmdbput2(TCMDB *mdb, const char *kstr, const char *vstr);
+
+
+/* Store a record of the value of two regions into an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `fvbuf' specifies the pointer to the former region of the value.
+ `fvsiz' specifies the size of the former region of the value.
+ `lvbuf' specifies the pointer to the latter region of the value.
+ `lvsiz' specifies the size of the latter region of the value.
+ If a record with the same key exists in the database, it is overwritten. */
+void tcmdbput3(TCMDB *mdb, const char *kbuf, int ksiz,
+ const void *fvbuf, int fvsiz, const char *lvbuf, int lvsiz);
+
+
+/* Store a new record into an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, this function has no effect. */
+bool tcmdbputkeep(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Store a new string record into an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ `kstr' specifies the string of the key.
+ `vstr' specifies the string of the value.
+ If successful, the return value is true, else, it is false.
+ If a record with the same key exists in the database, this function has no effect. */
+bool tcmdbputkeep2(TCMDB *mdb, const char *kstr, const char *vstr);
+
+
+/* Concatenate a value at the end of the value of the existing record in an on-memory database.
+ `mdb' specifies the on-memory database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `vbuf' specifies the pointer to the region of the value.
+ `vsiz' specifies the size of the region of the value.
+ If there is no corresponding record, a new record is created. */
+void tcmdbputcat(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
+
+/* Concatenate a string at the end of the value of the existing record in an on-memory database.
+ `mdb' specifies the on-memory database object.
+ `kstr' specifies the string of the key.
+ `vstr' specifies the string of the value.
+ If there is no corresponding record, a new record is created. */
+void tcmdbputcat2(TCMDB *mdb, const char *kstr, const char *vstr);
+
+
+/* Remove a record of an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is true. False is returned when no record corresponds to
+ the specified key. */
+bool tcmdbout(TCMDB *mdb, const void *kbuf, int ksiz);
+
+
+/* Remove a string record of an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ `kstr' specifies the string of the key.
+ If successful, the return value is true. False is returned when no record corresponds to
+ the specified key. */
+bool tcmdbout2(TCMDB *mdb, const char *kstr);
+
+
+/* Retrieve a record in an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the value of the
+ corresponding record. `NULL' is returned when no record corresponds.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when
+ it is no longer in use. */
+void *tcmdbget(TCMDB *mdb, const void *kbuf, int ksiz, int *sp);
+
+
+/* Retrieve a string record in an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ `kstr' specifies the string of the key.
+ If successful, the return value is the string of the value of the corresponding record.
+ `NULL' is returned when no record corresponds.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. */
+char *tcmdbget2(TCMDB *mdb, const char *kstr);
+
+
+/* Retrieve a record and move it astern in an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the value of the
+ corresponding record. `NULL' is returned when no record corresponds.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return value
+ is allocated with the `malloc' call, it should be released with the `free' call when it is no
+ longer in use. The internal region of the returned record is moved to the tail so that the
+ record will survive for a time under LRU cache algorithm removing records from the head. */
+void *tcmdbget3(TCMDB *mdb, const void *kbuf, int ksiz, int *sp);
+
+
+/* Get the size of the value of a record in an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ `kbuf' specifies the pointer to the region of the key.
+ `ksiz' specifies the size of the region of the key.
+ If successful, the return value is the size of the value of the corresponding record, else,
+ it is -1. */
+int tcmdbvsiz(TCMDB *mdb, const void *kbuf, int ksiz);
+
+
+/* Get the size of the value of a string record in an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ `kstr' specifies the string of the key.
+ If successful, the return value is the size of the value of the corresponding record, else,
+ it is -1. */
+int tcmdbvsiz2(TCMDB *mdb, const char *kstr);
+
+
+/* Initialize the iterator of an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ The iterator is used in order to access the key of every record stored in the on-memory
+ database. */
+void tcmdbiterinit(TCMDB *mdb);
+
+
+/* Get the next key of the iterator of an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the region of the next key, else, it is
+ `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when
+ it is no longer in use. The order of iteration is assured to be the same as the stored
+ order. */
+void *tcmdbiternext(TCMDB *mdb, int *sp);
+
+
+/* Get the next key string of the iterator of an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ If successful, the return value is the pointer to the region of the next key, else, it is
+ `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. The order of iteration is assured
+ to be the same as the stored order. */
+char *tcmdbiternext2(TCMDB *mdb);
+
+
+/* Get forward matching keys in an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ `pbuf' specifies the pointer to the region of the prefix.
+ `psiz' specifies the size of the region of the prefix.
+ `max' specifies the maximum number of keys to be fetched. If it is negative, no limit is
+ specified.
+ The return value is a list object of the corresponding keys. This function does never fail
+ and return an empty list even if no key corresponds.
+ Because the object of the return value is created with the function `tclistnew', it should be
+ deleted with the function `tclistdel' when it is no longer in use. Note that this function
+ may be very slow because every key in the database is scanned. */
+TCLIST *tcmdbfwmkeys(TCMDB *mdb, const void *pbuf, int psiz, int max);
+
+
+/* Get forward matching string keys in an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ `pstr' specifies the string of the prefix.
+ `max' specifies the maximum number of keys to be fetched. If it is negative, no limit is
+ specified.
+ The return value is a list object of the corresponding keys. This function does never fail
+ and return an empty list even if no key corresponds.
+ Because the object of the return value is created with the function `tclistnew', it should be
+ deleted with the function `tclistdel' when it is no longer in use. Note that this function
+ may be very slow because every key in the database is scanned. */
+TCLIST *tcmdbfwmkeys2(TCMDB *mdb, const char *pstr, int max);
+
+
+/* Get the number of records stored in an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ The return value is the number of the records stored in the database. */
+uint64_t tcmdbrnum(TCMDB *mdb);
+
+
+/* Get the total size of memory used in an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ The return value is the total size of memory used in the database. */
+uint64_t tcmdbmsiz(TCMDB *mdb);
+
+
+/* Clear an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ All records are removed. */
+void tcmdbvanish(TCMDB *mdb);
+
+
+/* Remove front records of an on-memory database object.
+ `mdb' specifies the on-memory database object.
+ `num' specifies the number of records to be removed.
+ If successful, the return value is true. False is returned when no record corresponds to
+ the specified key. */
+void tcmdbcutfront(TCMDB *mdb, int num);
+
+
+
+/*************************************************************************************************
+ * memory pool
+ *************************************************************************************************/
+
+
+typedef struct { /* type of an element of memory pool */
+ void *ptr; /* pointer */
+ void (*del)(void *); /* deleting function */
+} TCMPELEM;
+
+typedef struct { /* type of structure for a memory pool object */
+ void *mutex; /* mutex for operations */
+ TCMPELEM *elems; /* array of elements */
+ int anum; /* number of the elements of the array */
+ int num; /* number of used elements */
+} TCMPOOL;
+
+
+/* Create a memory pool object.
+ The return value is the new memory pool object. */
+TCMPOOL *tcmpoolnew(void);
+
+
+/* Delete a memory pool object.
+ `mpool' specifies the memory pool object.
+ Note that the deleted object and its derivatives can not be used anymore. */
+void tcmpooldel(TCMPOOL *mpool);
+
+
+/* Relegate an arbitrary object to a memory pool object.
+ `mpool' specifies the memory pool object.
+ `ptr' specifies the pointer to the object to be relegated.
+ `del' specifies the pointer to the function to delete the object.
+ This function assures that the specified object is deleted when the memory pool object is
+ deleted. */
+void tcmpoolput(TCMPOOL *mpool, void *ptr, void (*del)(void *));
+
+
+/* Relegate an allocated region to a memory pool object.
+ `ptr' specifies the pointer to the region to be relegated.
+ This function assures that the specified region is released when the memory pool object is
+ deleted. */
+void tcmpoolputptr(TCMPOOL *mpool, void *ptr);
+
+
+/* Relegate an extensible string object to a memory pool object.
+ `mpool' specifies the memory pool object.
+ `xstr' specifies the extensible string object.
+ This function assures that the specified object is deleted when the memory pool object is
+ deleted. */
+void tcmpoolputxstr(TCMPOOL *mpool, TCXSTR *xstr);
+
+
+/* Relegate a list object to a memory pool object.
+ `mpool' specifies the memory pool object.
+ `list' specifies the list object.
+ This function assures that the specified object is deleted when the memory pool object is
+ deleted. */
+void tcmpoolputlist(TCMPOOL *mpool, TCLIST *list);
+
+
+/* Relegate a map object to a memory pool object.
+ `mpool' specifies the memory pool object.
+ `map' specifies the map object.
+ This function assures that the specified object is deleted when the memory pool object is
+ deleted. */
+void tcmpoolputmap(TCMPOOL *mpool, TCMAP *map);
+
+
+/* Allocate a region relegated to a memory pool object.
+ `mpool' specifies the memory pool object.
+ The return value is the pointer to the allocated region under the memory pool. */
+void *tcmpoolmalloc(TCMPOOL *mpool, size_t size);
+
+
+/* Create an extensible string object relegated to a memory pool object.
+ The return value is the new extensible string object under the memory pool. */
+TCXSTR *tcmpoolxstrnew(TCMPOOL *mpool);
+
+
+/* Create a list object relegated to a memory pool object.
+ The return value is the new list object under the memory pool. */
+TCLIST *tcmpoollistnew(TCMPOOL *mpool);
+
+
+/* Create a map object relegated to a memory pool object.
+ The return value is the new map object under the memory pool. */
+TCMAP *tcmpoolmapnew(TCMPOOL *mpool);
+
+
+/* Get the global memory pool object.
+ The return value is the global memory pool object.
+ The global memory pool object is a singleton and assured to be deleted when the porcess is
+ terminating normally. */
+TCMPOOL *tcmpoolglobal(void);
+
+
+
+/*************************************************************************************************
+ * miscellaneous utilities
+ *************************************************************************************************/
+
+
+/* Get the larger value of two integers.
+ `a' specifies an integer.
+ `b' specifies the other integer.
+ The return value is the larger value of the two. */
+long tclmax(long a, long b);
+
+
+/* Get the lesser value of two integers.
+ `a' specifies an integer.
+ `b' specifies the other integer.
+ The return value is the lesser value of the two. */
+long tclmin(long a, long b);
+
+
+/* Get a random number as long integer based on uniform distribution.
+ The return value is the random number between 0 and `ULONG_MAX'.
+ This function uses the random number source device and generates a real random number if
+ possible. */
+unsigned long tclrand(void);
+
+
+/* Get a random number as double decimal based on uniform distribution.
+ The return value is the random number equal to or greater than 0, and less than 1.0.
+ This function uses the random number source device and generates a real random number if
+ possible. */
+double tcdrand(void);
+
+
+/* Get a random number as double decimal based on normal distribution.
+ `avg' specifies the average.
+ `sd' specifies the standard deviation.
+ The return value is the random number.
+ This function uses the random number source device and generates a real random number if
+ possible. */
+double tcdrandnd(double avg, double sd);
+
+
+/* Compare two strings with case insensitive evaluation.
+ `astr' specifies a string.
+ `bstr' specifies of the other string.
+ The return value is positive if the former is big, negative if the latter is big, 0 if both
+ are equivalent. */
+int tcstricmp(const char *astr, const char *bstr);
+
+
+/* Check whether a string begins with a key.
+ `str' specifies the target string.
+ `key' specifies the forward matching key string.
+ The return value is true if the target string begins with the key, else, it is false. */
+bool tcstrfwm(const char *str, const char *key);
+
+
+/* Check whether a string begins with a key with case insensitive evaluation.
+ `str' specifies the target string.
+ `key' specifies the forward matching key string.
+ The return value is true if the target string begins with the key, else, it is false. */
+bool tcstrifwm(const char *str, const char *key);
+
+
+/* Check whether a string ends with a key.
+ `str' specifies the target string.
+ `key' specifies the backward matching key string.
+ The return value is true if the target string ends with the key, else, it is false. */
+bool tcstrbwm(const char *str, const char *key);
+
+
+/* Check whether a string ends with a key with case insensitive evaluation.
+ `str' specifies the target string.
+ `key' specifies the backward matching key string.
+ The return value is true if the target string ends with the key, else, it is false. */
+bool tcstribwm(const char *str, const char *key);
+
+
+/* Calculate the edit distance of two strings.
+ `astr' specifies a string.
+ `bstr' specifies of the other string.
+ The return value is the edit distance which is known as the Levenshtein distance. The cost is
+ calculated by byte. */
+int tcstrdist(const char *astr, const char *bstr);
+
+
+/* Calculate the edit distance of two UTF-8 strings.
+ `astr' specifies a string.
+ `bstr' specifies of the other string.
+ The return value is the edit distance which is known as the Levenshtein distance. The cost is
+ calculated by Unicode character. */
+int tcstrdistutf(const char *astr, const char *bstr);
+
+
+/* Convert the letters of a string into upper case.
+ `str' specifies the string to be converted.
+ The return value is the string itself. */
+char *tcstrtoupper(char *str);
+
+
+/* Convert the letters of a string into lower case.
+ `str' specifies the string to be converted.
+ The return value is the string itself. */
+char *tcstrtolower(char *str);
+
+
+/* Cut space characters at head or tail of a string.
+ `str' specifies the string to be converted.
+ The return value is the string itself. */
+char *tcstrtrim(char *str);
+
+
+/* Squeeze space characters in a string and trim it.
+ `str' specifies the string to be converted.
+ The return value is the string itself. */
+char *tcstrsqzspc(char *str);
+
+
+/* Substitute characters in a string.
+ `str' specifies the string to be converted.
+ `rstr' specifies the string containing characters to be replaced.
+ `sstr' specifies the string containing characters to be substituted.
+ If the substitute string is shorter then the replacement string, corresponding characters are
+ removed. */
+char *tcstrsubchr(char *str, const char *rstr, const char *sstr);
+
+
+/* Count the number of characters in a string of UTF-8.
+ `str' specifies the string of UTF-8.
+ The return value is the number of characters in the string. */
+int tcstrcntutf(const char *str);
+
+
+/* Cut a string of UTF-8 at the specified number of characters.
+ `str' specifies the string of UTF-8.
+ `num' specifies the number of characters to be kept.
+ The return value is the string itself. */
+char *tcstrcututf(char *str, int num);
+
+
+/* Convert a UTF-8 string into a UCS-2 array.
+ `str' specifies the UTF-8 string.
+ `ary' specifies the pointer to the region into which the result UCS-2 codes are written. The
+ size of the buffer should be sufficient.
+ `np' specifies the pointer to a variable into which the number of elements of the result array
+ is assigned. */
+void tcstrutftoucs(const char *str, uint16_t *ary, int *np);
+
+
+/* Convert a UCS-2 array into a UTF-8 string.
+ `ary' specifies the array of UCS-2 code codes.
+ `num' specifies the number of the array.
+ `str' specifies the pointer to the region into which the result UTF-8 string is written. The
+ size of the buffer should be sufficient. */
+void tcstrucstoutf(const uint16_t *ary, int num, char *str);
+
+
+/* Create a list object by splitting a string.
+ `str' specifies the source string.
+ `delim' specifies a string containing delimiting characters.
+ The return value is a list object of the split elements.
+ If two delimiters are successive, it is assumed that an empty element is between the two.
+ Because the object of the return value is created with the function `tclistnew', it should
+ be deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcstrsplit(const char *str, const char *delims);
+
+
+/* Create a string by joining all elements of a list object.
+ `list' specifies a list object.
+ `delim' specifies a delimiting character.
+ The return value is the result string.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. */
+char *tcstrjoin(TCLIST *list, char delim);
+
+
+/* Check whether a string matches a regular expression.
+ `str' specifies the target string.
+ `regex' specifies the regular expression string. If it begins with `*', the trailing
+ substring is used as a case-insensitive regular expression.
+ The return value is true if matching is success, else, it is false. */
+bool tcregexmatch(const char *str, const char *regex);
+
+
+/* Replace each substring matching a regular expression string.
+ `str' specifies the target string.
+ `regex' specifies the regular expression string for substrings. If it begins with `*', the
+ trailing substring is used as a case-insensitive regular expression.
+ `alt' specifies the alternative string with which each substrings is replaced. Each `&' in
+ the string is replaced with the matched substring. Each `\' in the string escapes the
+ following character. Special escapes "\1" through "\9" referring to the corresponding
+ matching sub-expressions in the regular expression string are supported.
+ The return value is a new converted string. Even if the regular expression is invalid, a copy
+ of the original string is returned.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. */
+char *tcregexreplace(const char *str, const char *regex, const char *alt);
+
+
+/* Get the time of day in seconds.
+ The return value is the time of day in seconds. The accuracy is in microseconds. */
+double tctime(void);
+
+
+/* Get the Gregorian calendar of a time.
+ `t' specifies the source time in seconds from the epoch. If it is `INT64_MAX', the current
+ time is specified.
+ `jl' specifies the jet lag of a location in seconds. If it is `INT_MAX', the local jet lag is
+ specified.
+ `yearp' specifies the pointer to a variable to which the year is assigned. If it is `NULL',
+ it is not used.
+ `monp' specifies the pointer to a variable to which the month is assigned. If it is `NULL',
+ it is not used. 1 means January and 12 means December.
+ `dayp' specifies the pointer to a variable to which the day of the month is assigned. If it
+ is `NULL', it is not used.
+ `hourp' specifies the pointer to a variable to which the hours is assigned. If it is `NULL',
+ it is not used.
+ `minp' specifies the pointer to a variable to which the minutes is assigned. If it is `NULL',
+ it is not used.
+ `secp' specifies the pointer to a variable to which the seconds is assigned. If it is `NULL',
+ it is not used. */
+void tccalendar(int64_t t, int jl, int *yearp, int *monp, int *dayp,
+ int *hourp, int *minp, int *secp);
+
+
+/* Format a date as a string in W3CDTF.
+ `t' specifies the source time in seconds from the epoch. If it is `INT64_MAX', the current
+ time is specified.
+ `jl' specifies the jet lag of a location in seconds. If it is `INT_MAX', the local jet lag is
+ specified.
+ `buf' specifies the pointer to the region into which the result string is written. The size
+ of the buffer should be equal to or more than 48 bytes.
+ W3CDTF represents a date as "YYYY-MM-DDThh:mm:ddTZD". */
+void tcdatestrwww(int64_t t, int jl, char *buf);
+
+
+/* Format a date as a string in RFC 1123 format.
+ `t' specifies the source time in seconds from the epoch. If it is `INT64_MAX', the current
+ time is specified.
+ `jl' specifies the jet lag of a location in seconds. If it is `INT_MAX', the local jet lag is
+ specified.
+ `buf' specifies the pointer to the region into which the result string is written. The size
+ of the buffer should be equal to or more than 48 bytes.
+ RFC 1123 format represents a date as "Wdy, DD-Mon-YYYY hh:mm:dd TZD". */
+void tcdatestrhttp(int64_t t, int jl, char *buf);
+
+
+/* Get the time value of a date string.
+ `str' specifies the date string in decimal, hexadecimal, W3CDTF, or RFC 822 (1123). Decimal
+ can be trailed by "s" for in seconds, "m" for in minutes, "h" for in hours, and "d" for in
+ days.
+ The return value is the time value of the date or `INT64_MAX' if the format is invalid. */
+int64_t tcstrmktime(const char *str);
+
+
+/* Get the day of week of a date.
+ `year' specifies the year of a date.
+ `mon' specifies the month of the date.
+ `day' specifies the day of the date.
+ The return value is the day of week of the date. 0 means Sunday and 6 means Saturday. */
+int tcdayofweek(int year, int mon, int day);
+
+
+
+/*************************************************************************************************
+ * filesystem utilities
+ *************************************************************************************************/
+
+
+/* Get the canonicalized absolute path of a file.
+ `path' specifies the path of the file.
+ The return value is the canonicalized absolute path of a file, or `NULL' if the path is
+ invalid.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. */
+char *tcrealpath(const char *path);
+
+
+/* Read whole data of a file.
+ `path' specifies the path of the file. If it is `NULL', the standard input is specified.
+ `limit' specifies the limiting size of reading data. If it is not more than 0, the limitation
+ is not specified.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned. If it is `NULL', it is not used.
+ The return value is the pointer to the allocated region of the read data, or `NULL' if the
+ file could not be opened.
+ Because an additional zero code is appended at the end of the region of the return value, the
+ return value can be treated as a character string. Because the region of the return value is
+ allocated with the `malloc' call, it should be released with the `free' call when when is no
+ longer in use. */
+void *tcreadfile(const char *path, int limit, int *sp);
+
+
+/* Read every line of a file.
+ `path' specifies the path of the file. If it is `NULL', the standard input is specified.
+ The return value is a list object of every lines if successful, else it is `NULL'.
+ Line separators are cut out. Because the object of the return value is created with the
+ function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer
+ in use. */
+TCLIST *tcreadfilelines(const char *path);
+
+
+/* Write data into a file.
+ `path' specifies the path of the file. If it is `NULL', the standard output is specified.
+ `ptr' specifies the pointer to the data region.
+ `size' specifies the size of the region.
+ If successful, the return value is true, else, it is false. */
+bool tcwritefile(const char *path, const void *ptr, int size);
+
+
+/* Copy a file.
+ `src' specifies the path of the source file.
+ `dest' specifies the path of the destination file.
+ The return value is true if successful, else, it is false.
+ If the destination file exists, it is overwritten. */
+bool tccopyfile(const char *src, const char *dest);
+
+
+/* Read names of files in a directory.
+ `path' specifies the path of the directory.
+ The return value is a list object of names if successful, else it is `NULL'.
+ Links to the directory itself and to the parent directory are ignored.
+ Because the object of the return value is created with the function `tclistnew', it should
+ be deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcreaddir(const char *path);
+
+
+/* Expand a pattern into a list of matched paths.
+ `pattern' specifies the matching pattern.
+ The return value is a list object of matched paths. If no path is matched, an empty list is
+ returned.
+ Because the object of the return value is created with the function `tclistnew', it should
+ be deleted with the function `tclistdel' when it is no longer in use. */
+TCLIST *tcglobpat(const char *pattern);
+
+
+/* Remove a file or a directory and its sub ones recursively.
+ `path' specifies the path of the link.
+ If successful, the return value is true, else, it is false. False is returned when the link
+ does not exist or the permission is denied. */
+bool tcremovelink(const char *path);
+
+
+/* Write data into a file.
+ `fd' specifies the file descriptor.
+ `buf' specifies the buffer to be written.
+ `size' specifies the size of the buffer.
+ The return value is true if successful, else, it is false. */
+bool tcwrite(int fd, const void *buf, size_t size);
+
+
+/* Read data from a file.
+ `fd' specifies the file descriptor.
+ `buf' specifies the buffer to store into.
+ `size' specifies the size of the buffer.
+ The return value is true if successful, else, it is false. */
+bool tcread(int fd, void *buf, size_t size);
+
+
+/* Lock a file.
+ `fd' specifies the file descriptor.
+ `ex' specifies whether an exclusive lock or a shared lock is performed.
+ `nb' specifies whether to request with non-blocking.
+ The return value is true if successful, else, it is false. */
+bool tclock(int fd, bool ex, bool nb);
+
+
+
+/*************************************************************************************************
+ * encoding utilities
+ *************************************************************************************************/
+
+
+/* Encode a serial object with URL encoding.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region.
+ The return value is the result string.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call if when is no longer in use. */
+char *tcurlencode(const char *ptr, int size);
+
+
+/* Decode a string encoded with URL encoding.
+ `str' specifies the encoded string.
+ `sp' specifies the pointer to a variable into which the size of the region of the return
+ value is assigned.
+ The return value is the pointer to the region of the result.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when
+ it is no longer in use. */
+char *tcurldecode(const char *str, int *sp);
+
+
+/* Break up a URL into elements.
+ `str' specifies the URL string.
+ The return value is the map handle whose keys are the name of elements. The key "self"
+ specifies the URL itself. The key "scheme" specifies the scheme. The key "host" specifies
+ the host of the server. The key "port" specifies the port number of the server. The key
+ "authority" specifies the authority information. The key "path" specifies the path of the
+ resource. The key "file" specifies the file name without the directory section. The key
+ "query" specifies the query string. The key "fragment" specifies the fragment string.
+ Supported schema are HTTP, HTTPS, FTP, and FILE. Absolute URL and relative URL are supported.
+ Because the object of the return value is created with the function `tcmapnew', it should be
+ deleted with the function `tcmapdel' when it is no longer in use. */
+TCMAP *tcurlbreak(const char *str);
+
+
+/* Resolve a relative URL with an absolute URL.
+ `base' specifies the absolute URL of the base location.
+ `target' specifies the URL to be resolved.
+ The return value is the resolved URL. If the target URL is relative, a new URL of relative
+ location from the base location is returned. Else, a copy of the target URL is returned.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. */
+char *tcurlresolve(const char *base, const char *target);
+
+
+/* Encode a serial object with Base64 encoding.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region.
+ The return value is the result string.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call if when is no longer in use. */
+char *tcbaseencode(const char *ptr, int size);
+
+
+/* Decode a string encoded with Base64 encoding.
+ `str' specifies the encoded string.
+ `sp' specifies the pointer to a variable into which the size of the region of the return
+ value is assigned.
+ The return value is the pointer to the region of the result.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when
+ it is no longer in use. */
+char *tcbasedecode(const char *str, int *sp);
+
+
+/* Encode a serial object with Quoted-printable encoding.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region.
+ The return value is the result string.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call if when is no longer in use. */
+char *tcquoteencode(const char *ptr, int size);
+
+
+/* Decode a string encoded with Quoted-printable encoding.
+ `str' specifies the encoded string.
+ `sp' specifies the pointer to a variable into which the size of the region of the return
+ value is assigned.
+ The return value is the pointer to the region of the result.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when
+ it is no longer in use. */
+char *tcquotedecode(const char *str, int *sp);
+
+
+/* Encode a string with MIME encoding.
+ `str' specifies the string.
+ `encname' specifies the string of the name of the character encoding.
+ `base' specifies whether to use Base64 encoding. If it is false, Quoted-printable is used.
+ The return value is the result string.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. */
+char *tcmimeencode(const char *str, const char *encname, bool base);
+
+
+/* Decode a string encoded with MIME encoding.
+ `str' specifies the encoded string.
+ `enp' specifies the pointer to the region into which the name of encoding is written. If it
+ is `NULL', it is not used. The size of the buffer should be equal to or more than 32 bytes.
+ The return value is the result string.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. */
+char *tcmimedecode(const char *str, char *enp);
+
+
+/* Compress a serial object with Packbits encoding.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the result object, else, it is `NULL'.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. */
+char *tcpackencode(const char *ptr, int size, int *sp);
+
+
+/* Decompress a serial object compressed with Packbits encoding.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region.
+ `sp' specifies the pointer to a variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the result object, else, it is `NULL'.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when it
+ is no longer in use. */
+char *tcpackdecode(const char *ptr, int size, int *sp);
+
+
+/* Compress a serial object with TCBS encoding.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the result object, else, it is `NULL'.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. */
+char *tcbsencode(const char *ptr, int size, int *sp);
+
+
+/* Decompress a serial object compressed with TCBS encoding.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region.
+ `sp' specifies the pointer to a variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the result object, else, it is `NULL'.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when it
+ is no longer in use. */
+char *tcbsdecode(const char *ptr, int size, int *sp);
+
+
+/* Compress a serial object with Deflate encoding.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the result object, else, it is `NULL'.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. */
+char *tcdeflate(const char *ptr, int size, int *sp);
+
+
+/* Decompress a serial object compressed with Deflate encoding.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region.
+ `sp' specifies the pointer to a variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the result object, else, it is `NULL'.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when it
+ is no longer in use. */
+char *tcinflate(const char *ptr, int size, int *sp);
+
+
+/* Compress a serial object with GZIP encoding.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region.
+ `sp' specifies the pointer to the variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the result object, else, it is `NULL'.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call when it is no longer in use. */
+char *tcgzipencode(const char *ptr, int size, int *sp);
+
+
+/* Decompress a serial object compressed with GZIP encoding.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region.
+ `sp' specifies the pointer to a variable into which the size of the region of the return
+ value is assigned.
+ If successful, the return value is the pointer to the result object, else, it is `NULL'.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when it
+ is no longer in use. */
+char *tcgzipdecode(const char *ptr, int size, int *sp);
+
+
+/* Get the CRC32 checksum of a serial object.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region.
+ The return value is the CRC32 checksum of the object. */
+unsigned int tcgetcrc(const char *ptr, int size);
+
+
+/* Encode an array of nonnegative integers with BER encoding.
+ `ary' specifies the pointer to the array of nonnegative integers.
+ `anum' specifies the size of the array.
+ `sp' specifies the pointer to a variable into which the size of the region of the return
+ value is assigned.
+ The return value is the pointer to the region of the result.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call if when is no longer in use. */
+char *tcberencode(const unsigned int *ary, int anum, int *sp);
+
+
+/* Decode a serial object encoded with BER encoding.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region.
+ `np' specifies the pointer to a variable into which the number of elements of the return value
+ is assigned.
+ The return value is the pointer to the array of the result.
+ Because the region of the return value is allocated with the `malloc' call, it should be
+ released with the `free' call if when is no longer in use. */
+unsigned int *tcberdecode(const char *ptr, int size, int *np);
+
+
+/* Escape meta characters in a string with the entity references of XML.
+ `str' specifies the string.
+ The return value is the pointer to the escaped string.
+ This function escapes only `&', `<', `>', and `"'. Because the region of the return value
+ is allocated with the `malloc' call, it should be released with the `free' call when it is no
+ longer in use. */
+char *tcxmlescape(const char *str);
+
+
+/* Unescape entity references in a string of XML.
+ `str' specifies the string.
+ The return value is the unescaped string.
+ This function restores only `&', `<', `>', and `"'. Because the region of the
+ return value is allocated with the `malloc' call, it should be released with the `free' call
+ when it is no longer in use. */
+char *tcxmlunescape(const char *str);
+
+
+/* Split an XML string into tags and text sections.
+ `str' specifies the XML string.
+ The return value is the list object whose elements are strings of tags or text sections.
+ Because the object of the return value is created with the function `tclistnew', it should
+ be deleted with the function `tclistdel' when it is no longer in use. Because this function
+ does not check validation, it can handle also HTML and SGML. */
+TCLIST *tcxmlbreak(const char *str);
+
+
+/* Get the map of attributes of an XML tag.
+ `str' specifies the pointer to the region of a tag string.
+ The return value is the map object containing attribute names and their values which are
+ unescaped. You can get the name of the tag with the key of an empty string.
+ Because the object of the return value is created with the function `tcmapnew', it should
+ be deleted with the function `tcmapdel' when it is no longer in use. */
+TCMAP *tcxmlattrs(const char *str);
+
+
+
+/*************************************************************************************************
+ * bit operation utilities
+ *************************************************************************************************/
+
+
+typedef struct { /* type of structure for a bit stream object */
+ uint8_t *sp; /* start pointer */
+ uint8_t *cp; /* current pointer */
+ int idx; /* bit index */
+ int size; /* size of used region */
+} TCBITSTRM;
+
+
+/* Create a bitmap object. */
+#define TCBITMAPNEW(TC_num) \
+ tccalloc(((TC_num) >> 3) + 1, 1);
+
+
+/* Delete a bitmap object */
+#define TCBITMAPDEL(TC_bitmap) \
+ do { \
+ free((TC_bitmap)); \
+ } while(false);
+
+
+/* Turn on a field of a bitmap object. */
+#define TCBITMAPON(TC_bitmap, TC_idx) \
+ do { \
+ (TC_bitmap)[(TC_idx)>>3] |= 0x1 << ((TC_idx) & 0x7); \
+ } while(false);
+
+
+/* Turn off a field of a bitmap object. */
+#define TCBITMAPOFF(TC_bitmap, TC_idx) \
+ do { \
+ (TC_bitmap)[(TC_idx)>>3] &= ~(0x1 << ((TC_idx) & 0x7)); \
+ } while(false);
+
+
+/* Check a field of a bitmap object. */
+#define TCBITMAPCHECK(TC_bitmap, TC_idx) \
+ ((TC_bitmap)[(TC_idx)>>3] & 0x1 << ((TC_idx) & 0x7))
+
+
+/* Initialize a bit stream object as writer. */
+#define TCBITSTRMINITW(TC_bitstrm, TC_ptr) \
+ do { \
+ (TC_bitstrm).sp = (uint8_t *)(TC_ptr); \
+ (TC_bitstrm).cp = (TC_bitstrm).sp; \
+ *(TC_bitstrm).cp = 0; \
+ (TC_bitstrm).idx = 3; \
+ (TC_bitstrm).size = 1; \
+ } while(false);
+
+
+/* Concatenate a bit to a bit stream object. */
+#define TCBITSTRMCAT(TC_bitstrm, sign) \
+ do { \
+ if((TC_bitstrm).idx >= 8){ \
+ *(++(TC_bitstrm).cp) = 0; \
+ (TC_bitstrm).idx = 0; \
+ (TC_bitstrm).size++; \
+ } \
+ *(TC_bitstrm).cp |= (sign << (TC_bitstrm).idx); \
+ (TC_bitstrm).idx++; \
+ } while(false);
+
+
+/* Set the end mark to a bit stream object. */
+#define TCBITSTRMSETEND(TC_bitstrm) \
+ do { \
+ if((TC_bitstrm).idx >= 8){ \
+ *(++(TC_bitstrm).cp) = 0; \
+ (TC_bitstrm).idx = 0; \
+ (TC_bitstrm).size++; \
+ } \
+ *(TC_bitstrm).sp |= (TC_bitstrm).idx & 7; \
+ } while(false);
+
+
+/* Get the size of the used region of a bit stream object. */
+#define TCBITSTRMSIZE(TC_bitstrm) \
+ ((TC_bitstrm).size)
+
+
+/* Initialize a bit stream object as reader. */
+#define TCBITSTRMINITR(TC_bitstrm, TC_ptr, TC_size) \
+ do { \
+ (TC_bitstrm).sp = (uint8_t *)(TC_ptr); \
+ (TC_bitstrm).cp = (TC_bitstrm).sp; \
+ (TC_bitstrm).idx = 3; \
+ (TC_bitstrm).size = (TC_size); \
+ } while(false);
+
+
+/* Read a bit from a bit stream object. */
+#define TCBITSTRMREAD(TC_bitstrm, TC_sign) \
+ do { \
+ if((TC_bitstrm).idx >= 8){ \
+ (TC_bitstrm).cp++; \
+ (TC_bitstrm).idx = 0; \
+ } \
+ (TC_sign) = (*((TC_bitstrm).cp) & (1 << (TC_bitstrm).idx)) > 0; \
+ (TC_bitstrm).idx++; \
+ } while(false);
+
+
+/* Get the number of bits of a bit stream object. */
+#define TCBITSTRMNUM(TC_bitstrm) \
+ ((((TC_bitstrm).size - 1) << 3) + (*(TC_bitstrm).sp & 7) - 3)
+
+
+
+/*************************************************************************************************
+ * features for experts
+ *************************************************************************************************/
+
+
+#include <stdio.h>
+
+#define _TC_VERSION "1.2.5"
+#define _TC_LIBVER 306
+#define _TC_FORMATVER "1.0"
+
+
+/* Show error message on the standard error output and exit.
+ `message' specifies an error message.
+ This function does not return. */
+void *tcmyfatal(const char *message);
+
+
+/* Lock the global mutex object.
+ If successful, the return value is true, else, it is false. */
+bool tcglobalmutexlock(void);
+
+
+/* Lock the global mutex object by shared locking.
+ If successful, the return value is true, else, it is false. */
+bool tcglobalmutexlockshared(void);
+
+
+/* Unlock the global mutex object.
+ If successful, the return value is true, else, it is false. */
+bool tcglobalmutexunlock(void);
+
+
+/* Encode a serial object with BWT encoding.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region.
+ `idxp' specifies the pointer to the variable into which the index of the original string in
+ the rotation array is assigned.
+ The return value is the pointer to the result object.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when it
+ is no longer in use. */
+char *tcbwtencode(const char *ptr, int size, int *idxp);
+
+
+/* Decode a serial object encoded with BWT encoding.
+ `ptr' specifies the pointer to the region.
+ `size' specifies the size of the region.
+ `idx' specifies the index of the original string in the rotation array is assigned.
+ The return value is the pointer to the result object.
+ Because an additional zero code is appended at the end of the region of the return value,
+ the return value can be treated as a character string. Because the region of the return
+ value is allocated with the `malloc' call, it should be released with the `free' call when it
+ is no longer in use. */
+char *tcbwtdecode(const char *ptr, int size, int idx);
+
+
+/* Print debug information with a formatted string as with `printf'. */
+#if __STDC_VERSION__ >= 199901L
+#define TCDPRINTF(...) \
+ do { \
+ fprintf(stderr, "%s:%d:%s: ", __FILE__, __LINE__, __func__); \
+ fprintf(stderr, __VA_ARGS__); \
+ fprintf(stderr, "\n"); \
+ } while(false);
+#else
+#define TCDPRINTF(TC_str) \
+ do { \
+ fprintf(stderr, "%s:%d:%s: %s\n", __FILE__, __LINE__, __func__, TC_str); \
+ } while(false);
+#endif
+
+
+/* Print hexadecimal pattern of a binary region. */
+#define TCPRINTHEX(TC_ptr, TC_size) \
+ do { \
+ for(int TC_i = 0; TC_i < (TC_size); TC_i++){ \
+ if(TC_i > 0) putchar(' '); \
+ printf("%02X", ((unsigned char *)(TC_ptr))[TC_i]); \
+ } \
+ putchar('\n'); \
+ } while(false);
+
+
+/* Print an extensible string object. */
+#define TCPRINTXSTR(TC_xstr) \
+ do { \
+ fwrite(tcxstrptr((TC_xstr)), tcxstrsize((TC_xstr)), 1, stdout); \
+ putchar('\n'); \
+ } while(false);
+
+
+/* Print all elements of a list object. */
+#define TCPRINTLIST(TC_list) \
+ do { \
+ for(int TC_i = 0; TC_i < tclistnum((TC_list)); TC_i++){ \
+ int TC_size; \
+ const char *TC_ptr = tclistval((TC_list), TC_i, &TC_size); \
+ printf("%p\t", (void *)(TC_list)); \
+ fwrite(TC_ptr, TC_size, 1, stdout); \
+ putchar('\n'); \
+ } \
+ putchar('\n'); \
+ } while(false);
+
+
+/* Print all records of a list object. */
+#define TCPRINTMAP(TC_map) \
+ do { \
+ TCLIST *TC_keys = tcmapkeys((TC_map)); \
+ for(int TC_i = 0; TC_i < tclistnum(TC_keys); TC_i++){ \
+ int TC_ksiz; \
+ const char *TC_kbuf = tclistval(TC_keys, TC_i, &TC_ksiz); \
+ int TC_vsiz; \
+ const char *TC_vbuf = tcmapget((TC_map), TC_kbuf, TC_ksiz, &TC_vsiz); \
+ printf("%p\t", (void *)(TC_map)); \
+ fwrite(TC_kbuf, TC_ksiz, 1, stdout); \
+ putchar('\t'); \
+ fwrite(TC_vbuf, TC_vsiz, 1, stdout); \
+ putchar('\n'); \
+ } \
+ putchar('\n'); \
+ tclistdel(TC_keys); \
+ } while(false);
+
+
+/* Alias of `tcmemdup'. */
+#if defined(_MYFASTEST)
+#define TCMALLOC(TC_res, TC_size) \
+ do { \
+ (TC_res) = malloc(TC_size); \
+ } while(false)
+#else
+#define TCMALLOC(TC_res, TC_size) \
+ do { \
+ if(!((TC_res) = malloc(TC_size))) tcmyfatal("out of memory"); \
+ } while(false)
+#endif
+
+/* Alias of `tccalloc'. */
+#if defined(_MYFASTEST)
+#define TCCALLOC(TC_res, TC_nmemb, TC_size) \
+ do { \
+ (TC_res) = calloc((TC_nmemb), (TC_size)); \
+ } while(false)
+#else
+#define TCCALLOC(TC_res, TC_nmemb, TC_size) \
+ do { \
+ if(!((TC_res) = calloc((TC_nmemb), (TC_size)))) tcmyfatal("out of memory"); \
+ } while(false)
+#endif
+
+/* Alias of `tcrealloc'. */
+#if defined(_MYFASTEST)
+#define TCREALLOC(TC_res, TC_ptr, TC_size) \
+ do { \
+ (TC_res) = realloc((TC_ptr), (TC_size)); \
+ } while(false)
+#else
+#define TCREALLOC(TC_res, TC_ptr, TC_size) \
+ do { \
+ if(!((TC_res) = realloc((TC_ptr), (TC_size)))) tcmyfatal("out of memory"); \
+ } while(false)
+#endif
+
+/* Alias of `tcmemdup'. */
+#define TCMEMDUP(TC_res, TC_ptr, TC_size) \
+ do { \
+ TCMALLOC((TC_res), (TC_size) + 1); \
+ memcpy((TC_res), (TC_ptr), (TC_size)); \
+ (TC_res)[TC_size] = '\0'; \
+ } while(false)
+
+
+/* Alias of `tcxstrcat'. */
+#define TCXSTRCAT(TC_xstr, TC_ptr, TC_size) \
+ do { \
+ int TC_mysize = (TC_size); \
+ int TC_nsize = (TC_xstr)->size + TC_mysize + 1; \
+ if((TC_xstr)->asize < TC_nsize){ \
+ while((TC_xstr)->asize < TC_nsize){ \
+ (TC_xstr)->asize *= 2; \
+ if((TC_xstr)->asize < TC_nsize) (TC_xstr)->asize = TC_nsize; \
+ } \
+ TCREALLOC((TC_xstr)->ptr, (TC_xstr)->ptr, (TC_xstr)->asize); \
+ } \
+ memcpy((TC_xstr)->ptr + (TC_xstr)->size, (TC_ptr), TC_mysize); \
+ (TC_xstr)->size += TC_mysize; \
+ (TC_xstr)->ptr[(TC_xstr)->size] = '\0'; \
+ } while(false)
+
+
+/* Alias of `tcxstrptr'. */
+#define TCXSTRPTR(TC_xstr) \
+ ((TC_xstr)->ptr)
+
+
+/* Alias of `tcxstrsize'. */
+#define TCXSTRSIZE(TC_xstr) \
+ ((TC_xstr)->size)
+
+
+/* Alias of `tclistnum'. */
+#define TCLISTNUM(TC_list) \
+ ((TC_list)->num)
+
+
+/* Alias of `tclistval' but not checking size and not using the third parameter. */
+#define TCLISTVALPTR(TC_list, TC_index) \
+ ((void *)((TC_list)->array[(TC_index)+(TC_list)->start].ptr))
+
+
+/* Alias of `tclistval' but not checking size and returning the size of the value. */
+#define TCLISTVALNUM(TC_list, TC_index) \
+ ((TC_list)->array[(TC_index)+(TC_list)->start].size)
+
+
+/* Add an element at the end of a list object. */
+#define TCLISTPUSH(TC_list, TC_ptr, TC_size) \
+ do { \
+ int TC_mysize = (TC_size); \
+ int TC_index = (TC_list)->start + (TC_list)->num; \
+ if(TC_index >= (TC_list)->anum){ \
+ (TC_list)->anum += (TC_list)->num + 1; \
+ TCREALLOC((TC_list)->array, (TC_list)->array, \
+ (TC_list)->anum * sizeof((TC_list)->array[0])); \
+ } \
+ TCLISTDATUM *array = (TC_list)->array; \
+ TCMALLOC(array[TC_index].ptr, TC_mysize + 1); \
+ memcpy(array[TC_index].ptr, (TC_ptr), TC_mysize); \
+ array[TC_index].ptr[TC_mysize] = '\0'; \
+ array[TC_index].size = TC_mysize; \
+ (TC_list)->num++; \
+ } while(false)
+
+
+/* Alias of `tcmaprnum'. */
+#define TCMAPRNUM(TC_map) \
+ ((TC_map)->rnum)
+
+
+
+__TCUTIL_CLINKAGEEND
+#endif /* duplication check */
+
+
+/* END OF FILE */
--- /dev/null
+/*************************************************************************************************
+ * IDL for bindings of scripting languages
+ * Copyright (C) 2006-2008 Mikio Hirabayashi
+ * This file is part of Tokyo Cabinet.
+ * Tokyo Cabinet 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 any later version. Tokyo Cabinet 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 Tokyo
+ * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA.
+ *************************************************************************************************/
+
+
+/**
+ * namespace of Tokyo Cabinet
+ */
+module TokyoCabinet {
+ //----------------------------------------------------------------
+ // list of strings (substituted for by the native mechanism).
+ //----------------------------------------------------------------
+ interface List {
+ string get(in long index);
+ };
+ //----------------------------------------------------------------
+ // the error codes
+ //----------------------------------------------------------------
+ interface ECODE {
+ const long ESUCCESS = 0;
+ const long ETHREAD = 1;
+ const long EINVALID = 2;
+ const long ENOFILE = 3;
+ const long ENOPERM = 4;
+ const long EMETA = 5;
+ const long ERHEAD = 6;
+ const long EOPEN = 7;
+ const long ECLOSE = 8;
+ const long ETRUNC = 9;
+ const long ESYNC = 10;
+ const long ESTAT = 11;
+ const long ESEEK = 12;
+ const long EREAD = 13;
+ const long EWRITE = 14;
+ const long EMMAP = 15;
+ const long ELOCK = 16;
+ const long EUNLINK = 17;
+ const long ERENAME = 18;
+ const long EMKDIR = 19;
+ const long ERMDIR = 20;
+ const long EKEEP = 21;
+ const long ENOREC = 22;
+ const long EMISC = 9999;
+ string errmsg(in long ecode);
+ };
+ //----------------------------------------------------------------
+ // the hash database API
+ //----------------------------------------------------------------
+ interface HDB :ECODE {
+ const long TLARGE = 1 << 0;
+ const long TDEFLATE = 1 << 1;
+ const long TTCBS = 1 << 2;
+ const long OREADER = 1 << 0;
+ const long OWRITER = 1 << 1;
+ const long OCREAT = 1 << 2;
+ const long OTRUNC = 1 << 3;
+ const long ONOLCK = 1 << 4;
+ const long OLCKNB = 1 << 5;
+ long ecode();
+ boolean tune(in long long bnum, in long apow, in long fpow, in long opts);
+ boolean setcache(in long rcnum);
+ boolean open(in string path, in long omode);
+ boolean close();
+ boolean put(in string key, in string value);
+ boolean putkeep(in string key, in string value);
+ boolean putcat(in string key, in string value);
+ boolean putasync(in string key, in string value);
+ boolean out(in string key);
+ string get(in string key);
+ long vsiz(in string key);
+ boolean iterinit();
+ string iternext();
+ List fwmkeys(in string prefix, in long max);
+ boolean sync();
+ boolean optimize(in long long bnum, in long apow, in long fpow, in long opts);
+ boolean vanish();
+ boolean copy(in string path);
+ string path();
+ long long rnum();
+ long long fsiz();
+ };
+ //----------------------------------------------------------------
+ // the B+ tree database API
+ //----------------------------------------------------------------
+ interface BDB :ECODE {
+ const long TLARGE = 1 << 0;
+ const long TDEFLATE = 1 << 1;
+ const long TTCBS = 1 << 2;
+ const long OREADER = 1 << 0;
+ const long OWRITER = 1 << 1;
+ const long OCREAT = 1 << 2;
+ const long OTRUNC = 1 << 3;
+ const long ONOLCK = 1 << 4;
+ const long OLCKNB = 1 << 5;
+ long ecode();
+ boolean tune(in long lmemb, in long nmemb,
+ in long long bnum, in long apow, in long fpow, in long opts);
+ boolean setcache(in long lcnum, in long ncnum);
+ boolean open(in string path, in long omode);
+ boolean close();
+ boolean put(in string key, in string value);
+ boolean putkeep(in string key, in string value);
+ boolean putcat(in string key, in string value);
+ boolean putdup(in string key, in string value);
+ boolean putlist(in string key, in List values);
+ boolean out(in string key);
+ boolean outlist(in string key);
+ string get(in string key);
+ List getlist(in string key);
+ long vnum(in string key);
+ long vsiz(in string key);
+ List range(in string bkey, in boolean binc, in string ekey, in boolean einc, in long max);
+ List fwmkeys(in string prefix, in long max);
+ boolean sync();
+ boolean optimize(in long lmemb, in long nmemb,
+ in long long bnum, in long apow, in long fpow, in long opts);
+ boolean vanish();
+ boolean copy(in string path);
+ boolean tranbegin();
+ boolean trancommit();
+ boolean tranabort();
+ string path();
+ long long rnum();
+ long long fsiz();
+ };
+ //----------------------------------------------------------------
+ // the B+ tree cursor API
+ //----------------------------------------------------------------
+ interface BDBCUR {
+ const long CPCURRENT = 0;
+ const long CPBEFORE = 1;
+ const long CPAFTER = 2;
+ boolean first();
+ boolean last();
+ boolean jump(in string key);
+ boolean prev();
+ boolean next();
+ boolean put(in string value, in long cpmode);
+ boolean out();
+ string key();
+ string val();
+ };
+};
+
+
+
+/* END OF FILE */
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+datarootdir = @datarootdir@
+bindir=@bindir@
+libdir=@libdir@
+libexecdir=@libexecdir@
+includedir=@includedir@
+datadir=@datadir@
+
+Name: Tokyo Cabinet
+Description: a modern implementation of DBM
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -ltokyocabinet @LIBS@
+Cflags: -I${includedir}