From: Eric Bollengier Date: Mon, 19 May 2008 14:31:52 +0000 (+0000) Subject: ebl Add tokyocabinet source to bacula X-Git-Tag: Release-3.0.0~1420 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=cc7d9195df23b256ea9d984630f5d75e8314797e;p=bacula%2Fbacula ebl Add tokyocabinet source to bacula git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@6986 91ce42f0-d328-0410-95d8-f526ca767f89 --- diff --git a/bacula/src/lib/tokyocabinet/COPYING b/bacula/src/lib/tokyocabinet/COPYING new file mode 100644 index 0000000000..b1e3f5a263 --- /dev/null +++ b/bacula/src/lib/tokyocabinet/COPYING @@ -0,0 +1,504 @@ + 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. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/bacula/src/lib/tokyocabinet/ChangeLog b/bacula/src/lib/tokyocabinet/ChangeLog new file mode 100644 index 0000000000..4e8298e3ad --- /dev/null +++ b/bacula/src/lib/tokyocabinet/ChangeLog @@ -0,0 +1,418 @@ +2008-04-22 Mikio Hirabayashi + + * tcbdb.c (tcbdbcmpdecimal, tcbdbcmpint32, tcbdbcmpint64): bugs of overflow were fixed. + + - Release: 1.2.5 + +2008-04-13 Mikio Hirabayashi + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * tcatest.c (runwicked, procwicked): new functions. + + - Release: 1.1.14 + +2008-01-30 Mikio Hirabayashi + + * 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 + + * tcbdb.c (tcbdbleafkill, tcbdbnodesubidx): new functions. + + * tcbtest.c (runqueue, procqueue): new functions. + + - Release: 1.1.12 + +2008-01-20 Mikio Hirabayashi + + * 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 + + * 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 + + * 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 + + * tcutil.c (tcstrutftoucs, tcstrucstoutf, tcstrjoin): new function. + + * tcutil.c (tcstrdist, tcstrdistutf): new function. + + - Release: 1.1.8 + +2007-12-28 Mikio Hirabayashi + + * 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 + + * tchdb.c (tcseekread, tcseekwrite): pread and pwrite were to be used. + + - Release: 1.1.6 + +2007-12-21 Mikio Hirabayashi + + * 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 + + * 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 + + * 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 + + * tcutil.c (tcmpoolput): mutex for operations was added. + + - Release: 1.1.2 + +2007-12-08 Mikio Hirabayashi + + * tcutil.c (tcberencode, tcberdecode): new functions. + + * tcbdb.c (tcbdbleafload): speed was improved. + + - Release: 1.1.1 + +2007-12-07 Mikio Hirabayashi + + * 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 + + * 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 + + * 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 + + * 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 + + * tcbdb.c (tcbdboptimize): the default behaviour of bnum was changed. + + - Release: 1.0.6 + +2007-11-10 Mikio Hirabayashi + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * tokyocabinet.pc.in: new file. + + - document files were fulfilled. + + - Release: 1.0.0 + +2007-10-15 Mikio Hirabayashi + + * tcbdb.c (tchdbtranbegin): locking mode was aolished. + + * tcbdb.c (tcbdbsetcmpfunc): new function. + + - Release: 0.4.1 + +2007-10-11 Mikio Hirabayashi + + * 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 + + * tchdb.c (tchdbsetmutex, tchdblockobj, tchdbunlockobj): new functions. + + * tchmttest.c: new file. + + - Release: 0.3.4 + +2007-09-05 Mikio Hirabayashi + + * tchdb.c (tchdbopen): TCBS compression mode is now supported. + + - Release: 0.3.3 + +2007-09-01 Mikio Hirabayashi + + * tchdb.c (tcbsencode, tcbsdecode): new functions. + + - Release: 0.3.2 + +2007-08-25 Mikio Hirabayashi + + * tchdb.c (tcpackencode, tcpackdecode): new functions. + + * tchdb.c (tcbwtencode, tcbwtdecode): new functions. + + - Release: 0.3.1 + +2007-08-22 Mikio Hirabayashi + + * tchdb.c (tchdbputasync, tchdbputasync2): new functions. + + - Release: 0.3.0 + +2007-08-18 Mikio Hirabayashi + + * tchdb.c (tchdboptimize): a bug causing data corruption was fixed. + + - Release: 0.2.8 + +2007-08-15 Mikio Hirabayashi + + * tchdb.c (tchdboptimize): new function. + + - Release: 0.2.7 + +2007-08-14 Mikio Hirabayashi + + * 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 + + * tchdb.c (tchdbsavefbp, tchdbloadfbp): new functions. + + - Release: 0.2.5 + +2007-08-12 Mikio Hirabayashi + + - The initial version. + + - Release: 0.2.4 + diff --git a/bacula/src/lib/tokyocabinet/Makefile.in b/bacula/src/lib/tokyocabinet/Makefile.in new file mode 100644 index 0000000000..8e757fa100 --- /dev/null +++ b/bacula/src/lib/tokyocabinet/Makefile.in @@ -0,0 +1,512 @@ +# 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 diff --git a/bacula/src/lib/tokyocabinet/README b/bacula/src/lib/tokyocabinet/README new file mode 100644 index 0000000000..ed9ad3238d --- /dev/null +++ b/bacula/src/lib/tokyocabinet/README @@ -0,0 +1,38 @@ +================================================================ + 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 == diff --git a/bacula/src/lib/tokyocabinet/THANKS b/bacula/src/lib/tokyocabinet/THANKS new file mode 100644 index 0000000000..1a80e0b871 --- /dev/null +++ b/bacula/src/lib/tokyocabinet/THANKS @@ -0,0 +1,12 @@ +================================================================ + 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 == diff --git a/bacula/src/lib/tokyocabinet/TODO b/bacula/src/lib/tokyocabinet/TODO new file mode 100644 index 0000000000..1dc71db64c --- /dev/null +++ b/bacula/src/lib/tokyocabinet/TODO @@ -0,0 +1,4 @@ + +tcmdbfwmkeysを貝偏 + +adbも実装 \ No newline at end of file diff --git a/bacula/src/lib/tokyocabinet/configure b/bacula/src/lib/tokyocabinet/configure new file mode 100755 index 0000000000..269fee8b78 --- /dev/null +++ b/bacula/src/lib/tokyocabinet/configure @@ -0,0 +1,3868 @@ +#! /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 if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + +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 &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &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 +#include +#include +#include +/* 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 +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 +#include + +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 +#include + +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 ." +_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 diff --git a/bacula/src/lib/tokyocabinet/configure.in b/bacula/src/lib/tokyocabinet/configure.in new file mode 100644 index 0000000000..c84d1acbd1 --- /dev/null +++ b/bacula/src/lib/tokyocabinet/configure.in @@ -0,0 +1,232 @@ +# 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 diff --git a/bacula/src/lib/tokyocabinet/myconf.c b/bacula/src/lib/tokyocabinet/myconf.c new file mode 100644 index 0000000000..afcc086e9e --- /dev/null +++ b/bacula/src/lib/tokyocabinet/myconf.c @@ -0,0 +1,225 @@ +/************************************************************************************************* + * 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 + +#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 diff --git a/bacula/src/lib/tokyocabinet/myconf.h b/bacula/src/lib/tokyocabinet/myconf.h new file mode 100644 index 0000000000..3e44152873 --- /dev/null +++ b/bacula/src/lib/tokyocabinet/myconf.h @@ -0,0 +1,438 @@ +/************************************************************************************************* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if TCUSEPTHREAD +#include +#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 diff --git a/bacula/src/lib/tokyocabinet/tcadb.c b/bacula/src/lib/tokyocabinet/tcadb.c new file mode 100644 index 0000000000..73b952ebea --- /dev/null +++ b/bacula/src/lib/tokyocabinet/tcadb.c @@ -0,0 +1,523 @@ +/************************************************************************************************* + * 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 diff --git a/bacula/src/lib/tokyocabinet/tcadb.h b/bacula/src/lib/tokyocabinet/tcadb.h new file mode 100644 index 0000000000..b33773bf10 --- /dev/null +++ b/bacula/src/lib/tokyocabinet/tcadb.h @@ -0,0 +1,320 @@ +/************************************************************************************************* + * 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 +#include +#include +#include +#include +#include +#include + + + +/************************************************************************************************* + * 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 */ diff --git a/bacula/src/lib/tokyocabinet/tcamgr.c b/bacula/src/lib/tokyocabinet/tcamgr.c new file mode 100644 index 0000000000..691099b6e0 --- /dev/null +++ b/bacula/src/lib/tokyocabinet/tcamgr.c @@ -0,0 +1,538 @@ +/************************************************************************************************* + * 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 +#include +#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 diff --git a/bacula/src/lib/tokyocabinet/tcatest.c b/bacula/src/lib/tokyocabinet/tcatest.c new file mode 100644 index 0000000000..44a2567de0 --- /dev/null +++ b/bacula/src/lib/tokyocabinet/tcatest.c @@ -0,0 +1,797 @@ +/************************************************************************************************* + * 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 +#include +#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("\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("\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("\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("\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("\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("\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 diff --git a/bacula/src/lib/tokyocabinet/tcbdb.c b/bacula/src/lib/tokyocabinet/tcbdb.c new file mode 100644 index 0000000000..c97f837fd7 --- /dev/null +++ b/bacula/src/lib/tokyocabinet/tcbdb.c @@ -0,0 +1,3690 @@ +/************************************************************************************************* + * 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 diff --git a/bacula/src/lib/tokyocabinet/tcbdb.h b/bacula/src/lib/tokyocabinet/tcbdb.h new file mode 100644 index 0000000000..e4da2e603a --- /dev/null +++ b/bacula/src/lib/tokyocabinet/tcbdb.h @@ -0,0 +1,1027 @@ +/************************************************************************************************* + * 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 +#include +#include +#include +#include +#include + + + +/************************************************************************************************* + * 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 */ diff --git a/bacula/src/lib/tokyocabinet/tcbmgr.c b/bacula/src/lib/tokyocabinet/tcbmgr.c new file mode 100644 index 0000000000..6cb796cb9e --- /dev/null +++ b/bacula/src/lib/tokyocabinet/tcbmgr.c @@ -0,0 +1,977 @@ +/************************************************************************************************* + * 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 +#include +#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 diff --git a/bacula/src/lib/tokyocabinet/tcbmttest.c b/bacula/src/lib/tokyocabinet/tcbmttest.c new file mode 100644 index 0000000000..5f8ae0112b --- /dev/null +++ b/bacula/src/lib/tokyocabinet/tcbmttest.c @@ -0,0 +1,1325 @@ +/************************************************************************************************* + * 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 +#include +#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("\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("\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("\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("\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("\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 diff --git a/bacula/src/lib/tokyocabinet/tcbtest.c b/bacula/src/lib/tokyocabinet/tcbtest.c new file mode 100644 index 0000000000..dd31991ced --- /dev/null +++ b/bacula/src/lib/tokyocabinet/tcbtest.c @@ -0,0 +1,2030 @@ +/************************************************************************************************* + * 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 +#include +#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("\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("\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("\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("\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("\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("\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("\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 diff --git a/bacula/src/lib/tokyocabinet/tchdb.c b/bacula/src/lib/tokyocabinet/tchdb.c new file mode 100644 index 0000000000..c13956aa8b --- /dev/null +++ b/bacula/src/lib/tokyocabinet/tchdb.c @@ -0,0 +1,3093 @@ +/************************************************************************************************* + * 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 diff --git a/bacula/src/lib/tokyocabinet/tchdb.h b/bacula/src/lib/tokyocabinet/tchdb.h new file mode 100644 index 0000000000..9a20762935 --- /dev/null +++ b/bacula/src/lib/tokyocabinet/tchdb.h @@ -0,0 +1,649 @@ +/************************************************************************************************* + * 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 +#include +#include +#include +#include + + + +/************************************************************************************************* + * 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 */ diff --git a/bacula/src/lib/tokyocabinet/tchmgr.c b/bacula/src/lib/tokyocabinet/tchmgr.c new file mode 100644 index 0000000000..98febdaada --- /dev/null +++ b/bacula/src/lib/tokyocabinet/tchmgr.c @@ -0,0 +1,803 @@ +/************************************************************************************************* + * 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 +#include +#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 diff --git a/bacula/src/lib/tokyocabinet/tchmttest.c b/bacula/src/lib/tokyocabinet/tchmttest.c new file mode 100644 index 0000000000..2e64ae91e3 --- /dev/null +++ b/bacula/src/lib/tokyocabinet/tchmttest.c @@ -0,0 +1,1266 @@ +/************************************************************************************************* + * 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 +#include +#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("\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("\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("\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("\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("\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 diff --git a/bacula/src/lib/tokyocabinet/tchtest.c b/bacula/src/lib/tokyocabinet/tchtest.c new file mode 100644 index 0000000000..9dfffc4dd2 --- /dev/null +++ b/bacula/src/lib/tokyocabinet/tchtest.c @@ -0,0 +1,1439 @@ +/************************************************************************************************* + * 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 +#include +#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("\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("\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("\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("\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("\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("\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 diff --git a/bacula/src/lib/tokyocabinet/tcucodec.c b/bacula/src/lib/tokyocabinet/tcucodec.c new file mode 100644 index 0000000000..7a9d933ad6 --- /dev/null +++ b/bacula/src/lib/tokyocabinet/tcucodec.c @@ -0,0 +1,865 @@ +/************************************************************************************************* + * 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 +#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, ""); + 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("\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 diff --git a/bacula/src/lib/tokyocabinet/tcutil.c b/bacula/src/lib/tokyocabinet/tcutil.c new file mode 100644 index 0000000000..19cba35557 --- /dev/null +++ b/bacula/src/lib/tokyocabinet/tcutil.c @@ -0,0 +1,5014 @@ +/************************************************************************************************* + * 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, " 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 diff --git a/bacula/src/lib/tokyocabinet/tcutil.h b/bacula/src/lib/tokyocabinet/tcutil.h new file mode 100644 index 0000000000..05a1e1da00 --- /dev/null +++ b/bacula/src/lib/tokyocabinet/tcutil.h @@ -0,0 +1,2169 @@ +/************************************************************************************************* + * 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 +#include +#include +#include + + + +/************************************************************************************************* + * 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 + +#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 */ diff --git a/bacula/src/lib/tokyocabinet/tokyocabinet.idl b/bacula/src/lib/tokyocabinet/tokyocabinet.idl new file mode 100644 index 0000000000..77cdf9f376 --- /dev/null +++ b/bacula/src/lib/tokyocabinet/tokyocabinet.idl @@ -0,0 +1,158 @@ +/************************************************************************************************* + * 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 */ diff --git a/bacula/src/lib/tokyocabinet/tokyocabinet.pc.in b/bacula/src/lib/tokyocabinet/tokyocabinet.pc.in new file mode 100644 index 0000000000..e12d6063fb --- /dev/null +++ b/bacula/src/lib/tokyocabinet/tokyocabinet.pc.in @@ -0,0 +1,14 @@ +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}