diff --git a/.reuse/dep5 b/.reuse/dep5 index 979f2ea98..40fa0f81d 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -41,3 +41,10 @@ License: CC0-1.0 Files: docs/reference/glib/gvariant-*.svg Copyright: 2022 Philip Withnall License: CC-BY-SA-3.0 + +# libgirepository uses cmph as a copylib. Adding copyright/license data to the +# files there would cause divergence from upstream. See +# girepository/cmph/README-CMPH-IMPORT.txt. +Files: girepository/cmph/* +Copyright: CMPH contributors +License: LGPL-2.1-only or MPL-1.1 \ No newline at end of file diff --git a/LICENSES/LGPL-2.1-only.txt b/LICENSES/LGPL-2.1-only.txt new file mode 100644 index 000000000..c9aa53018 --- /dev/null +++ b/LICENSES/LGPL-2.1-only.txt @@ -0,0 +1,175 @@ +GNU LESSER GENERAL PUBLIC LICENSE + +Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] + +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. + +This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. + +When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. + +To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. + +When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. + +We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. + +For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. + +Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". + +A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. + +(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. + +However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. + +When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. + +If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: + + a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. + + e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. + + b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). + +To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + + one line to give the library's name and an idea of what it does. + Copyright (C) year name of author + + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in +the library `Frob' (a library for tweaking knobs) written +by James Random Hacker. + +signature of Ty Coon, 1 April 1990 +Ty Coon, President of Vice +That's all there is to it! diff --git a/LICENSES/MPL-1.1.txt b/LICENSES/MPL-1.1.txt new file mode 100644 index 000000000..e1c842828 --- /dev/null +++ b/LICENSES/MPL-1.1.txt @@ -0,0 +1,143 @@ +Mozilla Public License Version 1.1 + +1. Definitions. + + 1.0.1. "Commercial Use" means distribution or otherwise making the Covered Code available to a third party. + + 1.1. "Contributor" means each entity that creates or contributes to the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original Code, prior Modifications used by a Contributor, and the Modifications made by that particular Contributor. + + 1.3. "Covered Code" means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof. + + 1.4. "Electronic Distribution Mechanism" means a mechanism generally accepted in the software development community for the electronic transfer of data. + + 1.5. "Executable" means Covered Code in any form other than Source Code. + + 1.6. "Initial Developer" means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A. + + 1.7. "Larger Work" means a work which combines Covered Code or portions thereof with code not governed by the terms of this License. + + 1.8. "License" means this document. + + 1.8.1. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is: +Any addition to or deletion from the contents of a file containing Original Code or previous Modifications. +Any new file that contains any part of the Original Code or previous Modifications. + + 1.10. "Original Code" means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License. + + 1.10.1. "Patent Claims" means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor. + + 1.11. "Source Code" means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge. + + 1.12. "You" (or "Your") means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, "You" includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. + +2. Source Code License. + + 2.1. The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: + + a. under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and + b. under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof). + c. the licenses granted in this Section 2.1 (a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License. + d. Notwithstanding Section 2.1 (b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices. + + 2.2. Contributor Grant. Subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license + + a. under intellectual property rights (other than patent or trademark) Licensable by Contributor, to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof) either on an unmodified basis, with other Modifications, as Covered Code and/or as part of a Larger Work; and + b. under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: 1) Modifications made by that Contributor (or portions thereof); and 2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination). + c. the licenses granted in Sections 2.2 (a) and 2.2 (b) are effective on the date Contributor first makes Commercial Use of the Covered Code. + d. Notwithstanding Section 2.2 (b) above, no patent license is granted: 1) for any code that Contributor has deleted from the Contributor Version; 2) separate from the Contributor Version; 3) for infringements caused by: i) third party modifications of Contributor Version or ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or 4) under Patent Claims infringed by Covered Code in the absence of Modifications made by that Contributor. + +3. Distribution Obligations. + + 3.1. Application of License. The Modifications which You create or to which You contribute are governed by the terms of this License, including without limitation Section 2.2. The Source Code version of Covered Code may be distributed only under the terms of this License or a future version of this License released under Section 6.1, and You must include a copy of this License with every copy of the Source Code You distribute. You may not offer or impose any terms on any Source Code version that alters or restricts the applicable version of this License or the recipients' rights hereunder. However, You may include an additional document offering the additional rights described in Section 3.5. + + 3.2. Availability of Source Code. Any Modification which You create or to which You contribute must be made available in Source Code form under the terms of this License either on the same media as an Executable version or via an accepted Electronic Distribution Mechanism to anyone to whom you made an Executable version available; and if made available via Electronic Distribution Mechanism, must remain available for at least twelve (12) months after the date it initially became available, or at least six (6) months after a subsequent version of that particular Modification has been made available to such recipients. You are responsible for ensuring that the Source Code version remains available even if the Electronic Distribution Mechanism is maintained by a third party. + + 3.3. Description of Modifications. You must cause all Covered Code to which You contribute to contain a file documenting the changes You made to create that Covered Code and the date of any change. You must include a prominent statement that the Modification is derived, directly or indirectly, from Original Code provided by the Initial Developer and including the name of the Initial Developer in (a) the Source Code, and (b) in any notice in an Executable version or related documentation in which You describe the origin or ownership of the Covered Code. + + 3.4. Intellectual Property Matters + + (a) Third Party Claims + If Contributor has knowledge that a license under a third party's intellectual property rights is required to exercise the rights granted by such Contributor under Sections 2.1 or 2.2, Contributor must include a text file with the Source Code distribution titled "LEGAL" which describes the claim and the party making the claim in sufficient detail that a recipient will know whom to contact. If Contributor obtains such knowledge after the Modification is made available as described in Section 3.2, Contributor shall promptly modify the LEGAL file in all copies Contributor makes available thereafter and shall take other steps (such as notifying appropriate mailing lists or newsgroups) reasonably calculated to inform those who received the Covered Code that new knowledge has been obtained. + + (b) Contributor APIs + If Contributor's Modifications include an application programming interface and Contributor has knowledge of patent licenses which are reasonably necessary to implement that API, Contributor must also include this information in the LEGAL file. + + (c) Representations. + Contributor represents that, except as disclosed pursuant to Section 3.4 (a) above, Contributor believes that Contributor's Modifications are Contributor's original creation(s) and/or Contributor has sufficient rights to grant the rights conveyed by this License. + + 3.5. Required Notices. You must duplicate the notice in Exhibit A in each file of the Source Code. If it is not possible to put such notice in a particular Source Code file due to its structure, then You must include such notice in a location (such as a relevant directory) where a user would be likely to look for such a notice. If You created one or more Modification(s) You may add your name as a Contributor to the notice described in Exhibit A. You must also duplicate this License in any documentation for the Source Code where You describe recipients' rights or ownership rights relating to Covered Code. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Code. However, You may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear than any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. + + 3.6. Distribution of Executable Versions. You may distribute Covered Code in Executable form only if the requirements of Sections 3.1, 3.2, 3.3, 3.4 and 3.5 have been met for that Covered Code, and if You include a notice stating that the Source Code version of the Covered Code is available under the terms of this License, including a description of how and where You have fulfilled the obligations of Section 3.2. The notice must be conspicuously included in any notice in an Executable version, related documentation or collateral in which You describe recipients' rights relating to the Covered Code. You may distribute the Executable version of Covered Code or ownership rights under a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable version does not attempt to limit or alter the recipient's rights in the Source Code version from the rights set forth in this License. If You distribute the Executable version under a different license You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or any Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. + + 3.7. Larger Works. You may create a Larger Work by combining Covered Code with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Code. + +4. Inability to Comply Due to Statute or Regulation. + +If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Code due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be included in the LEGAL file described in Section 3.4 and must be included with all distributions of the Source Code. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. + +5. Application of this License. +This License applies to code to which the Initial Developer has attached the notice in Exhibit A and to related Covered Code. + +6. Versions of the License. + + 6.1. New Versions + Netscape Communications Corporation ("Netscape") may publish revised and/or new versions of the License from time to time. Each version will be given a distinguishing version number. + + 6.2. Effect of New Versions + Once Covered Code has been published under a particular version of the License, You may always continue to use it under the terms of that version. You may also choose to use such Covered Code under the terms of any subsequent version of the License published by Netscape. No one other than Netscape has the right to modify the terms applicable to Covered Code created under this License. + + 6.3. Derivative Works + If You create or use a modified version of this License (which you may only do in order to apply it to code which is not already Covered Code governed by this License), You must (a) rename Your license so that the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", "MPL", "NPL" or any confusingly similar phrase do not appear in your license (except to note that your license differs from this License) and (b) otherwise make it clear that Your version of the license contains terms which differ from the Mozilla Public License and Netscape Public License. (Filling in the name of the Initial Developer, Original Code or Contributor in the notice described in Exhibit A shall not of themselves be deemed to be modifications of this License.) + +7. DISCLAIMER OF WARRANTY +COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +8. Termination + + 8.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. All sublicenses to the Covered Code which are properly granted shall survive any termination of this License. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. + + 8.2. If You initiate litigation by asserting a patent infringement claim (excluding declatory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You file such action is referred to as "Participant") alleging that: + + a. such Participant's Contributor Version directly or indirectly infringes any patent, then any and all rights granted by such Participant to You under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively, unless if within 60 days after receipt of notice You either: (i) agree in writing to pay Participant a mutually agreeable reasonable royalty for Your past and future use of Modifications made by such Participant, or (ii) withdraw Your litigation claim with respect to the Contributor Version against such Participant. If within 60 days of notice, a reasonable royalty and payment arrangement are not mutually agreed upon in writing by the parties or the litigation claim is not withdrawn, the rights granted by Participant to You under Sections 2.1 and/or 2.2 automatically terminate at the expiration of the 60 day notice period specified above. + b. any software, hardware, or device, other than such Participant's Contributor Version, directly or indirectly infringes any patent, then any rights granted to You by such Participant under Sections 2.1(b) and 2.2(b) are revoked effective as of the date You first made, used, sold, distributed, or had made, Modifications made by that Participant. + + 8.3. If You assert a patent infringement claim against Participant alleging that such Participant's Contributor Version directly or indirectly infringes any patent where such claim is resolved (such as by license or settlement) prior to the initiation of patent infringement litigation, then the reasonable value of the licenses granted by such Participant under Sections 2.1 or 2.2 shall be taken into account in determining the amount or value of any payment or license. + + 8.4. In the event of termination under Sections 8.1 or 8.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or any distributor hereunder prior to termination shall survive termination. + +9. LIMITATION OF LIABILITY +UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +10. U.S. government end users +The Covered Code is a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation," as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Code with only those rights set forth herein. + +11. Miscellaneous +This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by California law provisions (except to the extent applicable law, if any, provides otherwise), excluding its conflict-of-law provisions. With respect to disputes in which at least one party is a citizen of, or an entity chartered or registered to do business in the United States of America, any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California, with venue lying in Santa Clara County, California, with the losing party responsible for costs, including without limitation, court costs and reasonable attorneys' fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. + +12. Responsibility for claims +As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability. + +13. Multiple-licensed code +Initial Developer may designate portions of the Covered Code as "Multiple-Licensed". "Multiple-Licensed" means that the Initial Developer permits you to utilize portions of the Covered Code under Your choice of the MPL or the alternative licenses, if any, specified by the Initial Developer in the file described in Exhibit A. + +Exhibit A - Mozilla Public License. + +"The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. + +The Original Code is ______________________________________. + +The Initial Developer of the Original Code is ________________________. +Portions created by ______________________ are Copyright (C) ______ +_______________________. All Rights Reserved. + +Contributor(s): ______________________________________. + +Alternatively, the contents of this file may be used under the terms of the _____ license (the "[___] License"), in which case the provisions of [______] License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the [___] License. If you do not delete the provisions above, a recipient may use your version of this file under either the MPL or the [___] License." + +NOTE: The text of this Exhibit A may differ slightly from the text of the notices in the Source Code files of the Original Code. You should use the text of this Exhibit A rather than the text found in the Original Code Source Code for Your Modifications. diff --git a/girepository/cmph-bdz-test.c b/girepository/cmph-bdz-test.c new file mode 100644 index 000000000..cf74f1a52 --- /dev/null +++ b/girepository/cmph-bdz-test.c @@ -0,0 +1,142 @@ +/* GObject introspection: Test cmph hashing + * + * Copyright (C) 2010 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include +#include "cmph.h" + +static cmph_t * +build (void) +{ + cmph_config_t *config; + cmph_io_adapter_t *io; + char **strings; + cmph_t *c; + guint32 size; + + strings = g_strsplit ("foo,bar,baz", ",", -1); + + io = cmph_io_vector_adapter (strings, g_strv_length (strings)); + config = cmph_config_new (io); + cmph_config_set_algo (config, CMPH_BDZ); + + c = cmph_new (config); + size = cmph_size (c); + g_assert (size == g_strv_length (strings)); + + return c; +} + +static void +assert_hashes_unique (guint n_hashes, + guint32* hashes) +{ + guint i; + + for (i = 0; i < n_hashes; i++) + { + guint j = 0; + for (j = 0; j < n_hashes; j++) + { + if (j != i) + g_assert (hashes[i] != hashes[j]); + } + } +} + +static void +test_search (void) +{ + cmph_t *c = build(); + guint i; + guint32 hash; + guint32 hashes[3]; + guint32 size; + + size = cmph_size (c); + + i = 0; + hash = cmph_search (c, "foo", 3); + g_assert (hash >= 0 && hash < size); + hashes[i++] = hash; + + hash = cmph_search (c, "bar", 3); + g_assert (hash >= 0 && hash < size); + hashes[i++] = hash; + + hash = cmph_search (c, "baz", 3); + g_assert (hash >= 0 && hash < size); + hashes[i++] = hash; + + assert_hashes_unique (G_N_ELEMENTS (hashes), &hashes[0]); +} + +static void +test_search_packed (void) +{ + cmph_t *c = build(); + guint32 bufsize; + guint i; + guint32 hash; + guint32 hashes[3]; + guint32 size; + guint8 *buf; + + bufsize = cmph_packed_size (c); + buf = g_malloc (bufsize); + cmph_pack (c, buf); + + size = cmph_size (c); + + cmph_destroy (c); + c = NULL; + + i = 0; + hash = cmph_search_packed (buf, "foo", 3); + g_assert (hash >= 0 && hash < size); + hashes[i++] = hash; + + hash = cmph_search_packed (buf, "bar", 3); + g_assert (hash >= 0 && hash < size); + hashes[i++] = hash; + + hash = cmph_search_packed (buf, "baz", 3); + g_assert (hash >= 0 && hash < size); + hashes[i++] = hash; + + assert_hashes_unique (G_N_ELEMENTS (hashes), &hashes[0]); +} + +int +main(int argc, char **argv) +{ + gint ret; + + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/cmph-bdz/search", test_search); + g_test_add_func ("/cmph-bdz/search-packed", test_search_packed); + + ret = g_test_run (); + + return ret; +} + diff --git a/girepository/cmph/README-CMPH-IMPORT.txt b/girepository/cmph/README-CMPH-IMPORT.txt new file mode 100644 index 000000000..a1c23c246 --- /dev/null +++ b/girepository/cmph/README-CMPH-IMPORT.txt @@ -0,0 +1,5 @@ +This import of CMPH was made from revision bfdcc3a3a18dfb9 of +git://cmph.git.sourceforge.net/gitroot/cmph/cmph + +Only the following files were taken, and everything else deleted: +COPYING src/*.[ch] diff --git a/girepository/cmph/bdz.c b/girepository/cmph/bdz.c new file mode 100644 index 000000000..e70f1183e --- /dev/null +++ b/girepository/cmph/bdz.c @@ -0,0 +1,721 @@ +#include "bdz.h" +#include "cmph_structs.h" +#include "bdz_structs.h" +#include "hash.h" +#include "bitbool.h" + +#include +#include +#include +#include +#include +#include +//#define DEBUG +#include "debug.h" +#define UNASSIGNED 3U +#define NULL_EDGE 0xffffffff + +//cmph_uint32 ngrafos = 0; +//cmph_uint32 ngrafos_aciclicos = 0; +// table used for looking up the number of assigned vertices a 8-bit integer +const cmph_uint8 bdz_lookup_table[] = +{ +4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, +4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, +4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, +3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, +4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, +4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, +4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, +3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, +4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, +4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, +4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, +3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, +3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, +3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, +3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, +2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 1, 1, 1, 1, 0 +}; + +typedef struct +{ + cmph_uint32 vertices[3]; + cmph_uint32 next_edges[3]; +}bdz_edge_t; + +typedef cmph_uint32 * bdz_queue_t; + +static void bdz_alloc_queue(bdz_queue_t * queuep, cmph_uint32 nedges) +{ + (*queuep)=malloc(nedges*sizeof(cmph_uint32)); +}; +static void bdz_free_queue(bdz_queue_t * queue) +{ + free(*queue); +}; + +typedef struct +{ + cmph_uint32 nedges; + bdz_edge_t * edges; + cmph_uint32 * first_edge; + cmph_uint8 * vert_degree; +}bdz_graph3_t; + + +static void bdz_alloc_graph3(bdz_graph3_t * graph3, cmph_uint32 nedges, cmph_uint32 nvertices) +{ + graph3->edges=malloc(nedges*sizeof(bdz_edge_t)); + graph3->first_edge=malloc(nvertices*sizeof(cmph_uint32)); + graph3->vert_degree=malloc((size_t)nvertices); +}; +static void bdz_init_graph3(bdz_graph3_t * graph3, cmph_uint32 nedges, cmph_uint32 nvertices) +{ + memset(graph3->first_edge,0xff,nvertices*sizeof(cmph_uint32)); + memset(graph3->vert_degree,0,(size_t)nvertices); + graph3->nedges=0; +}; +static void bdz_free_graph3(bdz_graph3_t *graph3) +{ + free(graph3->edges); + free(graph3->first_edge); + free(graph3->vert_degree); +}; + +static void bdz_partial_free_graph3(bdz_graph3_t *graph3) +{ + free(graph3->first_edge); + free(graph3->vert_degree); + graph3->first_edge = NULL; + graph3->vert_degree = NULL; +}; + +static void bdz_add_edge(bdz_graph3_t * graph3, cmph_uint32 v0, cmph_uint32 v1, cmph_uint32 v2) +{ + graph3->edges[graph3->nedges].vertices[0]=v0; + graph3->edges[graph3->nedges].vertices[1]=v1; + graph3->edges[graph3->nedges].vertices[2]=v2; + graph3->edges[graph3->nedges].next_edges[0]=graph3->first_edge[v0]; + graph3->edges[graph3->nedges].next_edges[1]=graph3->first_edge[v1]; + graph3->edges[graph3->nedges].next_edges[2]=graph3->first_edge[v2]; + graph3->first_edge[v0]=graph3->first_edge[v1]=graph3->first_edge[v2]=graph3->nedges; + graph3->vert_degree[v0]++; + graph3->vert_degree[v1]++; + graph3->vert_degree[v2]++; + graph3->nedges++; +}; + +static void bdz_dump_graph(bdz_graph3_t* graph3, cmph_uint32 nedges, cmph_uint32 nvertices) +{ + cmph_uint32 i; + for(i=0;iedges[i].vertices[0], + graph3->edges[i].vertices[1],graph3->edges[i].vertices[2]); + printf(" nexts %d %d %d",graph3->edges[i].next_edges[0], + graph3->edges[i].next_edges[1],graph3->edges[i].next_edges[2]); + }; + + for(i=0;ifirst_edge[i]); + + }; +}; + +static void bdz_remove_edge(bdz_graph3_t * graph3, cmph_uint32 curr_edge) +{ + cmph_uint32 i,j=0,vert,edge1,edge2; + for(i=0;i<3;i++){ + vert=graph3->edges[curr_edge].vertices[i]; + edge1=graph3->first_edge[vert]; + edge2=NULL_EDGE; + while(edge1!=curr_edge&&edge1!=NULL_EDGE){ + edge2=edge1; + if(graph3->edges[edge1].vertices[0]==vert){ + j=0; + } else if(graph3->edges[edge1].vertices[1]==vert){ + j=1; + } else + j=2; + edge1=graph3->edges[edge1].next_edges[j]; + }; + if(edge1==NULL_EDGE){ + printf("\nerror remove edge %d dump graph",curr_edge); + bdz_dump_graph(graph3,graph3->nedges,graph3->nedges+graph3->nedges/4); + exit(-1); + }; + + if(edge2!=NULL_EDGE){ + graph3->edges[edge2].next_edges[j] = + graph3->edges[edge1].next_edges[i]; + } else + graph3->first_edge[vert]= + graph3->edges[edge1].next_edges[i]; + graph3->vert_degree[vert]--; + }; + +}; + +static int bdz_generate_queue(cmph_uint32 nedges, cmph_uint32 nvertices, bdz_queue_t queue, bdz_graph3_t* graph3) +{ + cmph_uint32 i,v0,v1,v2; + cmph_uint32 queue_head=0,queue_tail=0; + cmph_uint32 curr_edge; + cmph_uint32 tmp_edge; + cmph_uint8 * marked_edge =malloc((size_t)(nedges >> 3) + 1); + memset(marked_edge, 0, (size_t)(nedges >> 3) + 1); + + for(i=0;iedges[i].vertices[0]; + v1=graph3->edges[i].vertices[1]; + v2=graph3->edges[i].vertices[2]; + if(graph3->vert_degree[v0]==1 || + graph3->vert_degree[v1]==1 || + graph3->vert_degree[v2]==1){ + if(!GETBIT(marked_edge,i)) { + queue[queue_head++]=i; + SETBIT(marked_edge,i); + } + }; + }; + while(queue_tail!=queue_head){ + curr_edge=queue[queue_tail++]; + bdz_remove_edge(graph3,curr_edge); + v0=graph3->edges[curr_edge].vertices[0]; + v1=graph3->edges[curr_edge].vertices[1]; + v2=graph3->edges[curr_edge].vertices[2]; + if(graph3->vert_degree[v0]==1 ) { + tmp_edge=graph3->first_edge[v0]; + if(!GETBIT(marked_edge,tmp_edge)) { + queue[queue_head++]=tmp_edge; + SETBIT(marked_edge,tmp_edge); + }; + + }; + if(graph3->vert_degree[v1]==1) { + tmp_edge=graph3->first_edge[v1]; + if(!GETBIT(marked_edge,tmp_edge)){ + queue[queue_head++]=tmp_edge; + SETBIT(marked_edge,tmp_edge); + }; + + }; + if(graph3->vert_degree[v2]==1){ + tmp_edge=graph3->first_edge[v2]; + if(!GETBIT(marked_edge,tmp_edge)){ + queue[queue_head++]=tmp_edge; + SETBIT(marked_edge,tmp_edge); + }; + }; + }; + free(marked_edge); + return (int)(queue_head-nedges);/* returns 0 if successful otherwies return negative number*/ +}; + +static int bdz_mapping(cmph_config_t *mph, bdz_graph3_t* graph3, bdz_queue_t queue); +static void assigning(bdz_config_data_t *bdz, bdz_graph3_t* graph3, bdz_queue_t queue); +static void ranking(bdz_config_data_t *bdz); +static cmph_uint32 rank(cmph_uint32 b, cmph_uint32 * ranktable, cmph_uint8 * g, cmph_uint32 vertex); + +bdz_config_data_t *bdz_config_new(void) +{ + bdz_config_data_t *bdz; + bdz = (bdz_config_data_t *)malloc(sizeof(bdz_config_data_t)); + assert(bdz); + memset(bdz, 0, sizeof(bdz_config_data_t)); + bdz->hashfunc = CMPH_HASH_JENKINS; + bdz->g = NULL; + bdz->hl = NULL; + bdz->k = 0; //kth index in ranktable, $k = log_2(n=3r)/\varepsilon$ + bdz->b = 7; // number of bits of k + bdz->ranktablesize = 0; //number of entries in ranktable, $n/k +1$ + bdz->ranktable = NULL; // rank table + return bdz; +} + +void bdz_config_destroy(cmph_config_t *mph) +{ + bdz_config_data_t *data = (bdz_config_data_t *)mph->data; + DEBUGP("Destroying algorithm dependent data\n"); + free(data); +} + +void bdz_config_set_b(cmph_config_t *mph, cmph_uint32 b) +{ + bdz_config_data_t *bdz = (bdz_config_data_t *)mph->data; + if (b <= 2 || b > 10) b = 7; // validating restrictions over parameter b. + bdz->b = (cmph_uint8)b; + DEBUGP("b: %u\n", b); + +} + +void bdz_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + bdz_config_data_t *bdz = (bdz_config_data_t *)mph->data; + CMPH_HASH *hashptr = hashfuncs; + cmph_uint32 i = 0; + while(*hashptr != CMPH_HASH_COUNT) + { + if (i >= 1) break; //bdz only uses one linear hash function + bdz->hashfunc = *hashptr; + ++i, ++hashptr; + } +} + +cmph_t *bdz_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + bdz_data_t *bdzf = NULL; + cmph_uint32 iterations; + bdz_queue_t edges; + bdz_graph3_t graph3; + bdz_config_data_t *bdz = (bdz_config_data_t *)mph->data; + #ifdef CMPH_TIMING + double construction_time_begin = 0.0; + double construction_time = 0.0; + ELAPSED_TIME_IN_SECONDS(&construction_time_begin); + #endif + + + if (c == 0) c = 1.23; // validating restrictions over parameter c. + DEBUGP("c: %f\n", c); + bdz->m = mph->key_source->nkeys; + bdz->r = (cmph_uint32)ceil((c * mph->key_source->nkeys)/3); + if ((bdz->r % 2) == 0) bdz->r+=1; + bdz->n = 3*bdz->r; + + bdz->k = (1U << bdz->b); + DEBUGP("b: %u -- k: %u\n", bdz->b, bdz->k); + + bdz->ranktablesize = (cmph_uint32)ceil(bdz->n/(double)bdz->k); + DEBUGP("ranktablesize: %u\n", bdz->ranktablesize); + + + bdz_alloc_graph3(&graph3, bdz->m, bdz->n); + bdz_alloc_queue(&edges,bdz->m); + DEBUGP("Created hypergraph\n"); + + DEBUGP("m (edges): %u n (vertices): %u r: %u c: %f \n", bdz->m, bdz->n, bdz->r, c); + + // Mapping step + iterations = 1000; + if (mph->verbosity) + { + fprintf(stderr, "Entering mapping step for mph creation of %u keys with graph sized %u\n", bdz->m, bdz->n); + } + while(1) + { + int ok; + DEBUGP("linear hash function \n"); + bdz->hl = hash_state_new(bdz->hashfunc, 15); + + ok = bdz_mapping(mph, &graph3, edges); + //ok = 0; + if (!ok) + { + --iterations; + hash_state_destroy(bdz->hl); + bdz->hl = NULL; + DEBUGP("%u iterations remaining\n", iterations); + if (mph->verbosity) + { + fprintf(stderr, "acyclic graph creation failure - %u iterations remaining\n", iterations); + } + if (iterations == 0) break; + } + else break; + } + + if (iterations == 0) + { + bdz_free_queue(&edges); + bdz_free_graph3(&graph3); + return NULL; + } + bdz_partial_free_graph3(&graph3); + // Assigning step + if (mph->verbosity) + { + fprintf(stderr, "Entering assigning step for mph creation of %u keys with graph sized %u\n", bdz->m, bdz->n); + } + assigning(bdz, &graph3, edges); + + bdz_free_queue(&edges); + bdz_free_graph3(&graph3); + if (mph->verbosity) + { + fprintf(stderr, "Entering ranking step for mph creation of %u keys with graph sized %u\n", bdz->m, bdz->n); + } + ranking(bdz); + #ifdef CMPH_TIMING + ELAPSED_TIME_IN_SECONDS(&construction_time); + #endif + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + bdzf = (bdz_data_t *)malloc(sizeof(bdz_data_t)); + bdzf->g = bdz->g; + bdz->g = NULL; //transfer memory ownership + bdzf->hl = bdz->hl; + bdz->hl = NULL; //transfer memory ownership + bdzf->ranktable = bdz->ranktable; + bdz->ranktable = NULL; //transfer memory ownership + bdzf->ranktablesize = bdz->ranktablesize; + bdzf->k = bdz->k; + bdzf->b = bdz->b; + bdzf->n = bdz->n; + bdzf->m = bdz->m; + bdzf->r = bdz->r; + mphf->data = bdzf; + mphf->size = bdz->m; + + DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + + + #ifdef CMPH_TIMING + register cmph_uint32 space_usage = bdz_packed_size(mphf)*8; + register cmph_uint32 keys_per_bucket = 1; + construction_time = construction_time - construction_time_begin; + fprintf(stdout, "%u\t%.2f\t%u\t%.4f\t%.4f\n", bdz->m, bdz->m/(double)bdz->n, keys_per_bucket, construction_time, space_usage/(double)bdz->m); + #endif + + return mphf; +} + + +static int bdz_mapping(cmph_config_t *mph, bdz_graph3_t* graph3, bdz_queue_t queue) +{ + cmph_uint32 e; + int cycles = 0; + cmph_uint32 hl[3]; + bdz_config_data_t *bdz = (bdz_config_data_t *)mph->data; + bdz_init_graph3(graph3, bdz->m, bdz->n); + mph->key_source->rewind(mph->key_source->data); + for (e = 0; e < mph->key_source->nkeys; ++e) + { + cmph_uint32 h0, h1, h2; + cmph_uint32 keylen; + char *key = NULL; + mph->key_source->read(mph->key_source->data, &key, &keylen); + hash_vector(bdz->hl, key, keylen,hl); + h0 = hl[0] % bdz->r; + h1 = hl[1] % bdz->r + bdz->r; + h2 = hl[2] % bdz->r + (bdz->r << 1); + mph->key_source->dispose(mph->key_source->data, key, keylen); + bdz_add_edge(graph3,h0,h1,h2); + } + cycles = bdz_generate_queue(bdz->m, bdz->n, queue, graph3); + return (cycles == 0); +} + +static void assigning(bdz_config_data_t *bdz, bdz_graph3_t* graph3, bdz_queue_t queue) +{ + cmph_uint32 i; + cmph_uint32 nedges=graph3->nedges; + cmph_uint32 curr_edge; + cmph_uint32 v0,v1,v2; + cmph_uint8 * marked_vertices =malloc((size_t)(bdz->n >> 3) + 1); + cmph_uint32 sizeg = (cmph_uint32)ceil(bdz->n/4.0); + bdz->g = (cmph_uint8 *)calloc((size_t)(sizeg), sizeof(cmph_uint8)); + memset(marked_vertices, 0, (size_t)(bdz->n >> 3) + 1); + memset(bdz->g, 0xff, (size_t)(sizeg)); + + for(i=nedges-1;i+1>0;i--){ + curr_edge=queue[i]; + v0=graph3->edges[curr_edge].vertices[0]; + v1=graph3->edges[curr_edge].vertices[1]; + v2=graph3->edges[curr_edge].vertices[2]; + DEBUGP("B:%u %u %u -- %u %u %u\n", v0, v1, v2, GETVALUE(bdz->g, v0), GETVALUE(bdz->g, v1), GETVALUE(bdz->g, v2)); + if(!GETBIT(marked_vertices, v0)){ + if(!GETBIT(marked_vertices,v1)) + { + SETVALUE1(bdz->g, v1, UNASSIGNED); + SETBIT(marked_vertices, v1); + } + if(!GETBIT(marked_vertices,v2)) + { + SETVALUE1(bdz->g, v2, UNASSIGNED); + SETBIT(marked_vertices, v2); + } + SETVALUE1(bdz->g, v0, (6-(GETVALUE(bdz->g, v1) + GETVALUE(bdz->g,v2)))%3); + SETBIT(marked_vertices, v0); + } else if(!GETBIT(marked_vertices, v1)) { + if(!GETBIT(marked_vertices, v2)) + { + SETVALUE1(bdz->g, v2, UNASSIGNED); + SETBIT(marked_vertices, v2); + } + SETVALUE1(bdz->g, v1, (7-(GETVALUE(bdz->g, v0)+GETVALUE(bdz->g, v2)))%3); + SETBIT(marked_vertices, v1); + }else { + SETVALUE1(bdz->g, v2, (8-(GETVALUE(bdz->g,v0)+GETVALUE(bdz->g, v1)))%3); + SETBIT(marked_vertices, v2); + } + DEBUGP("A:%u %u %u -- %u %u %u\n", v0, v1, v2, GETVALUE(bdz->g, v0), GETVALUE(bdz->g, v1), GETVALUE(bdz->g, v2)); + }; + free(marked_vertices); +} + + +static void ranking(bdz_config_data_t *bdz) +{ + cmph_uint32 i, j, offset = 0U, count = 0U, size = (bdz->k >> 2U), nbytes_total = (cmph_uint32)ceil(bdz->n/4.0), nbytes; + bdz->ranktable = (cmph_uint32 *)calloc((size_t)bdz->ranktablesize, sizeof(cmph_uint32)); + // ranktable computation + bdz->ranktable[0] = 0; + i = 1; + while(1) + { + if(i == bdz->ranktablesize) break; + nbytes = size < nbytes_total? size : nbytes_total; + for(j = 0; j < nbytes; j++) + { + count += bdz_lookup_table[*(bdz->g + offset + j)]; + } + bdz->ranktable[i] = count; + offset += nbytes; + nbytes_total -= size; + i++; + } +} + + +int bdz_dump(cmph_t *mphf, FILE *fd) +{ + char *buf = NULL; + cmph_uint32 buflen; + register size_t nbytes; + bdz_data_t *data = (bdz_data_t *)mphf->data; + cmph_uint32 sizeg; +#ifdef DEBUG + cmph_uint32 i; +#endif + __cmph_dump(mphf, fd); + + hash_state_dump(data->hl, &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + nbytes = fwrite(&(data->n), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->m), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->r), sizeof(cmph_uint32), (size_t)1, fd); + + sizeg = (cmph_uint32)ceil(data->n/4.0); + nbytes = fwrite(data->g, sizeof(cmph_uint8)*sizeg, (size_t)1, fd); + + nbytes = fwrite(&(data->k), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->b), sizeof(cmph_uint8), (size_t)1, fd); + nbytes = fwrite(&(data->ranktablesize), sizeof(cmph_uint32), (size_t)1, fd); + + nbytes = fwrite(data->ranktable, sizeof(cmph_uint32)*(data->ranktablesize), (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < data->n; ++i) fprintf(stderr, "%u ", GETVALUE(data->g, i)); + fprintf(stderr, "\n"); + #endif + return 1; +} + +void bdz_load(FILE *f, cmph_t *mphf) +{ + char *buf = NULL; + cmph_uint32 buflen, sizeg; + register size_t nbytes; + bdz_data_t *bdz = (bdz_data_t *)malloc(sizeof(bdz_data_t)); +#ifdef DEBUG + cmph_uint32 i = 0; +#endif + + DEBUGP("Loading bdz mphf\n"); + mphf->data = bdz; + + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + DEBUGP("Hash state has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + bdz->hl = hash_state_load(buf, buflen); + free(buf); + + + DEBUGP("Reading m and n\n"); + nbytes = fread(&(bdz->n), sizeof(cmph_uint32), (size_t)1, f); + nbytes = fread(&(bdz->m), sizeof(cmph_uint32), (size_t)1, f); + nbytes = fread(&(bdz->r), sizeof(cmph_uint32), (size_t)1, f); + sizeg = (cmph_uint32)ceil(bdz->n/4.0); + bdz->g = (cmph_uint8 *)calloc((size_t)(sizeg), sizeof(cmph_uint8)); + nbytes = fread(bdz->g, sizeg*sizeof(cmph_uint8), (size_t)1, f); + + nbytes = fread(&(bdz->k), sizeof(cmph_uint32), (size_t)1, f); + nbytes = fread(&(bdz->b), sizeof(cmph_uint8), (size_t)1, f); + nbytes = fread(&(bdz->ranktablesize), sizeof(cmph_uint32), (size_t)1, f); + + bdz->ranktable = (cmph_uint32 *)calloc((size_t)bdz->ranktablesize, sizeof(cmph_uint32)); + nbytes = fread(bdz->ranktable, sizeof(cmph_uint32)*(bdz->ranktablesize), (size_t)1, f); + if (nbytes == 0 && ferror(f)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return; + } + + #ifdef DEBUG + i = 0; + fprintf(stderr, "G: "); + for (i = 0; i < bdz->n; ++i) fprintf(stderr, "%u ", GETVALUE(bdz->g,i)); + fprintf(stderr, "\n"); + #endif + return; +} + + +/* +static cmph_uint32 bdz_search_ph(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + bdz_data_t *bdz = mphf->data; + cmph_uint32 hl[3]; + hash_vector(bdz->hl, key, keylen, hl); + cmph_uint32 vertex; + hl[0] = hl[0] % bdz->r; + hl[1] = hl[1] % bdz->r + bdz->r; + hl[2] = hl[2] % bdz->r + (bdz->r << 1); + vertex = hl[(GETVALUE(bdz->g, hl[0]) + GETVALUE(bdz->g, hl[1]) + GETVALUE(bdz->g, hl[2])) % 3]; + return vertex; +} +*/ + +static inline cmph_uint32 rank(cmph_uint32 b, cmph_uint32 * ranktable, cmph_uint8 * g, cmph_uint32 vertex) +{ + register cmph_uint32 index = vertex >> b; + register cmph_uint32 base_rank = ranktable[index]; + register cmph_uint32 beg_idx_v = index << b; + register cmph_uint32 beg_idx_b = beg_idx_v >> 2; + register cmph_uint32 end_idx_b = vertex >> 2; + while(beg_idx_b < end_idx_b) + { + base_rank += bdz_lookup_table[*(g + beg_idx_b++)]; + + } + beg_idx_v = beg_idx_b << 2; + while(beg_idx_v < vertex) + { + if(GETVALUE(g, beg_idx_v) != UNASSIGNED) base_rank++; + beg_idx_v++; + } + + return base_rank; +} + +cmph_uint32 bdz_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + register cmph_uint32 vertex; + register bdz_data_t *bdz = mphf->data; + cmph_uint32 hl[3]; + hash_vector(bdz->hl, key, keylen, hl); + hl[0] = hl[0] % bdz->r; + hl[1] = hl[1] % bdz->r + bdz->r; + hl[2] = hl[2] % bdz->r + (bdz->r << 1); + vertex = hl[(GETVALUE(bdz->g, hl[0]) + GETVALUE(bdz->g, hl[1]) + GETVALUE(bdz->g, hl[2])) % 3]; + return rank(bdz->b, bdz->ranktable, bdz->g, vertex); +} + + +void bdz_destroy(cmph_t *mphf) +{ + bdz_data_t *data = (bdz_data_t *)mphf->data; + free(data->g); + hash_state_destroy(data->hl); + free(data->ranktable); + free(data); + free(mphf); +} + +/** \fn void bdz_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void bdz_pack(cmph_t *mphf, void *packed_mphf) +{ + bdz_data_t *data = (bdz_data_t *)mphf->data; + cmph_uint8 * ptr = packed_mphf; + cmph_uint32 sizeg; + + // packing hl type + CMPH_HASH hl_type = hash_get_type(data->hl); + *((cmph_uint32 *) ptr) = hl_type; + ptr += sizeof(cmph_uint32); + + // packing hl + hash_state_pack(data->hl, ptr); + ptr += hash_state_packed_size(hl_type); + + // packing r + *((cmph_uint32 *) ptr) = data->r; + ptr += sizeof(data->r); + + // packing ranktablesize + *((cmph_uint32 *) ptr) = data->ranktablesize; + ptr += sizeof(data->ranktablesize); + + // packing ranktable + memcpy(ptr, data->ranktable, sizeof(cmph_uint32)*(data->ranktablesize)); + ptr += sizeof(cmph_uint32)*(data->ranktablesize); + + // packing b + *ptr++ = data->b; + + // packing g + sizeg = (cmph_uint32)ceil(data->n/4.0); + memcpy(ptr, data->g, sizeof(cmph_uint8)*sizeg); +} + +/** \fn cmph_uint32 bdz_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 bdz_packed_size(cmph_t *mphf) +{ + bdz_data_t *data = (bdz_data_t *)mphf->data; + + CMPH_HASH hl_type = hash_get_type(data->hl); + + return (cmph_uint32)(sizeof(CMPH_ALGO) + hash_state_packed_size(hl_type) + 3*sizeof(cmph_uint32) + sizeof(cmph_uint32)*(data->ranktablesize) + sizeof(cmph_uint8) + sizeof(cmph_uint8)* (cmph_uint32)(ceil(data->n/4.0))); +} + +/** cmph_uint32 bdz_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 bdz_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + + register cmph_uint32 vertex; + register CMPH_HASH hl_type = *(cmph_uint32 *)packed_mphf; + register cmph_uint8 *hl_ptr = (cmph_uint8 *)(packed_mphf) + 4; + + register cmph_uint32 *ranktable = (cmph_uint32*)(hl_ptr + hash_state_packed_size(hl_type)); + + register cmph_uint32 r = *ranktable++; + register cmph_uint32 ranktablesize = *ranktable++; + register cmph_uint8 * g = (cmph_uint8 *)(ranktable + ranktablesize); + register cmph_uint8 b = *g++; + + cmph_uint32 hl[3]; + hash_vector_packed(hl_ptr, hl_type, key, keylen, hl); + hl[0] = hl[0] % r; + hl[1] = hl[1] % r + r; + hl[2] = hl[2] % r + (r << 1); + vertex = hl[(GETVALUE(g, hl[0]) + GETVALUE(g, hl[1]) + GETVALUE(g, hl[2])) % 3]; + return rank(b, ranktable, g, vertex); +} diff --git a/girepository/cmph/bdz.h b/girepository/cmph/bdz.h new file mode 100644 index 000000000..7116933b4 --- /dev/null +++ b/girepository/cmph/bdz.h @@ -0,0 +1,43 @@ +#ifndef __CMPH_BDZ_H__ +#define __CMPH_BDZ_H__ + +#include "cmph.h" + +typedef struct __bdz_data_t bdz_data_t; +typedef struct __bdz_config_data_t bdz_config_data_t; + +bdz_config_data_t *bdz_config_new(void); +void bdz_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); +void bdz_config_destroy(cmph_config_t *mph); +void bdz_config_set_b(cmph_config_t *mph, cmph_uint32 b); +cmph_t *bdz_new(cmph_config_t *mph, double c); + +void bdz_load(FILE *f, cmph_t *mphf); +int bdz_dump(cmph_t *mphf, FILE *f); +void bdz_destroy(cmph_t *mphf); +cmph_uint32 bdz_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +/** \fn void bdz_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void bdz_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 bdz_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 bdz_packed_size(cmph_t *mphf); + +/** cmph_uint32 bdz_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 bdz_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +#endif diff --git a/girepository/cmph/bdz_gen_lookup_table.c b/girepository/cmph/bdz_gen_lookup_table.c new file mode 100644 index 000000000..b8f66068d --- /dev/null +++ b/girepository/cmph/bdz_gen_lookup_table.c @@ -0,0 +1,33 @@ +#include +#include +#include +void help(char * prname) +{ + fprintf(stderr, "USE: %s \n", prname); + exit(1); +} + +int main(int argc, char ** argv) +{ + if(argc != 3) help(argv[0]); + int n = atoi(argv[1]); + int wordsize = (atoi(argv[2]) >> 1); + int i, j, n_assigned; + for(i = 0; i < n; i++) + { + int num = i; + n_assigned = 0; + for(j = 0; j < wordsize; j++) + { + if ((num & 0x0003) != 3) + { + n_assigned++; + //fprintf(stderr, "num:%d\n", num); + } + num = num >> 2; + } + if(i%16 == 0) fprintf(stderr, "\n"); + fprintf(stderr, "%d, ", n_assigned); + } + fprintf(stderr, "\n"); +} diff --git a/girepository/cmph/bdz_ph.c b/girepository/cmph/bdz_ph.c new file mode 100644 index 000000000..2095f1161 --- /dev/null +++ b/girepository/cmph/bdz_ph.c @@ -0,0 +1,633 @@ +#include "bdz_ph.h" +#include "cmph_structs.h" +#include "bdz_structs_ph.h" +#include "hash.h" +#include "bitbool.h" + +#include +#include +#include +#include +#include +#include +//#define DEBUG +#include "debug.h" +#define UNASSIGNED 3 +#define NULL_EDGE 0xffffffff + + +static cmph_uint8 pow3_table[5] = {1,3,9,27,81}; +static cmph_uint8 lookup_table[5][256] = { + {0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0}, + {0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +}; + +typedef struct +{ + cmph_uint32 vertices[3]; + cmph_uint32 next_edges[3]; +}bdz_ph_edge_t; + +typedef cmph_uint32 * bdz_ph_queue_t; + +static void bdz_ph_alloc_queue(bdz_ph_queue_t * queuep, cmph_uint32 nedges) +{ + (*queuep)=malloc(nedges*sizeof(cmph_uint32)); +}; +static void bdz_ph_free_queue(bdz_ph_queue_t * queue) +{ + free(*queue); +}; + +typedef struct +{ + cmph_uint32 nedges; + bdz_ph_edge_t * edges; + cmph_uint32 * first_edge; + cmph_uint8 * vert_degree; +}bdz_ph_graph3_t; + + +static void bdz_ph_alloc_graph3(bdz_ph_graph3_t * graph3, cmph_uint32 nedges, cmph_uint32 nvertices) +{ + graph3->edges=malloc(nedges*sizeof(bdz_ph_edge_t)); + graph3->first_edge=malloc(nvertices*sizeof(cmph_uint32)); + graph3->vert_degree=malloc((size_t)nvertices); +}; +static void bdz_ph_init_graph3(bdz_ph_graph3_t * graph3, cmph_uint32 nedges, cmph_uint32 nvertices) +{ + memset(graph3->first_edge,0xff,nvertices*sizeof(cmph_uint32)); + memset(graph3->vert_degree,0,(size_t)nvertices); + graph3->nedges=0; +}; +static void bdz_ph_free_graph3(bdz_ph_graph3_t *graph3) +{ + free(graph3->edges); + free(graph3->first_edge); + free(graph3->vert_degree); +}; + +static void bdz_ph_partial_free_graph3(bdz_ph_graph3_t *graph3) +{ + free(graph3->first_edge); + free(graph3->vert_degree); + graph3->first_edge = NULL; + graph3->vert_degree = NULL; +}; + +static void bdz_ph_add_edge(bdz_ph_graph3_t * graph3, cmph_uint32 v0, cmph_uint32 v1, cmph_uint32 v2) +{ + graph3->edges[graph3->nedges].vertices[0]=v0; + graph3->edges[graph3->nedges].vertices[1]=v1; + graph3->edges[graph3->nedges].vertices[2]=v2; + graph3->edges[graph3->nedges].next_edges[0]=graph3->first_edge[v0]; + graph3->edges[graph3->nedges].next_edges[1]=graph3->first_edge[v1]; + graph3->edges[graph3->nedges].next_edges[2]=graph3->first_edge[v2]; + graph3->first_edge[v0]=graph3->first_edge[v1]=graph3->first_edge[v2]=graph3->nedges; + graph3->vert_degree[v0]++; + graph3->vert_degree[v1]++; + graph3->vert_degree[v2]++; + graph3->nedges++; +}; + +static void bdz_ph_dump_graph(bdz_ph_graph3_t* graph3, cmph_uint32 nedges, cmph_uint32 nvertices) +{ + cmph_uint32 i; + for(i=0;iedges[i].vertices[0], + graph3->edges[i].vertices[1],graph3->edges[i].vertices[2]); + printf(" nexts %d %d %d",graph3->edges[i].next_edges[0], + graph3->edges[i].next_edges[1],graph3->edges[i].next_edges[2]); + }; + + for(i=0;ifirst_edge[i]); + + }; +}; + +static void bdz_ph_remove_edge(bdz_ph_graph3_t * graph3, cmph_uint32 curr_edge) +{ + cmph_uint32 i,j=0,vert,edge1,edge2; + for(i=0;i<3;i++){ + vert=graph3->edges[curr_edge].vertices[i]; + edge1=graph3->first_edge[vert]; + edge2=NULL_EDGE; + while(edge1!=curr_edge&&edge1!=NULL_EDGE){ + edge2=edge1; + if(graph3->edges[edge1].vertices[0]==vert){ + j=0; + } else if(graph3->edges[edge1].vertices[1]==vert){ + j=1; + } else + j=2; + edge1=graph3->edges[edge1].next_edges[j]; + }; + if(edge1==NULL_EDGE){ + printf("\nerror remove edge %d dump graph",curr_edge); + bdz_ph_dump_graph(graph3,graph3->nedges,graph3->nedges+graph3->nedges/4); + exit(-1); + }; + + if(edge2!=NULL_EDGE){ + graph3->edges[edge2].next_edges[j] = + graph3->edges[edge1].next_edges[i]; + } else + graph3->first_edge[vert]= + graph3->edges[edge1].next_edges[i]; + graph3->vert_degree[vert]--; + }; + +}; + +static int bdz_ph_generate_queue(cmph_uint32 nedges, cmph_uint32 nvertices, bdz_ph_queue_t queue, bdz_ph_graph3_t* graph3) +{ + cmph_uint32 i,v0,v1,v2; + cmph_uint32 queue_head=0,queue_tail=0; + cmph_uint32 curr_edge; + cmph_uint32 tmp_edge; + cmph_uint8 * marked_edge =malloc((size_t)(nedges >> 3) + 1); + memset(marked_edge, 0, (size_t)(nedges >> 3) + 1); + + for(i=0;iedges[i].vertices[0]; + v1=graph3->edges[i].vertices[1]; + v2=graph3->edges[i].vertices[2]; + if(graph3->vert_degree[v0]==1 || + graph3->vert_degree[v1]==1 || + graph3->vert_degree[v2]==1){ + if(!GETBIT(marked_edge,i)) { + queue[queue_head++]=i; + SETBIT(marked_edge,i); + } + }; + }; + while(queue_tail!=queue_head){ + curr_edge=queue[queue_tail++]; + bdz_ph_remove_edge(graph3,curr_edge); + v0=graph3->edges[curr_edge].vertices[0]; + v1=graph3->edges[curr_edge].vertices[1]; + v2=graph3->edges[curr_edge].vertices[2]; + if(graph3->vert_degree[v0]==1 ) { + tmp_edge=graph3->first_edge[v0]; + if(!GETBIT(marked_edge,tmp_edge)) { + queue[queue_head++]=tmp_edge; + SETBIT(marked_edge,tmp_edge); + }; + + }; + if(graph3->vert_degree[v1]==1) { + tmp_edge=graph3->first_edge[v1]; + if(!GETBIT(marked_edge,tmp_edge)){ + queue[queue_head++]=tmp_edge; + SETBIT(marked_edge,tmp_edge); + }; + + }; + if(graph3->vert_degree[v2]==1){ + tmp_edge=graph3->first_edge[v2]; + if(!GETBIT(marked_edge,tmp_edge)){ + queue[queue_head++]=tmp_edge; + SETBIT(marked_edge,tmp_edge); + }; + }; + }; + free(marked_edge); + return (int)queue_head - (int)nedges;/* returns 0 if successful otherwies return negative number*/ +}; + +static int bdz_ph_mapping(cmph_config_t *mph, bdz_ph_graph3_t* graph3, bdz_ph_queue_t queue); +static void assigning(bdz_ph_config_data_t *bdz_ph, bdz_ph_graph3_t* graph3, bdz_ph_queue_t queue); +static void bdz_ph_optimization(bdz_ph_config_data_t *bdz_ph); + +bdz_ph_config_data_t *bdz_ph_config_new(void) +{ + bdz_ph_config_data_t *bdz_ph; + bdz_ph = (bdz_ph_config_data_t *)malloc(sizeof(bdz_ph_config_data_t)); + assert(bdz_ph); + memset(bdz_ph, 0, sizeof(bdz_ph_config_data_t)); + bdz_ph->hashfunc = CMPH_HASH_JENKINS; + bdz_ph->g = NULL; + bdz_ph->hl = NULL; + return bdz_ph; +} + +void bdz_ph_config_destroy(cmph_config_t *mph) +{ + bdz_ph_config_data_t *data = (bdz_ph_config_data_t *)mph->data; + DEBUGP("Destroying algorithm dependent data\n"); + free(data); +} + +void bdz_ph_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + bdz_ph_config_data_t *bdz_ph = (bdz_ph_config_data_t *)mph->data; + CMPH_HASH *hashptr = hashfuncs; + cmph_uint32 i = 0; + while(*hashptr != CMPH_HASH_COUNT) + { + if (i >= 1) break; //bdz_ph only uses one linear hash function + bdz_ph->hashfunc = *hashptr; + ++i, ++hashptr; + } +} + +cmph_t *bdz_ph_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + bdz_ph_data_t *bdz_phf = NULL; + cmph_uint32 iterations; + bdz_ph_queue_t edges; + bdz_ph_graph3_t graph3; + bdz_ph_config_data_t *bdz_ph = (bdz_ph_config_data_t *)mph->data; + #ifdef CMPH_TIMING + double construction_time_begin = 0.0; + double construction_time = 0.0; + ELAPSED_TIME_IN_SECONDS(&construction_time_begin); + #endif + + + if (c == 0) c = 1.23; // validating restrictions over parameter c. + DEBUGP("c: %f\n", c); + bdz_ph->m = mph->key_source->nkeys; + bdz_ph->r = (cmph_uint32)ceil((c * mph->key_source->nkeys)/3); + if ((bdz_ph->r % 2) == 0) bdz_ph->r += 1; + bdz_ph->n = 3*bdz_ph->r; + + + bdz_ph_alloc_graph3(&graph3, bdz_ph->m, bdz_ph->n); + bdz_ph_alloc_queue(&edges,bdz_ph->m); + DEBUGP("Created hypergraph\n"); + + DEBUGP("m (edges): %u n (vertices): %u r: %u c: %f \n", bdz_ph->m, bdz_ph->n, bdz_ph->r, c); + + // Mapping step + iterations = 100; + if (mph->verbosity) + { + fprintf(stderr, "Entering mapping step for mph creation of %u keys with graph sized %u\n", bdz_ph->m, bdz_ph->n); + } + while(1) + { + int ok; + DEBUGP("linear hash function \n"); + bdz_ph->hl = hash_state_new(bdz_ph->hashfunc, 15); + + ok = bdz_ph_mapping(mph, &graph3, edges); + if (!ok) + { + --iterations; + hash_state_destroy(bdz_ph->hl); + bdz_ph->hl = NULL; + DEBUGP("%u iterations remaining\n", iterations); + if (mph->verbosity) + { + fprintf(stderr, "acyclic graph creation failure - %u iterations remaining\n", iterations); + } + if (iterations == 0) break; + } + else break; + } + + if (iterations == 0) + { +// free(bdz_ph->g); + bdz_ph_free_queue(&edges); + bdz_ph_free_graph3(&graph3); + return NULL; + } + bdz_ph_partial_free_graph3(&graph3); + // Assigning step + if (mph->verbosity) + { + fprintf(stderr, "Entering assigning step for mph creation of %u keys with graph sized %u\n", bdz_ph->m, bdz_ph->n); + } + assigning(bdz_ph, &graph3, edges); + + bdz_ph_free_queue(&edges); + bdz_ph_free_graph3(&graph3); + + if (mph->verbosity) + { + fprintf(stderr, "Starting optimization step\n"); + } + + bdz_ph_optimization(bdz_ph); + + #ifdef CMPH_TIMING + ELAPSED_TIME_IN_SECONDS(&construction_time); + #endif + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + bdz_phf = (bdz_ph_data_t *)malloc(sizeof(bdz_ph_data_t)); + bdz_phf->g = bdz_ph->g; + bdz_ph->g = NULL; //transfer memory ownership + bdz_phf->hl = bdz_ph->hl; + bdz_ph->hl = NULL; //transfer memory ownership + bdz_phf->n = bdz_ph->n; + bdz_phf->m = bdz_ph->m; + bdz_phf->r = bdz_ph->r; + mphf->data = bdz_phf; + mphf->size = bdz_ph->n; + + DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + + #ifdef CMPH_TIMING + register cmph_uint32 space_usage = bdz_ph_packed_size(mphf)*8; + register cmph_uint32 keys_per_bucket = 1; + construction_time = construction_time - construction_time_begin; + fprintf(stdout, "%u\t%.2f\t%u\t%.4f\t%.4f\n", bdz_ph->m, bdz_ph->m/(double)bdz_ph->n, keys_per_bucket, construction_time, space_usage/(double)bdz_ph->m); + #endif + + return mphf; +} + + +static int bdz_ph_mapping(cmph_config_t *mph, bdz_ph_graph3_t* graph3, bdz_ph_queue_t queue) +{ + cmph_uint32 e; + int cycles = 0; + cmph_uint32 hl[3]; + + bdz_ph_config_data_t *bdz_ph = (bdz_ph_config_data_t *)mph->data; + bdz_ph_init_graph3(graph3, bdz_ph->m, bdz_ph->n); + mph->key_source->rewind(mph->key_source->data); + for (e = 0; e < mph->key_source->nkeys; ++e) + { + cmph_uint32 h0, h1, h2; + cmph_uint32 keylen; + char *key = NULL; + mph->key_source->read(mph->key_source->data, &key, &keylen); + hash_vector(bdz_ph->hl, key, keylen, hl); + h0 = hl[0] % bdz_ph->r; + h1 = hl[1] % bdz_ph->r + bdz_ph->r; + h2 = hl[2] % bdz_ph->r + (bdz_ph->r << 1); + mph->key_source->dispose(mph->key_source->data, key, keylen); + bdz_ph_add_edge(graph3,h0,h1,h2); + } + cycles = bdz_ph_generate_queue(bdz_ph->m, bdz_ph->n, queue, graph3); + return (cycles == 0); +} + +static void assigning(bdz_ph_config_data_t *bdz_ph, bdz_ph_graph3_t* graph3, bdz_ph_queue_t queue) +{ + cmph_uint32 i; + cmph_uint32 nedges=graph3->nedges; + cmph_uint32 curr_edge; + cmph_uint32 v0,v1,v2; + cmph_uint8 * marked_vertices =malloc((size_t)(bdz_ph->n >> 3) + 1); + cmph_uint32 sizeg = (cmph_uint32)ceil(bdz_ph->n/4.0); + bdz_ph->g = (cmph_uint8 *)calloc((size_t)sizeg, sizeof(cmph_uint8)); + memset(marked_vertices, 0, (size_t)(bdz_ph->n >> 3) + 1); + //memset(bdz_ph->g, 0xff, sizeg); + + for(i=nedges-1;i+1>=1;i--){ + curr_edge=queue[i]; + v0=graph3->edges[curr_edge].vertices[0]; + v1=graph3->edges[curr_edge].vertices[1]; + v2=graph3->edges[curr_edge].vertices[2]; + DEBUGP("B:%u %u %u -- %u %u %u\n", v0, v1, v2, GETVALUE(bdz_ph->g, v0), GETVALUE(bdz_ph->g, v1), GETVALUE(bdz_ph->g, v2)); + if(!GETBIT(marked_vertices, v0)){ + if(!GETBIT(marked_vertices,v1)) + { + //SETVALUE(bdz_ph->g, v1, UNASSIGNED); + SETBIT(marked_vertices, v1); + } + if(!GETBIT(marked_vertices,v2)) + { + //SETVALUE(bdz_ph->g, v2, UNASSIGNED); + SETBIT(marked_vertices, v2); + } + SETVALUE0(bdz_ph->g, v0, (6-(GETVALUE(bdz_ph->g, v1) + GETVALUE(bdz_ph->g,v2)))%3); + SETBIT(marked_vertices, v0); + } else if(!GETBIT(marked_vertices, v1)) { + if(!GETBIT(marked_vertices, v2)) + { + //SETVALUE(bdz_ph->g, v2, UNASSIGNED); + SETBIT(marked_vertices, v2); + } + SETVALUE0(bdz_ph->g, v1, (7 - (GETVALUE(bdz_ph->g, v0)+GETVALUE(bdz_ph->g, v2)))%3); + SETBIT(marked_vertices, v1); + }else { + SETVALUE0(bdz_ph->g, v2, (8-(GETVALUE(bdz_ph->g,v0)+GETVALUE(bdz_ph->g, v1)))%3); + SETBIT(marked_vertices, v2); + } + DEBUGP("A:%u %u %u -- %u %u %u\n", v0, v1, v2, GETVALUE(bdz_ph->g, v0), GETVALUE(bdz_ph->g, v1), GETVALUE(bdz_ph->g, v2)); + }; + free(marked_vertices); +} + +static void bdz_ph_optimization(bdz_ph_config_data_t *bdz_ph) +{ + cmph_uint32 i; + cmph_uint8 byte = 0; + cmph_uint32 sizeg = (cmph_uint32)ceil(bdz_ph->n/5.0); + cmph_uint8 * new_g = (cmph_uint8 *)calloc((size_t)sizeg, sizeof(cmph_uint8)); + cmph_uint8 value; + cmph_uint32 idx; + for(i = 0; i < bdz_ph->n; i++) + { + idx = i/5; + byte = new_g[idx]; + value = GETVALUE(bdz_ph->g, i); + byte = (cmph_uint8) (byte + value*pow3_table[i%5U]); + new_g[idx] = byte; + } + free(bdz_ph->g); + bdz_ph->g = new_g; +} + + +int bdz_ph_dump(cmph_t *mphf, FILE *fd) +{ + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint32 sizeg = 0; + register size_t nbytes; + bdz_ph_data_t *data = (bdz_ph_data_t *)mphf->data; +#ifdef DEBUG + cmph_uint32 i; +#endif + + __cmph_dump(mphf, fd); + + hash_state_dump(data->hl, &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + nbytes = fwrite(&(data->n), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->m), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->r), sizeof(cmph_uint32), (size_t)1, fd); + sizeg = (cmph_uint32)ceil(data->n/5.0); + nbytes = fwrite(data->g, sizeof(cmph_uint8)*sizeg, (size_t)1, fd); + + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < data->n; ++i) fprintf(stderr, "%u ", GETVALUE(data->g, i)); + fprintf(stderr, "\n"); + #endif + return 1; +} + +void bdz_ph_load(FILE *f, cmph_t *mphf) +{ + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint32 sizeg = 0; + register size_t nbytes; + bdz_ph_data_t *bdz_ph = (bdz_ph_data_t *)malloc(sizeof(bdz_ph_data_t)); + + DEBUGP("Loading bdz_ph mphf\n"); + mphf->data = bdz_ph; + + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + DEBUGP("Hash state has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + bdz_ph->hl = hash_state_load(buf, buflen); + free(buf); + + + DEBUGP("Reading m and n\n"); + nbytes = fread(&(bdz_ph->n), sizeof(cmph_uint32), (size_t)1, f); + nbytes = fread(&(bdz_ph->m), sizeof(cmph_uint32), (size_t)1, f); + nbytes = fread(&(bdz_ph->r), sizeof(cmph_uint32), (size_t)1, f); + sizeg = (cmph_uint32)ceil(bdz_ph->n/5.0); + bdz_ph->g = (cmph_uint8 *)calloc((size_t)sizeg, sizeof(cmph_uint8)); + nbytes = fread(bdz_ph->g, sizeg*sizeof(cmph_uint8), (size_t)1, f); + + if (nbytes == 0 && ferror(f)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + } + return; +} + + +cmph_uint32 bdz_ph_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + register bdz_ph_data_t *bdz_ph = mphf->data; + cmph_uint32 hl[3]; + register cmph_uint8 byte0, byte1, byte2; + register cmph_uint32 vertex; + + hash_vector(bdz_ph->hl, key, keylen,hl); + hl[0] = hl[0] % bdz_ph->r; + hl[1] = hl[1] % bdz_ph->r + bdz_ph->r; + hl[2] = hl[2] % bdz_ph->r + (bdz_ph->r << 1); + + byte0 = bdz_ph->g[hl[0]/5]; + byte1 = bdz_ph->g[hl[1]/5]; + byte2 = bdz_ph->g[hl[2]/5]; + + byte0 = lookup_table[hl[0]%5U][byte0]; + byte1 = lookup_table[hl[1]%5U][byte1]; + byte2 = lookup_table[hl[2]%5U][byte2]; + vertex = hl[(byte0 + byte1 + byte2)%3]; + + return vertex; +} + + +void bdz_ph_destroy(cmph_t *mphf) +{ + bdz_ph_data_t *data = (bdz_ph_data_t *)mphf->data; + free(data->g); + hash_state_destroy(data->hl); + free(data); + free(mphf); +} + +/** \fn void bdz_ph_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void bdz_ph_pack(cmph_t *mphf, void *packed_mphf) +{ + bdz_ph_data_t *data = (bdz_ph_data_t *)mphf->data; + cmph_uint8 * ptr = packed_mphf; + cmph_uint32 sizeg; + + // packing hl type + CMPH_HASH hl_type = hash_get_type(data->hl); + *((cmph_uint32 *) ptr) = hl_type; + ptr += sizeof(cmph_uint32); + + // packing hl + hash_state_pack(data->hl, ptr); + ptr += hash_state_packed_size(hl_type); + + // packing r + *((cmph_uint32 *) ptr) = data->r; + ptr += sizeof(data->r); + + // packing g + sizeg = (cmph_uint32)ceil(data->n/5.0); + memcpy(ptr, data->g, sizeof(cmph_uint8)*sizeg); +} + +/** \fn cmph_uint32 bdz_ph_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 bdz_ph_packed_size(cmph_t *mphf) +{ + bdz_ph_data_t *data = (bdz_ph_data_t *)mphf->data; + CMPH_HASH hl_type = hash_get_type(data->hl); + cmph_uint32 sizeg = (cmph_uint32)ceil(data->n/5.0); + return (cmph_uint32) (sizeof(CMPH_ALGO) + hash_state_packed_size(hl_type) + 2*sizeof(cmph_uint32) + sizeof(cmph_uint8)*sizeg); +} + +/** cmph_uint32 bdz_ph_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 bdz_ph_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + + register CMPH_HASH hl_type = *(cmph_uint32 *)packed_mphf; + register cmph_uint8 *hl_ptr = (cmph_uint8 *)(packed_mphf) + 4; + + register cmph_uint8 * ptr = hl_ptr + hash_state_packed_size(hl_type); + + register cmph_uint32 r = *((cmph_uint32*) ptr); + register cmph_uint8 * g = ptr + 4; + + cmph_uint32 hl[3]; + register cmph_uint8 byte0, byte1, byte2; + register cmph_uint32 vertex; + + hash_vector_packed(hl_ptr, hl_type, key, keylen, hl); + + hl[0] = hl[0] % r; + hl[1] = hl[1] % r + r; + hl[2] = hl[2] % r + (r << 1); + + byte0 = g[hl[0]/5]; + byte1 = g[hl[1]/5]; + byte2 = g[hl[2]/5]; + + byte0 = lookup_table[hl[0]%5][byte0]; + byte1 = lookup_table[hl[1]%5][byte1]; + byte2 = lookup_table[hl[2]%5][byte2]; + vertex = hl[(byte0 + byte1 + byte2)%3]; + + return vertex; +} diff --git a/girepository/cmph/bdz_ph.h b/girepository/cmph/bdz_ph.h new file mode 100644 index 000000000..67b1facbe --- /dev/null +++ b/girepository/cmph/bdz_ph.h @@ -0,0 +1,42 @@ +#ifndef __CMPH_BDZ_PH_H__ +#define __CMPH_BDZ_PH_H__ + +#include "cmph.h" + +typedef struct __bdz_ph_data_t bdz_ph_data_t; +typedef struct __bdz_ph_config_data_t bdz_ph_config_data_t; + +bdz_ph_config_data_t *bdz_ph_config_new(void); +void bdz_ph_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); +void bdz_ph_config_destroy(cmph_config_t *mph); +cmph_t *bdz_ph_new(cmph_config_t *mph, double c); + +void bdz_ph_load(FILE *f, cmph_t *mphf); +int bdz_ph_dump(cmph_t *mphf, FILE *f); +void bdz_ph_destroy(cmph_t *mphf); +cmph_uint32 bdz_ph_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +/** \fn void bdz_ph_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void bdz_ph_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 bdz_ph_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 bdz_ph_packed_size(cmph_t *mphf); + +/** cmph_uint32 bdz_ph_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 bdz_ph_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +#endif diff --git a/girepository/cmph/bdz_structs.h b/girepository/cmph/bdz_structs.h new file mode 100644 index 000000000..ba7dc3c66 --- /dev/null +++ b/girepository/cmph/bdz_structs.h @@ -0,0 +1,36 @@ +#ifndef __CMPH_BDZ_STRUCTS_H__ +#define __CMPH_BDZ_STRUCTS_H__ + +#include "hash_state.h" + +struct __bdz_data_t +{ + cmph_uint32 m; //edges (words) count + cmph_uint32 n; //vertex count + cmph_uint32 r; //partition vertex count + cmph_uint8 *g; + hash_state_t *hl; // linear hashing + + cmph_uint32 k; //kth index in ranktable, $k = log_2(n=3r)/\varepsilon$ + cmph_uint8 b; // number of bits of k + cmph_uint32 ranktablesize; //number of entries in ranktable, $n/k +1$ + cmph_uint32 *ranktable; // rank table +}; + + +struct __bdz_config_data_t +{ + cmph_uint32 m; //edges (words) count + cmph_uint32 n; //vertex count + cmph_uint32 r; //partition vertex count + cmph_uint8 *g; + hash_state_t *hl; // linear hashing + + cmph_uint32 k; //kth index in ranktable, $k = log_2(n=3r)/\varepsilon$ + cmph_uint8 b; // number of bits of k + cmph_uint32 ranktablesize; //number of entries in ranktable, $n/k +1$ + cmph_uint32 *ranktable; // rank table + CMPH_HASH hashfunc; +}; + +#endif diff --git a/girepository/cmph/bdz_structs_ph.h b/girepository/cmph/bdz_structs_ph.h new file mode 100644 index 000000000..5874a26df --- /dev/null +++ b/girepository/cmph/bdz_structs_ph.h @@ -0,0 +1,26 @@ +#ifndef __CMPH_BDZ_STRUCTS_PH_H__ +#define __CMPH_BDZ_STRUCTS_PH_H__ + +#include "hash_state.h" + +struct __bdz_ph_data_t +{ + cmph_uint32 m; //edges (words) count + cmph_uint32 n; //vertex count + cmph_uint32 r; //partition vertex count + cmph_uint8 *g; + hash_state_t *hl; // linear hashing +}; + + +struct __bdz_ph_config_data_t +{ + CMPH_HASH hashfunc; + cmph_uint32 m; //edges (words) count + cmph_uint32 n; //vertex count + cmph_uint32 r; //partition vertex count + cmph_uint8 *g; + hash_state_t *hl; // linear hashing +}; + +#endif diff --git a/girepository/cmph/bitbool.h b/girepository/cmph/bitbool.h new file mode 100644 index 000000000..a3286c3c9 --- /dev/null +++ b/girepository/cmph/bitbool.h @@ -0,0 +1,179 @@ +#ifndef _CMPH_BITBOOL_H__ +#define _CMPH_BITBOOL_H__ +#include "cmph_types.h" + +static const cmph_uint8 bitmask[] = { 1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7 }; + +static const cmph_uint32 bitmask32[] = { 1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7, + 1 << 8, 1 << 9, 1 << 10, 1 << 11, 1 << 12, 1 << 13, 1 << 14, 1 << 15, + 1 << 16, 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 21, 1 << 22, 1 << 23, + 1 << 24, 1 << 25, 1 << 26, 1 << 27, 1 << 28, 1 << 29, 1 << 30, 1U << 31 + }; + +static const cmph_uint8 valuemask[] = { 0xfc, 0xf3, 0xcf, 0x3f}; + + +/** \def GETBIT(array, i) + * \brief get the value of an 1-bit integer stored in an array. + * \param array to get 1-bit integer values from + * \param i is the index in array to get the 1-bit integer value from + * + * GETBIT(array, i) is a macro that gets the value of an 1-bit integer stored in array. + */ +#define GETBIT(array, i) ((array[i >> 3] & bitmask[i & 0x00000007]) >> (i & 0x00000007)) + +/** \def SETBIT(array, i) + * \brief set 1 to an 1-bit integer stored in an array. + * \param array to store 1-bit integer values + * \param i is the index in array to set the the bit to 1 + * + * SETBIT(array, i) is a macro that sets 1 to an 1-bit integer stored in an array. + */ +#define SETBIT(array, i) (array[i >> 3] |= bitmask[i & 0x00000007]) + +/** \def UNSETBIT(array, i) + * \brief set 0 to an 1-bit integer stored in an array. + * \param array to store 1-bit integer values + * \param i is the index in array to set the the bit to 0 + * + * UNSETBIT(array, i) is a macro that sets 0 to an 1-bit integer stored in an array. + */ +#define UNSETBIT(array, i) (array[i >> 3] ^= ((bitmask[i & 0x00000007]))) + +//#define GETBIT(array, i) (array[(i) / 8] & bitmask[(i) % 8]) +//#define SETBIT(array, i) (array[(i) / 8] |= bitmask[(i) % 8]) +//#define UNSETBIT(array, i) (array[(i) / 8] ^= ((bitmask[(i) % 8]))) + + +/** \def SETVALUE1(array, i, v) + * \brief set a value for a 2-bit integer stored in an array initialized with 1s. + * \param array to store 2-bit integer values + * \param i is the index in array to set the value v + * \param v is the value to be set + * + * SETVALUE1(array, i, v) is a macro that set a value for a 2-bit integer stored in an array. + * The array should be initialized with all bits set to 1. For example: + * memset(array, 0xff, arraySize); + */ +#define SETVALUE1(array, i, v) (array[i >> 2] &= (cmph_uint8)((v << ((i & 0x00000003) << 1)) | valuemask[i & 0x00000003])) + +/** \def SETVALUE0(array, i, v) + * \brief set a value for a 2-bit integer stored in an array initialized with 0s. + * \param array to store 2-bit integer values + * \param i is the index in array to set the value v + * \param v is the value to be set + * + * SETVALUE0(array, i, v) is a macro that set a value for a 2-bit integer stored in an array. + * The array should be initialized with all bits set to 0. For example: + * memset(array, 0, arraySize); + */ +#define SETVALUE0(array, i, v) (array[i >> 2] |= (cmph_uint8)(v << ((i & 0x00000003) << 1))) + + +/** \def GETVALUE(array, i) + * \brief get a value for a 2-bit integer stored in an array. + * \param array to get 2-bit integer values from + * \param i is the index in array to get the value from + * + * GETVALUE(array, i) is a macro that get a value for a 2-bit integer stored in an array. + */ +#define GETVALUE(array, i) ((cmph_uint8)((array[i >> 2] >> ((i & 0x00000003U) << 1U)) & 0x00000003U)) + + + +/** \def SETBIT32(array, i) + * \brief set 1 to an 1-bit integer stored in an array of 32-bit words. + * \param array to store 1-bit integer values. The entries are 32-bit words. + * \param i is the index in array to set the the bit to 1 + * + * SETBIT32(array, i) is a macro that sets 1 to an 1-bit integer stored in an array of 32-bit words. + */ +#define SETBIT32(array, i) (array[i >> 5] |= bitmask32[i & 0x0000001f]) + +/** \def GETBIT32(array, i) + * \brief get the value of an 1-bit integer stored in an array of 32-bit words. + * \param array to get 1-bit integer values from. The entries are 32-bit words. + * \param i is the index in array to get the 1-bit integer value from + * + * GETBIT32(array, i) is a macro that gets the value of an 1-bit integer stored in an array of 32-bit words. + */ +#define GETBIT32(array, i) (array[i >> 5] & bitmask32[i & 0x0000001f]) + +/** \def UNSETBIT32(array, i) + * \brief set 0 to an 1-bit integer stored in an array of 32-bit words. + * \param array to store 1-bit integer values. The entries ar 32-bit words + * \param i is the index in array to set the the bit to 0 + * + * UNSETBIT32(array, i) is a macro that sets 0 to an 1-bit integer stored in an array of 32-bit words. + */ +#define UNSETBIT32(array, i) (array[i >> 5] ^= ((bitmask32[i & 0x0000001f]))) + +#define BITS_TABLE_SIZE(n, bits_length) ((n * bits_length + 31) >> 5) + +static inline void set_bits_value(cmph_uint32 * bits_table, cmph_uint32 index, cmph_uint32 bits_string, + cmph_uint32 string_length, cmph_uint32 string_mask) +{ + register cmph_uint32 bit_idx = index * string_length; + register cmph_uint32 word_idx = bit_idx >> 5; + register cmph_uint32 shift1 = bit_idx & 0x0000001f; + register cmph_uint32 shift2 = 32 - shift1; + + bits_table[word_idx] &= ~((string_mask) << shift1); + bits_table[word_idx] |= bits_string << shift1; + + if(shift2 < string_length) + { + bits_table[word_idx+1] &= ~((string_mask) >> shift2); + bits_table[word_idx+1] |= bits_string >> shift2; + }; +}; + +static inline cmph_uint32 get_bits_value(cmph_uint32 * bits_table,cmph_uint32 index, cmph_uint32 string_length, cmph_uint32 string_mask) +{ + register cmph_uint32 bit_idx = index * string_length; + register cmph_uint32 word_idx = bit_idx >> 5; + register cmph_uint32 shift1 = bit_idx & 0x0000001f; + register cmph_uint32 shift2 = 32-shift1; + register cmph_uint32 bits_string; + + bits_string = (bits_table[word_idx] >> shift1) & string_mask; + + if(shift2 < string_length) + bits_string |= (bits_table[word_idx+1] << shift2) & string_mask; + + return bits_string; +}; + +static inline void set_bits_at_pos(cmph_uint32 * bits_table, cmph_uint32 pos, cmph_uint32 bits_string, cmph_uint32 string_length) +{ + register cmph_uint32 word_idx = pos >> 5; + register cmph_uint32 shift1 = pos & 0x0000001f; + register cmph_uint32 shift2 = 32-shift1; + register cmph_uint32 string_mask = (1U << string_length) - 1; + + bits_table[word_idx] &= ~((string_mask) << shift1); + bits_table[word_idx] |= bits_string << shift1; + if(shift2 < string_length) + { + bits_table[word_idx+1] &= ~((string_mask) >> shift2); + bits_table[word_idx+1] |= bits_string >> shift2; + } +}; + +static inline cmph_uint32 get_bits_at_pos(cmph_uint32 * bits_table,cmph_uint32 pos,cmph_uint32 string_length) +{ + register cmph_uint32 word_idx = pos >> 5; + register cmph_uint32 shift1 = pos & 0x0000001f; + register cmph_uint32 shift2 = 32 - shift1; + register cmph_uint32 string_mask = (1U << string_length) - 1; + register cmph_uint32 bits_string; + + bits_string = (bits_table[word_idx] >> shift1) & string_mask; + + if(shift2 < string_length) + bits_string |= (bits_table[word_idx+1] << shift2) & string_mask; + return bits_string; +} + + +#endif diff --git a/girepository/cmph/bmz.c b/girepository/cmph/bmz.c new file mode 100644 index 000000000..9573825af --- /dev/null +++ b/girepository/cmph/bmz.c @@ -0,0 +1,638 @@ +#include "graph.h" +#include "bmz.h" +#include "cmph_structs.h" +#include "bmz_structs.h" +#include "hash.h" +#include "vqueue.h" +#include "bitbool.h" + +#include +#include +#include +#include +#include +#include + +//#define DEBUG +#include "debug.h" + +static int bmz_gen_edges(cmph_config_t *mph); +static cmph_uint8 bmz_traverse_critical_nodes(bmz_config_data_t *bmz, cmph_uint32 v, cmph_uint32 * biggest_g_value, cmph_uint32 * biggest_edge_value, cmph_uint8 * used_edges, cmph_uint8 * visited); +static cmph_uint8 bmz_traverse_critical_nodes_heuristic(bmz_config_data_t *bmz, cmph_uint32 v, cmph_uint32 * biggest_g_value, cmph_uint32 * biggest_edge_value, cmph_uint8 * used_edges, cmph_uint8 * visited); +static void bmz_traverse_non_critical_nodes(bmz_config_data_t *bmz, cmph_uint8 * used_edges, cmph_uint8 * visited); + +bmz_config_data_t *bmz_config_new(void) +{ + bmz_config_data_t *bmz = NULL; + bmz = (bmz_config_data_t *)malloc(sizeof(bmz_config_data_t)); + assert(bmz); + memset(bmz, 0, sizeof(bmz_config_data_t)); + bmz->hashfuncs[0] = CMPH_HASH_JENKINS; + bmz->hashfuncs[1] = CMPH_HASH_JENKINS; + bmz->g = NULL; + bmz->graph = NULL; + bmz->hashes = NULL; + return bmz; +} + +void bmz_config_destroy(cmph_config_t *mph) +{ + bmz_config_data_t *data = (bmz_config_data_t *)mph->data; + DEBUGP("Destroying algorithm dependent data\n"); + free(data); +} + +void bmz_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + bmz_config_data_t *bmz = (bmz_config_data_t *)mph->data; + CMPH_HASH *hashptr = hashfuncs; + cmph_uint32 i = 0; + while(*hashptr != CMPH_HASH_COUNT) + { + if (i >= 2) break; //bmz only uses two hash functions + bmz->hashfuncs[i] = *hashptr; + ++i, ++hashptr; + } +} + +cmph_t *bmz_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + bmz_data_t *bmzf = NULL; + cmph_uint32 i; + cmph_uint32 iterations; + cmph_uint32 iterations_map = 20; + cmph_uint8 *used_edges = NULL; + cmph_uint8 restart_mapping = 0; + cmph_uint8 * visited = NULL; + + bmz_config_data_t *bmz = (bmz_config_data_t *)mph->data; + if (c == 0) c = 1.15; // validating restrictions over parameter c. + DEBUGP("c: %f\n", c); + bmz->m = mph->key_source->nkeys; + bmz->n = (cmph_uint32)ceil(c * mph->key_source->nkeys); + DEBUGP("m (edges): %u n (vertices): %u c: %f\n", bmz->m, bmz->n, c); + bmz->graph = graph_new(bmz->n, bmz->m); + DEBUGP("Created graph\n"); + + bmz->hashes = (hash_state_t **)malloc(sizeof(hash_state_t *)*3); + for(i = 0; i < 3; ++i) bmz->hashes[i] = NULL; + + do + { + // Mapping step + cmph_uint32 biggest_g_value = 0; + cmph_uint32 biggest_edge_value = 1; + iterations = 100; + if (mph->verbosity) + { + fprintf(stderr, "Entering mapping step for mph creation of %u keys with graph sized %u\n", bmz->m, bmz->n); + } + while(1) + { + int ok; + DEBUGP("hash function 1\n"); + bmz->hashes[0] = hash_state_new(bmz->hashfuncs[0], bmz->n); + DEBUGP("hash function 2\n"); + bmz->hashes[1] = hash_state_new(bmz->hashfuncs[1], bmz->n); + DEBUGP("Generating edges\n"); + ok = bmz_gen_edges(mph); + if (!ok) + { + --iterations; + hash_state_destroy(bmz->hashes[0]); + bmz->hashes[0] = NULL; + hash_state_destroy(bmz->hashes[1]); + bmz->hashes[1] = NULL; + DEBUGP("%u iterations remaining\n", iterations); + if (mph->verbosity) + { + fprintf(stderr, "simple graph creation failure - %u iterations remaining\n", iterations); + } + if (iterations == 0) break; + } + else break; + } + if (iterations == 0) + { + graph_destroy(bmz->graph); + return NULL; + } + // Ordering step + if (mph->verbosity) + { + fprintf(stderr, "Starting ordering step\n"); + } + graph_obtain_critical_nodes(bmz->graph); + + // Searching step + if (mph->verbosity) + { + fprintf(stderr, "Starting Searching step.\n"); + fprintf(stderr, "\tTraversing critical vertices.\n"); + } + DEBUGP("Searching step\n"); + visited = (cmph_uint8 *)malloc((size_t)bmz->n/8 + 1); + memset(visited, 0, (size_t)bmz->n/8 + 1); + used_edges = (cmph_uint8 *)malloc((size_t)bmz->m/8 + 1); + memset(used_edges, 0, (size_t)bmz->m/8 + 1); + free(bmz->g); + bmz->g = (cmph_uint32 *)calloc((size_t)bmz->n, sizeof(cmph_uint32)); + assert(bmz->g); + for (i = 0; i < bmz->n; ++i) // critical nodes + { + if (graph_node_is_critical(bmz->graph, i) && (!GETBIT(visited,i))) + { + if(c > 1.14) restart_mapping = bmz_traverse_critical_nodes(bmz, i, &biggest_g_value, &biggest_edge_value, used_edges, visited); + else restart_mapping = bmz_traverse_critical_nodes_heuristic(bmz, i, &biggest_g_value, &biggest_edge_value, used_edges, visited); + if(restart_mapping) break; + } + } + if(!restart_mapping) + { + if (mph->verbosity) + { + fprintf(stderr, "\tTraversing non critical vertices.\n"); + } + bmz_traverse_non_critical_nodes(bmz, used_edges, visited); // non_critical_nodes + } + else + { + iterations_map--; + if (mph->verbosity) fprintf(stderr, "Restarting mapping step. %u iterations remaining.\n", iterations_map); + } + free(used_edges); + free(visited); + }while(restart_mapping && iterations_map > 0); + graph_destroy(bmz->graph); + bmz->graph = NULL; + if (iterations_map == 0) + { + return NULL; + } + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + bmzf = (bmz_data_t *)malloc(sizeof(bmz_data_t)); + bmzf->g = bmz->g; + bmz->g = NULL; //transfer memory ownership + bmzf->hashes = bmz->hashes; + bmz->hashes = NULL; //transfer memory ownership + bmzf->n = bmz->n; + bmzf->m = bmz->m; + mphf->data = bmzf; + mphf->size = bmz->m; + + DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + return mphf; +} + +static cmph_uint8 bmz_traverse_critical_nodes(bmz_config_data_t *bmz, cmph_uint32 v, cmph_uint32 * biggest_g_value, cmph_uint32 * biggest_edge_value, cmph_uint8 * used_edges, cmph_uint8 * visited) +{ + cmph_uint32 next_g; + cmph_uint32 u; /* Auxiliary vertex */ + cmph_uint32 lav; /* lookahead vertex */ + cmph_uint8 collision; + vqueue_t * q = vqueue_new((cmph_uint32)(graph_ncritical_nodes(bmz->graph)) + 1); + graph_iterator_t it, it1; + + DEBUGP("Labelling critical vertices\n"); + bmz->g[v] = (cmph_uint32)ceil ((double)(*biggest_edge_value)/2) - 1; + SETBIT(visited, v); + next_g = (cmph_uint32)floor((double)(*biggest_edge_value/2)); /* next_g is incremented in the do..while statement*/ + vqueue_insert(q, v); + while(!vqueue_is_empty(q)) + { + v = vqueue_remove(q); + it = graph_neighbors_it(bmz->graph, v); + while ((u = graph_next_neighbor(bmz->graph, &it)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz->graph, u) && (!GETBIT(visited,u))) + { + collision = 1; + while(collision) // lookahead to resolve collisions + { + next_g = *biggest_g_value + 1; + it1 = graph_neighbors_it(bmz->graph, u); + collision = 0; + while((lav = graph_next_neighbor(bmz->graph, &it1)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz->graph, lav) && GETBIT(visited,lav)) + { + if(next_g + bmz->g[lav] >= bmz->m) + { + vqueue_destroy(q); + return 1; // restart mapping step. + } + if (GETBIT(used_edges, (next_g + bmz->g[lav]))) + { + collision = 1; + break; + } + } + } + if (next_g > *biggest_g_value) *biggest_g_value = next_g; + } + // Marking used edges... + it1 = graph_neighbors_it(bmz->graph, u); + while((lav = graph_next_neighbor(bmz->graph, &it1)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz->graph, lav) && GETBIT(visited, lav)) + { + SETBIT(used_edges,(next_g + bmz->g[lav])); + if(next_g + bmz->g[lav] > *biggest_edge_value) *biggest_edge_value = next_g + bmz->g[lav]; + } + } + bmz->g[u] = next_g; // Labelling vertex u. + SETBIT(visited,u); + vqueue_insert(q, u); + } + } + + } + vqueue_destroy(q); + return 0; +} + +static cmph_uint8 bmz_traverse_critical_nodes_heuristic(bmz_config_data_t *bmz, cmph_uint32 v, cmph_uint32 * biggest_g_value, cmph_uint32 * biggest_edge_value, cmph_uint8 * used_edges, cmph_uint8 * visited) +{ + cmph_uint32 next_g; + cmph_uint32 u; /* Auxiliary vertex */ + cmph_uint32 lav; /* lookahead vertex */ + cmph_uint8 collision; + cmph_uint32 * unused_g_values = NULL; + cmph_uint32 unused_g_values_capacity = 0; + cmph_uint32 nunused_g_values = 0; + vqueue_t * q = vqueue_new((cmph_uint32)(0.5*graph_ncritical_nodes(bmz->graph))+1); + graph_iterator_t it, it1; + + DEBUGP("Labelling critical vertices\n"); + bmz->g[v] = (cmph_uint32)ceil ((double)(*biggest_edge_value)/2) - 1; + SETBIT(visited, v); + next_g = (cmph_uint32)floor((double)(*biggest_edge_value/2)); /* next_g is incremented in the do..while statement*/ + vqueue_insert(q, v); + while(!vqueue_is_empty(q)) + { + v = vqueue_remove(q); + it = graph_neighbors_it(bmz->graph, v); + while ((u = graph_next_neighbor(bmz->graph, &it)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz->graph, u) && (!GETBIT(visited,u))) + { + cmph_uint32 next_g_index = 0; + collision = 1; + while(collision) // lookahead to resolve collisions + { + if (next_g_index < nunused_g_values) + { + next_g = unused_g_values[next_g_index++]; + } + else + { + next_g = *biggest_g_value + 1; + next_g_index = UINT_MAX; + } + it1 = graph_neighbors_it(bmz->graph, u); + collision = 0; + while((lav = graph_next_neighbor(bmz->graph, &it1)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz->graph, lav) && GETBIT(visited,lav)) + { + if(next_g + bmz->g[lav] >= bmz->m) + { + vqueue_destroy(q); + free(unused_g_values); + return 1; // restart mapping step. + } + if (GETBIT(used_edges, (next_g + bmz->g[lav]))) + { + collision = 1; + break; + } + } + } + if(collision && (next_g > *biggest_g_value)) // saving the current g value stored in next_g. + { + if(nunused_g_values == unused_g_values_capacity) + { + unused_g_values = (cmph_uint32 *)realloc(unused_g_values, (unused_g_values_capacity + BUFSIZ)*sizeof(cmph_uint32)); + unused_g_values_capacity += BUFSIZ; + } + unused_g_values[nunused_g_values++] = next_g; + + } + if (next_g > *biggest_g_value) *biggest_g_value = next_g; + } + next_g_index--; + if (next_g_index < nunused_g_values) unused_g_values[next_g_index] = unused_g_values[--nunused_g_values]; + + // Marking used edges... + it1 = graph_neighbors_it(bmz->graph, u); + while((lav = graph_next_neighbor(bmz->graph, &it1)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz->graph, lav) && GETBIT(visited, lav)) + { + SETBIT(used_edges,(next_g + bmz->g[lav])); + if(next_g + bmz->g[lav] > *biggest_edge_value) *biggest_edge_value = next_g + bmz->g[lav]; + } + } + bmz->g[u] = next_g; // Labelling vertex u. + SETBIT(visited, u); + vqueue_insert(q, u); + } + } + + } + vqueue_destroy(q); + free(unused_g_values); + return 0; +} + +static cmph_uint32 next_unused_edge(bmz_config_data_t *bmz, cmph_uint8 * used_edges, cmph_uint32 unused_edge_index) +{ + while(1) + { + assert(unused_edge_index < bmz->m); + if(GETBIT(used_edges, unused_edge_index)) unused_edge_index ++; + else break; + } + return unused_edge_index; +} + +static void bmz_traverse(bmz_config_data_t *bmz, cmph_uint8 * used_edges, cmph_uint32 v, cmph_uint32 * unused_edge_index, cmph_uint8 * visited) +{ + graph_iterator_t it = graph_neighbors_it(bmz->graph, v); + cmph_uint32 neighbor = 0; + while((neighbor = graph_next_neighbor(bmz->graph, &it)) != GRAPH_NO_NEIGHBOR) + { + if(GETBIT(visited,neighbor)) continue; + //DEBUGP("Visiting neighbor %u\n", neighbor); + *unused_edge_index = next_unused_edge(bmz, used_edges, *unused_edge_index); + bmz->g[neighbor] = *unused_edge_index - bmz->g[v]; + //if (bmz->g[neighbor] >= bmz->m) bmz->g[neighbor] += bmz->m; + SETBIT(visited, neighbor); + (*unused_edge_index)++; + bmz_traverse(bmz, used_edges, neighbor, unused_edge_index, visited); + + } +} + +static void bmz_traverse_non_critical_nodes(bmz_config_data_t *bmz, cmph_uint8 * used_edges, cmph_uint8 * visited) +{ + + cmph_uint32 i, v1, v2, unused_edge_index = 0; + DEBUGP("Labelling non critical vertices\n"); + for(i = 0; i < bmz->m; i++) + { + v1 = graph_vertex_id(bmz->graph, i, 0); + v2 = graph_vertex_id(bmz->graph, i, 1); + if((GETBIT(visited,v1) && GETBIT(visited,v2)) || (!GETBIT(visited,v1) && !GETBIT(visited,v2))) continue; + if(GETBIT(visited,v1)) bmz_traverse(bmz, used_edges, v1, &unused_edge_index, visited); + else bmz_traverse(bmz, used_edges, v2, &unused_edge_index, visited); + + } + + for(i = 0; i < bmz->n; i++) + { + if(!GETBIT(visited,i)) + { + bmz->g[i] = 0; + SETBIT(visited, i); + bmz_traverse(bmz, used_edges, i, &unused_edge_index, visited); + } + } + +} + +static int bmz_gen_edges(cmph_config_t *mph) +{ + cmph_uint32 e; + bmz_config_data_t *bmz = (bmz_config_data_t *)mph->data; + cmph_uint8 multiple_edges = 0; + DEBUGP("Generating edges for %u vertices\n", bmz->n); + graph_clear_edges(bmz->graph); + mph->key_source->rewind(mph->key_source->data); + for (e = 0; e < mph->key_source->nkeys; ++e) + { + cmph_uint32 h1, h2; + cmph_uint32 keylen; + char *key = NULL; + mph->key_source->read(mph->key_source->data, &key, &keylen); + +// if (key == NULL)fprintf(stderr, "key = %s -- read BMZ\n", key); + h1 = hash(bmz->hashes[0], key, keylen) % bmz->n; + h2 = hash(bmz->hashes[1], key, keylen) % bmz->n; + if (h1 == h2) if (++h2 >= bmz->n) h2 = 0; + if (h1 == h2) + { + if (mph->verbosity) fprintf(stderr, "Self loop for key %u\n", e); + mph->key_source->dispose(mph->key_source->data, key, keylen); + return 0; + } + //DEBUGP("Adding edge: %u -> %u for key %s\n", h1, h2, key); + mph->key_source->dispose(mph->key_source->data, key, keylen); +// fprintf(stderr, "key = %s -- dispose BMZ\n", key); + multiple_edges = graph_contains_edge(bmz->graph, h1, h2); + if (mph->verbosity && multiple_edges) fprintf(stderr, "A non simple graph was generated\n"); + if (multiple_edges) return 0; // checking multiple edge restriction. + graph_add_edge(bmz->graph, h1, h2); + } + return !multiple_edges; +} + +int bmz_dump(cmph_t *mphf, FILE *fd) +{ + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint32 two = 2; //number of hash functions + bmz_data_t *data = (bmz_data_t *)mphf->data; + register size_t nbytes; +#ifdef DEBUG + cmph_uint32 i; +#endif + + __cmph_dump(mphf, fd); + + nbytes = fwrite(&two, sizeof(cmph_uint32), (size_t)1, fd); + + hash_state_dump(data->hashes[0], &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + hash_state_dump(data->hashes[1], &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + nbytes = fwrite(&(data->n), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->m), sizeof(cmph_uint32), (size_t)1, fd); + + nbytes = fwrite(data->g, sizeof(cmph_uint32)*(data->n), (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < data->n; ++i) fprintf(stderr, "%u ", data->g[i]); + fprintf(stderr, "\n"); + #endif + return 1; +} + +void bmz_load(FILE *f, cmph_t *mphf) +{ + cmph_uint32 nhashes; + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint32 i; + bmz_data_t *bmz = (bmz_data_t *)malloc(sizeof(bmz_data_t)); + register size_t nbytes; + DEBUGP("Loading bmz mphf\n"); + mphf->data = bmz; + nbytes = fread(&nhashes, sizeof(cmph_uint32), (size_t)1, f); + bmz->hashes = (hash_state_t **)malloc(sizeof(hash_state_t *)*(nhashes + 1)); + bmz->hashes[nhashes] = NULL; + DEBUGP("Reading %u hashes\n", nhashes); + for (i = 0; i < nhashes; ++i) + { + hash_state_t *state = NULL; + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + DEBUGP("Hash state has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + state = hash_state_load(buf, buflen); + bmz->hashes[i] = state; + free(buf); + } + + DEBUGP("Reading m and n\n"); + nbytes = fread(&(bmz->n), sizeof(cmph_uint32), (size_t)1, f); + nbytes = fread(&(bmz->m), sizeof(cmph_uint32), (size_t)1, f); + + bmz->g = (cmph_uint32 *)malloc(sizeof(cmph_uint32)*bmz->n); + nbytes = fread(bmz->g, bmz->n*sizeof(cmph_uint32), (size_t)1, f); + if (nbytes == 0 && ferror(f)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return; + } + + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < bmz->n; ++i) fprintf(stderr, "%u ", bmz->g[i]); + fprintf(stderr, "\n"); + #endif + return; +} + + +cmph_uint32 bmz_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + bmz_data_t *bmz = mphf->data; + cmph_uint32 h1 = hash(bmz->hashes[0], key, keylen) % bmz->n; + cmph_uint32 h2 = hash(bmz->hashes[1], key, keylen) % bmz->n; + DEBUGP("key: %s h1: %u h2: %u\n", key, h1, h2); + if (h1 == h2 && ++h2 > bmz->n) h2 = 0; + DEBUGP("key: %s g[h1]: %u g[h2]: %u edges: %u\n", key, bmz->g[h1], bmz->g[h2], bmz->m); + return bmz->g[h1] + bmz->g[h2]; +} +void bmz_destroy(cmph_t *mphf) +{ + bmz_data_t *data = (bmz_data_t *)mphf->data; + free(data->g); + hash_state_destroy(data->hashes[0]); + hash_state_destroy(data->hashes[1]); + free(data->hashes); + free(data); + free(mphf); +} + +/** \fn void bmz_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void bmz_pack(cmph_t *mphf, void *packed_mphf) +{ + + bmz_data_t *data = (bmz_data_t *)mphf->data; + cmph_uint8 * ptr = packed_mphf; + CMPH_HASH h2_type; + + // packing h1 type + CMPH_HASH h1_type = hash_get_type(data->hashes[0]); + *((cmph_uint32 *) ptr) = h1_type; + ptr += sizeof(cmph_uint32); + + // packing h1 + hash_state_pack(data->hashes[0], ptr); + ptr += hash_state_packed_size(h1_type); + + // packing h2 type + h2_type = hash_get_type(data->hashes[1]); + *((cmph_uint32 *) ptr) = h2_type; + ptr += sizeof(cmph_uint32); + + // packing h2 + hash_state_pack(data->hashes[1], ptr); + ptr += hash_state_packed_size(h2_type); + + // packing n + *((cmph_uint32 *) ptr) = data->n; + ptr += sizeof(data->n); + + // packing g + memcpy(ptr, data->g, sizeof(cmph_uint32)*data->n); +} + +/** \fn cmph_uint32 bmz_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 bmz_packed_size(cmph_t *mphf) +{ + bmz_data_t *data = (bmz_data_t *)mphf->data; + CMPH_HASH h1_type = hash_get_type(data->hashes[0]); + CMPH_HASH h2_type = hash_get_type(data->hashes[1]); + + return (cmph_uint32)(sizeof(CMPH_ALGO) + hash_state_packed_size(h1_type) + hash_state_packed_size(h2_type) + + 3*sizeof(cmph_uint32) + sizeof(cmph_uint32)*data->n); +} + +/** cmph_uint32 bmz_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 bmz_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + register cmph_uint8 *h1_ptr = packed_mphf; + register CMPH_HASH h1_type = *((cmph_uint32 *)h1_ptr); + register cmph_uint8 *h2_ptr; + register CMPH_HASH h2_type; + register cmph_uint32 *g_ptr, n, h1, h2; + + h1_ptr += 4; + + h2_ptr = h1_ptr + hash_state_packed_size(h1_type); + h2_type = *((cmph_uint32 *)h2_ptr); + h2_ptr += 4; + + g_ptr = (cmph_uint32 *)(h2_ptr + hash_state_packed_size(h2_type)); + + n = *g_ptr++; + + h1 = hash_packed(h1_ptr, h1_type, key, keylen) % n; + h2 = hash_packed(h2_ptr, h2_type, key, keylen) % n; + if (h1 == h2 && ++h2 > n) h2 = 0; + return (g_ptr[h1] + g_ptr[h2]); +} diff --git a/girepository/cmph/bmz.h b/girepository/cmph/bmz.h new file mode 100644 index 000000000..9821aa88c --- /dev/null +++ b/girepository/cmph/bmz.h @@ -0,0 +1,42 @@ +#ifndef __CMPH_BMZ_H__ +#define __CMPH_BMZ_H__ + +#include "cmph.h" + +typedef struct __bmz_data_t bmz_data_t; +typedef struct __bmz_config_data_t bmz_config_data_t; + +bmz_config_data_t *bmz_config_new(void); +void bmz_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); +void bmz_config_destroy(cmph_config_t *mph); +cmph_t *bmz_new(cmph_config_t *mph, double c); + +void bmz_load(FILE *f, cmph_t *mphf); +int bmz_dump(cmph_t *mphf, FILE *f); +void bmz_destroy(cmph_t *mphf); +cmph_uint32 bmz_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +/** \fn void bmz_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void bmz_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 bmz_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 bmz_packed_size(cmph_t *mphf); + +/** cmph_uint32 bmz_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 bmz_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +#endif diff --git a/girepository/cmph/bmz8.c b/girepository/cmph/bmz8.c new file mode 100644 index 000000000..15853c00b --- /dev/null +++ b/girepository/cmph/bmz8.c @@ -0,0 +1,647 @@ +#include "graph.h" +#include "bmz8.h" +#include "cmph_structs.h" +#include "bmz8_structs.h" +#include "hash.h" +#include "vqueue.h" +#include "bitbool.h" +#include +#include +#include +#include +#include +#include + +//#define DEBUG +#include "debug.h" + +static int bmz8_gen_edges(cmph_config_t *mph); +static cmph_uint8 bmz8_traverse_critical_nodes(bmz8_config_data_t *bmz8, cmph_uint32 v, cmph_uint8 * biggest_g_value, cmph_uint8 * biggest_edge_value, cmph_uint8 * used_edges, cmph_uint8 * visited); +static cmph_uint8 bmz8_traverse_critical_nodes_heuristic(bmz8_config_data_t *bmz8, cmph_uint32 v, cmph_uint8 * biggest_g_value, cmph_uint8 * biggest_edge_value, cmph_uint8 * used_edges, cmph_uint8 * visited); +static void bmz8_traverse_non_critical_nodes(bmz8_config_data_t *bmz8, cmph_uint8 * used_edges, cmph_uint8 * visited); + +bmz8_config_data_t *bmz8_config_new(void) +{ + bmz8_config_data_t *bmz8; + bmz8 = (bmz8_config_data_t *)malloc(sizeof(bmz8_config_data_t)); + assert(bmz8); + memset(bmz8, 0, sizeof(bmz8_config_data_t)); + bmz8->hashfuncs[0] = CMPH_HASH_JENKINS; + bmz8->hashfuncs[1] = CMPH_HASH_JENKINS; + bmz8->g = NULL; + bmz8->graph = NULL; + bmz8->hashes = NULL; + return bmz8; +} + +void bmz8_config_destroy(cmph_config_t *mph) +{ + bmz8_config_data_t *data = (bmz8_config_data_t *)mph->data; + DEBUGP("Destroying algorithm dependent data\n"); + free(data); +} + +void bmz8_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + bmz8_config_data_t *bmz8 = (bmz8_config_data_t *)mph->data; + CMPH_HASH *hashptr = hashfuncs; + cmph_uint8 i = 0; + while(*hashptr != CMPH_HASH_COUNT) + { + if (i >= 2) break; //bmz8 only uses two hash functions + bmz8->hashfuncs[i] = *hashptr; + ++i, ++hashptr; + } +} + +cmph_t *bmz8_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + bmz8_data_t *bmz8f = NULL; + cmph_uint8 i; + cmph_uint8 iterations; + cmph_uint8 iterations_map = 20; + cmph_uint8 *used_edges = NULL; + cmph_uint8 restart_mapping = 0; + cmph_uint8 * visited = NULL; + bmz8_config_data_t *bmz8 = (bmz8_config_data_t *)mph->data; + + if (mph->key_source->nkeys >= 256) + { + if (mph->verbosity) fprintf(stderr, "The number of keys in BMZ8 must be lower than 256.\n"); + return NULL; + } + if (c == 0) c = 1.15; // validating restrictions over parameter c. + DEBUGP("c: %f\n", c); + bmz8->m = (cmph_uint8) mph->key_source->nkeys; + bmz8->n = (cmph_uint8) ceil(c * mph->key_source->nkeys); + DEBUGP("m (edges): %u n (vertices): %u c: %f\n", bmz8->m, bmz8->n, c); + bmz8->graph = graph_new(bmz8->n, bmz8->m); + DEBUGP("Created graph\n"); + + bmz8->hashes = (hash_state_t **)malloc(sizeof(hash_state_t *)*3); + for(i = 0; i < 3; ++i) bmz8->hashes[i] = NULL; + + do + { + // Mapping step + cmph_uint8 biggest_g_value = 0; + cmph_uint8 biggest_edge_value = 1; + iterations = 100; + if (mph->verbosity) + { + fprintf(stderr, "Entering mapping step for mph creation of %u keys with graph sized %u\n", bmz8->m, bmz8->n); + } + while(1) + { + int ok; + DEBUGP("hash function 1\n"); + bmz8->hashes[0] = hash_state_new(bmz8->hashfuncs[0], bmz8->n); + DEBUGP("hash function 2\n"); + bmz8->hashes[1] = hash_state_new(bmz8->hashfuncs[1], bmz8->n); + DEBUGP("Generating edges\n"); + ok = bmz8_gen_edges(mph); + if (!ok) + { + --iterations; + hash_state_destroy(bmz8->hashes[0]); + bmz8->hashes[0] = NULL; + hash_state_destroy(bmz8->hashes[1]); + bmz8->hashes[1] = NULL; + DEBUGP("%u iterations remaining\n", iterations); + if (mph->verbosity) + { + fprintf(stderr, "simple graph creation failure - %u iterations remaining\n", iterations); + } + if (iterations == 0) break; + } + else break; + } + if (iterations == 0) + { + graph_destroy(bmz8->graph); + return NULL; + } + + // Ordering step + if (mph->verbosity) + { + fprintf(stderr, "Starting ordering step\n"); + } + + graph_obtain_critical_nodes(bmz8->graph); + + // Searching step + if (mph->verbosity) + { + fprintf(stderr, "Starting Searching step.\n"); + fprintf(stderr, "\tTraversing critical vertices.\n"); + } + DEBUGP("Searching step\n"); + visited = (cmph_uint8 *)malloc((size_t)bmz8->n/8 + 1); + memset(visited, 0, (size_t)bmz8->n/8 + 1); + used_edges = (cmph_uint8 *)malloc((size_t)bmz8->m/8 + 1); + memset(used_edges, 0, (size_t)bmz8->m/8 + 1); + free(bmz8->g); + bmz8->g = (cmph_uint8 *)calloc((size_t)bmz8->n, sizeof(cmph_uint8)); + assert(bmz8->g); + for (i = 0; i < bmz8->n; ++i) // critical nodes + { + if (graph_node_is_critical(bmz8->graph, i) && (!GETBIT(visited,i))) + { + if(c > 1.14) restart_mapping = bmz8_traverse_critical_nodes(bmz8, i, &biggest_g_value, &biggest_edge_value, used_edges, visited); + else restart_mapping = bmz8_traverse_critical_nodes_heuristic(bmz8, i, &biggest_g_value, &biggest_edge_value, used_edges, visited); + if(restart_mapping) break; + } + } + if(!restart_mapping) + { + if (mph->verbosity) + { + fprintf(stderr, "\tTraversing non critical vertices.\n"); + } + bmz8_traverse_non_critical_nodes(bmz8, used_edges, visited); // non_critical_nodes + } + else + { + iterations_map--; + if (mph->verbosity) fprintf(stderr, "Restarting mapping step. %u iterations remaining.\n", iterations_map); + } + + free(used_edges); + free(visited); + + }while(restart_mapping && iterations_map > 0); + graph_destroy(bmz8->graph); + bmz8->graph = NULL; + if (iterations_map == 0) + { + return NULL; + } + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + bmz8f = (bmz8_data_t *)malloc(sizeof(bmz8_data_t)); + bmz8f->g = bmz8->g; + bmz8->g = NULL; //transfer memory ownership + bmz8f->hashes = bmz8->hashes; + bmz8->hashes = NULL; //transfer memory ownership + bmz8f->n = bmz8->n; + bmz8f->m = bmz8->m; + mphf->data = bmz8f; + mphf->size = bmz8->m; + DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + return mphf; +} + +static cmph_uint8 bmz8_traverse_critical_nodes(bmz8_config_data_t *bmz8, cmph_uint32 v, cmph_uint8 * biggest_g_value, cmph_uint8 * biggest_edge_value, cmph_uint8 * used_edges, cmph_uint8 * visited) +{ + cmph_uint8 next_g; + cmph_uint32 u; /* Auxiliary vertex */ + cmph_uint32 lav; /* lookahead vertex */ + cmph_uint8 collision; + vqueue_t * q = vqueue_new((cmph_uint32)(graph_ncritical_nodes(bmz8->graph))); + graph_iterator_t it, it1; + + DEBUGP("Labelling critical vertices\n"); + bmz8->g[v] = (cmph_uint8)(ceil ((double)(*biggest_edge_value)/2) - 1); + SETBIT(visited, v); + next_g = (cmph_uint8)floor((double)(*biggest_edge_value/2)); /* next_g is incremented in the do..while statement*/ + vqueue_insert(q, v); + while(!vqueue_is_empty(q)) + { + v = vqueue_remove(q); + it = graph_neighbors_it(bmz8->graph, v); + while ((u = graph_next_neighbor(bmz8->graph, &it)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz8->graph, u) && (!GETBIT(visited,u))) + { + collision = 1; + while(collision) // lookahead to resolve collisions + { + next_g = (cmph_uint8)(*biggest_g_value + 1); + it1 = graph_neighbors_it(bmz8->graph, u); + collision = 0; + while((lav = graph_next_neighbor(bmz8->graph, &it1)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz8->graph, lav) && GETBIT(visited,lav)) + { + if(next_g + bmz8->g[lav] >= bmz8->m) + { + vqueue_destroy(q); + return 1; // restart mapping step. + } + if (GETBIT(used_edges, (next_g + bmz8->g[lav]))) + { + collision = 1; + break; + } + } + } + if (next_g > *biggest_g_value) *biggest_g_value = next_g; + } + // Marking used edges... + it1 = graph_neighbors_it(bmz8->graph, u); + while((lav = graph_next_neighbor(bmz8->graph, &it1)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz8->graph, lav) && GETBIT(visited, lav)) + { + SETBIT(used_edges,(next_g + bmz8->g[lav])); + + if(next_g + bmz8->g[lav] > *biggest_edge_value) + *biggest_edge_value = (cmph_uint8)(next_g + bmz8->g[lav]); + } + } + bmz8->g[u] = next_g; // Labelling vertex u. + SETBIT(visited,u); + vqueue_insert(q, u); + } + } + + } + vqueue_destroy(q); + return 0; +} + +static cmph_uint8 bmz8_traverse_critical_nodes_heuristic(bmz8_config_data_t *bmz8, cmph_uint32 v, cmph_uint8 * biggest_g_value, cmph_uint8 * biggest_edge_value, cmph_uint8 * used_edges, cmph_uint8 * visited) +{ + cmph_uint8 next_g; + cmph_uint32 u; + cmph_uint32 lav; + cmph_uint8 collision; + cmph_uint8 * unused_g_values = NULL; + cmph_uint8 unused_g_values_capacity = 0; + cmph_uint8 nunused_g_values = 0; + vqueue_t * q = vqueue_new((cmph_uint32)(graph_ncritical_nodes(bmz8->graph))); + graph_iterator_t it, it1; + + DEBUGP("Labelling critical vertices\n"); + bmz8->g[v] = (cmph_uint8)(ceil ((double)(*biggest_edge_value)/2) - 1); + SETBIT(visited, v); + next_g = (cmph_uint8)floor((double)(*biggest_edge_value/2)); + vqueue_insert(q, v); + while(!vqueue_is_empty(q)) + { + v = vqueue_remove(q); + it = graph_neighbors_it(bmz8->graph, v); + while ((u = graph_next_neighbor(bmz8->graph, &it)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz8->graph, u) && (!GETBIT(visited,u))) + { + cmph_uint8 next_g_index = 0; + collision = 1; + while(collision) // lookahead to resolve collisions + { + if (next_g_index < nunused_g_values) + { + next_g = unused_g_values[next_g_index++]; + } + else + { + next_g = (cmph_uint8)(*biggest_g_value + 1); + next_g_index = 255;//UINT_MAX; + } + it1 = graph_neighbors_it(bmz8->graph, u); + collision = 0; + while((lav = graph_next_neighbor(bmz8->graph, &it1)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz8->graph, lav) && GETBIT(visited,lav)) + { + if(next_g + bmz8->g[lav] >= bmz8->m) + { + vqueue_destroy(q); + free(unused_g_values); + return 1; // restart mapping step. + } + if (GETBIT(used_edges, (next_g + bmz8->g[lav]))) + { + collision = 1; + break; + } + } + } + if(collision && (next_g > *biggest_g_value)) // saving the current g value stored in next_g. + { + if(nunused_g_values == unused_g_values_capacity) + { + unused_g_values = (cmph_uint8*)realloc(unused_g_values, ((size_t)(unused_g_values_capacity + BUFSIZ))*sizeof(cmph_uint8)); + unused_g_values_capacity += (cmph_uint8)BUFSIZ; + } + unused_g_values[nunused_g_values++] = next_g; + + } + if (next_g > *biggest_g_value) *biggest_g_value = next_g; + } + + next_g_index--; + if (next_g_index < nunused_g_values) unused_g_values[next_g_index] = unused_g_values[--nunused_g_values]; + + // Marking used edges... + it1 = graph_neighbors_it(bmz8->graph, u); + while((lav = graph_next_neighbor(bmz8->graph, &it1)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz8->graph, lav) && GETBIT(visited, lav)) + { + SETBIT(used_edges,(next_g + bmz8->g[lav])); + if(next_g + bmz8->g[lav] > *biggest_edge_value) + *biggest_edge_value = (cmph_uint8)(next_g + bmz8->g[lav]); + } + } + + bmz8->g[u] = next_g; // Labelling vertex u. + SETBIT(visited, u); + vqueue_insert(q, u); + + } + } + + } + vqueue_destroy(q); + free(unused_g_values); + return 0; +} + +static cmph_uint8 next_unused_edge(bmz8_config_data_t *bmz8, cmph_uint8 * used_edges, cmph_uint32 unused_edge_index) +{ + while(1) + { + assert(unused_edge_index < bmz8->m); + if(GETBIT(used_edges, unused_edge_index)) unused_edge_index ++; + else break; + } + return (cmph_uint8)unused_edge_index; +} + +static void bmz8_traverse(bmz8_config_data_t *bmz8, cmph_uint8 * used_edges, cmph_uint32 v, cmph_uint8 * unused_edge_index, cmph_uint8 * visited) +{ + graph_iterator_t it = graph_neighbors_it(bmz8->graph, v); + cmph_uint32 neighbor = 0; + while((neighbor = graph_next_neighbor(bmz8->graph, &it)) != GRAPH_NO_NEIGHBOR) + { + if(GETBIT(visited,neighbor)) continue; + //DEBUGP("Visiting neighbor %u\n", neighbor); + *unused_edge_index = next_unused_edge(bmz8, used_edges, *unused_edge_index); + bmz8->g[neighbor] = (cmph_uint8)(*unused_edge_index - bmz8->g[v]); + //if (bmz8->g[neighbor] >= bmz8->m) bmz8->g[neighbor] += bmz8->m; + SETBIT(visited, neighbor); + (*unused_edge_index)++; + bmz8_traverse(bmz8, used_edges, neighbor, unused_edge_index, visited); + + } +} + +static void bmz8_traverse_non_critical_nodes(bmz8_config_data_t *bmz8, cmph_uint8 * used_edges, cmph_uint8 * visited) +{ + + cmph_uint8 i, v1, v2, unused_edge_index = 0; + DEBUGP("Labelling non critical vertices\n"); + for(i = 0; i < bmz8->m; i++) + { + v1 = (cmph_uint8)graph_vertex_id(bmz8->graph, i, 0); + v2 = (cmph_uint8)graph_vertex_id(bmz8->graph, i, 1); + if((GETBIT(visited,v1) && GETBIT(visited,v2)) || (!GETBIT(visited,v1) && !GETBIT(visited,v2))) continue; + if(GETBIT(visited,v1)) bmz8_traverse(bmz8, used_edges, v1, &unused_edge_index, visited); + else bmz8_traverse(bmz8, used_edges, v2, &unused_edge_index, visited); + + } + + for(i = 0; i < bmz8->n; i++) + { + if(!GETBIT(visited,i)) + { + bmz8->g[i] = 0; + SETBIT(visited, i); + bmz8_traverse(bmz8, used_edges, i, &unused_edge_index, visited); + } + } + +} + +static int bmz8_gen_edges(cmph_config_t *mph) +{ + cmph_uint8 e; + bmz8_config_data_t *bmz8 = (bmz8_config_data_t *)mph->data; + cmph_uint8 multiple_edges = 0; + DEBUGP("Generating edges for %u vertices\n", bmz8->n); + graph_clear_edges(bmz8->graph); + mph->key_source->rewind(mph->key_source->data); + for (e = 0; e < mph->key_source->nkeys; ++e) + { + cmph_uint8 h1, h2; + cmph_uint32 keylen; + char *key = NULL; + mph->key_source->read(mph->key_source->data, &key, &keylen); + +// if (key == NULL)fprintf(stderr, "key = %s -- read BMZ\n", key); + h1 = (cmph_uint8)(hash(bmz8->hashes[0], key, keylen) % bmz8->n); + h2 = (cmph_uint8)(hash(bmz8->hashes[1], key, keylen) % bmz8->n); + if (h1 == h2) if (++h2 >= bmz8->n) h2 = 0; + if (h1 == h2) + { + if (mph->verbosity) fprintf(stderr, "Self loop for key %u\n", e); + mph->key_source->dispose(mph->key_source->data, key, keylen); + return 0; + } + //DEBUGP("Adding edge: %u -> %u for key %s\n", h1, h2, key); + mph->key_source->dispose(mph->key_source->data, key, keylen); +// fprintf(stderr, "key = %s -- dispose BMZ\n", key); + multiple_edges = graph_contains_edge(bmz8->graph, h1, h2); + if (mph->verbosity && multiple_edges) fprintf(stderr, "A non simple graph was generated\n"); + if (multiple_edges) return 0; // checking multiple edge restriction. + graph_add_edge(bmz8->graph, h1, h2); + } + return !multiple_edges; +} + +int bmz8_dump(cmph_t *mphf, FILE *fd) +{ + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint8 two = 2; //number of hash functions + bmz8_data_t *data = (bmz8_data_t *)mphf->data; + register size_t nbytes; + __cmph_dump(mphf, fd); + + nbytes = fwrite(&two, sizeof(cmph_uint8), (size_t)1, fd); + + hash_state_dump(data->hashes[0], &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + hash_state_dump(data->hashes[1], &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + nbytes = fwrite(&(data->n), sizeof(cmph_uint8), (size_t)1, fd); + nbytes = fwrite(&(data->m), sizeof(cmph_uint8), (size_t)1, fd); + + nbytes = fwrite(data->g, sizeof(cmph_uint8)*(data->n), (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } +/* #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < data->n; ++i) fprintf(stderr, "%u ", data->g[i]); + fprintf(stderr, "\n"); + #endif*/ + return 1; +} + +void bmz8_load(FILE *f, cmph_t *mphf) +{ + cmph_uint8 nhashes; + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint8 i; + register size_t nbytes; + bmz8_data_t *bmz8 = (bmz8_data_t *)malloc(sizeof(bmz8_data_t)); + + DEBUGP("Loading bmz8 mphf\n"); + mphf->data = bmz8; + nbytes = fread(&nhashes, sizeof(cmph_uint8), (size_t)1, f); + bmz8->hashes = (hash_state_t **)malloc(sizeof(hash_state_t *)*(size_t)(nhashes + 1)); + bmz8->hashes[nhashes] = NULL; + DEBUGP("Reading %u hashes\n", nhashes); + for (i = 0; i < nhashes; ++i) + { + hash_state_t *state = NULL; + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + DEBUGP("Hash state has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + state = hash_state_load(buf, buflen); + bmz8->hashes[i] = state; + free(buf); + } + + DEBUGP("Reading m and n\n"); + nbytes = fread(&(bmz8->n), sizeof(cmph_uint8), (size_t)1, f); + nbytes = fread(&(bmz8->m), sizeof(cmph_uint8), (size_t)1, f); + + bmz8->g = (cmph_uint8 *)malloc(sizeof(cmph_uint8)*bmz8->n); + nbytes = fread(bmz8->g, bmz8->n*sizeof(cmph_uint8), (size_t)1, f); + if (nbytes == 0 && ferror(f)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return; + } + + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < bmz8->n; ++i) fprintf(stderr, "%u ", bmz8->g[i]); + fprintf(stderr, "\n"); + #endif + return; +} + + +cmph_uint8 bmz8_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + bmz8_data_t *bmz8 = mphf->data; + cmph_uint8 h1 = (cmph_uint8)(hash(bmz8->hashes[0], key, keylen) % bmz8->n); + cmph_uint8 h2 = (cmph_uint8)(hash(bmz8->hashes[1], key, keylen) % bmz8->n); + DEBUGP("key: %s h1: %u h2: %u\n", key, h1, h2); + if (h1 == h2 && ++h2 > bmz8->n) h2 = 0; + DEBUGP("key: %s g[h1]: %u g[h2]: %u edges: %u\n", key, bmz8->g[h1], bmz8->g[h2], bmz8->m); + return (cmph_uint8)(bmz8->g[h1] + bmz8->g[h2]); +} +void bmz8_destroy(cmph_t *mphf) +{ + bmz8_data_t *data = (bmz8_data_t *)mphf->data; + free(data->g); + hash_state_destroy(data->hashes[0]); + hash_state_destroy(data->hashes[1]); + free(data->hashes); + free(data); + free(mphf); +} + +/** \fn void bmz8_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void bmz8_pack(cmph_t *mphf, void *packed_mphf) +{ + bmz8_data_t *data = (bmz8_data_t *)mphf->data; + cmph_uint8 * ptr = packed_mphf; + CMPH_HASH h2_type; + + // packing h1 type + CMPH_HASH h1_type = hash_get_type(data->hashes[0]); + *((cmph_uint32 *) ptr) = h1_type; + ptr += sizeof(cmph_uint32); + + // packing h1 + hash_state_pack(data->hashes[0], ptr); + ptr += hash_state_packed_size(h1_type); + + // packing h2 type + h2_type = hash_get_type(data->hashes[1]); + *((cmph_uint32 *) ptr) = h2_type; + ptr += sizeof(cmph_uint32); + + // packing h2 + hash_state_pack(data->hashes[1], ptr); + ptr += hash_state_packed_size(h2_type); + + // packing n + *ptr++ = data->n; + + // packing g + memcpy(ptr, data->g, sizeof(cmph_uint8)*data->n); +} + +/** \fn cmph_uint32 bmz8_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 bmz8_packed_size(cmph_t *mphf) +{ + bmz8_data_t *data = (bmz8_data_t *)mphf->data; + CMPH_HASH h1_type = hash_get_type(data->hashes[0]); + CMPH_HASH h2_type = hash_get_type(data->hashes[1]); + + return (cmph_uint32)(sizeof(CMPH_ALGO) + hash_state_packed_size(h1_type) + hash_state_packed_size(h2_type) + + 2*sizeof(cmph_uint32) + sizeof(cmph_uint8) + sizeof(cmph_uint8)*data->n); +} + +/** cmph_uint8 bmz8_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint8 bmz8_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + register cmph_uint8 *h1_ptr = packed_mphf; + register CMPH_HASH h1_type = *((cmph_uint32 *)h1_ptr); + register cmph_uint8 *h2_ptr; + register CMPH_HASH h2_type; + register cmph_uint8 *g_ptr, n, h1, h2; + + h1_ptr += 4; + + h2_ptr = h1_ptr + hash_state_packed_size(h1_type); + h2_type = *((cmph_uint32 *)h2_ptr); + h2_ptr += 4; + + g_ptr = h2_ptr + hash_state_packed_size(h2_type); + + n = *g_ptr++; + + h1 = (cmph_uint8)(hash_packed(h1_ptr, h1_type, key, keylen) % n); + h2 = (cmph_uint8)(hash_packed(h2_ptr, h2_type, key, keylen) % n); + DEBUGP("key: %s h1: %u h2: %u\n", key, h1, h2); + if (h1 == h2 && ++h2 > n) h2 = 0; + return (cmph_uint8)(g_ptr[h1] + g_ptr[h2]); +} diff --git a/girepository/cmph/bmz8.h b/girepository/cmph/bmz8.h new file mode 100644 index 000000000..99f7e30d6 --- /dev/null +++ b/girepository/cmph/bmz8.h @@ -0,0 +1,42 @@ +#ifndef __CMPH_BMZ8_H__ +#define __CMPH_BMZ8_H__ + +#include "cmph.h" + +typedef struct __bmz8_data_t bmz8_data_t; +typedef struct __bmz8_config_data_t bmz8_config_data_t; + +bmz8_config_data_t *bmz8_config_new(void); +void bmz8_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); +void bmz8_config_destroy(cmph_config_t *mph); +cmph_t *bmz8_new(cmph_config_t *mph, double c); + +void bmz8_load(FILE *f, cmph_t *mphf); +int bmz8_dump(cmph_t *mphf, FILE *f); +void bmz8_destroy(cmph_t *mphf); +cmph_uint8 bmz8_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +/** \fn void bmz8_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void bmz8_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 bmz8_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 bmz8_packed_size(cmph_t *mphf); + +/** cmph_uint8 bmz8_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint8 bmz8_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +#endif diff --git a/girepository/cmph/bmz8_structs.h b/girepository/cmph/bmz8_structs.h new file mode 100644 index 000000000..408b52998 --- /dev/null +++ b/girepository/cmph/bmz8_structs.h @@ -0,0 +1,25 @@ +#ifndef __CMPH_BMZ8_STRUCTS_H__ +#define __CMPH_BMZ8_STRUCTS_H__ + +#include "hash_state.h" + +struct __bmz8_data_t +{ + cmph_uint8 m; //edges (words) count + cmph_uint8 n; //vertex count + cmph_uint8 *g; + hash_state_t **hashes; +}; + + +struct __bmz8_config_data_t +{ + CMPH_HASH hashfuncs[2]; + cmph_uint8 m; //edges (words) count + cmph_uint8 n; //vertex count + graph_t *graph; + cmph_uint8 *g; + hash_state_t **hashes; +}; + +#endif diff --git a/girepository/cmph/bmz_structs.h b/girepository/cmph/bmz_structs.h new file mode 100644 index 000000000..67065a005 --- /dev/null +++ b/girepository/cmph/bmz_structs.h @@ -0,0 +1,25 @@ +#ifndef __CMPH_BMZ_STRUCTS_H__ +#define __CMPH_BMZ_STRUCTS_H__ + +#include "hash_state.h" + +struct __bmz_data_t +{ + cmph_uint32 m; //edges (words) count + cmph_uint32 n; //vertex count + cmph_uint32 *g; + hash_state_t **hashes; +}; + + +struct __bmz_config_data_t +{ + CMPH_HASH hashfuncs[2]; + cmph_uint32 m; //edges (words) count + cmph_uint32 n; //vertex count + graph_t *graph; + cmph_uint32 *g; + hash_state_t **hashes; +}; + +#endif diff --git a/girepository/cmph/brz.c b/girepository/cmph/brz.c new file mode 100644 index 000000000..25feb6536 --- /dev/null +++ b/girepository/cmph/brz.c @@ -0,0 +1,1040 @@ +#include "graph.h" +#include "fch.h" +#include "fch_structs.h" +#include "bmz8.h" +#include "bmz8_structs.h" +#include "brz.h" +#include "cmph_structs.h" +#include "brz_structs.h" +#include "buffer_manager.h" +#include "cmph.h" +#include "hash.h" +#include "bitbool.h" +#include +#include +#include +#include +#include +#include +#define MAX_BUCKET_SIZE 255 +//#define DEBUG +#include "debug.h" + +#if defined (__ia64) || defined (__x86_64__) || defined (_WIN64) +# define __brz_use_64bit__ +#endif + +static int brz_gen_mphf(cmph_config_t *mph); +static cmph_uint32 brz_min_index(cmph_uint32 * vector, cmph_uint32 n); +static void brz_destroy_keys_vd(cmph_uint8 ** keys_vd, cmph_uint32 nkeys); +static char * brz_copy_partial_fch_mphf(brz_config_data_t *brz, fch_data_t * fchf, cmph_uint32 index, cmph_uint32 *buflen); +static char * brz_copy_partial_bmz8_mphf(brz_config_data_t *brz, bmz8_data_t * bmzf, cmph_uint32 index, cmph_uint32 *buflen); +brz_config_data_t *brz_config_new(void) +{ + brz_config_data_t *brz = NULL; + brz = (brz_config_data_t *)malloc(sizeof(brz_config_data_t)); + brz->algo = CMPH_FCH; + brz->b = 128; + brz->hashfuncs[0] = CMPH_HASH_JENKINS; + brz->hashfuncs[1] = CMPH_HASH_JENKINS; + brz->hashfuncs[2] = CMPH_HASH_JENKINS; + brz->size = NULL; + brz->offset = NULL; + brz->g = NULL; + brz->h1 = NULL; + brz->h2 = NULL; + brz->h0 = NULL; + brz->memory_availability = 1024*1024; + brz->tmp_dir = (cmph_uint8 *)calloc((size_t)10, sizeof(cmph_uint8)); + brz->mphf_fd = NULL; + strcpy((char *)(brz->tmp_dir), "/var/tmp/"); + assert(brz); + return brz; +} + +void brz_config_destroy(cmph_config_t *mph) +{ + brz_config_data_t *data = (brz_config_data_t *)mph->data; + free(data->tmp_dir); + DEBUGP("Destroying algorithm dependent data\n"); + free(data); +} + +void brz_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + brz_config_data_t *brz = (brz_config_data_t *)mph->data; + CMPH_HASH *hashptr = hashfuncs; + cmph_uint32 i = 0; + while(*hashptr != CMPH_HASH_COUNT) + { + if (i >= 3) break; //brz only uses three hash functions + brz->hashfuncs[i] = *hashptr; + ++i, ++hashptr; + } +} + +void brz_config_set_memory_availability(cmph_config_t *mph, cmph_uint32 memory_availability) +{ + brz_config_data_t *brz = (brz_config_data_t *)mph->data; + if(memory_availability > 0) brz->memory_availability = memory_availability*1024*1024; +} + +void brz_config_set_tmp_dir(cmph_config_t *mph, cmph_uint8 *tmp_dir) +{ + brz_config_data_t *brz = (brz_config_data_t *)mph->data; + if(tmp_dir) + { + size_t len = strlen((char *)tmp_dir); + free(brz->tmp_dir); + if(tmp_dir[len-1] != '/') + { + brz->tmp_dir = (cmph_uint8 *)calloc((size_t)len+2, sizeof(cmph_uint8)); + sprintf((char *)(brz->tmp_dir), "%s/", (char *)tmp_dir); + } + else + { + brz->tmp_dir = (cmph_uint8 *)calloc((size_t)len+1, sizeof(cmph_uint8)); + sprintf((char *)(brz->tmp_dir), "%s", (char *)tmp_dir); + } + + } +} + +void brz_config_set_mphf_fd(cmph_config_t *mph, FILE *mphf_fd) +{ + brz_config_data_t *brz = (brz_config_data_t *)mph->data; + brz->mphf_fd = mphf_fd; + assert(brz->mphf_fd); +} + +void brz_config_set_b(cmph_config_t *mph, cmph_uint32 b) +{ + brz_config_data_t *brz = (brz_config_data_t *)mph->data; + if(b <= 64 || b >= 175) + { + b = 128; + } + brz->b = (cmph_uint8)b; +} + +void brz_config_set_algo(cmph_config_t *mph, CMPH_ALGO algo) +{ + if (algo == CMPH_BMZ8 || algo == CMPH_FCH) // supported algorithms + { + brz_config_data_t *brz = (brz_config_data_t *)mph->data; + brz->algo = algo; + } +} + +cmph_t *brz_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + brz_data_t *brzf = NULL; + cmph_uint32 i; + cmph_uint32 iterations = 20; + brz_config_data_t *brz; + + DEBUGP("c: %f\n", c); + brz = (brz_config_data_t *)mph->data; + switch(brz->algo) // validating restrictions over parameter c. + { + case CMPH_BMZ8: + if (c == 0 || c >= 2.0) c = 1; + break; + case CMPH_FCH: + if (c <= 2.0) c = 2.6; + break; + default: + assert(0); + } + brz->c = c; + brz->m = mph->key_source->nkeys; + DEBUGP("m: %u\n", brz->m); + brz->k = (cmph_uint32)ceil(brz->m/((double)brz->b)); + DEBUGP("k: %u\n", brz->k); + brz->size = (cmph_uint8 *) calloc((size_t)brz->k, sizeof(cmph_uint8)); + + // Clustering the keys by graph id. + if (mph->verbosity) + { + fprintf(stderr, "Partioning the set of keys.\n"); + } + + while(1) + { + int ok; + DEBUGP("hash function 3\n"); + brz->h0 = hash_state_new(brz->hashfuncs[2], brz->k); + DEBUGP("Generating graphs\n"); + ok = brz_gen_mphf(mph); + if (!ok) + { + --iterations; + hash_state_destroy(brz->h0); + brz->h0 = NULL; + DEBUGP("%u iterations remaining to create the graphs in a external file\n", iterations); + if (mph->verbosity) + { + fprintf(stderr, "Failure: A graph with more than 255 keys was created - %u iterations remaining\n", iterations); + } + if (iterations == 0) break; + } + else break; + } + if (iterations == 0) + { + DEBUGP("Graphs with more than 255 keys were created in all 20 iterations\n"); + free(brz->size); + return NULL; + } + DEBUGP("Graphs generated\n"); + + brz->offset = (cmph_uint32 *)calloc((size_t)brz->k, sizeof(cmph_uint32)); + for (i = 1; i < brz->k; ++i) + { + brz->offset[i] = brz->size[i-1] + brz->offset[i-1]; + } + // Generating a mphf + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + brzf = (brz_data_t *)malloc(sizeof(brz_data_t)); + brzf->g = brz->g; + brz->g = NULL; //transfer memory ownership + brzf->h1 = brz->h1; + brz->h1 = NULL; //transfer memory ownership + brzf->h2 = brz->h2; + brz->h2 = NULL; //transfer memory ownership + brzf->h0 = brz->h0; + brz->h0 = NULL; //transfer memory ownership + brzf->size = brz->size; + brz->size = NULL; //transfer memory ownership + brzf->offset = brz->offset; + brz->offset = NULL; //transfer memory ownership + brzf->k = brz->k; + brzf->c = brz->c; + brzf->m = brz->m; + brzf->algo = brz->algo; + mphf->data = brzf; + mphf->size = brz->m; + DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + return mphf; +} + +static int brz_gen_mphf(cmph_config_t *mph) +{ + cmph_uint32 i, e, error; + brz_config_data_t *brz = (brz_config_data_t *)mph->data; + cmph_uint32 memory_usage = 0; + cmph_uint32 nkeys_in_buffer = 0; + cmph_uint8 *buffer = (cmph_uint8 *)malloc((size_t)brz->memory_availability); + cmph_uint32 *buckets_size = (cmph_uint32 *)calloc((size_t)brz->k, sizeof(cmph_uint32)); + cmph_uint32 *keys_index = NULL; + cmph_uint8 **buffer_merge = NULL; + cmph_uint32 *buffer_h0 = NULL; + cmph_uint32 nflushes = 0; + cmph_uint32 h0; + register size_t nbytes; + FILE * tmp_fd = NULL; + buffer_manager_t * buff_manager = NULL; + char *filename = NULL; + char *key = NULL; + cmph_uint32 keylen; + cmph_uint32 cur_bucket = 0; + cmph_uint8 nkeys_vd = 0; + cmph_uint8 ** keys_vd = NULL; + + mph->key_source->rewind(mph->key_source->data); + DEBUGP("Generating graphs from %u keys\n", brz->m); + // Partitioning + for (e = 0; e < brz->m; ++e) + { + mph->key_source->read(mph->key_source->data, &key, &keylen); + + /* Buffers management */ + if (memory_usage + keylen + sizeof(keylen) > brz->memory_availability) // flush buffers + { + cmph_uint32 value, sum, keylen1; + if(mph->verbosity) + { + fprintf(stderr, "Flushing %u\n", nkeys_in_buffer); + } + value = buckets_size[0]; + sum = 0; + keylen1 = 0; + buckets_size[0] = 0; + for(i = 1; i < brz->k; i++) + { + if(buckets_size[i] == 0) continue; + sum += value; + value = buckets_size[i]; + buckets_size[i] = sum; + + } + memory_usage = 0; + keys_index = (cmph_uint32 *)calloc((size_t)nkeys_in_buffer, sizeof(cmph_uint32)); + for(i = 0; i < nkeys_in_buffer; i++) + { + memcpy(&keylen1, buffer + memory_usage, sizeof(keylen1)); + h0 = hash(brz->h0, (char *)(buffer + memory_usage + sizeof(keylen1)), keylen1) % brz->k; + keys_index[buckets_size[h0]] = memory_usage; + buckets_size[h0]++; + memory_usage += keylen1 + (cmph_uint32)sizeof(keylen1); + } + filename = (char *)calloc(strlen((char *)(brz->tmp_dir)) + 11, sizeof(char)); + sprintf(filename, "%s%u.cmph",brz->tmp_dir, nflushes); + tmp_fd = fopen(filename, "wb"); + free(filename); + filename = NULL; + for(i = 0; i < nkeys_in_buffer; i++) + { + memcpy(&keylen1, buffer + keys_index[i], sizeof(keylen1)); + nbytes = fwrite(buffer + keys_index[i], (size_t)1, keylen1 + sizeof(keylen1), tmp_fd); + } + nkeys_in_buffer = 0; + memory_usage = 0; + memset((void *)buckets_size, 0, brz->k*sizeof(cmph_uint32)); + nflushes++; + free(keys_index); + fclose(tmp_fd); + } + memcpy(buffer + memory_usage, &keylen, sizeof(keylen)); + memcpy(buffer + memory_usage + sizeof(keylen), key, (size_t)keylen); + memory_usage += keylen + (cmph_uint32)sizeof(keylen); + h0 = hash(brz->h0, key, keylen) % brz->k; + + if ((brz->size[h0] == MAX_BUCKET_SIZE) || (brz->algo == CMPH_BMZ8 && ((brz->c >= 1.0) && (cmph_uint8)(brz->c * brz->size[h0]) < brz->size[h0]))) + { + free(buffer); + free(buckets_size); + return 0; + } + brz->size[h0] = (cmph_uint8)(brz->size[h0] + 1U); + buckets_size[h0] ++; + nkeys_in_buffer++; + mph->key_source->dispose(mph->key_source->data, key, keylen); + } + if (memory_usage != 0) // flush buffers + { + cmph_uint32 value; + cmph_uint32 sum, keylen1; + if(mph->verbosity) + { + fprintf(stderr, "Flushing %u\n", nkeys_in_buffer); + } + value = buckets_size[0]; + sum = 0; + keylen1 = 0; + buckets_size[0] = 0; + for(i = 1; i < brz->k; i++) + { + if(buckets_size[i] == 0) continue; + sum += value; + value = buckets_size[i]; + buckets_size[i] = sum; + } + memory_usage = 0; + keys_index = (cmph_uint32 *)calloc((size_t)nkeys_in_buffer, sizeof(cmph_uint32)); + for(i = 0; i < nkeys_in_buffer; i++) + { + memcpy(&keylen1, buffer + memory_usage, sizeof(keylen1)); + h0 = hash(brz->h0, (char *)(buffer + memory_usage + sizeof(keylen1)), keylen1) % brz->k; + keys_index[buckets_size[h0]] = memory_usage; + buckets_size[h0]++; + memory_usage += keylen1 + (cmph_uint32)sizeof(keylen1); + } + filename = (char *)calloc(strlen((char *)(brz->tmp_dir)) + 11, sizeof(char)); + sprintf(filename, "%s%u.cmph",brz->tmp_dir, nflushes); + tmp_fd = fopen(filename, "wb"); + free(filename); + filename = NULL; + for(i = 0; i < nkeys_in_buffer; i++) + { + memcpy(&keylen1, buffer + keys_index[i], sizeof(keylen1)); + nbytes = fwrite(buffer + keys_index[i], (size_t)1, keylen1 + sizeof(keylen1), tmp_fd); + } + nkeys_in_buffer = 0; + memory_usage = 0; + memset((void *)buckets_size, 0, brz->k*sizeof(cmph_uint32)); + nflushes++; + free(keys_index); + fclose(tmp_fd); + } + + free(buffer); + free(buckets_size); + if(nflushes > 1024) return 0; // Too many files generated. + // mphf generation + if(mph->verbosity) + { + fprintf(stderr, "\nMPHF generation \n"); + } + /* Starting to dump to disk the resultant MPHF: __cmph_dump function */ + nbytes = fwrite(cmph_names[CMPH_BRZ], (size_t)(strlen(cmph_names[CMPH_BRZ]) + 1), (size_t)1, brz->mphf_fd); + nbytes = fwrite(&(brz->m), sizeof(brz->m), (size_t)1, brz->mphf_fd); + nbytes = fwrite(&(brz->c), sizeof(double), (size_t)1, brz->mphf_fd); + nbytes = fwrite(&(brz->algo), sizeof(brz->algo), (size_t)1, brz->mphf_fd); + nbytes = fwrite(&(brz->k), sizeof(cmph_uint32), (size_t)1, brz->mphf_fd); // number of MPHFs + nbytes = fwrite(brz->size, sizeof(cmph_uint8)*(brz->k), (size_t)1, brz->mphf_fd); + if (nbytes == 0 && ferror(brz->mphf_fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } + + //tmp_fds = (FILE **)calloc(nflushes, sizeof(FILE *)); + buff_manager = buffer_manager_new(brz->memory_availability, nflushes); + buffer_merge = (cmph_uint8 **)calloc((size_t)nflushes, sizeof(cmph_uint8 *)); + buffer_h0 = (cmph_uint32 *)calloc((size_t)nflushes, sizeof(cmph_uint32)); + + memory_usage = 0; + for(i = 0; i < nflushes; i++) + { + filename = (char *)calloc(strlen((char *)(brz->tmp_dir)) + 11, sizeof(char)); + sprintf(filename, "%s%u.cmph",brz->tmp_dir, i); + buffer_manager_open(buff_manager, i, filename); + free(filename); + filename = NULL; + key = (char *)buffer_manager_read_key(buff_manager, i, &keylen); + h0 = hash(brz->h0, key+sizeof(keylen), keylen) % brz->k; + buffer_h0[i] = h0; + buffer_merge[i] = (cmph_uint8 *)key; + key = NULL; //transfer memory ownership + } + e = 0; + keys_vd = (cmph_uint8 **)calloc((size_t)MAX_BUCKET_SIZE, sizeof(cmph_uint8 *)); + nkeys_vd = 0; + error = 0; + while(e < brz->m) + { + i = brz_min_index(buffer_h0, nflushes); + cur_bucket = buffer_h0[i]; + key = (char *)buffer_manager_read_key(buff_manager, i, &keylen); + if(key) + { + while(key) + { + //keylen = strlen(key); + h0 = hash(brz->h0, key+sizeof(keylen), keylen) % brz->k; + if (h0 != buffer_h0[i]) break; + keys_vd[nkeys_vd++] = (cmph_uint8 *)key; + key = NULL; //transfer memory ownership + e++; + key = (char *)buffer_manager_read_key(buff_manager, i, &keylen); + } + if (key) + { + assert(nkeys_vd < brz->size[cur_bucket]); + keys_vd[nkeys_vd++] = buffer_merge[i]; + buffer_merge[i] = NULL; //transfer memory ownership + e++; + buffer_h0[i] = h0; + buffer_merge[i] = (cmph_uint8 *)key; + } + } + if(!key) + { + assert(nkeys_vd < brz->size[cur_bucket]); + keys_vd[nkeys_vd++] = buffer_merge[i]; + buffer_merge[i] = NULL; //transfer memory ownership + e++; + buffer_h0[i] = UINT_MAX; + } + + if(nkeys_vd == brz->size[cur_bucket]) // Generating mphf for each bucket. + { + cmph_io_adapter_t *source = NULL; + cmph_config_t *config = NULL; + cmph_t *mphf_tmp = NULL; + char *bufmphf = NULL; + cmph_uint32 buflenmphf = 0; + // Source of keys + source = cmph_io_byte_vector_adapter(keys_vd, (cmph_uint32)nkeys_vd); + config = cmph_config_new(source); + cmph_config_set_algo(config, brz->algo); + //cmph_config_set_algo(config, CMPH_BMZ8); + cmph_config_set_graphsize(config, brz->c); + mphf_tmp = cmph_new(config); + if (mphf_tmp == NULL) + { + if(mph->verbosity) fprintf(stderr, "ERROR: Can't generate MPHF for bucket %u out of %u\n", cur_bucket + 1, brz->k); + error = 1; + cmph_config_destroy(config); + brz_destroy_keys_vd(keys_vd, nkeys_vd); + cmph_io_byte_vector_adapter_destroy(source); + break; + } + if(mph->verbosity) + { + if (cur_bucket % 1000 == 0) + { + fprintf(stderr, "MPHF for bucket %u out of %u was generated.\n", cur_bucket + 1, brz->k); + } + } + switch(brz->algo) + { + case CMPH_FCH: + { + fch_data_t * fchf = NULL; + fchf = (fch_data_t *)mphf_tmp->data; + bufmphf = brz_copy_partial_fch_mphf(brz, fchf, cur_bucket, &buflenmphf); + } + break; + case CMPH_BMZ8: + { + bmz8_data_t * bmzf = NULL; + bmzf = (bmz8_data_t *)mphf_tmp->data; + bufmphf = brz_copy_partial_bmz8_mphf(brz, bmzf, cur_bucket, &buflenmphf); + } + break; + default: assert(0); + } + nbytes = fwrite(bufmphf, (size_t)buflenmphf, (size_t)1, brz->mphf_fd); + free(bufmphf); + bufmphf = NULL; + cmph_config_destroy(config); + brz_destroy_keys_vd(keys_vd, nkeys_vd); + cmph_destroy(mphf_tmp); + cmph_io_byte_vector_adapter_destroy(source); + nkeys_vd = 0; + } + } + buffer_manager_destroy(buff_manager); + free(keys_vd); + free(buffer_merge); + free(buffer_h0); + if (error) return 0; + return 1; +} + +static cmph_uint32 brz_min_index(cmph_uint32 * vector, cmph_uint32 n) +{ + cmph_uint32 i, min_index = 0; + for(i = 1; i < n; i++) + { + if(vector[i] < vector[min_index]) min_index = i; + } + return min_index; +} + +static void brz_destroy_keys_vd(cmph_uint8 ** keys_vd, cmph_uint32 nkeys) +{ + cmph_uint8 i; + for(i = 0; i < nkeys; i++) { free(keys_vd[i]); keys_vd[i] = NULL;} +} + +static char * brz_copy_partial_fch_mphf(brz_config_data_t *brz, fch_data_t * fchf, cmph_uint32 index, cmph_uint32 *buflen) +{ + cmph_uint32 i = 0; + cmph_uint32 buflenh1 = 0; + cmph_uint32 buflenh2 = 0; + char * bufh1 = NULL; + char * bufh2 = NULL; + char * buf = NULL; + cmph_uint32 n = fchf->b;//brz->size[index]; + hash_state_dump(fchf->h1, &bufh1, &buflenh1); + hash_state_dump(fchf->h2, &bufh2, &buflenh2); + *buflen = buflenh1 + buflenh2 + n + 2U * (cmph_uint32)sizeof(cmph_uint32); + buf = (char *)malloc((size_t)(*buflen)); + memcpy(buf, &buflenh1, sizeof(cmph_uint32)); + memcpy(buf+sizeof(cmph_uint32), bufh1, (size_t)buflenh1); + memcpy(buf+sizeof(cmph_uint32)+buflenh1, &buflenh2, sizeof(cmph_uint32)); + memcpy(buf+2*sizeof(cmph_uint32)+buflenh1, bufh2, (size_t)buflenh2); + for (i = 0; i < n; i++) memcpy(buf+2*sizeof(cmph_uint32)+buflenh1+buflenh2+i,(fchf->g + i), (size_t)1); + free(bufh1); + free(bufh2); + return buf; +} +static char * brz_copy_partial_bmz8_mphf(brz_config_data_t *brz, bmz8_data_t * bmzf, cmph_uint32 index, cmph_uint32 *buflen) +{ + cmph_uint32 buflenh1 = 0; + cmph_uint32 buflenh2 = 0; + char * bufh1 = NULL; + char * bufh2 = NULL; + char * buf = NULL; + cmph_uint32 n = (cmph_uint32)ceil(brz->c * brz->size[index]); + hash_state_dump(bmzf->hashes[0], &bufh1, &buflenh1); + hash_state_dump(bmzf->hashes[1], &bufh2, &buflenh2); + *buflen = buflenh1 + buflenh2 + n + 2U * (cmph_uint32)sizeof(cmph_uint32); + buf = (char *)malloc((size_t)(*buflen)); + memcpy(buf, &buflenh1, sizeof(cmph_uint32)); + memcpy(buf+sizeof(cmph_uint32), bufh1, (size_t)buflenh1); + memcpy(buf+sizeof(cmph_uint32)+buflenh1, &buflenh2, sizeof(cmph_uint32)); + memcpy(buf+2*sizeof(cmph_uint32)+buflenh1, bufh2, (size_t)buflenh2); + memcpy(buf+2*sizeof(cmph_uint32)+buflenh1+buflenh2,bmzf->g, (size_t)n); + free(bufh1); + free(bufh2); + return buf; +} + + +int brz_dump(cmph_t *mphf, FILE *fd) +{ + brz_data_t *data = (brz_data_t *)mphf->data; + char *buf = NULL; + cmph_uint32 buflen; + register size_t nbytes; + DEBUGP("Dumping brzf\n"); + // The initial part of the MPHF have already been dumped to disk during construction + // Dumping h0 + hash_state_dump(data->h0, &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + // Dumping m and the vector offset. + nbytes = fwrite(&(data->m), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(data->offset, sizeof(cmph_uint32)*(data->k), (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } + return 1; +} + +void brz_load(FILE *f, cmph_t *mphf) +{ + char *buf = NULL; + cmph_uint32 buflen; + register size_t nbytes; + cmph_uint32 i, n; + brz_data_t *brz = (brz_data_t *)malloc(sizeof(brz_data_t)); + + DEBUGP("Loading brz mphf\n"); + mphf->data = brz; + nbytes = fread(&(brz->c), sizeof(double), (size_t)1, f); + nbytes = fread(&(brz->algo), sizeof(brz->algo), (size_t)1, f); // Reading algo. + nbytes = fread(&(brz->k), sizeof(cmph_uint32), (size_t)1, f); + brz->size = (cmph_uint8 *) malloc(sizeof(cmph_uint8)*brz->k); + nbytes = fread(brz->size, sizeof(cmph_uint8)*(brz->k), (size_t)1, f); + brz->h1 = (hash_state_t **)malloc(sizeof(hash_state_t *)*brz->k); + brz->h2 = (hash_state_t **)malloc(sizeof(hash_state_t *)*brz->k); + brz->g = (cmph_uint8 **) calloc((size_t)brz->k, sizeof(cmph_uint8 *)); + DEBUGP("Reading c = %f k = %u algo = %u \n", brz->c, brz->k, brz->algo); + //loading h_i1, h_i2 and g_i. + for(i = 0; i < brz->k; i++) + { + // h1 + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + DEBUGP("Hash state 1 has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + brz->h1[i] = hash_state_load(buf, buflen); + free(buf); + //h2 + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + DEBUGP("Hash state 2 has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + brz->h2[i] = hash_state_load(buf, buflen); + free(buf); + switch(brz->algo) + { + case CMPH_FCH: + n = fch_calc_b(brz->c, brz->size[i]); + break; + case CMPH_BMZ8: + n = (cmph_uint32)ceil(brz->c * brz->size[i]); + break; + default: assert(0); + } + DEBUGP("g_i has %u bytes\n", n); + brz->g[i] = (cmph_uint8 *)calloc((size_t)n, sizeof(cmph_uint8)); + nbytes = fread(brz->g[i], sizeof(cmph_uint8)*n, (size_t)1, f); + } + //loading h0 + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + DEBUGP("Hash state has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + brz->h0 = hash_state_load(buf, buflen); + free(buf); + + //loading c, m, and the vector offset. + nbytes = fread(&(brz->m), sizeof(cmph_uint32), (size_t)1, f); + brz->offset = (cmph_uint32 *)malloc(sizeof(cmph_uint32)*brz->k); + nbytes = fread(brz->offset, sizeof(cmph_uint32)*(brz->k), (size_t)1, f); + if (nbytes == 0 && ferror(f)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + } + + return; +} + +static cmph_uint32 brz_bmz8_search(brz_data_t *brz, const char *key, cmph_uint32 keylen, cmph_uint32 * fingerprint) +{ + register cmph_uint32 h0; + register cmph_uint32 m, n, h1, h2; + register cmph_uint8 mphf_bucket; + + hash_vector(brz->h0, key, keylen, fingerprint); + h0 = fingerprint[2] % brz->k; + + m = brz->size[h0]; + n = (cmph_uint32)ceil(brz->c * m); + h1 = hash(brz->h1[h0], key, keylen) % n; + h2 = hash(brz->h2[h0], key, keylen) % n; + + if (h1 == h2 && ++h2 >= n) h2 = 0; + mphf_bucket = (cmph_uint8)(brz->g[h0][h1] + brz->g[h0][h2]); + DEBUGP("key: %s h1: %u h2: %u h0: %u\n", key, h1, h2, h0); + DEBUGP("key: %s g[h1]: %u g[h2]: %u offset[h0]: %u edges: %u\n", key, brz->g[h0][h1], brz->g[h0][h2], brz->offset[h0], brz->m); + DEBUGP("Address: %u\n", mphf_bucket + brz->offset[h0]); + return (mphf_bucket + brz->offset[h0]); +} + +static cmph_uint32 brz_fch_search(brz_data_t *brz, const char *key, cmph_uint32 keylen, cmph_uint32 * fingerprint) +{ + register cmph_uint32 h0; + register cmph_uint32 m, b, h1, h2; + register double p1, p2; + register cmph_uint8 mphf_bucket; + + hash_vector(brz->h0, key, keylen, fingerprint); + h0 = fingerprint[2] % brz->k; + + m = brz->size[h0]; + b = fch_calc_b(brz->c, m); + p1 = fch_calc_p1(m); + p2 = fch_calc_p2(b); + h1 = hash(brz->h1[h0], key, keylen) % m; + h2 = hash(brz->h2[h0], key, keylen) % m; + mphf_bucket = 0; + h1 = mixh10h11h12(b, p1, p2, h1); + mphf_bucket = (cmph_uint8)((h2 + brz->g[h0][h1]) % m); + return (mphf_bucket + brz->offset[h0]); +} + +cmph_uint32 brz_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + brz_data_t *brz = mphf->data; + cmph_uint32 fingerprint[3]; + switch(brz->algo) + { + case CMPH_FCH: + return brz_fch_search(brz, key, keylen, fingerprint); + case CMPH_BMZ8: + return brz_bmz8_search(brz, key, keylen, fingerprint); + default: assert(0); + } + return 0; +} +void brz_destroy(cmph_t *mphf) +{ + cmph_uint32 i; + brz_data_t *data = (brz_data_t *)mphf->data; + if(data->g) + { + for(i = 0; i < data->k; i++) + { + free(data->g[i]); + hash_state_destroy(data->h1[i]); + hash_state_destroy(data->h2[i]); + } + free(data->g); + free(data->h1); + free(data->h2); + } + hash_state_destroy(data->h0); + free(data->size); + free(data->offset); + free(data); + free(mphf); +} + +/** \fn void brz_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void brz_pack(cmph_t *mphf, void *packed_mphf) +{ + brz_data_t *data = (brz_data_t *)mphf->data; + cmph_uint8 * ptr = packed_mphf; + cmph_uint32 i,n; + CMPH_HASH h0_type, h1_type, h2_type; +#ifdef __brz_use_64bit__ + cmph_uint64 * g_is_ptr; +#else + cmph_uint32 * g_is_ptr; +#endif + cmph_uint8 * g_i; + + // packing internal algo type + memcpy(ptr, &(data->algo), sizeof(data->algo)); + ptr += sizeof(data->algo); + + // packing h0 type + h0_type = hash_get_type(data->h0); + memcpy(ptr, &h0_type, sizeof(h0_type)); + ptr += sizeof(h0_type); + + // packing h0 + hash_state_pack(data->h0, ptr); + ptr += hash_state_packed_size(h0_type); + + // packing k + memcpy(ptr, &(data->k), sizeof(data->k)); + ptr += sizeof(data->k); + + // packing c + *((cmph_uint64 *)ptr) = (cmph_uint64)data->c; + ptr += sizeof(data->c); + + // packing h1 type + h1_type = hash_get_type(data->h1[0]); + memcpy(ptr, &h1_type, sizeof(h1_type)); + ptr += sizeof(h1_type); + + // packing h2 type + h2_type = hash_get_type(data->h2[0]); + memcpy(ptr, &h2_type, sizeof(h2_type)); + ptr += sizeof(h2_type); + + // packing size + memcpy(ptr, data->size, sizeof(cmph_uint8)*data->k); + ptr += data->k; + + // packing offset + memcpy(ptr, data->offset, sizeof(cmph_uint32)*data->k); + ptr += sizeof(cmph_uint32)*data->k; + + #ifdef __brz_use_64bit__ + g_is_ptr = (cmph_uint64 *)ptr; + #else + g_is_ptr = (cmph_uint32 *)ptr; + #endif + + g_i = (cmph_uint8 *) (g_is_ptr + data->k); + + for(i = 0; i < data->k; i++) + { + #ifdef __brz_use_64bit__ + *g_is_ptr++ = (cmph_uint64)g_i; + #else + *g_is_ptr++ = (cmph_uint32)g_i; + #endif + // packing h1[i] + hash_state_pack(data->h1[i], g_i); + g_i += hash_state_packed_size(h1_type); + + // packing h2[i] + hash_state_pack(data->h2[i], g_i); + g_i += hash_state_packed_size(h2_type); + + // packing g_i + switch(data->algo) + { + case CMPH_FCH: + n = fch_calc_b(data->c, data->size[i]); + break; + case CMPH_BMZ8: + n = (cmph_uint32)ceil(data->c * data->size[i]); + break; + default: assert(0); + } + memcpy(g_i, data->g[i], sizeof(cmph_uint8)*n); + g_i += n; + + } + +} + +/** \fn cmph_uint32 brz_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 brz_packed_size(cmph_t *mphf) +{ + cmph_uint32 i; + cmph_uint32 size = 0; + brz_data_t *data = (brz_data_t *)mphf->data; + CMPH_HASH h0_type = hash_get_type(data->h0); + CMPH_HASH h1_type = hash_get_type(data->h1[0]); + CMPH_HASH h2_type = hash_get_type(data->h2[0]); + cmph_uint32 n; + size = (cmph_uint32)(2*sizeof(CMPH_ALGO) + 3*sizeof(CMPH_HASH) + hash_state_packed_size(h0_type) + sizeof(cmph_uint32) + + sizeof(double) + sizeof(cmph_uint8)*data->k + sizeof(cmph_uint32)*data->k); + // pointers to g_is + #ifdef __brz_use_64bit__ + size += (cmph_uint32) sizeof(cmph_uint64)*data->k; + #else + size += (cmph_uint32) sizeof(cmph_uint32)*data->k; + #endif + + size += hash_state_packed_size(h1_type) * data->k; + size += hash_state_packed_size(h2_type) * data->k; + + n = 0; + for(i = 0; i < data->k; i++) + { + switch(data->algo) + { + case CMPH_FCH: + n = fch_calc_b(data->c, data->size[i]); + break; + case CMPH_BMZ8: + n = (cmph_uint32)ceil(data->c * data->size[i]); + break; + default: assert(0); + } + size += n; + } + return size; +} + + + +static cmph_uint32 brz_bmz8_search_packed(cmph_uint32 *packed_mphf, const char *key, cmph_uint32 keylen, cmph_uint32 * fingerprint) +{ + register CMPH_HASH h0_type = *packed_mphf++; + register cmph_uint32 *h0_ptr = packed_mphf; + register cmph_uint32 k, h0, m, n, h1, h2; + register cmph_uint32 *offset; + register double c; + register CMPH_HASH h1_type, h2_type; + register cmph_uint8 * size; +#ifdef __brz_use_64bit__ + register cmph_uint64 * g_is_ptr; +#else + register cmph_uint32 * g_is_ptr; +#endif + register cmph_uint8 *h1_ptr, *h2_ptr, *g; + register cmph_uint8 mphf_bucket; + + packed_mphf = (cmph_uint32 *)(((cmph_uint8 *)packed_mphf) + hash_state_packed_size(h0_type)); + + k = *packed_mphf++; + + c = (double)(*((cmph_uint64*)packed_mphf)); + packed_mphf += 2; + + h1_type = *packed_mphf++; + + h2_type = *packed_mphf++; + + size = (cmph_uint8 *) packed_mphf; + packed_mphf = (cmph_uint32 *)(size + k); + + offset = packed_mphf; + packed_mphf += k; + + + hash_vector_packed(h0_ptr, h0_type, key, keylen, fingerprint); + h0 = fingerprint[2] % k; + + m = size[h0]; + n = (cmph_uint32)ceil(c * m); + + #ifdef __brz_use_64bit__ + g_is_ptr = (cmph_uint64 *)packed_mphf; + #else + g_is_ptr = packed_mphf; + #endif + + h1_ptr = (cmph_uint8 *) g_is_ptr[h0]; + + h2_ptr = h1_ptr + hash_state_packed_size(h1_type); + + g = h2_ptr + hash_state_packed_size(h2_type); + + h1 = hash_packed(h1_ptr, h1_type, key, keylen) % n; + h2 = hash_packed(h2_ptr, h2_type, key, keylen) % n; + + if (h1 == h2 && ++h2 >= n) h2 = 0; + mphf_bucket = (cmph_uint8)(g[h1] + g[h2]); + DEBUGP("key: %s h1: %u h2: %u h0: %u\n", key, h1, h2, h0); + DEBUGP("Address: %u\n", mphf_bucket + offset[h0]); + return (mphf_bucket + offset[h0]); +} + +static cmph_uint32 brz_fch_search_packed(cmph_uint32 *packed_mphf, const char *key, cmph_uint32 keylen, cmph_uint32 * fingerprint) +{ + register CMPH_HASH h0_type = *packed_mphf++; + + register cmph_uint32 *h0_ptr = packed_mphf; + register cmph_uint32 k, h0, m, b, h1, h2; + register double c, p1, p2; + register CMPH_HASH h1_type, h2_type; + register cmph_uint8 *size, *h1_ptr, *h2_ptr, *g; + register cmph_uint32 *offset; +#ifdef __brz_use_64bit__ + register cmph_uint64 * g_is_ptr; +#else + register cmph_uint32 * g_is_ptr; +#endif + register cmph_uint8 mphf_bucket; + + packed_mphf = (cmph_uint32 *)(((cmph_uint8 *)packed_mphf) + hash_state_packed_size(h0_type)); + + k = *packed_mphf++; + + c = (double)(*((cmph_uint64*)packed_mphf)); + packed_mphf += 2; + + h1_type = *packed_mphf++; + + h2_type = *packed_mphf++; + + size = (cmph_uint8 *) packed_mphf; + packed_mphf = (cmph_uint32 *)(size + k); + + offset = packed_mphf; + packed_mphf += k; + + hash_vector_packed(h0_ptr, h0_type, key, keylen, fingerprint); + h0 = fingerprint[2] % k; + + m = size[h0]; + b = fch_calc_b(c, m); + p1 = fch_calc_p1(m); + p2 = fch_calc_p2(b); + + #ifdef __brz_use_64bit__ + g_is_ptr = (cmph_uint64 *)packed_mphf; + #else + g_is_ptr = packed_mphf; + #endif + + h1_ptr = (cmph_uint8 *) g_is_ptr[h0]; + + h2_ptr = h1_ptr + hash_state_packed_size(h1_type); + + g = h2_ptr + hash_state_packed_size(h2_type); + + h1 = hash_packed(h1_ptr, h1_type, key, keylen) % m; + h2 = hash_packed(h2_ptr, h2_type, key, keylen) % m; + + mphf_bucket = 0; + h1 = mixh10h11h12(b, p1, p2, h1); + mphf_bucket = (cmph_uint8)((h2 + g[h1]) % m); + return (mphf_bucket + offset[h0]); +} + +/** cmph_uint32 brz_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 brz_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + register cmph_uint32 *ptr = (cmph_uint32 *)packed_mphf; + register CMPH_ALGO algo = *ptr++; + cmph_uint32 fingerprint[3]; + switch(algo) + { + case CMPH_FCH: + return brz_fch_search_packed(ptr, key, keylen, fingerprint); + case CMPH_BMZ8: + return brz_bmz8_search_packed(ptr, key, keylen, fingerprint); + default: + assert(0); + return 0; /* To avoid warnings that value must be returned */ + } +} + diff --git a/girepository/cmph/brz.h b/girepository/cmph/brz.h new file mode 100644 index 000000000..648f174b5 --- /dev/null +++ b/girepository/cmph/brz.h @@ -0,0 +1,47 @@ +#ifndef __CMPH_BRZ_H__ +#define __CMPH_BRZ_H__ + +#include "cmph.h" + +typedef struct __brz_data_t brz_data_t; +typedef struct __brz_config_data_t brz_config_data_t; + +brz_config_data_t *brz_config_new(void); +void brz_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); +void brz_config_set_tmp_dir(cmph_config_t *mph, cmph_uint8 *tmp_dir); +void brz_config_set_mphf_fd(cmph_config_t *mph, FILE *mphf_fd); +void brz_config_set_b(cmph_config_t *mph, cmph_uint32 b); +void brz_config_set_algo(cmph_config_t *mph, CMPH_ALGO algo); +void brz_config_set_memory_availability(cmph_config_t *mph, cmph_uint32 memory_availability); +void brz_config_destroy(cmph_config_t *mph); +cmph_t *brz_new(cmph_config_t *mph, double c); + +void brz_load(FILE *f, cmph_t *mphf); +int brz_dump(cmph_t *mphf, FILE *f); +void brz_destroy(cmph_t *mphf); +cmph_uint32 brz_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +/** \fn void brz_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void brz_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 brz_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 brz_packed_size(cmph_t *mphf); + +/** cmph_uint32 brz_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 brz_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +#endif diff --git a/girepository/cmph/brz_structs.h b/girepository/cmph/brz_structs.h new file mode 100644 index 000000000..b781107f9 --- /dev/null +++ b/girepository/cmph/brz_structs.h @@ -0,0 +1,39 @@ +#ifndef __CMPH_BRZ_STRUCTS_H__ +#define __CMPH_BRZ_STRUCTS_H__ + +#include "hash_state.h" + +struct __brz_data_t +{ + CMPH_ALGO algo; // CMPH algo for generating the MPHFs for the buckets (Just CMPH_FCH and CMPH_BMZ8) + cmph_uint32 m; // edges (words) count + double c; // constant c + cmph_uint8 *size; // size[i] stores the number of edges represented by g[i][...]. + cmph_uint32 *offset; // offset[i] stores the sum: size[0] + size[1] + ... size[i-1]. + cmph_uint8 **g; // g function. + cmph_uint32 k; // number of components + hash_state_t **h1; + hash_state_t **h2; + hash_state_t * h0; +}; + +struct __brz_config_data_t +{ + CMPH_HASH hashfuncs[3]; + CMPH_ALGO algo; // CMPH algo for generating the MPHFs for the buckets (Just CMPH_FCH and CMPH_BMZ8) + double c; // constant c + cmph_uint32 m; // edges (words) count + cmph_uint8 *size; // size[i] stores the number of edges represented by g[i][...]. + cmph_uint32 *offset; // offset[i] stores the sum: size[0] + size[1] + ... size[i-1]. + cmph_uint8 **g; // g function. + cmph_uint8 b; // parameter b. + cmph_uint32 k; // number of components + hash_state_t **h1; + hash_state_t **h2; + hash_state_t * h0; + cmph_uint32 memory_availability; + cmph_uint8 * tmp_dir; // temporary directory + FILE * mphf_fd; // mphf file +}; + +#endif diff --git a/girepository/cmph/buffer_entry.c b/girepository/cmph/buffer_entry.c new file mode 100644 index 000000000..5dcc4d577 --- /dev/null +++ b/girepository/cmph/buffer_entry.c @@ -0,0 +1,103 @@ +#include "buffer_entry.h" +#include +#include +#include +#include + +struct __buffer_entry_t +{ + FILE *fd; + cmph_uint8 * buff; + cmph_uint32 capacity, // buffer entry capacity + nbytes, // buffer entry used bytes + pos; // current read position in buffer entry + cmph_uint8 eof; // flag to indicate end of file +}; + +buffer_entry_t * buffer_entry_new(cmph_uint32 capacity) +{ + buffer_entry_t *buff_entry = (buffer_entry_t *)malloc(sizeof(buffer_entry_t)); + assert(buff_entry); + buff_entry->fd = NULL; + buff_entry->buff = NULL; + buff_entry->capacity = capacity; + buff_entry->nbytes = capacity; + buff_entry->pos = capacity; + buff_entry->eof = 0; + return buff_entry; +} + +void buffer_entry_open(buffer_entry_t * buffer_entry, char * filename) +{ + buffer_entry->fd = fopen(filename, "rb"); +} + +void buffer_entry_set_capacity(buffer_entry_t * buffer_entry, cmph_uint32 capacity) +{ + buffer_entry->capacity = capacity; +} + + +cmph_uint32 buffer_entry_get_capacity(buffer_entry_t * buffer_entry) +{ + return buffer_entry->capacity; +} + +static void buffer_entry_load(buffer_entry_t * buffer_entry) +{ + free(buffer_entry->buff); + buffer_entry->buff = (cmph_uint8 *)calloc((size_t)buffer_entry->capacity, sizeof(cmph_uint8)); + buffer_entry->nbytes = (cmph_uint32)fread(buffer_entry->buff, (size_t)1, (size_t)buffer_entry->capacity, buffer_entry->fd); + if (buffer_entry->nbytes != buffer_entry->capacity) buffer_entry->eof = 1; + buffer_entry->pos = 0; +} + +cmph_uint8 * buffer_entry_read_key(buffer_entry_t * buffer_entry, cmph_uint32 * keylen) +{ + cmph_uint8 * buf = NULL; + cmph_uint32 lacked_bytes = sizeof(*keylen); + cmph_uint32 copied_bytes = 0; + if(buffer_entry->eof && (buffer_entry->pos == buffer_entry->nbytes)) // end + { + free(buf); + return NULL; + } + if((buffer_entry->pos + lacked_bytes) > buffer_entry->nbytes) + { + copied_bytes = buffer_entry->nbytes - buffer_entry->pos; + lacked_bytes = (buffer_entry->pos + lacked_bytes) - buffer_entry->nbytes; + if (copied_bytes != 0) memcpy(keylen, buffer_entry->buff + buffer_entry->pos, (size_t)copied_bytes); + buffer_entry_load(buffer_entry); + } + memcpy(keylen + copied_bytes, buffer_entry->buff + buffer_entry->pos, (size_t)lacked_bytes); + buffer_entry->pos += lacked_bytes; + + lacked_bytes = *keylen; + copied_bytes = 0; + buf = (cmph_uint8 *)malloc(*keylen + sizeof(*keylen)); + memcpy(buf, keylen, sizeof(*keylen)); + if((buffer_entry->pos + lacked_bytes) > buffer_entry->nbytes) { + copied_bytes = buffer_entry->nbytes - buffer_entry->pos; + lacked_bytes = (buffer_entry->pos + lacked_bytes) - buffer_entry->nbytes; + if (copied_bytes != 0) { + memcpy(buf + sizeof(*keylen), buffer_entry->buff + buffer_entry->pos, (size_t)copied_bytes); + } + buffer_entry_load(buffer_entry); + } + memcpy(buf+sizeof(*keylen)+copied_bytes, buffer_entry->buff + buffer_entry->pos, (size_t)lacked_bytes); + buffer_entry->pos += lacked_bytes; + return buf; +} + +void buffer_entry_destroy(buffer_entry_t * buffer_entry) +{ + fclose(buffer_entry->fd); + buffer_entry->fd = NULL; + free(buffer_entry->buff); + buffer_entry->buff = NULL; + buffer_entry->capacity = 0; + buffer_entry->nbytes = 0; + buffer_entry->pos = 0; + buffer_entry->eof = 0; + free(buffer_entry); +} diff --git a/girepository/cmph/buffer_entry.h b/girepository/cmph/buffer_entry.h new file mode 100644 index 000000000..62102baba --- /dev/null +++ b/girepository/cmph/buffer_entry.h @@ -0,0 +1,14 @@ +#ifndef __CMPH_BUFFER_ENTRY_H__ +#define __CMPH_BUFFER_ENTRY_H__ + +#include "cmph_types.h" +#include +typedef struct __buffer_entry_t buffer_entry_t; + +buffer_entry_t * buffer_entry_new(cmph_uint32 capacity); +void buffer_entry_set_capacity(buffer_entry_t * buffer_entry, cmph_uint32 capacity); +cmph_uint32 buffer_entry_get_capacity(buffer_entry_t * buffer_entry); +void buffer_entry_open(buffer_entry_t * buffer_entry, char * filename); +cmph_uint8 * buffer_entry_read_key(buffer_entry_t * buffer_entry, cmph_uint32 * keylen); +void buffer_entry_destroy(buffer_entry_t * buffer_entry); +#endif diff --git a/girepository/cmph/buffer_manage.c b/girepository/cmph/buffer_manage.c new file mode 100644 index 000000000..fdefc620d --- /dev/null +++ b/girepository/cmph/buffer_manage.c @@ -0,0 +1,66 @@ +#include "buffer_manage.h" +#include "buffer_entry.h" +#include +#include +#include +struct __buffer_manage_t +{ + cmph_uint32 memory_avail; // memory available + buffer_entry_t ** buffer_entries; // buffer entries to be managed + cmph_uint32 nentries; // number of entries to be managed + cmph_uint32 *memory_avail_list; // memory available list + int pos_avail_list; // current position in memory available list +}; + +buffer_manage_t * buffer_manage_new(cmph_uint32 memory_avail, cmph_uint32 nentries) +{ + cmph_uint32 memory_avail_entry, i; + buffer_manage_t *buff_manage = (buffer_manage_t *)malloc(sizeof(buffer_manage_t)); + assert(buff_manage); + buff_manage->memory_avail = memory_avail; + buff_manage->buffer_entries = (buffer_entry_t **)calloc((size_t)nentries, sizeof(buffer_entry_t *)); + buff_manage->memory_avail_list = (cmph_uint32 *)calloc((size_t)nentries, sizeof(cmph_uint32)); + buff_manage->pos_avail_list = -1; + buff_manage->nentries = nentries; + memory_avail_entry = buff_manage->memory_avail/buff_manage->nentries + 1; + for(i = 0; i < buff_manage->nentries; i++) + { + buff_manage->buffer_entries[i] = buffer_entry_new(memory_avail_entry); + } + return buff_manage; +} + +void buffer_manage_open(buffer_manage_t * buffer_manage, cmph_uint32 index, char * filename) +{ + buffer_entry_open(buffer_manage->buffer_entries[index], filename); +} + +cmph_uint8 * buffer_manage_read_key(buffer_manage_t * buffer_manage, cmph_uint32 index) +{ + cmph_uint8 * key = NULL; + if (buffer_manage->pos_avail_list >= 0 ) // recovering memory + { + cmph_uint32 new_capacity = buffer_entry_get_capacity(buffer_manage->buffer_entries[index]) + buffer_manage->memory_avail_list[(buffer_manage->pos_avail_list)--]; + buffer_entry_set_capacity(buffer_manage->buffer_entries[index], new_capacity); + //fprintf(stderr, "recovering memory\n"); + } + key = buffer_entry_read_key(buffer_manage->buffer_entries[index]); + if (key == NULL) // storing memory to be recovered + { + buffer_manage->memory_avail_list[++(buffer_manage->pos_avail_list)] = buffer_entry_get_capacity(buffer_manage->buffer_entries[index]); + //fprintf(stderr, "storing memory to be recovered\n"); + } + return key; +} + +void buffer_manage_destroy(buffer_manage_t * buffer_manage) +{ + cmph_uint32 i; + for(i = 0; i < buffer_manage->nentries; i++) + { + buffer_entry_destroy(buffer_manage->buffer_entries[i]); + } + free(buffer_manage->memory_avail_list); + free(buffer_manage->buffer_entries); + free(buffer_manage); +} diff --git a/girepository/cmph/buffer_manage.h b/girepository/cmph/buffer_manage.h new file mode 100644 index 000000000..8c66cffc1 --- /dev/null +++ b/girepository/cmph/buffer_manage.h @@ -0,0 +1,12 @@ +#ifndef __CMPH_BUFFER_MANAGE_H__ +#define __CMPH_BUFFER_MANAGE_H__ + +#include "cmph_types.h" +#include +typedef struct __buffer_manage_t buffer_manage_t; + +buffer_manage_t * buffer_manage_new(cmph_uint32 memory_avail, cmph_uint32 nentries); +void buffer_manage_open(buffer_manage_t * buffer_manage, cmph_uint32 index, char * filename); +cmph_uint8 * buffer_manage_read_key(buffer_manage_t * buffer_manage, cmph_uint32 index); +void buffer_manage_destroy(buffer_manage_t * buffer_manage); +#endif diff --git a/girepository/cmph/buffer_manager.c b/girepository/cmph/buffer_manager.c new file mode 100644 index 000000000..5a051e2f0 --- /dev/null +++ b/girepository/cmph/buffer_manager.c @@ -0,0 +1,64 @@ +#include "buffer_manager.h" +#include "buffer_entry.h" +#include +#include +#include +struct __buffer_manager_t +{ + cmph_uint32 memory_avail; // memory available + buffer_entry_t ** buffer_entries; // buffer entries to be managed + cmph_uint32 nentries; // number of entries to be managed + cmph_uint32 *memory_avail_list; // memory available list + int pos_avail_list; // current position in memory available list +}; + +buffer_manager_t * buffer_manager_new(cmph_uint32 memory_avail, cmph_uint32 nentries) +{ + cmph_uint32 memory_avail_entry, i; + buffer_manager_t *buff_manager = (buffer_manager_t *)malloc(sizeof(buffer_manager_t)); + assert(buff_manager); + buff_manager->memory_avail = memory_avail; + buff_manager->buffer_entries = (buffer_entry_t **)calloc((size_t)nentries, sizeof(buffer_entry_t *)); + buff_manager->memory_avail_list = (cmph_uint32 *)calloc((size_t)nentries, sizeof(cmph_uint32)); + buff_manager->pos_avail_list = -1; + buff_manager->nentries = nentries; + memory_avail_entry = buff_manager->memory_avail/buff_manager->nentries + 1; + for(i = 0; i < buff_manager->nentries; i++) + { + buff_manager->buffer_entries[i] = buffer_entry_new(memory_avail_entry); + } + return buff_manager; +} + +void buffer_manager_open(buffer_manager_t * buffer_manager, cmph_uint32 index, char * filename) +{ + buffer_entry_open(buffer_manager->buffer_entries[index], filename); +} + +cmph_uint8 * buffer_manager_read_key(buffer_manager_t * buffer_manager, cmph_uint32 index, cmph_uint32 * keylen) +{ + cmph_uint8 * key = NULL; + if (buffer_manager->pos_avail_list >= 0 ) // recovering memory + { + cmph_uint32 new_capacity = buffer_entry_get_capacity(buffer_manager->buffer_entries[index]) + buffer_manager->memory_avail_list[(buffer_manager->pos_avail_list)--]; + buffer_entry_set_capacity(buffer_manager->buffer_entries[index], new_capacity); + } + key = buffer_entry_read_key(buffer_manager->buffer_entries[index], keylen); + if (key == NULL) // storing memory to be recovered + { + buffer_manager->memory_avail_list[++(buffer_manager->pos_avail_list)] = buffer_entry_get_capacity(buffer_manager->buffer_entries[index]); + } + return key; +} + +void buffer_manager_destroy(buffer_manager_t * buffer_manager) +{ + cmph_uint32 i; + for(i = 0; i < buffer_manager->nentries; i++) + { + buffer_entry_destroy(buffer_manager->buffer_entries[i]); + } + free(buffer_manager->memory_avail_list); + free(buffer_manager->buffer_entries); + free(buffer_manager); +} diff --git a/girepository/cmph/buffer_manager.h b/girepository/cmph/buffer_manager.h new file mode 100644 index 000000000..af99c20f6 --- /dev/null +++ b/girepository/cmph/buffer_manager.h @@ -0,0 +1,12 @@ +#ifndef __CMPH_BUFFER_MANAGE_H__ +#define __CMPH_BUFFER_MANAGE_H__ + +#include "cmph_types.h" +#include +typedef struct __buffer_manager_t buffer_manager_t; + +buffer_manager_t * buffer_manager_new(cmph_uint32 memory_avail, cmph_uint32 nentries); +void buffer_manager_open(buffer_manager_t * buffer_manager, cmph_uint32 index, char * filename); +cmph_uint8 * buffer_manager_read_key(buffer_manager_t * buffer_manager, cmph_uint32 index, cmph_uint32 * keylen); +void buffer_manager_destroy(buffer_manager_t * buffer_manager); +#endif diff --git a/girepository/cmph/chd.c b/girepository/cmph/chd.c new file mode 100644 index 000000000..46aec52db --- /dev/null +++ b/girepository/cmph/chd.c @@ -0,0 +1,280 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cmph_structs.h" +#include "chd_structs.h" +#include "chd.h" +#include "bitbool.h" +//#define DEBUG +#include "debug.h" + +chd_config_data_t *chd_config_new(cmph_config_t *mph) +{ + cmph_io_adapter_t *key_source = mph->key_source; + chd_config_data_t *chd; + chd = (chd_config_data_t *)malloc(sizeof(chd_config_data_t)); + assert(chd); + memset(chd, 0, sizeof(chd_config_data_t)); + + chd->chd_ph = cmph_config_new(key_source); + cmph_config_set_algo(chd->chd_ph, CMPH_CHD_PH); + + return chd; +} + +void chd_config_destroy(cmph_config_t *mph) +{ + chd_config_data_t *data = (chd_config_data_t *) mph->data; + DEBUGP("Destroying algorithm dependent data\n"); + if(data->chd_ph) + { + cmph_config_destroy(data->chd_ph); + data->chd_ph = NULL; + } + free(data); +} + + +void chd_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + chd_config_data_t *data = (chd_config_data_t *) mph->data; + cmph_config_set_hashfuncs(data->chd_ph, hashfuncs); +} + + +void chd_config_set_b(cmph_config_t *mph, cmph_uint32 keys_per_bucket) +{ + chd_config_data_t *data = (chd_config_data_t *) mph->data; + cmph_config_set_b(data->chd_ph, keys_per_bucket); +} + + +void chd_config_set_keys_per_bin(cmph_config_t *mph, cmph_uint32 keys_per_bin) +{ + chd_config_data_t *data = (chd_config_data_t *) mph->data; + cmph_config_set_keys_per_bin(data->chd_ph, keys_per_bin); +} + + +cmph_t *chd_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + chd_data_t *chdf = NULL; + chd_config_data_t *chd = (chd_config_data_t *)mph->data; + chd_ph_config_data_t * chd_ph = (chd_ph_config_data_t *)chd->chd_ph->data; + compressed_rank_t cr; + + register cmph_t * chd_phf = NULL; + register cmph_uint32 packed_chd_phf_size = 0; + cmph_uint8 * packed_chd_phf = NULL; + + register cmph_uint32 packed_cr_size = 0; + cmph_uint8 * packed_cr = NULL; + + register cmph_uint32 i, idx, nkeys, nvals, nbins; + cmph_uint32 * vals_table = NULL; + register cmph_uint32 * occup_table = NULL; + #ifdef CMPH_TIMING + double construction_time_begin = 0.0; + double construction_time = 0.0; + ELAPSED_TIME_IN_SECONDS(&construction_time_begin); + #endif + + cmph_config_set_verbosity(chd->chd_ph, mph->verbosity); + cmph_config_set_graphsize(chd->chd_ph, c); + + if (mph->verbosity) + { + fprintf(stderr, "Generating a CHD_PH perfect hash function with a load factor equal to %.3f\n", c); + } + + chd_phf = cmph_new(chd->chd_ph); + + if(chd_phf == NULL) + { + return NULL; + } + + packed_chd_phf_size = cmph_packed_size(chd_phf); + DEBUGP("packed_chd_phf_size = %u\n", packed_chd_phf_size); + + /* Make sure that we have enough space to pack the mphf. */ + packed_chd_phf = calloc((size_t)packed_chd_phf_size,(size_t)1); + + /* Pack the mphf. */ + cmph_pack(chd_phf, packed_chd_phf); + + cmph_destroy(chd_phf); + + + if (mph->verbosity) + { + fprintf(stderr, "Compressing the range of the resulting CHD_PH perfect hash function\n"); + } + + compressed_rank_init(&cr); + nbins = chd_ph->n; + nkeys = chd_ph->m; + nvals = nbins - nkeys; + + vals_table = (cmph_uint32 *)calloc(nvals, sizeof(cmph_uint32)); + occup_table = (cmph_uint32 *)chd_ph->occup_table; + + for(i = 0, idx = 0; i < nbins; i++) + { + if(!GETBIT32(occup_table, i)) + { + vals_table[idx++] = i; + } + } + + compressed_rank_generate(&cr, vals_table, nvals); + free(vals_table); + + packed_cr_size = compressed_rank_packed_size(&cr); + packed_cr = (cmph_uint8 *) calloc(packed_cr_size, sizeof(cmph_uint8)); + compressed_rank_pack(&cr, packed_cr); + compressed_rank_destroy(&cr); + + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + chdf = (chd_data_t *)malloc(sizeof(chd_data_t)); + + chdf->packed_cr = packed_cr; + packed_cr = NULL; //transfer memory ownership + + chdf->packed_chd_phf = packed_chd_phf; + packed_chd_phf = NULL; //transfer memory ownership + + chdf->packed_chd_phf_size = packed_chd_phf_size; + chdf->packed_cr_size = packed_cr_size; + + mphf->data = chdf; + mphf->size = nkeys; + + DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + #ifdef CMPH_TIMING + ELAPSED_TIME_IN_SECONDS(&construction_time); + register cmph_uint32 space_usage = chd_packed_size(mphf)*8; + construction_time = construction_time - construction_time_begin; + fprintf(stdout, "%u\t%.2f\t%u\t%.4f\t%.4f\n", nkeys, c, chd_ph->keys_per_bucket, construction_time, space_usage/(double)nkeys); + #endif + + return mphf; +} + +void chd_load(FILE *fd, cmph_t *mphf) +{ + register size_t nbytes; + chd_data_t *chd = (chd_data_t *)malloc(sizeof(chd_data_t)); + + DEBUGP("Loading chd mphf\n"); + mphf->data = chd; + + nbytes = fread(&chd->packed_chd_phf_size, sizeof(cmph_uint32), (size_t)1, fd); + DEBUGP("Loading CHD_PH perfect hash function with %u bytes to disk\n", chd->packed_chd_phf_size); + chd->packed_chd_phf = (cmph_uint8 *) calloc((size_t)chd->packed_chd_phf_size,(size_t)1); + nbytes = fread(chd->packed_chd_phf, chd->packed_chd_phf_size, (size_t)1, fd); + + nbytes = fread(&chd->packed_cr_size, sizeof(cmph_uint32), (size_t)1, fd); + DEBUGP("Loading Compressed rank structure, which has %u bytes\n", chd->packed_cr_size); + chd->packed_cr = (cmph_uint8 *) calloc((size_t)chd->packed_cr_size, (size_t)1); + nbytes = fread(chd->packed_cr, chd->packed_cr_size, (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + } + +} + +int chd_dump(cmph_t *mphf, FILE *fd) +{ + register size_t nbytes; + chd_data_t *data = (chd_data_t *)mphf->data; + + __cmph_dump(mphf, fd); + // Dumping CHD_PH perfect hash function + + DEBUGP("Dumping CHD_PH perfect hash function with %u bytes to disk\n", data->packed_chd_phf_size); + nbytes = fwrite(&data->packed_chd_phf_size, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(data->packed_chd_phf, data->packed_chd_phf_size, (size_t)1, fd); + + DEBUGP("Dumping compressed rank structure with %u bytes to disk\n", data->packed_cr_size); + nbytes = fwrite(&data->packed_cr_size, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(data->packed_cr, data->packed_cr_size, (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } + + return 1; +} + +void chd_destroy(cmph_t *mphf) +{ + chd_data_t *data = (chd_data_t *)mphf->data; + free(data->packed_chd_phf); + free(data->packed_cr); + free(data); + free(mphf); +} + +static inline cmph_uint32 _chd_search(void * packed_chd_phf, void * packed_cr, const char *key, cmph_uint32 keylen) +{ + register cmph_uint32 bin_idx = cmph_search_packed(packed_chd_phf, key, keylen); + register cmph_uint32 rank = compressed_rank_query_packed(packed_cr, bin_idx); + return bin_idx - rank; +} + +cmph_uint32 chd_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + register chd_data_t * chd = mphf->data; + return _chd_search(chd->packed_chd_phf, chd->packed_cr, key, keylen); +} + +void chd_pack(cmph_t *mphf, void *packed_mphf) +{ + chd_data_t *data = (chd_data_t *)mphf->data; + cmph_uint32 * ptr = packed_mphf; + cmph_uint8 * ptr8; + + // packing packed_cr_size and packed_cr + *ptr = data->packed_cr_size; + ptr8 = (cmph_uint8 *) (ptr + 1); + + memcpy(ptr8, data->packed_cr, data->packed_cr_size); + ptr8 += data->packed_cr_size; + + ptr = (cmph_uint32 *) ptr8; + *ptr = data->packed_chd_phf_size; + + ptr8 = (cmph_uint8 *) (ptr + 1); + memcpy(ptr8, data->packed_chd_phf, data->packed_chd_phf_size); +} + +cmph_uint32 chd_packed_size(cmph_t *mphf) +{ + register chd_data_t *data = (chd_data_t *)mphf->data; + return (cmph_uint32)(sizeof(CMPH_ALGO) + 2*sizeof(cmph_uint32) + data->packed_cr_size + data->packed_chd_phf_size); + +} + +cmph_uint32 chd_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + + register cmph_uint32 * ptr = packed_mphf; + register cmph_uint32 packed_cr_size = *ptr++; + register cmph_uint8 * packed_chd_phf = ((cmph_uint8 *) ptr) + packed_cr_size + sizeof(cmph_uint32); + return _chd_search(packed_chd_phf, ptr, key, keylen); +} + + diff --git a/girepository/cmph/chd.h b/girepository/cmph/chd.h new file mode 100644 index 000000000..e829df816 --- /dev/null +++ b/girepository/cmph/chd.h @@ -0,0 +1,59 @@ +#ifndef _CMPH_CHD_H__ +#define _CMPH_CHD_H__ + +#include "cmph.h" + +typedef struct __chd_data_t chd_data_t; +typedef struct __chd_config_data_t chd_config_data_t; + +/* Config API */ +chd_config_data_t *chd_config_new(cmph_config_t * mph); +void chd_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); + +/** \fn void chd_config_set_keys_per_bin(cmph_config_t *mph, cmph_uint32 keys_per_bin); + * \brief Allows to set the number of keys per bin. + * \param mph pointer to the configuration structure + * \param keys_per_bin value for the number of keys per bin + */ +void chd_config_set_keys_per_bin(cmph_config_t *mph, cmph_uint32 keys_per_bin); + +/** \fn void chd_config_set_b(cmph_config_t *mph, cmph_uint32 keys_per_bucket); + * \brief Allows to set the number of keys per bucket. + * \param mph pointer to the configuration structure + * \param keys_per_bucket value for the number of keys per bucket + */ +void chd_config_set_b(cmph_config_t *mph, cmph_uint32 keys_per_bucket); +void chd_config_destroy(cmph_config_t *mph); + + +/* Chd algorithm API */ +cmph_t *chd_new(cmph_config_t *mph, double c); +void chd_load(FILE *fd, cmph_t *mphf); +int chd_dump(cmph_t *mphf, FILE *fd); +void chd_destroy(cmph_t *mphf); +cmph_uint32 chd_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +/** \fn void chd_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void chd_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 chd_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 chd_packed_size(cmph_t *mphf); + +/** cmph_uint32 chd_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 chd_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +#endif diff --git a/girepository/cmph/chd_ph.c b/girepository/cmph/chd_ph.c new file mode 100644 index 000000000..ae986b425 --- /dev/null +++ b/girepository/cmph/chd_ph.c @@ -0,0 +1,1001 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cmph_structs.h" +#include "chd_structs_ph.h" +#include "chd_ph.h" +#include"miller_rabin.h" +#include"bitbool.h" + + +//#define DEBUG +#include "debug.h" + +// NO_ELEMENT is equivalent to null pointer +#ifndef NO_ELEMENT +#define NO_ELEMENT UINT_MAX +#endif + +// struct used to represent items at mapping, ordering and searching phases +struct _chd_ph_item_t +{ + cmph_uint32 f; + cmph_uint32 h; +}; +typedef struct _chd_ph_item_t chd_ph_item_t; + +// struct to represent the items at mapping phase only. +struct _chd_ph_map_item_t +{ + cmph_uint32 f; + cmph_uint32 h; + cmph_uint32 bucket_num; +}; +typedef struct _chd_ph_map_item_t chd_ph_map_item_t; + +// struct to represent a bucket +struct _chd_ph_bucket_t +{ + cmph_uint32 items_list; // offset + union + { + cmph_uint32 size; + cmph_uint32 bucket_id; + }; +}; + +typedef struct _chd_ph_bucket_t chd_ph_bucket_t; + +struct _chd_ph_sorted_list_t +{ + cmph_uint32 buckets_list; + cmph_uint32 size; +}; + +typedef struct _chd_ph_sorted_list_t chd_ph_sorted_list_t; + + +static inline chd_ph_bucket_t * chd_ph_bucket_new(cmph_uint32 nbuckets); +static inline void chd_ph_bucket_clean(chd_ph_bucket_t * buckets, cmph_uint32 nbuckets); +static inline void chd_ph_bucket_destroy(chd_ph_bucket_t * buckets); + +chd_ph_bucket_t * chd_ph_bucket_new(cmph_uint32 nbuckets) +{ + chd_ph_bucket_t * buckets = (chd_ph_bucket_t *) calloc(nbuckets, sizeof(chd_ph_bucket_t)); + return buckets; +} + +void chd_ph_bucket_clean(chd_ph_bucket_t * buckets, cmph_uint32 nbuckets) +{ + register cmph_uint32 i = 0; + assert(buckets); + for(i = 0; i < nbuckets; i++) + buckets[i].size = 0; +} +static cmph_uint8 chd_ph_bucket_insert(chd_ph_bucket_t * buckets,chd_ph_map_item_t * map_items, chd_ph_item_t * items, + cmph_uint32 nbuckets,cmph_uint32 item_idx) +{ + register cmph_uint32 i = 0; + register chd_ph_item_t * tmp_item; + register chd_ph_map_item_t * tmp_map_item = map_items + item_idx; + register chd_ph_bucket_t * bucket = buckets + tmp_map_item->bucket_num; + tmp_item = items + bucket->items_list; + + for(i = 0; i < bucket->size; i++) + { + if(tmp_item->f == tmp_map_item->f && tmp_item->h == tmp_map_item->h) + { + DEBUGP("Item not added\n"); + return 0; + }; + tmp_item++; + }; + tmp_item->f = tmp_map_item->f; + tmp_item->h = tmp_map_item->h; + bucket->size++; + return 1; +}; +void chd_ph_bucket_destroy(chd_ph_bucket_t * buckets) +{ + free(buckets); +} + +static inline cmph_uint8 chd_ph_mapping(cmph_config_t *mph, chd_ph_bucket_t * buckets, chd_ph_item_t * items, + cmph_uint32 *max_bucket_size); + +static chd_ph_sorted_list_t * chd_ph_ordering(chd_ph_bucket_t ** _buckets,chd_ph_item_t ** items, + cmph_uint32 nbuckets,cmph_uint32 nitems, cmph_uint32 max_bucket_size); + +static cmph_uint8 chd_ph_searching(chd_ph_config_data_t *chd_ph, chd_ph_bucket_t *buckets, chd_ph_item_t *items , + cmph_uint32 max_bucket_size, chd_ph_sorted_list_t *sorted_lists, cmph_uint32 max_probes, cmph_uint32 * disp_table); + +static inline double chd_ph_space_lower_bound(cmph_uint32 _n, cmph_uint32 _r) +{ + double r = _r, n = _n; + return (1 + (r/n - 1.0 + 1.0/(2.0*n))*log(1 - n/r))/log(2); +}; + +/* computes the entropy of non empty buckets.*/ +static inline double chd_ph_get_entropy(cmph_uint32 * disp_table, cmph_uint32 n, cmph_uint32 max_probes) +{ + register cmph_uint32 * probe_counts = (cmph_uint32 *) calloc(max_probes, sizeof(cmph_uint32)); + register cmph_uint32 i; + register double entropy = 0; + + for(i = 0; i < n; i++) + { + probe_counts[disp_table[i]]++; + }; + + for(i = 0; i < max_probes; i++) + { + if(probe_counts[i] > 0) + entropy -= probe_counts[i]*log((double)probe_counts[i]/(double)n)/log(2); + }; + free(probe_counts); + return entropy; +}; + +chd_ph_config_data_t *chd_ph_config_new(void) +{ + chd_ph_config_data_t *chd_ph; + chd_ph = (chd_ph_config_data_t *)malloc(sizeof(chd_ph_config_data_t)); + assert(chd_ph); + memset(chd_ph, 0, sizeof(chd_ph_config_data_t)); + + chd_ph->hashfunc = CMPH_HASH_JENKINS; + chd_ph->cs = NULL; + chd_ph->nbuckets = 0; + chd_ph->n = 0; + chd_ph->hl = NULL; + + chd_ph->m = 0; + chd_ph->use_h = 1; + chd_ph->keys_per_bin = 1; + chd_ph->keys_per_bucket = 4; + chd_ph->occup_table = 0; + + return chd_ph; +} + +void chd_ph_config_destroy(cmph_config_t *mph) +{ + chd_ph_config_data_t *data = (chd_ph_config_data_t *) mph->data; + DEBUGP("Destroying algorithm dependent data\n"); + if(data->occup_table) + { + free(data->occup_table); + data->occup_table = NULL; + } + free(data); +} + + +void chd_ph_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + chd_ph_config_data_t *chd_ph = (chd_ph_config_data_t *)mph->data; + CMPH_HASH *hashptr = hashfuncs; + cmph_uint32 i = 0; + while(*hashptr != CMPH_HASH_COUNT) + { + if (i >= 1) break; //chd_ph only uses one linear hash function + chd_ph->hashfunc = *hashptr; + ++i, ++hashptr; + } +} + + +void chd_ph_config_set_b(cmph_config_t *mph, cmph_uint32 keys_per_bucket) +{ + chd_ph_config_data_t *chd_ph; + assert(mph); + chd_ph = (chd_ph_config_data_t *)mph->data; + if(keys_per_bucket < 1 || keys_per_bucket >= 15) + { + keys_per_bucket = 4; + } + chd_ph->keys_per_bucket = keys_per_bucket; +} + + +void chd_ph_config_set_keys_per_bin(cmph_config_t *mph, cmph_uint32 keys_per_bin) +{ + chd_ph_config_data_t *chd_ph; + assert(mph); + chd_ph = (chd_ph_config_data_t *)mph->data; + if(keys_per_bin <= 1 || keys_per_bin >= 128) + { + keys_per_bin = 1; + } + chd_ph->keys_per_bin = keys_per_bin; +} + +cmph_uint8 chd_ph_mapping(cmph_config_t *mph, chd_ph_bucket_t * buckets, chd_ph_item_t * items, cmph_uint32 *max_bucket_size) +{ + register cmph_uint32 i = 0, g = 0; + cmph_uint32 hl[3]; + chd_ph_config_data_t *chd_ph = (chd_ph_config_data_t *)mph->data; + char * key = NULL; + cmph_uint32 keylen = 0; + chd_ph_map_item_t * map_item; + chd_ph_map_item_t * map_items = malloc(chd_ph->m*sizeof(chd_ph_map_item_t)); + register cmph_uint32 mapping_iterations = 1000; + *max_bucket_size = 0; + while(1) + { + mapping_iterations--; + if (chd_ph->hl) hash_state_destroy(chd_ph->hl); + chd_ph->hl = hash_state_new(chd_ph->hashfunc, chd_ph->m); + + chd_ph_bucket_clean(buckets, chd_ph->nbuckets); + + mph->key_source->rewind(mph->key_source->data); + + for(i = 0; i < chd_ph->m; i++) + { + mph->key_source->read(mph->key_source->data, &key, &keylen); + hash_vector(chd_ph->hl, key, keylen, hl); + + map_item = (map_items + i); + + g = hl[0] % chd_ph->nbuckets; + map_item->f = hl[1] % chd_ph->n; + map_item->h = hl[2] % (chd_ph->n - 1) + 1; + map_item->bucket_num=g; + mph->key_source->dispose(mph->key_source->data, key, keylen); +// if(buckets[g].size == (chd_ph->keys_per_bucket << 2)) +// { +// DEBUGP("BUCKET = %u -- SIZE = %u -- MAXIMUM SIZE = %u\n", g, buckets[g].size, (chd_ph->keys_per_bucket << 2)); +// goto error; +// } + buckets[g].size++; + if(buckets[g].size > *max_bucket_size) + { + *max_bucket_size = buckets[g].size; + } + } + buckets[0].items_list = 0; + for(i = 1; i < chd_ph->nbuckets; i++) + { + buckets[i].items_list = buckets[i-1].items_list + buckets[i - 1].size; + buckets[i - 1].size = 0; + }; + buckets[i - 1].size = 0; + for(i = 0; i < chd_ph->m; i++) + { + map_item = (map_items + i); + if(!chd_ph_bucket_insert(buckets, map_items, items, chd_ph->nbuckets, i)) + break; + } + if(i == chd_ph->m) + { + free(map_items); + return 1; // SUCCESS + } + + if(mapping_iterations == 0) + { + goto error; + } + } +error: + free(map_items); + hash_state_destroy(chd_ph->hl); + chd_ph->hl = NULL; + return 0; // FAILURE +} + +chd_ph_sorted_list_t * chd_ph_ordering(chd_ph_bucket_t ** _buckets, chd_ph_item_t ** _items, + cmph_uint32 nbuckets, cmph_uint32 nitems, cmph_uint32 max_bucket_size) +{ + chd_ph_sorted_list_t * sorted_lists = (chd_ph_sorted_list_t *) calloc(max_bucket_size + 1, sizeof(chd_ph_sorted_list_t)); + + chd_ph_bucket_t * input_buckets = (*_buckets); + chd_ph_bucket_t * output_buckets; + chd_ph_item_t * input_items = (*_items); + chd_ph_item_t * output_items; + register cmph_uint32 i, j, bucket_size, position, position2; +// cmph_uint32 non_empty_buckets; + DEBUGP("MAX BUCKET SIZE = %u\n", max_bucket_size); + // Determine size of each list of buckets + for(i = 0; i < nbuckets; i++) + { + bucket_size = input_buckets[i].size; + if(bucket_size == 0) + continue; + sorted_lists[bucket_size].size++; + }; + sorted_lists[1].buckets_list = 0; + // Determine final position of list of buckets into the contiguous array that will store all the buckets + for(i = 2; i <= max_bucket_size; i++) + { + sorted_lists[i].buckets_list = sorted_lists[i-1].buckets_list + sorted_lists[i-1].size; + sorted_lists[i-1].size = 0; + }; + sorted_lists[i-1].size = 0; + // Store the buckets in a new array which is sorted by bucket sizes + output_buckets = calloc(nbuckets, sizeof(chd_ph_bucket_t)); // everything is initialized with zero +// non_empty_buckets = nbuckets; + + for(i = 0; i < nbuckets; i++) + { + bucket_size = input_buckets[i].size; + if(bucket_size == 0) + { +// non_empty_buckets--; + continue; + }; + position = sorted_lists[bucket_size].buckets_list + sorted_lists[bucket_size].size; + output_buckets[position].bucket_id = i; + output_buckets[position].items_list = input_buckets[i].items_list; + sorted_lists[bucket_size].size++; + }; +/* for(i = non_empty_buckets; i < nbuckets; i++) + output_buckets[i].size=0;*/ + // Return the buckets sorted in new order and free the old buckets sorted in old order + free(input_buckets); + (*_buckets) = output_buckets; + + + // Store the items according to the new order of buckets. + output_items = (chd_ph_item_t*)calloc(nitems, sizeof(chd_ph_item_t)); + position = 0; + i = 0; + for(bucket_size = 1; bucket_size <= max_bucket_size; bucket_size++) + { + for(i = sorted_lists[bucket_size].buckets_list; i < sorted_lists[bucket_size].size + sorted_lists[bucket_size].buckets_list; i++) + { + position2 = output_buckets[i].items_list; + output_buckets[i].items_list = position; + for(j = 0; j < bucket_size; j++) + { + output_items[position].f = input_items[position2].f; + output_items[position].h = input_items[position2].h; + position++; + position2++; + }; + }; + }; + //Return the items sorted in new order and free the old items sorted in old order + free(input_items); + (*_items) = output_items; + return sorted_lists; +}; + +static inline cmph_uint8 place_bucket_probe(chd_ph_config_data_t *chd_ph, chd_ph_bucket_t *buckets, + chd_ph_item_t *items, cmph_uint32 probe0_num, cmph_uint32 probe1_num, + cmph_uint32 bucket_num, cmph_uint32 size) +{ + register cmph_uint32 i; + register chd_ph_item_t * item; + register cmph_uint32 position; + + item = items + buckets[bucket_num].items_list; + // try place bucket with probe_num + if(chd_ph->keys_per_bin > 1) + { + for(i = 0; i < size; i++) // placement + { + position = (cmph_uint32)((item->f + ((cmph_uint64)item->h)*probe0_num + probe1_num) % chd_ph->n); + if(chd_ph->occup_table[position] >= chd_ph->keys_per_bin) + { + break; + } + (chd_ph->occup_table[position])++; + item++; + }; + } else + { + for(i = 0; i < size; i++) // placement + { + position = (cmph_uint32)((item->f + ((cmph_uint64)item->h)*probe0_num + probe1_num) % chd_ph->n); + if(GETBIT32(((cmph_uint32 *)chd_ph->occup_table), position)) + { + break; + } + SETBIT32(((cmph_uint32*)chd_ph->occup_table), position); + item++; + }; + }; + if(i != size) // Undo the placement + { + item = items + buckets[bucket_num].items_list; + if(chd_ph->keys_per_bin > 1) + { + while(1) + { + if(i == 0) + { + break; + } + position = (cmph_uint32)((item->f + ((cmph_uint64 )item->h) * probe0_num + probe1_num) % chd_ph->n); + (chd_ph->occup_table[position])--; + item++; + i--; + }; + } else + { + while(1) + { + if(i == 0) + { + break; + } + position = (cmph_uint32)((item->f + ((cmph_uint64 )item->h) * probe0_num + probe1_num) % chd_ph->n); + UNSETBIT32(((cmph_uint32*)chd_ph->occup_table), position); + +// ([position/32]^=(1<<(position%32)); + item++; + i--; + }; + }; + return 0; + } + return 1; +}; + +static inline cmph_uint8 place_bucket(chd_ph_config_data_t *chd_ph, chd_ph_bucket_t *buckets, chd_ph_item_t * items, cmph_uint32 max_probes, + cmph_uint32 * disp_table, cmph_uint32 bucket_num, cmph_uint32 size) + +{ + register cmph_uint32 probe0_num, probe1_num, probe_num; + probe0_num = 0; + probe1_num = 0; + probe_num = 0; + + while(1) + { + if(place_bucket_probe(chd_ph, buckets, items, probe0_num, probe1_num, bucket_num,size)) + { + disp_table[buckets[bucket_num].bucket_id] = probe0_num + probe1_num * chd_ph->n; + return 1; + } + probe0_num++; + if(probe0_num >= chd_ph->n) + { + probe0_num -= chd_ph->n; + probe1_num++; + }; + probe_num++; + if(probe_num >= max_probes || probe1_num >= chd_ph->n) + { + return 0; + }; + }; + return 0; +}; + +static inline cmph_uint8 place_buckets1(chd_ph_config_data_t *chd_ph, chd_ph_bucket_t * buckets, chd_ph_item_t *items, + cmph_uint32 max_bucket_size, chd_ph_sorted_list_t *sorted_lists, cmph_uint32 max_probes, + cmph_uint32 * disp_table) +{ + register cmph_uint32 i = 0; + register cmph_uint32 curr_bucket = 0; + + for(i = max_bucket_size; i > 0; i--) + { + curr_bucket = sorted_lists[i].buckets_list; + while(curr_bucket < sorted_lists[i].size + sorted_lists[i].buckets_list) + { + if(!place_bucket(chd_ph, buckets, items, max_probes, disp_table, curr_bucket, i)) + { + return 0; + } + curr_bucket++; + }; + }; + return 1; +}; + +static inline cmph_uint8 place_buckets2(chd_ph_config_data_t *chd_ph, chd_ph_bucket_t *buckets, chd_ph_item_t * items, + cmph_uint32 max_bucket_size, chd_ph_sorted_list_t *sorted_lists, cmph_uint32 max_probes, + cmph_uint32 * disp_table) +{ + register cmph_uint32 i,j, non_placed_bucket; + register cmph_uint32 curr_bucket; + register cmph_uint32 probe_num, probe0_num, probe1_num; + cmph_uint32 sorted_list_size; +#ifdef DEBUG + cmph_uint32 items_list; + cmph_uint32 bucket_id; +#endif + DEBUGP("USING HEURISTIC TO PLACE BUCKETS\n"); + for(i = max_bucket_size; i > 0; i--) + { + probe_num = 0; + probe0_num = 0; + probe1_num = 0; + sorted_list_size = sorted_lists[i].size; + while(sorted_lists[i].size != 0) + { + curr_bucket = sorted_lists[i].buckets_list; + for(j = 0, non_placed_bucket = 0; j < sorted_lists[i].size; j++) + { + // if bucket is successfully placed remove it from list + if(place_bucket_probe(chd_ph, buckets, items, probe0_num, probe1_num, curr_bucket, i)) + { + disp_table[buckets[curr_bucket].bucket_id] = probe0_num + probe1_num * chd_ph->n; +// DEBUGP("BUCKET %u PLACED --- DISPLACEMENT = %u\n", curr_bucket, disp_table[curr_bucket]); + } + else + { +// DEBUGP("BUCKET %u NOT PLACED\n", curr_bucket); +#ifdef DEBUG + items_list = buckets[non_placed_bucket + sorted_lists[i].buckets_list].items_list; + bucket_id = buckets[non_placed_bucket + sorted_lists[i].buckets_list].bucket_id; +#endif + buckets[non_placed_bucket + sorted_lists[i].buckets_list].items_list = buckets[curr_bucket].items_list; + buckets[non_placed_bucket + sorted_lists[i].buckets_list].bucket_id = buckets[curr_bucket].bucket_id; +#ifdef DEBUG + buckets[curr_bucket].items_list=items_list; + buckets[curr_bucket].bucket_id=bucket_id; +#endif + non_placed_bucket++; + } + curr_bucket++; + }; + sorted_lists[i].size = non_placed_bucket; + probe0_num++; + if(probe0_num >= chd_ph->n) + { + probe0_num -= chd_ph->n; + probe1_num++; + }; + probe_num++; + if(probe_num >= max_probes || probe1_num >= chd_ph->n) + { + sorted_lists[i].size = sorted_list_size; + return 0; + }; + }; + sorted_lists[i].size = sorted_list_size; + }; + return 1; +}; + +cmph_uint8 chd_ph_searching(chd_ph_config_data_t *chd_ph, chd_ph_bucket_t *buckets, chd_ph_item_t *items , + cmph_uint32 max_bucket_size, chd_ph_sorted_list_t *sorted_lists, cmph_uint32 max_probes, + cmph_uint32 * disp_table) +{ + if(chd_ph->use_h) + { + return place_buckets2(chd_ph, buckets, items, max_bucket_size, sorted_lists, max_probes, disp_table); + } + else + { + return place_buckets1(chd_ph, buckets, items, max_bucket_size, sorted_lists, max_probes, disp_table); + } + +} + +static inline cmph_uint8 chd_ph_check_bin_hashing(chd_ph_config_data_t *chd_ph, chd_ph_bucket_t *buckets, chd_ph_item_t *items, + cmph_uint32 * disp_table, chd_ph_sorted_list_t * sorted_lists,cmph_uint32 max_bucket_size) +{ + register cmph_uint32 bucket_size, i, j; + register cmph_uint32 position, probe0_num, probe1_num; + G_GNUC_UNUSED register cmph_uint32 m = 0; + register chd_ph_item_t * item; + if(chd_ph->keys_per_bin > 1) + memset(chd_ph->occup_table, 0, chd_ph->n); + else + memset(chd_ph->occup_table, 0, ((chd_ph->n + 31)/32) * sizeof(cmph_uint32)); + + for(bucket_size = 1; bucket_size <= max_bucket_size; bucket_size++) + for(i = sorted_lists[bucket_size].buckets_list; i < sorted_lists[bucket_size].size + + sorted_lists[bucket_size].buckets_list; i++) + { + j = bucket_size; + item = items + buckets[i].items_list; + probe0_num = disp_table[buckets[i].bucket_id] % chd_ph->n; + probe1_num = disp_table[buckets[i].bucket_id] / chd_ph->n; + for(; j > 0; j--) + { +#ifdef DEBUG + m++; +#endif + position = (cmph_uint32)((item->f + ((cmph_uint64 )item->h) * probe0_num + probe1_num) % chd_ph->n); + if(chd_ph->keys_per_bin > 1) + { + if(chd_ph->occup_table[position] >= chd_ph->keys_per_bin) + { + return 0; + } + (chd_ph->occup_table[position])++; + } + else + { + if(GETBIT32(((cmph_uint32*)chd_ph->occup_table), position)) + { + return 0; + } + SETBIT32(((cmph_uint32*)chd_ph->occup_table), position); + }; + item++; + }; + }; + DEBUGP("We were able to place m = %u keys\n", m); + return 1; +}; + + +cmph_t *chd_ph_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + chd_ph_data_t *chd_phf = NULL; + chd_ph_config_data_t *chd_ph = (chd_ph_config_data_t *)mph->data; + + register double load_factor = c; + register cmph_uint8 searching_success = 0; + register cmph_uint32 max_probes = 1 << 20; // default value for max_probes + register cmph_uint32 iterations = 100; + chd_ph_bucket_t * buckets = NULL; + chd_ph_item_t * items = NULL; + register cmph_uint8 failure = 0; + cmph_uint32 max_bucket_size = 0; + chd_ph_sorted_list_t * sorted_lists = NULL; + cmph_uint32 * disp_table = NULL; + register double space_lower_bound = 0; + #ifdef CMPH_TIMING + double construction_time_begin = 0.0; + double construction_time = 0.0; + ELAPSED_TIME_IN_SECONDS(&construction_time_begin); + #endif + + + chd_ph->m = mph->key_source->nkeys; + DEBUGP("m = %u\n", chd_ph->m); + + chd_ph->nbuckets = (cmph_uint32)(chd_ph->m/chd_ph->keys_per_bucket) + 1; + DEBUGP("nbuckets = %u\n", chd_ph->nbuckets); + + if(load_factor < 0.5 ) + { + load_factor = 0.5; + } + + if(load_factor >= 0.99) + { + load_factor = 0.99; + } + + DEBUGP("load_factor = %.3f\n", load_factor); + + chd_ph->n = (cmph_uint32)(chd_ph->m/(chd_ph->keys_per_bin * load_factor)) + 1; + + //Round the number of bins to the prime immediately above + if(chd_ph->n % 2 == 0) chd_ph->n++; + for(;;) + { + if(check_primality(chd_ph->n) == 1) + break; + chd_ph->n += 2; // just odd numbers can be primes for n > 2 + + }; + + DEBUGP("n = %u \n", chd_ph->n); + if(chd_ph->keys_per_bin == 1) + { + space_lower_bound = chd_ph_space_lower_bound(chd_ph->m, chd_ph->n); + } + + if(mph->verbosity) + { + fprintf(stderr, "space lower bound is %.3f bits per key\n", space_lower_bound); + } + + // We allocate the working tables + buckets = chd_ph_bucket_new(chd_ph->nbuckets); + items = (chd_ph_item_t *) calloc(chd_ph->m, sizeof(chd_ph_item_t)); + + max_probes = (cmph_uint32)(((log(chd_ph->m)/log(2))/20) * max_probes); + + if(chd_ph->keys_per_bin == 1) + chd_ph->occup_table = (cmph_uint8 *) calloc(((chd_ph->n + 31)/32), sizeof(cmph_uint32)); + else + chd_ph->occup_table = (cmph_uint8 *) calloc(chd_ph->n, sizeof(cmph_uint8)); + + disp_table = (cmph_uint32 *) calloc(chd_ph->nbuckets, sizeof(cmph_uint32)); +// +// init_genrand(time(0)); + + while(1) + { + iterations --; + if (mph->verbosity) + { + fprintf(stderr, "Starting mapping step for mph creation of %u keys with %u bins\n", chd_ph->m, chd_ph->n); + } + + if(!chd_ph_mapping(mph, buckets, items, &max_bucket_size)) + { + if (mph->verbosity) + { + fprintf(stderr, "Failure in mapping step\n"); + } + failure = 1; + goto cleanup; + } + + if (mph->verbosity) + { + fprintf(stderr, "Starting ordering step\n"); + } + if(sorted_lists) + { + free(sorted_lists); + } + + sorted_lists = chd_ph_ordering(&buckets, &items, chd_ph->nbuckets, chd_ph->m, max_bucket_size); + + if (mph->verbosity) + { + fprintf(stderr, "Starting searching step\n"); + } + + searching_success = chd_ph_searching(chd_ph, buckets, items, max_bucket_size, sorted_lists, max_probes, disp_table); + if(searching_success) break; + + // reset occup_table + if(chd_ph->keys_per_bin > 1) + memset(chd_ph->occup_table, 0, chd_ph->n); + else + memset(chd_ph->occup_table, 0, ((chd_ph->n + 31)/32) * sizeof(cmph_uint32)); + if(iterations == 0) + { + // Cleanup memory + if (mph->verbosity) + { + fprintf(stderr, "Failure because the max trials was exceeded\n"); + } + failure = 1; + goto cleanup; + }; + } + + #ifdef DEBUG + { + if(!chd_ph_check_bin_hashing(chd_ph, buckets, items, disp_table,sorted_lists,max_bucket_size)) + { + + DEBUGP("Error for bin packing generation"); + failure = 1; + goto cleanup; + } + } + #endif + + if (mph->verbosity) + { + fprintf(stderr, "Starting compressing step\n"); + } + + if(chd_ph->cs) + { + free(chd_ph->cs); + } + chd_ph->cs = (compressed_seq_t *) calloc(1, sizeof(compressed_seq_t)); + compressed_seq_init(chd_ph->cs); + compressed_seq_generate(chd_ph->cs, disp_table, chd_ph->nbuckets); + + #ifdef CMPH_TIMING + ELAPSED_TIME_IN_SECONDS(&construction_time); + register double entropy = chd_ph_get_entropy(disp_table, chd_ph->nbuckets, max_probes); + DEBUGP("Entropy = %.4f\n", entropy/chd_ph->m); + #endif + +cleanup: + chd_ph_bucket_destroy(buckets); + free(items); + free(sorted_lists); + free(disp_table); + if(failure) + { + if(chd_ph->hl) + { + hash_state_destroy(chd_ph->hl); + } + chd_ph->hl = NULL; + return NULL; + } + + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + chd_phf = (chd_ph_data_t *)malloc(sizeof(chd_ph_data_t)); + + chd_phf->cs = chd_ph->cs; + chd_ph->cs = NULL; //transfer memory ownership + chd_phf->hl = chd_ph->hl; + chd_ph->hl = NULL; //transfer memory ownership + chd_phf->n = chd_ph->n; + chd_phf->nbuckets = chd_ph->nbuckets; + + mphf->data = chd_phf; + mphf->size = chd_ph->n; + + DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + + #ifdef CMPH_TIMING + register cmph_uint32 space_usage = chd_ph_packed_size(mphf)*8; + construction_time = construction_time - construction_time_begin; + fprintf(stdout, "%u\t%.2f\t%u\t%.4f\t%.4f\t%.4f\t%.4f\n", chd_ph->m, load_factor, chd_ph->keys_per_bucket, construction_time, space_usage/(double)chd_ph->m, space_lower_bound, entropy/chd_ph->m); + #endif + + return mphf; +} + + + +void chd_ph_load(FILE *fd, cmph_t *mphf) +{ + char *buf = NULL; + cmph_uint32 buflen; + register size_t nbytes; + chd_ph_data_t *chd_ph = (chd_ph_data_t *)malloc(sizeof(chd_ph_data_t)); + + DEBUGP("Loading chd_ph mphf\n"); + mphf->data = chd_ph; + + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + DEBUGP("Hash state has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, fd); + chd_ph->hl = hash_state_load(buf, buflen); + free(buf); + + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + DEBUGP("Compressed sequence structure has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, fd); + chd_ph->cs = (compressed_seq_t *) calloc(1, sizeof(compressed_seq_t)); + compressed_seq_load(chd_ph->cs, buf, buflen); + free(buf); + + // loading n and nbuckets + DEBUGP("Reading n and nbuckets\n"); + nbytes = fread(&(chd_ph->n), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fread(&(chd_ph->nbuckets), sizeof(cmph_uint32), (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + } + +} + +int chd_ph_dump(cmph_t *mphf, FILE *fd) +{ + char *buf = NULL; + cmph_uint32 buflen; + register size_t nbytes; + chd_ph_data_t *data = (chd_ph_data_t *)mphf->data; + + __cmph_dump(mphf, fd); + + hash_state_dump(data->hl, &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + compressed_seq_dump(data->cs, &buf, &buflen); + DEBUGP("Dumping compressed sequence structure with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + // dumping n and nbuckets + nbytes = fwrite(&(data->n), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->nbuckets), sizeof(cmph_uint32), (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } + return 1; +} + +void chd_ph_destroy(cmph_t *mphf) +{ + chd_ph_data_t *data = (chd_ph_data_t *)mphf->data; + compressed_seq_destroy(data->cs); + free(data->cs); + hash_state_destroy(data->hl); + free(data); + free(mphf); + +} + +cmph_uint32 chd_ph_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + register chd_ph_data_t * chd_ph = mphf->data; + cmph_uint32 hl[3]; + register cmph_uint32 disp,position; + register cmph_uint32 probe0_num,probe1_num; + register cmph_uint32 f,g,h; + hash_vector(chd_ph->hl, key, keylen, hl); + g = hl[0] % chd_ph->nbuckets; + f = hl[1] % chd_ph->n; + h = hl[2] % (chd_ph->n-1) + 1; + + disp = compressed_seq_query(chd_ph->cs, g); + probe0_num = disp % chd_ph->n; + probe1_num = disp/chd_ph->n; + position = (cmph_uint32)((f + ((cmph_uint64 )h)*probe0_num + probe1_num) % chd_ph->n); + return position; +} + +void chd_ph_pack(cmph_t *mphf, void *packed_mphf) +{ + chd_ph_data_t *data = (chd_ph_data_t *)mphf->data; + cmph_uint8 * ptr = packed_mphf; + + // packing hl type + CMPH_HASH hl_type = hash_get_type(data->hl); + *((cmph_uint32 *) ptr) = hl_type; + ptr += sizeof(cmph_uint32); + + // packing hl + hash_state_pack(data->hl, ptr); + ptr += hash_state_packed_size(hl_type); + + // packing n + *((cmph_uint32 *) ptr) = data->n; + ptr += sizeof(data->n); + + // packing nbuckets + *((cmph_uint32 *) ptr) = data->nbuckets; + ptr += sizeof(data->nbuckets); + + // packing cs + compressed_seq_pack(data->cs, ptr); + //ptr += compressed_seq_packed_size(data->cs); + +} + +cmph_uint32 chd_ph_packed_size(cmph_t *mphf) +{ + register chd_ph_data_t *data = (chd_ph_data_t *)mphf->data; + register CMPH_HASH hl_type = hash_get_type(data->hl); + register cmph_uint32 hash_state_pack_size = hash_state_packed_size(hl_type); + register cmph_uint32 cs_pack_size = compressed_seq_packed_size(data->cs); + + return (cmph_uint32)(sizeof(CMPH_ALGO) + hash_state_pack_size + cs_pack_size + 3*sizeof(cmph_uint32)); + +} + +cmph_uint32 chd_ph_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + register CMPH_HASH hl_type = *(cmph_uint32 *)packed_mphf; + register cmph_uint8 *hl_ptr = (cmph_uint8 *)(packed_mphf) + 4; + + register cmph_uint32 * ptr = (cmph_uint32 *)(hl_ptr + hash_state_packed_size(hl_type)); + register cmph_uint32 n = *ptr++; + register cmph_uint32 nbuckets = *ptr++; + cmph_uint32 hl[3]; + + register cmph_uint32 disp,position; + register cmph_uint32 probe0_num,probe1_num; + register cmph_uint32 f,g,h; + + hash_vector_packed(hl_ptr, hl_type, key, keylen, hl); + + g = hl[0] % nbuckets; + f = hl[1] % n; + h = hl[2] % (n-1) + 1; + + disp = compressed_seq_query_packed(ptr, g); + probe0_num = disp % n; + probe1_num = disp/n; + position = (cmph_uint32)((f + ((cmph_uint64 )h)*probe0_num + probe1_num) % n); + return position; +} + + + diff --git a/girepository/cmph/chd_ph.h b/girepository/cmph/chd_ph.h new file mode 100644 index 000000000..03e4087c9 --- /dev/null +++ b/girepository/cmph/chd_ph.h @@ -0,0 +1,59 @@ +#ifndef _CMPH_CHD_PH_H__ +#define _CMPH_CHD_PH_H__ + +#include "cmph.h" + +typedef struct __chd_ph_data_t chd_ph_data_t; +typedef struct __chd_ph_config_data_t chd_ph_config_data_t; + +/* Config API */ +chd_ph_config_data_t *chd_ph_config_new(void); +void chd_ph_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); + +/** \fn void chd_ph_config_set_keys_per_bin(cmph_config_t *mph, cmph_uint32 keys_per_bin); + * \brief Allows to set the number of keys per bin. + * \param mph pointer to the configuration structure + * \param keys_per_bin value for the number of keys per bin + */ +void chd_ph_config_set_keys_per_bin(cmph_config_t *mph, cmph_uint32 keys_per_bin); + +/** \fn void chd_ph_config_set_b(cmph_config_t *mph, cmph_uint32 keys_per_bucket); + * \brief Allows to set the number of keys per bucket. + * \param mph pointer to the configuration structure + * \param keys_per_bucket value for the number of keys per bucket + */ +void chd_ph_config_set_b(cmph_config_t *mph, cmph_uint32 keys_per_bucket); +void chd_ph_config_destroy(cmph_config_t *mph); + + +/* Chd algorithm API */ +cmph_t *chd_ph_new(cmph_config_t *mph, double c); +void chd_ph_load(FILE *fd, cmph_t *mphf); +int chd_ph_dump(cmph_t *mphf, FILE *fd); +void chd_ph_destroy(cmph_t *mphf); +cmph_uint32 chd_ph_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +/** \fn void chd_ph_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void chd_ph_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 chd_ph_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 chd_ph_packed_size(cmph_t *mphf); + +/** cmph_uint32 chd_ph_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 chd_ph_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +#endif diff --git a/girepository/cmph/chd_structs.h b/girepository/cmph/chd_structs.h new file mode 100644 index 000000000..d62f68268 --- /dev/null +++ b/girepository/cmph/chd_structs.h @@ -0,0 +1,21 @@ +#ifndef __CMPH_CHD_STRUCTS_H__ +#define __CMPH_CHD_STRUCTS_H__ + +#include "chd_structs_ph.h" +#include "chd_ph.h" +#include "compressed_rank.h" + +struct __chd_data_t +{ + cmph_uint32 packed_cr_size; + cmph_uint8 * packed_cr; // packed compressed rank structure to control the number of zeros in a bit vector + + cmph_uint32 packed_chd_phf_size; + cmph_uint8 * packed_chd_phf; +}; + +struct __chd_config_data_t +{ + cmph_config_t *chd_ph; // chd_ph algorithm must be used here +}; +#endif diff --git a/girepository/cmph/chd_structs_ph.h b/girepository/cmph/chd_structs_ph.h new file mode 100644 index 000000000..d86921822 --- /dev/null +++ b/girepository/cmph/chd_structs_ph.h @@ -0,0 +1,29 @@ +#ifndef __CMPH_CHD_PH_STRUCTS_H__ +#define __CMPH_CHD_PH_STRUCTS_H__ + +#include "hash_state.h" +#include "compressed_seq.h" + +struct __chd_ph_data_t +{ + compressed_seq_t * cs; // compressed displacement values + cmph_uint32 nbuckets; // number of buckets + cmph_uint32 n; // number of bins + hash_state_t *hl; // linear hash function +}; + +struct __chd_ph_config_data_t +{ + CMPH_HASH hashfunc; // linear hash function to be used + compressed_seq_t * cs; // compressed displacement values + cmph_uint32 nbuckets; // number of buckets + cmph_uint32 n; // number of bins + hash_state_t *hl; // linear hash function + + cmph_uint32 m; // number of keys + cmph_uint8 use_h; // flag to indicate the of use of a heuristic (use_h = 1) + cmph_uint32 keys_per_bin;//maximum number of keys per bin + cmph_uint32 keys_per_bucket; // average number of keys per bucket + cmph_uint8 *occup_table; // table that indicates occupied positions +}; +#endif diff --git a/girepository/cmph/chm.c b/girepository/cmph/chm.c new file mode 100644 index 000000000..36a07a0d3 --- /dev/null +++ b/girepository/cmph/chm.c @@ -0,0 +1,396 @@ +#include "graph.h" +#include "chm.h" +#include "cmph_structs.h" +#include "chm_structs.h" +#include "hash.h" +#include "bitbool.h" + +#include +#include +#include +#include +#include +#include + +//#define DEBUG +#include "debug.h" + +static int chm_gen_edges(cmph_config_t *mph); +static void chm_traverse(chm_config_data_t *chm, cmph_uint8 *visited, cmph_uint32 v); + +chm_config_data_t *chm_config_new(void) +{ + chm_config_data_t *chm = NULL; + chm = (chm_config_data_t *)malloc(sizeof(chm_config_data_t)); + assert(chm); + memset(chm, 0, sizeof(chm_config_data_t)); + chm->hashfuncs[0] = CMPH_HASH_JENKINS; + chm->hashfuncs[1] = CMPH_HASH_JENKINS; + chm->g = NULL; + chm->graph = NULL; + chm->hashes = NULL; + return chm; +} +void chm_config_destroy(cmph_config_t *mph) +{ + chm_config_data_t *data = (chm_config_data_t *)mph->data; + DEBUGP("Destroying algorithm dependent data\n"); + free(data); +} + +void chm_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + chm_config_data_t *chm = (chm_config_data_t *)mph->data; + CMPH_HASH *hashptr = hashfuncs; + cmph_uint32 i = 0; + while(*hashptr != CMPH_HASH_COUNT) + { + if (i >= 2) break; //chm only uses two hash functions + chm->hashfuncs[i] = *hashptr; + ++i, ++hashptr; + } +} + +cmph_t *chm_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + chm_data_t *chmf = NULL; + + cmph_uint32 i; + cmph_uint32 iterations = 20; + cmph_uint8 *visited = NULL; + chm_config_data_t *chm = (chm_config_data_t *)mph->data; + chm->m = mph->key_source->nkeys; + if (c == 0) c = 2.09; + chm->n = (cmph_uint32)ceil(c * mph->key_source->nkeys); + DEBUGP("m (edges): %u n (vertices): %u c: %f\n", chm->m, chm->n, c); + chm->graph = graph_new(chm->n, chm->m); + DEBUGP("Created graph\n"); + + chm->hashes = (hash_state_t **)malloc(sizeof(hash_state_t *)*3); + for(i = 0; i < 3; ++i) chm->hashes[i] = NULL; + //Mapping step + if (mph->verbosity) + { + fprintf(stderr, "Entering mapping step for mph creation of %u keys with graph sized %u\n", chm->m, chm->n); + } + while(1) + { + int ok; + chm->hashes[0] = hash_state_new(chm->hashfuncs[0], chm->n); + chm->hashes[1] = hash_state_new(chm->hashfuncs[1], chm->n); + ok = chm_gen_edges(mph); + if (!ok) + { + --iterations; + hash_state_destroy(chm->hashes[0]); + chm->hashes[0] = NULL; + hash_state_destroy(chm->hashes[1]); + chm->hashes[1] = NULL; + DEBUGP("%u iterations remaining\n", iterations); + if (mph->verbosity) + { + fprintf(stderr, "Acyclic graph creation failure - %u iterations remaining\n", iterations); + } + if (iterations == 0) break; + } + else break; + } + if (iterations == 0) + { + graph_destroy(chm->graph); + return NULL; + } + + //Assignment step + if (mph->verbosity) + { + fprintf(stderr, "Starting assignment step\n"); + } + DEBUGP("Assignment step\n"); + visited = (cmph_uint8 *)malloc((size_t)(chm->n/8 + 1)); + memset(visited, 0, (size_t)(chm->n/8 + 1)); + free(chm->g); + chm->g = (cmph_uint32 *)malloc(chm->n * sizeof(cmph_uint32)); + assert(chm->g); + for (i = 0; i < chm->n; ++i) + { + if (!GETBIT(visited,i)) + { + chm->g[i] = 0; + chm_traverse(chm, visited, i); + } + } + graph_destroy(chm->graph); + free(visited); + chm->graph = NULL; + + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + chmf = (chm_data_t *)malloc(sizeof(chm_data_t)); + chmf->g = chm->g; + chm->g = NULL; //transfer memory ownership + chmf->hashes = chm->hashes; + chm->hashes = NULL; //transfer memory ownership + chmf->n = chm->n; + chmf->m = chm->m; + mphf->data = chmf; + mphf->size = chm->m; + DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + return mphf; +} + +static void chm_traverse(chm_config_data_t *chm, cmph_uint8 *visited, cmph_uint32 v) +{ + + graph_iterator_t it = graph_neighbors_it(chm->graph, v); + cmph_uint32 neighbor = 0; + SETBIT(visited,v); + + DEBUGP("Visiting vertex %u\n", v); + while((neighbor = graph_next_neighbor(chm->graph, &it)) != GRAPH_NO_NEIGHBOR) + { + DEBUGP("Visiting neighbor %u\n", neighbor); + if(GETBIT(visited,neighbor)) continue; + DEBUGP("Visiting neighbor %u\n", neighbor); + DEBUGP("Visiting edge %u->%u with id %u\n", v, neighbor, graph_edge_id(chm->graph, v, neighbor)); + chm->g[neighbor] = graph_edge_id(chm->graph, v, neighbor) - chm->g[v]; + DEBUGP("g is %u (%u - %u mod %u)\n", chm->g[neighbor], graph_edge_id(chm->graph, v, neighbor), chm->g[v], chm->m); + chm_traverse(chm, visited, neighbor); + } +} + +static int chm_gen_edges(cmph_config_t *mph) +{ + cmph_uint32 e; + chm_config_data_t *chm = (chm_config_data_t *)mph->data; + int cycles = 0; + + DEBUGP("Generating edges for %u vertices with hash functions %s and %s\n", chm->n, cmph_hash_names[chm->hashfuncs[0]], cmph_hash_names[chm->hashfuncs[1]]); + graph_clear_edges(chm->graph); + mph->key_source->rewind(mph->key_source->data); + for (e = 0; e < mph->key_source->nkeys; ++e) + { + cmph_uint32 h1, h2; + cmph_uint32 keylen; + char *key; + mph->key_source->read(mph->key_source->data, &key, &keylen); + h1 = hash(chm->hashes[0], key, keylen) % chm->n; + h2 = hash(chm->hashes[1], key, keylen) % chm->n; + if (h1 == h2) if (++h2 >= chm->n) h2 = 0; + if (h1 == h2) + { + if (mph->verbosity) fprintf(stderr, "Self loop for key %u\n", e); + mph->key_source->dispose(mph->key_source->data, key, keylen); + return 0; + } + DEBUGP("Adding edge: %u -> %u for key %s\n", h1, h2, key); + mph->key_source->dispose(mph->key_source->data, key, keylen); + graph_add_edge(chm->graph, h1, h2); + } + cycles = graph_is_cyclic(chm->graph); + if (mph->verbosity && cycles) fprintf(stderr, "Cyclic graph generated\n"); + DEBUGP("Looking for cycles: %u\n", cycles); + + return ! cycles; +} + +int chm_dump(cmph_t *mphf, FILE *fd) +{ + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint32 two = 2; //number of hash functions + chm_data_t *data = (chm_data_t *)mphf->data; + register size_t nbytes; + + __cmph_dump(mphf, fd); + + nbytes = fwrite(&two, sizeof(cmph_uint32), (size_t)1, fd); + hash_state_dump(data->hashes[0], &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + hash_state_dump(data->hashes[1], &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + nbytes = fwrite(&(data->n), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->m), sizeof(cmph_uint32), (size_t)1, fd); + + nbytes = fwrite(data->g, sizeof(cmph_uint32)*data->n, (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } +/* #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < data->n; ++i) fprintf(stderr, "%u ", data->g[i]); + fprintf(stderr, "\n"); + #endif*/ + return 1; +} + +void chm_load(FILE *f, cmph_t *mphf) +{ + cmph_uint32 nhashes; + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint32 i; + chm_data_t *chm = (chm_data_t *)malloc(sizeof(chm_data_t)); + register size_t nbytes; + DEBUGP("Loading chm mphf\n"); + mphf->data = chm; + nbytes = fread(&nhashes, sizeof(cmph_uint32), (size_t)1, f); + chm->hashes = (hash_state_t **)malloc(sizeof(hash_state_t *)*(nhashes + 1)); + chm->hashes[nhashes] = NULL; + DEBUGP("Reading %u hashes\n", nhashes); + for (i = 0; i < nhashes; ++i) + { + hash_state_t *state = NULL; + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + DEBUGP("Hash state has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + state = hash_state_load(buf, buflen); + chm->hashes[i] = state; + free(buf); + } + + DEBUGP("Reading m and n\n"); + nbytes = fread(&(chm->n), sizeof(cmph_uint32), (size_t)1, f); + nbytes = fread(&(chm->m), sizeof(cmph_uint32), (size_t)1, f); + + chm->g = (cmph_uint32 *)malloc(sizeof(cmph_uint32)*chm->n); + nbytes = fread(chm->g, chm->n*sizeof(cmph_uint32), (size_t)1, f); + if (nbytes == 0 && ferror(f)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return; + } + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < chm->n; ++i) fprintf(stderr, "%u ", chm->g[i]); + fprintf(stderr, "\n"); + #endif + return; +} + + +cmph_uint32 chm_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + chm_data_t *chm = mphf->data; + cmph_uint32 h1 = hash(chm->hashes[0], key, keylen) % chm->n; + cmph_uint32 h2 = hash(chm->hashes[1], key, keylen) % chm->n; + DEBUGP("key: %s h1: %u h2: %u\n", key, h1, h2); + if (h1 == h2 && ++h2 >= chm->n) h2 = 0; + DEBUGP("key: %s g[h1]: %u g[h2]: %u edges: %u\n", key, chm->g[h1], chm->g[h2], chm->m); + return (chm->g[h1] + chm->g[h2]) % chm->m; +} +void chm_destroy(cmph_t *mphf) +{ + chm_data_t *data = (chm_data_t *)mphf->data; + free(data->g); + hash_state_destroy(data->hashes[0]); + hash_state_destroy(data->hashes[1]); + free(data->hashes); + free(data); + free(mphf); +} + +/** \fn void chm_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void chm_pack(cmph_t *mphf, void *packed_mphf) +{ + chm_data_t *data = (chm_data_t *)mphf->data; + cmph_uint8 * ptr = packed_mphf; + CMPH_HASH h2_type; + + // packing h1 type + CMPH_HASH h1_type = hash_get_type(data->hashes[0]); + *((cmph_uint32 *) ptr) = h1_type; + ptr += sizeof(cmph_uint32); + + // packing h1 + hash_state_pack(data->hashes[0], ptr); + ptr += hash_state_packed_size(h1_type); + + // packing h2 type + h2_type = hash_get_type(data->hashes[1]); + *((cmph_uint32 *) ptr) = h2_type; + ptr += sizeof(cmph_uint32); + + // packing h2 + hash_state_pack(data->hashes[1], ptr); + ptr += hash_state_packed_size(h2_type); + + // packing n + *((cmph_uint32 *) ptr) = data->n; + ptr += sizeof(data->n); + + // packing m + *((cmph_uint32 *) ptr) = data->m; + ptr += sizeof(data->m); + + // packing g + memcpy(ptr, data->g, sizeof(cmph_uint32)*data->n); +} + +/** \fn cmph_uint32 chm_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 chm_packed_size(cmph_t *mphf) +{ + chm_data_t *data = (chm_data_t *)mphf->data; + CMPH_HASH h1_type = hash_get_type(data->hashes[0]); + CMPH_HASH h2_type = hash_get_type(data->hashes[1]); + + return (cmph_uint32)(sizeof(CMPH_ALGO) + hash_state_packed_size(h1_type) + hash_state_packed_size(h2_type) + + 4*sizeof(cmph_uint32) + sizeof(cmph_uint32)*data->n); +} + +/** cmph_uint32 chm_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 chm_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + register cmph_uint8 *h1_ptr = packed_mphf; + register CMPH_HASH h1_type = *((cmph_uint32 *)h1_ptr); + register cmph_uint8 *h2_ptr; + register CMPH_HASH h2_type; + register cmph_uint32 *g_ptr; + register cmph_uint32 n, m, h1, h2; + + h1_ptr += 4; + + h2_ptr = h1_ptr + hash_state_packed_size(h1_type); + h2_type = *((cmph_uint32 *)h2_ptr); + h2_ptr += 4; + + g_ptr = (cmph_uint32 *)(h2_ptr + hash_state_packed_size(h2_type)); + + n = *g_ptr++; + m = *g_ptr++; + + h1 = hash_packed(h1_ptr, h1_type, key, keylen) % n; + h2 = hash_packed(h2_ptr, h2_type, key, keylen) % n; + DEBUGP("key: %s h1: %u h2: %u\n", key, h1, h2); + if (h1 == h2 && ++h2 >= n) h2 = 0; + DEBUGP("key: %s g[h1]: %u g[h2]: %u edges: %u\n", key, g_ptr[h1], g_ptr[h2], m); + return (g_ptr[h1] + g_ptr[h2]) % m; +} diff --git a/girepository/cmph/chm.h b/girepository/cmph/chm.h new file mode 100644 index 000000000..392d23aaf --- /dev/null +++ b/girepository/cmph/chm.h @@ -0,0 +1,42 @@ +#ifndef __CMPH_CHM_H__ +#define __CMPH_CHM_H__ + +#include "cmph.h" + +typedef struct __chm_data_t chm_data_t; +typedef struct __chm_config_data_t chm_config_data_t; + +chm_config_data_t *chm_config_new(void); +void chm_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); +void chm_config_destroy(cmph_config_t *mph); +cmph_t *chm_new(cmph_config_t *mph, double c); + +void chm_load(FILE *f, cmph_t *mphf); +int chm_dump(cmph_t *mphf, FILE *f); +void chm_destroy(cmph_t *mphf); +cmph_uint32 chm_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +/** \fn void chm_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void chm_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 chm_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 chm_packed_size(cmph_t *mphf); + +/** cmph_uint32 chm_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 chm_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +#endif diff --git a/girepository/cmph/chm_structs.h b/girepository/cmph/chm_structs.h new file mode 100644 index 000000000..fcad1bc3d --- /dev/null +++ b/girepository/cmph/chm_structs.h @@ -0,0 +1,24 @@ +#ifndef __CMPH_CHM_STRUCTS_H__ +#define __CMPH_CHM_STRUCTS_H__ + +#include "hash_state.h" + +struct __chm_data_t +{ + cmph_uint32 m; //edges (words) count + cmph_uint32 n; //vertex count + cmph_uint32 *g; + hash_state_t **hashes; +}; + +struct __chm_config_data_t +{ + CMPH_HASH hashfuncs[2]; + cmph_uint32 m; //edges (words) count + cmph_uint32 n; //vertex count + graph_t *graph; + cmph_uint32 *g; + hash_state_t **hashes; +}; + +#endif diff --git a/girepository/cmph/cmph.c b/girepository/cmph/cmph.c new file mode 100644 index 000000000..3fd40a297 --- /dev/null +++ b/girepository/cmph/cmph.c @@ -0,0 +1,844 @@ +#include "cmph.h" +#include "cmph_structs.h" +#include "chm.h" +#include "bmz.h" +#include "bmz8.h" +#include "brz.h" +#include "fch.h" +#include "bdz.h" +#include "bdz_ph.h" +#include "chd_ph.h" +#include "chd.h" + +#include +#include +#include +//#define DEBUG +#include "debug.h" + +const char *cmph_names[] = {"bmz", "bmz8", "chm", "brz", "fch", "bdz", "bdz_ph", "chd_ph", "chd", NULL }; + +typedef struct +{ + void *vector; + cmph_uint32 position; // access position when data is a vector +} cmph_vector_t; + + + +/** + * Support a vector of struct as the source of keys. + * + * E.g. The keys could be the fieldB's in a vector of struct rec where + * struct rec is defined as: + * struct rec { + * fieldA; + * fieldB; + * fieldC; + * } + */ +typedef struct +{ + void *vector; /* Pointer to the vector of struct */ + cmph_uint32 position; /* current position */ + cmph_uint32 struct_size; /* The size of the struct */ + cmph_uint32 key_offset; /* The byte offset of the key in the struct */ + cmph_uint32 key_len; /* The length of the key */ +} cmph_struct_vector_t; + + +static cmph_io_adapter_t *cmph_io_vector_new(void * vector, cmph_uint32 nkeys); +static void cmph_io_vector_destroy(cmph_io_adapter_t * key_source); + +static cmph_io_adapter_t *cmph_io_struct_vector_new(void * vector, cmph_uint32 struct_size, cmph_uint32 key_offset, cmph_uint32 key_len, cmph_uint32 nkeys); +static void cmph_io_struct_vector_destroy(cmph_io_adapter_t * key_source); + +static int key_nlfile_read(void *data, char **key, cmph_uint32 *keylen) +{ + FILE *fd = (FILE *)data; + *key = NULL; + *keylen = 0; + while(1) + { + char buf[BUFSIZ]; + char *c = fgets(buf, BUFSIZ, fd); + if (c == NULL) return -1; + if (feof(fd)) return -1; + *key = (char *)realloc(*key, *keylen + strlen(buf) + 1); + memcpy(*key + *keylen, buf, strlen(buf)); + *keylen += (cmph_uint32)strlen(buf); + if (buf[strlen(buf) - 1] != '\n') continue; + break; + } + if ((*keylen) && (*key)[*keylen - 1] == '\n') + { + (*key)[(*keylen) - 1] = 0; + --(*keylen); + } + return (int)(*keylen); +} + +static int key_byte_vector_read(void *data, char **key, cmph_uint32 *keylen) +{ + cmph_vector_t *cmph_vector = (cmph_vector_t *)data; + cmph_uint8 **keys_vd = (cmph_uint8 **)cmph_vector->vector; + size_t size; + memcpy(keylen, keys_vd[cmph_vector->position], sizeof(*keylen)); + size = *keylen; + *key = (char *)malloc(size); + memcpy(*key, keys_vd[cmph_vector->position] + sizeof(*keylen), size); + cmph_vector->position = cmph_vector->position + 1; + return (int)(*keylen); + +} + +static int key_struct_vector_read(void *data, char **key, cmph_uint32 *keylen) +{ + cmph_struct_vector_t *cmph_struct_vector = (cmph_struct_vector_t *)data; + char *keys_vd = (char *)cmph_struct_vector->vector; + size_t size; + *keylen = cmph_struct_vector->key_len; + size = *keylen; + *key = (char *)malloc(size); + memcpy(*key, (keys_vd + (cmph_struct_vector->position * cmph_struct_vector->struct_size) + cmph_struct_vector->key_offset), size); + cmph_struct_vector->position = cmph_struct_vector->position + 1; + return (int)(*keylen); +} + +static int key_vector_read(void *data, char **key, cmph_uint32 *keylen) +{ + cmph_vector_t *cmph_vector = (cmph_vector_t *)data; + char **keys_vd = (char **)cmph_vector->vector; + size_t size; + *keylen = (cmph_uint32)strlen(keys_vd[cmph_vector->position]); + size = *keylen; + *key = (char *)malloc(size + 1); + strcpy(*key, keys_vd[cmph_vector->position]); + cmph_vector->position = cmph_vector->position + 1; + return (int)(*keylen); + +} + + +static void key_nlfile_dispose(void *data, char *key, cmph_uint32 keylen) +{ + free(key); +} + +static void key_vector_dispose(void *data, char *key, cmph_uint32 keylen) +{ + free(key); +} + +static void key_nlfile_rewind(void *data) +{ + FILE *fd = (FILE *)data; + rewind(fd); +} + +static void key_struct_vector_rewind(void *data) +{ + cmph_struct_vector_t *cmph_struct_vector = (cmph_struct_vector_t *)data; + cmph_struct_vector->position = 0; +} + +static void key_vector_rewind(void *data) +{ + cmph_vector_t *cmph_vector = (cmph_vector_t *)data; + cmph_vector->position = 0; +} + +static cmph_uint32 count_nlfile_keys(FILE *fd) +{ + cmph_uint32 count = 0; + rewind(fd); + while(1) + { + char buf[BUFSIZ]; + if (fgets(buf, BUFSIZ, fd) == NULL) break; + if (feof(fd)) break; + if (buf[strlen(buf) - 1] != '\n') continue; + ++count; + } + rewind(fd); + return count; +} + +cmph_io_adapter_t *cmph_io_nlfile_adapter(FILE * keys_fd) +{ + cmph_io_adapter_t * key_source = (cmph_io_adapter_t *)malloc(sizeof(cmph_io_adapter_t)); + assert(key_source); + key_source->data = (void *)keys_fd; + key_source->nkeys = count_nlfile_keys(keys_fd); + key_source->read = key_nlfile_read; + key_source->dispose = key_nlfile_dispose; + key_source->rewind = key_nlfile_rewind; + return key_source; +} + +void cmph_io_nlfile_adapter_destroy(cmph_io_adapter_t * key_source) +{ + free(key_source); +} + +cmph_io_adapter_t *cmph_io_nlnkfile_adapter(FILE * keys_fd, cmph_uint32 nkeys) +{ + cmph_io_adapter_t * key_source = (cmph_io_adapter_t *)malloc(sizeof(cmph_io_adapter_t)); + assert(key_source); + key_source->data = (void *)keys_fd; + key_source->nkeys = nkeys; + key_source->read = key_nlfile_read; + key_source->dispose = key_nlfile_dispose; + key_source->rewind = key_nlfile_rewind; + return key_source; +} + +void cmph_io_nlnkfile_adapter_destroy(cmph_io_adapter_t * key_source) +{ + free(key_source); +} + + +static cmph_io_adapter_t *cmph_io_struct_vector_new(void * vector, cmph_uint32 struct_size, cmph_uint32 key_offset, cmph_uint32 key_len, cmph_uint32 nkeys) +{ + cmph_io_adapter_t * key_source = (cmph_io_adapter_t *)malloc(sizeof(cmph_io_adapter_t)); + cmph_struct_vector_t * cmph_struct_vector = (cmph_struct_vector_t *)malloc(sizeof(cmph_struct_vector_t)); + assert(key_source); + assert(cmph_struct_vector); + cmph_struct_vector->vector = vector; + cmph_struct_vector->position = 0; + cmph_struct_vector->struct_size = struct_size; + cmph_struct_vector->key_offset = key_offset; + cmph_struct_vector->key_len = key_len; + key_source->data = (void *)cmph_struct_vector; + key_source->nkeys = nkeys; + return key_source; +} + +static void cmph_io_struct_vector_destroy(cmph_io_adapter_t * key_source) +{ + cmph_struct_vector_t *cmph_struct_vector = (cmph_struct_vector_t *)key_source->data; + cmph_struct_vector->vector = NULL; + free(cmph_struct_vector); + free(key_source); +} + +static cmph_io_adapter_t *cmph_io_vector_new(void * vector, cmph_uint32 nkeys) +{ + cmph_io_adapter_t * key_source = (cmph_io_adapter_t *)malloc(sizeof(cmph_io_adapter_t)); + cmph_vector_t * cmph_vector = (cmph_vector_t *)malloc(sizeof(cmph_vector_t)); + assert(key_source); + assert(cmph_vector); + cmph_vector->vector = vector; + cmph_vector->position = 0; + key_source->data = (void *)cmph_vector; + key_source->nkeys = nkeys; + return key_source; +} + +static void cmph_io_vector_destroy(cmph_io_adapter_t * key_source) +{ + cmph_vector_t *cmph_vector = (cmph_vector_t *)key_source->data; + cmph_vector->vector = NULL; + free(cmph_vector); + free(key_source); +} + +cmph_io_adapter_t *cmph_io_byte_vector_adapter(cmph_uint8 ** vector, cmph_uint32 nkeys) +{ + cmph_io_adapter_t * key_source = cmph_io_vector_new(vector, nkeys); + key_source->read = key_byte_vector_read; + key_source->dispose = key_vector_dispose; + key_source->rewind = key_vector_rewind; + return key_source; +} +void cmph_io_byte_vector_adapter_destroy(cmph_io_adapter_t * key_source) +{ + cmph_io_vector_destroy(key_source); +} + +cmph_io_adapter_t *cmph_io_struct_vector_adapter(void * vector, cmph_uint32 struct_size, cmph_uint32 key_offset, cmph_uint32 key_len, cmph_uint32 nkeys) +{ + cmph_io_adapter_t * key_source = cmph_io_struct_vector_new(vector, struct_size, key_offset, key_len, nkeys); + key_source->read = key_struct_vector_read; + key_source->dispose = key_vector_dispose; + key_source->rewind = key_struct_vector_rewind; + return key_source; +} + +void cmph_io_struct_vector_adapter_destroy(cmph_io_adapter_t * key_source) +{ + cmph_io_struct_vector_destroy(key_source); +} + +cmph_io_adapter_t *cmph_io_vector_adapter(char ** vector, cmph_uint32 nkeys) +{ + cmph_io_adapter_t * key_source = cmph_io_vector_new(vector, nkeys); + key_source->read = key_vector_read; + key_source->dispose = key_vector_dispose; + key_source->rewind = key_vector_rewind; + return key_source; +} + +void cmph_io_vector_adapter_destroy(cmph_io_adapter_t * key_source) +{ + cmph_io_vector_destroy(key_source); +} + +cmph_config_t *cmph_config_new(cmph_io_adapter_t *key_source) +{ + cmph_config_t *mph = NULL; + mph = __config_new(key_source); + assert(mph); + mph->algo = CMPH_CHM; // default value + mph->data = chm_config_new(); + return mph; +} + +void cmph_config_set_algo(cmph_config_t *mph, CMPH_ALGO algo) +{ + if (algo != mph->algo) + { + switch (mph->algo) + { + case CMPH_CHM: + chm_config_destroy(mph); + break; + case CMPH_BMZ: + bmz_config_destroy(mph); + break; + case CMPH_BMZ8: + bmz8_config_destroy(mph); + break; + case CMPH_BRZ: + brz_config_destroy(mph); + break; + case CMPH_FCH: + fch_config_destroy(mph); + break; + case CMPH_BDZ: + bdz_config_destroy(mph); + break; + case CMPH_BDZ_PH: + bdz_ph_config_destroy(mph); + break; + case CMPH_CHD_PH: + chd_ph_config_destroy(mph); + break; + case CMPH_CHD: + chd_config_destroy(mph); + break; + default: + assert(0); + } + switch(algo) + { + case CMPH_CHM: + mph->data = chm_config_new(); + break; + case CMPH_BMZ: + mph->data = bmz_config_new(); + break; + case CMPH_BMZ8: + mph->data = bmz8_config_new(); + break; + case CMPH_BRZ: + mph->data = brz_config_new(); + break; + case CMPH_FCH: + mph->data = fch_config_new(); + break; + case CMPH_BDZ: + mph->data = bdz_config_new(); + break; + case CMPH_BDZ_PH: + mph->data = bdz_ph_config_new(); + break; + case CMPH_CHD_PH: + mph->data = chd_ph_config_new(); + break; + case CMPH_CHD: + mph->data = chd_config_new(mph); + break; + default: + assert(0); + } + } + mph->algo = algo; +} + +void cmph_config_set_tmp_dir(cmph_config_t *mph, cmph_uint8 *tmp_dir) +{ + if (mph->algo == CMPH_BRZ) + { + brz_config_set_tmp_dir(mph, tmp_dir); + } +} + + +void cmph_config_set_mphf_fd(cmph_config_t *mph, FILE *mphf_fd) +{ + if (mph->algo == CMPH_BRZ) + { + brz_config_set_mphf_fd(mph, mphf_fd); + } +} + +void cmph_config_set_b(cmph_config_t *mph, cmph_uint32 b) +{ + if (mph->algo == CMPH_BRZ) + { + brz_config_set_b(mph, b); + } + else if (mph->algo == CMPH_BDZ) + { + bdz_config_set_b(mph, b); + } + else if (mph->algo == CMPH_CHD_PH) + { + chd_ph_config_set_b(mph, b); + } + else if (mph->algo == CMPH_CHD) + { + chd_config_set_b(mph, b); + } +} + +void cmph_config_set_keys_per_bin(cmph_config_t *mph, cmph_uint32 keys_per_bin) +{ + if (mph->algo == CMPH_CHD_PH) + { + chd_ph_config_set_keys_per_bin(mph, keys_per_bin); + } + else if (mph->algo == CMPH_CHD) + { + chd_config_set_keys_per_bin(mph, keys_per_bin); + } +} + +void cmph_config_set_memory_availability(cmph_config_t *mph, cmph_uint32 memory_availability) +{ + if (mph->algo == CMPH_BRZ) + { + brz_config_set_memory_availability(mph, memory_availability); + } +} + +void cmph_config_destroy(cmph_config_t *mph) +{ + if(mph) + { + DEBUGP("Destroying mph with algo %s\n", cmph_names[mph->algo]); + switch (mph->algo) + { + case CMPH_CHM: + chm_config_destroy(mph); + break; + case CMPH_BMZ: /* included -- Fabiano */ + bmz_config_destroy(mph); + break; + case CMPH_BMZ8: /* included -- Fabiano */ + bmz8_config_destroy(mph); + break; + case CMPH_BRZ: /* included -- Fabiano */ + brz_config_destroy(mph); + break; + case CMPH_FCH: /* included -- Fabiano */ + fch_config_destroy(mph); + break; + case CMPH_BDZ: /* included -- Fabiano */ + bdz_config_destroy(mph); + break; + case CMPH_BDZ_PH: /* included -- Fabiano */ + bdz_ph_config_destroy(mph); + break; + case CMPH_CHD_PH: /* included -- Fabiano */ + chd_ph_config_destroy(mph); + break; + case CMPH_CHD: /* included -- Fabiano */ + chd_config_destroy(mph); + break; + default: + assert(0); + } + __config_destroy(mph); + } +} + +void cmph_config_set_verbosity(cmph_config_t *mph, cmph_uint32 verbosity) +{ + mph->verbosity = verbosity; +} + +void cmph_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + switch (mph->algo) + { + case CMPH_CHM: + chm_config_set_hashfuncs(mph, hashfuncs); + break; + case CMPH_BMZ: /* included -- Fabiano */ + bmz_config_set_hashfuncs(mph, hashfuncs); + break; + case CMPH_BMZ8: /* included -- Fabiano */ + bmz8_config_set_hashfuncs(mph, hashfuncs); + break; + case CMPH_BRZ: /* included -- Fabiano */ + brz_config_set_hashfuncs(mph, hashfuncs); + break; + case CMPH_FCH: /* included -- Fabiano */ + fch_config_set_hashfuncs(mph, hashfuncs); + break; + case CMPH_BDZ: /* included -- Fabiano */ + bdz_config_set_hashfuncs(mph, hashfuncs); + break; + case CMPH_BDZ_PH: /* included -- Fabiano */ + bdz_ph_config_set_hashfuncs(mph, hashfuncs); + break; + case CMPH_CHD_PH: /* included -- Fabiano */ + chd_ph_config_set_hashfuncs(mph, hashfuncs); + break; + case CMPH_CHD: /* included -- Fabiano */ + chd_config_set_hashfuncs(mph, hashfuncs); + break; + default: + break; + } + return; +} +void cmph_config_set_graphsize(cmph_config_t *mph, double c) +{ + mph->c = c; + return; +} + +cmph_t *cmph_new(cmph_config_t *mph) +{ + cmph_t *mphf = NULL; + double c = mph->c; + + DEBUGP("Creating mph with algorithm %s\n", cmph_names[mph->algo]); + switch (mph->algo) + { + case CMPH_CHM: + DEBUGP("Creating chm hash\n"); + mphf = chm_new(mph, c); + break; + case CMPH_BMZ: /* included -- Fabiano */ + DEBUGP("Creating bmz hash\n"); + mphf = bmz_new(mph, c); + break; + case CMPH_BMZ8: /* included -- Fabiano */ + DEBUGP("Creating bmz8 hash\n"); + mphf = bmz8_new(mph, c); + break; + case CMPH_BRZ: /* included -- Fabiano */ + DEBUGP("Creating brz hash\n"); + if (c >= 2.0) brz_config_set_algo(mph, CMPH_FCH); + else brz_config_set_algo(mph, CMPH_BMZ8); + mphf = brz_new(mph, c); + break; + case CMPH_FCH: /* included -- Fabiano */ + DEBUGP("Creating fch hash\n"); + mphf = fch_new(mph, c); + break; + case CMPH_BDZ: /* included -- Fabiano */ + DEBUGP("Creating bdz hash\n"); + mphf = bdz_new(mph, c); + break; + case CMPH_BDZ_PH: /* included -- Fabiano */ + DEBUGP("Creating bdz_ph hash\n"); + mphf = bdz_ph_new(mph, c); + break; + case CMPH_CHD_PH: /* included -- Fabiano */ + DEBUGP("Creating chd_ph hash\n"); + mphf = chd_ph_new(mph, c); + break; + case CMPH_CHD: /* included -- Fabiano */ + DEBUGP("Creating chd hash\n"); + mphf = chd_new(mph, c); + break; + default: + assert(0); + } + return mphf; +} + +int cmph_dump(cmph_t *mphf, FILE *f) +{ + switch (mphf->algo) + { + case CMPH_CHM: + return chm_dump(mphf, f); + case CMPH_BMZ: /* included -- Fabiano */ + return bmz_dump(mphf, f); + case CMPH_BMZ8: /* included -- Fabiano */ + return bmz8_dump(mphf, f); + case CMPH_BRZ: /* included -- Fabiano */ + return brz_dump(mphf, f); + case CMPH_FCH: /* included -- Fabiano */ + return fch_dump(mphf, f); + case CMPH_BDZ: /* included -- Fabiano */ + return bdz_dump(mphf, f); + case CMPH_BDZ_PH: /* included -- Fabiano */ + return bdz_ph_dump(mphf, f); + case CMPH_CHD_PH: /* included -- Fabiano */ + return chd_ph_dump(mphf, f); + case CMPH_CHD: /* included -- Fabiano */ + return chd_dump(mphf, f); + default: + assert(0); + } + assert(0); + return 0; +} +cmph_t *cmph_load(FILE *f) +{ + cmph_t *mphf = NULL; + DEBUGP("Loading mphf generic parts\n"); + mphf = __cmph_load(f); + if (mphf == NULL) return NULL; + DEBUGP("Loading mphf algorithm dependent parts\n"); + + switch (mphf->algo) + { + case CMPH_CHM: + chm_load(f, mphf); + break; + case CMPH_BMZ: /* included -- Fabiano */ + DEBUGP("Loading bmz algorithm dependent parts\n"); + bmz_load(f, mphf); + break; + case CMPH_BMZ8: /* included -- Fabiano */ + DEBUGP("Loading bmz8 algorithm dependent parts\n"); + bmz8_load(f, mphf); + break; + case CMPH_BRZ: /* included -- Fabiano */ + DEBUGP("Loading brz algorithm dependent parts\n"); + brz_load(f, mphf); + break; + case CMPH_FCH: /* included -- Fabiano */ + DEBUGP("Loading fch algorithm dependent parts\n"); + fch_load(f, mphf); + break; + case CMPH_BDZ: /* included -- Fabiano */ + DEBUGP("Loading bdz algorithm dependent parts\n"); + bdz_load(f, mphf); + break; + case CMPH_BDZ_PH: /* included -- Fabiano */ + DEBUGP("Loading bdz_ph algorithm dependent parts\n"); + bdz_ph_load(f, mphf); + break; + case CMPH_CHD_PH: /* included -- Fabiano */ + DEBUGP("Loading chd_ph algorithm dependent parts\n"); + chd_ph_load(f, mphf); + break; + case CMPH_CHD: /* included -- Fabiano */ + DEBUGP("Loading chd algorithm dependent parts\n"); + chd_load(f, mphf); + break; + default: + assert(0); + } + DEBUGP("Loaded mphf\n"); + return mphf; +} + + +cmph_uint32 cmph_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + DEBUGP("mphf algorithm: %u \n", mphf->algo); + switch(mphf->algo) + { + case CMPH_CHM: + return chm_search(mphf, key, keylen); + case CMPH_BMZ: /* included -- Fabiano */ + DEBUGP("bmz algorithm search\n"); + return bmz_search(mphf, key, keylen); + case CMPH_BMZ8: /* included -- Fabiano */ + DEBUGP("bmz8 algorithm search\n"); + return bmz8_search(mphf, key, keylen); + case CMPH_BRZ: /* included -- Fabiano */ + DEBUGP("brz algorithm search\n"); + return brz_search(mphf, key, keylen); + case CMPH_FCH: /* included -- Fabiano */ + DEBUGP("fch algorithm search\n"); + return fch_search(mphf, key, keylen); + case CMPH_BDZ: /* included -- Fabiano */ + DEBUGP("bdz algorithm search\n"); + return bdz_search(mphf, key, keylen); + case CMPH_BDZ_PH: /* included -- Fabiano */ + DEBUGP("bdz_ph algorithm search\n"); + return bdz_ph_search(mphf, key, keylen); + case CMPH_CHD_PH: /* included -- Fabiano */ + DEBUGP("chd_ph algorithm search\n"); + return chd_ph_search(mphf, key, keylen); + case CMPH_CHD: /* included -- Fabiano */ + DEBUGP("chd algorithm search\n"); + return chd_search(mphf, key, keylen); + default: + assert(0); + } + assert(0); + return 0; +} + +cmph_uint32 cmph_size(cmph_t *mphf) +{ + return mphf->size; +} + +void cmph_destroy(cmph_t *mphf) +{ + switch(mphf->algo) + { + case CMPH_CHM: + chm_destroy(mphf); + return; + case CMPH_BMZ: /* included -- Fabiano */ + bmz_destroy(mphf); + return; + case CMPH_BMZ8: /* included -- Fabiano */ + bmz8_destroy(mphf); + return; + case CMPH_BRZ: /* included -- Fabiano */ + brz_destroy(mphf); + return; + case CMPH_FCH: /* included -- Fabiano */ + fch_destroy(mphf); + return; + case CMPH_BDZ: /* included -- Fabiano */ + bdz_destroy(mphf); + return; + case CMPH_BDZ_PH: /* included -- Fabiano */ + bdz_ph_destroy(mphf); + return; + case CMPH_CHD_PH: /* included -- Fabiano */ + chd_ph_destroy(mphf); + return; + case CMPH_CHD: /* included -- Fabiano */ + chd_destroy(mphf); + return; + default: + assert(0); + } + assert(0); + return; +} + + +/** \fn void cmph_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void cmph_pack(cmph_t *mphf, void *packed_mphf) +{ + // packing algorithm type to be used in cmph.c + cmph_uint32 * ptr = (cmph_uint32 *) packed_mphf; + *ptr++ = mphf->algo; + DEBUGP("mphf->algo = %u\n", mphf->algo); + switch(mphf->algo) + { + case CMPH_CHM: + chm_pack(mphf, ptr); + break; + case CMPH_BMZ: /* included -- Fabiano */ + bmz_pack(mphf, ptr); + break; + case CMPH_BMZ8: /* included -- Fabiano */ + bmz8_pack(mphf, ptr); + break; + case CMPH_BRZ: /* included -- Fabiano */ + brz_pack(mphf, ptr); + break; + case CMPH_FCH: /* included -- Fabiano */ + fch_pack(mphf, ptr); + break; + case CMPH_BDZ: /* included -- Fabiano */ + bdz_pack(mphf, ptr); + break; + case CMPH_BDZ_PH: /* included -- Fabiano */ + bdz_ph_pack(mphf, ptr); + break; + case CMPH_CHD_PH: /* included -- Fabiano */ + chd_ph_pack(mphf, ptr); + break; + case CMPH_CHD: /* included -- Fabiano */ + chd_pack(mphf, ptr); + break; + default: + assert(0); + } + return; +} + +/** \fn cmph_uint32 cmph_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 cmph_packed_size(cmph_t *mphf) +{ + switch(mphf->algo) + { + case CMPH_CHM: + return chm_packed_size(mphf); + case CMPH_BMZ: /* included -- Fabiano */ + return bmz_packed_size(mphf); + case CMPH_BMZ8: /* included -- Fabiano */ + return bmz8_packed_size(mphf); + case CMPH_BRZ: /* included -- Fabiano */ + return brz_packed_size(mphf); + case CMPH_FCH: /* included -- Fabiano */ + return fch_packed_size(mphf); + case CMPH_BDZ: /* included -- Fabiano */ + return bdz_packed_size(mphf); + case CMPH_BDZ_PH: /* included -- Fabiano */ + return bdz_ph_packed_size(mphf); + case CMPH_CHD_PH: /* included -- Fabiano */ + return chd_ph_packed_size(mphf); + case CMPH_CHD: /* included -- Fabiano */ + return chd_packed_size(mphf); + default: + assert(0); + } + return 0; // FAILURE +} + +/** cmph_uint32 cmph_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 cmph_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + cmph_uint32 *ptr = (cmph_uint32 *)packed_mphf; +// fprintf(stderr, "algo:%u\n", *ptr); + switch(*ptr) + { + case CMPH_CHM: + return chm_search_packed(++ptr, key, keylen); + case CMPH_BMZ: /* included -- Fabiano */ + return bmz_search_packed(++ptr, key, keylen); + case CMPH_BMZ8: /* included -- Fabiano */ + return bmz8_search_packed(++ptr, key, keylen); + case CMPH_BRZ: /* included -- Fabiano */ + return brz_search_packed(++ptr, key, keylen); + case CMPH_FCH: /* included -- Fabiano */ + return fch_search_packed(++ptr, key, keylen); + case CMPH_BDZ: /* included -- Fabiano */ + return bdz_search_packed(++ptr, key, keylen); + case CMPH_BDZ_PH: /* included -- Fabiano */ + return bdz_ph_search_packed(++ptr, key, keylen); + case CMPH_CHD_PH: /* included -- Fabiano */ + return chd_ph_search_packed(++ptr, key, keylen); + case CMPH_CHD: /* included -- Fabiano */ + return chd_search_packed(++ptr, key, keylen); + default: + assert(0); + } + return 0; // FAILURE +} diff --git a/girepository/cmph/cmph.h b/girepository/cmph/cmph.h new file mode 100644 index 000000000..1bc009e19 --- /dev/null +++ b/girepository/cmph/cmph.h @@ -0,0 +1,112 @@ +#ifndef __CMPH_H__ +#define __CMPH_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "cmph_types.h" + +typedef struct __config_t cmph_config_t; +typedef struct __cmph_t cmph_t; + +typedef struct +{ + void *data; + cmph_uint32 nkeys; + int (*read)(void *, char **, cmph_uint32 *); + void (*dispose)(void *, char *, cmph_uint32); + void (*rewind)(void *); +} cmph_io_adapter_t; + +/** Adapter pattern API **/ +/* please call free() in the created adapters */ +cmph_io_adapter_t *cmph_io_nlfile_adapter(FILE * keys_fd); +void cmph_io_nlfile_adapter_destroy(cmph_io_adapter_t * key_source); + +cmph_io_adapter_t *cmph_io_nlnkfile_adapter(FILE * keys_fd, cmph_uint32 nkeys); +void cmph_io_nlnkfile_adapter_destroy(cmph_io_adapter_t * key_source); + +cmph_io_adapter_t *cmph_io_vector_adapter(char ** vector, cmph_uint32 nkeys); +void cmph_io_vector_adapter_destroy(cmph_io_adapter_t * key_source); + +cmph_io_adapter_t *cmph_io_byte_vector_adapter(cmph_uint8 ** vector, cmph_uint32 nkeys); +void cmph_io_byte_vector_adapter_destroy(cmph_io_adapter_t * key_source); + +cmph_io_adapter_t *cmph_io_struct_vector_adapter(void * vector, + cmph_uint32 struct_size, + cmph_uint32 key_offset, + cmph_uint32 key_len, + cmph_uint32 nkeys); + +void cmph_io_struct_vector_adapter_destroy(cmph_io_adapter_t * key_source); + +/** Hash configuration API **/ +cmph_config_t *cmph_config_new(cmph_io_adapter_t *key_source); +void cmph_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); +void cmph_config_set_verbosity(cmph_config_t *mph, cmph_uint32 verbosity); +void cmph_config_set_graphsize(cmph_config_t *mph, double c); +void cmph_config_set_algo(cmph_config_t *mph, CMPH_ALGO algo); +void cmph_config_set_tmp_dir(cmph_config_t *mph, cmph_uint8 *tmp_dir); +void cmph_config_set_mphf_fd(cmph_config_t *mph, FILE *mphf_fd); +void cmph_config_set_b(cmph_config_t *mph, cmph_uint32 b); +void cmph_config_set_keys_per_bin(cmph_config_t *mph, cmph_uint32 keys_per_bin); +void cmph_config_set_memory_availability(cmph_config_t *mph, cmph_uint32 memory_availability); +void cmph_config_destroy(cmph_config_t *mph); + +/** Hash API **/ +cmph_t *cmph_new(cmph_config_t *mph); + +/** cmph_uint32 cmph_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + * \brief Computes the mphf value. + * \param mphf pointer to the resulting function + * \param key is the key to be hashed + * \param keylen is the key legth in bytes + * \return The mphf value + */ +cmph_uint32 cmph_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +cmph_uint32 cmph_size(cmph_t *mphf); +void cmph_destroy(cmph_t *mphf); + +/** Hash serialization/deserialization */ +int cmph_dump(cmph_t *mphf, FILE *f); +cmph_t *cmph_load(FILE *f); + +/** \fn void cmph_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the + * \param resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void cmph_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 cmph_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 cmph_packed_size(cmph_t *mphf); + +/** cmph_uint32 cmph_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 cmph_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +// TIMING functions. To use the macro CMPH_TIMING must be defined +#include "cmph_time.h" + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/girepository/cmph/cmph_structs.c b/girepository/cmph/cmph_structs.c new file mode 100644 index 000000000..9ecf5fc0e --- /dev/null +++ b/girepository/cmph/cmph_structs.c @@ -0,0 +1,76 @@ +#include "cmph_structs.h" + +#include +#include + +//#define DEBUG +#include "debug.h" + +cmph_config_t *__config_new(cmph_io_adapter_t *key_source) +{ + cmph_config_t *mph = (cmph_config_t *)malloc(sizeof(cmph_config_t)); + memset(mph, 0, sizeof(cmph_config_t)); + if (mph == NULL) return NULL; + mph->key_source = key_source; + mph->verbosity = 0; + mph->data = NULL; + mph->c = 0; + return mph; +} + +void __config_destroy(cmph_config_t *mph) +{ + free(mph); +} + +void __cmph_dump(cmph_t *mphf, FILE *fd) +{ + register size_t nbytes; + nbytes = fwrite(cmph_names[mphf->algo], (size_t)(strlen(cmph_names[mphf->algo]) + 1), (size_t)1, fd); + nbytes = fwrite(&(mphf->size), sizeof(mphf->size), (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + } +} +cmph_t *__cmph_load(FILE *f) +{ + cmph_t *mphf = NULL; + cmph_uint32 i; + char algo_name[BUFSIZ]; + char *ptr = algo_name; + CMPH_ALGO algo = CMPH_COUNT; + register size_t nbytes; + + DEBUGP("Loading mphf\n"); + while(1) + { + size_t c = fread(ptr, (size_t)1, (size_t)1, f); + if (c != 1) return NULL; + if (*ptr == 0) break; + ++ptr; + } + for(i = 0; i < CMPH_COUNT; ++i) + { + if (strcmp(algo_name, cmph_names[i]) == 0) + { + algo = i; + } + } + if (algo == CMPH_COUNT) + { + DEBUGP("Algorithm %s not found\n", algo_name); + return NULL; + } + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = algo; + nbytes = fread(&(mphf->size), sizeof(mphf->size), (size_t)1, f); + mphf->data = NULL; + DEBUGP("Algorithm is %s and mphf is sized %u\n", cmph_names[algo], mphf->size); + if (nbytes == 0 && ferror(f)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + } + + return mphf; +} + + diff --git a/girepository/cmph/cmph_structs.h b/girepository/cmph/cmph_structs.h new file mode 100644 index 000000000..88fafb6ce --- /dev/null +++ b/girepository/cmph/cmph_structs.h @@ -0,0 +1,33 @@ +#ifndef __CMPH_STRUCTS_H__ +#define __CMPH_STRUCTS_H__ + +#include "cmph.h" + +/** Hash generation algorithm data + */ +struct __config_t +{ + CMPH_ALGO algo; + cmph_io_adapter_t *key_source; + cmph_uint32 verbosity; + double c; + void *data; // algorithm dependent data +}; + +/** Hash querying algorithm data + */ +struct __cmph_t +{ + CMPH_ALGO algo; + cmph_uint32 size; + cmph_io_adapter_t *key_source; + void *data; // algorithm dependent data +}; + +cmph_config_t *__config_new(cmph_io_adapter_t *key_source); +void __config_destroy(cmph_config_t*); +void __cmph_dump(cmph_t *mphf, FILE *); +cmph_t *__cmph_load(FILE *f); + + +#endif diff --git a/girepository/cmph/cmph_time.h b/girepository/cmph/cmph_time.h new file mode 100644 index 000000000..5d5d89330 --- /dev/null +++ b/girepository/cmph/cmph_time.h @@ -0,0 +1,62 @@ +#ifdef ELAPSED_TIME_IN_SECONDS +#undef ELAPSED_TIME_IN_SECONDS +#endif + +#ifdef ELAPSED_TIME_IN_uSECONDS +#undef ELAPSED_TIME_IN_uSECONDS +#endif + +#ifdef __GNUC__ + #include + #ifndef WIN32 + #include + #endif +#endif + +#ifdef __GNUC__ + #ifndef __CMPH_TIME_H__ + #define __CMPH_TIME_H__ + static inline void elapsed_time_in_seconds(double * elapsed_time) + { + struct timeval e_time; + if (gettimeofday(&e_time, NULL) < 0) { + return; + } + *elapsed_time = (double)e_time.tv_sec + ((double)e_time.tv_usec/1000000.0); + } + static inline void dummy_elapsed_time_in_seconds(double * elapsed_time) + { + (void) elapsed_time; + } + static inline void elapsed_time_in_useconds(cmph_uint64 * elapsed_time) + { + struct timeval e_time; + if (gettimeofday(&e_time, NULL) < 0) { + return; + } + *elapsed_time = (cmph_uint64)(e_time.tv_sec*1000000 + e_time.tv_usec); + } + static inline void dummy_elapsed_time_in_useconds(cmph_uint64 * elapsed_time) + { + (void) elapsed_time; + } + #endif +#endif + +#ifdef CMPH_TIMING + #ifdef __GNUC__ + #define ELAPSED_TIME_IN_SECONDS elapsed_time_in_seconds + #define ELAPSED_TIME_IN_uSECONDS elapsed_time_in_useconds + #else + #define ELAPSED_TIME_IN_SECONDS dummy_elapsed_time_in_seconds + #define ELAPSED_TIME_IN_uSECONDS dummy_elapsed_time_in_useconds + #endif +#else + #ifdef __GNUC__ + #define ELAPSED_TIME_IN_SECONDS + #define ELAPSED_TIME_IN_uSECONDS + #else + #define ELAPSED_TIME_IN_SECONDS dummy_elapsed_time_in_seconds + #define ELAPSED_TIME_IN_uSECONDS dummy_elapsed_time_in_useconds + #endif +#endif diff --git a/girepository/cmph/cmph_types.h b/girepository/cmph/cmph_types.h new file mode 100644 index 000000000..288323587 --- /dev/null +++ b/girepository/cmph/cmph_types.h @@ -0,0 +1,25 @@ +#include + +#ifndef __CMPH_TYPES_H__ +#define __CMPH_TYPES_H__ + +typedef gint8 cmph_int8; +typedef guint8 cmph_uint8; + +typedef gint16 cmph_int16; +typedef guint16 cmph_uint16; + +typedef gint32 cmph_int32; +typedef guint32 cmph_uint32; + +typedef gint64 cmph_int64; +typedef guint64 cmph_uint64; + +typedef enum { CMPH_HASH_JENKINS, CMPH_HASH_COUNT } CMPH_HASH; +extern const char *cmph_hash_names[]; +typedef enum { CMPH_BMZ, CMPH_BMZ8, CMPH_CHM, CMPH_BRZ, CMPH_FCH, + CMPH_BDZ, CMPH_BDZ_PH, + CMPH_CHD_PH, CMPH_CHD, CMPH_COUNT } CMPH_ALGO; +extern const char *cmph_names[]; + +#endif diff --git a/girepository/cmph/compressed_rank.c b/girepository/cmph/compressed_rank.c new file mode 100644 index 000000000..8019dbe5c --- /dev/null +++ b/girepository/cmph/compressed_rank.c @@ -0,0 +1,327 @@ +#include +#include +#include +#include +#include"compressed_rank.h" +#include"bitbool.h" +// #define DEBUG +#include"debug.h" +static inline cmph_uint32 compressed_rank_i_log2(cmph_uint32 x) +{ + register cmph_uint32 res = 0; + + while(x > 1) + { + x >>= 1; + res++; + } + return res; +}; + +void compressed_rank_init(compressed_rank_t * cr) +{ + cr->max_val = 0; + cr->n = 0; + cr->rem_r = 0; + select_init(&cr->sel); + cr->vals_rems = 0; +} + +void compressed_rank_destroy(compressed_rank_t * cr) +{ + free(cr->vals_rems); + cr->vals_rems = 0; + select_destroy(&cr->sel); +} + +void compressed_rank_generate(compressed_rank_t * cr, cmph_uint32 * vals_table, cmph_uint32 n) +{ + register cmph_uint32 i,j; + register cmph_uint32 rems_mask; + register cmph_uint32 * select_vec = 0; + cr->n = n; + cr->max_val = vals_table[cr->n - 1]; + cr->rem_r = compressed_rank_i_log2(cr->max_val/cr->n); + if(cr->rem_r == 0) + { + cr->rem_r = 1; + } + select_vec = (cmph_uint32 *) calloc(cr->max_val >> cr->rem_r, sizeof(cmph_uint32)); + cr->vals_rems = (cmph_uint32 *) calloc(BITS_TABLE_SIZE(cr->n, cr->rem_r), sizeof(cmph_uint32)); + rems_mask = (1U << cr->rem_r) - 1U; + + for(i = 0; i < cr->n; i++) + { + set_bits_value(cr->vals_rems, i, vals_table[i] & rems_mask, cr->rem_r, rems_mask); + } + + for(i = 1, j = 0; i <= cr->max_val >> cr->rem_r; i++) + { + while(i > (vals_table[j] >> cr->rem_r)) + { + j++; + } + select_vec[i - 1] = j; + }; + + + // FABIANO: before it was (cr->total_length >> cr->rem_r) + 1. But I wiped out the + 1 because + // I changed the select structure to work up to m, instead of up to m - 1. + select_generate(&cr->sel, select_vec, cr->max_val >> cr->rem_r, cr->n); + + free(select_vec); +} + +cmph_uint32 compressed_rank_query(compressed_rank_t * cr, cmph_uint32 idx) +{ + register cmph_uint32 rems_mask; + register cmph_uint32 val_quot, val_rem; + register cmph_uint32 sel_res, rank; + + if(idx > cr->max_val) + { + return cr->n; + } + + val_quot = idx >> cr->rem_r; + rems_mask = (1U << cr->rem_r) - 1U; + val_rem = idx & rems_mask; + if(val_quot == 0) + { + rank = sel_res = 0; + } + else + { + sel_res = select_query(&cr->sel, val_quot - 1) + 1; + rank = sel_res - val_quot; + } + + do + { + if(GETBIT32(cr->sel.bits_vec, sel_res)) + { + break; + } + if(get_bits_value(cr->vals_rems, rank, cr->rem_r, rems_mask) >= val_rem) + { + break; + } + sel_res++; + rank++; + } while(1); + + return rank; +} + +cmph_uint32 compressed_rank_get_space_usage(compressed_rank_t * cr) +{ + register cmph_uint32 space_usage = select_get_space_usage(&cr->sel); + space_usage += BITS_TABLE_SIZE(cr->n, cr->rem_r)*(cmph_uint32)sizeof(cmph_uint32)*8; + space_usage += 3*(cmph_uint32)sizeof(cmph_uint32)*8; + return space_usage; +} + +void compressed_rank_dump(compressed_rank_t * cr, char **buf, cmph_uint32 *buflen) +{ + register cmph_uint32 sel_size = select_packed_size(&(cr->sel)); + register cmph_uint32 vals_rems_size = BITS_TABLE_SIZE(cr->n, cr->rem_r) * (cmph_uint32)sizeof(cmph_uint32); + register cmph_uint32 pos = 0; + char * buf_sel = 0; + cmph_uint32 buflen_sel = 0; +#ifdef DEBUG + cmph_uint32 i; +#endif + + *buflen = 4*(cmph_uint32)sizeof(cmph_uint32) + sel_size + vals_rems_size; + + DEBUGP("sel_size = %u\n", sel_size); + DEBUGP("vals_rems_size = %u\n", vals_rems_size); + + *buf = (char *)calloc(*buflen, sizeof(char)); + + if (!*buf) + { + *buflen = UINT_MAX; + return; + } + + // dumping max_val, n and rem_r + memcpy(*buf, &(cr->max_val), sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("max_val = %u\n", cr->max_val); + + memcpy(*buf + pos, &(cr->n), sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("n = %u\n", cr->n); + + memcpy(*buf + pos, &(cr->rem_r), sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("rem_r = %u\n", cr->rem_r); + + // dumping sel + select_dump(&cr->sel, &buf_sel, &buflen_sel); + memcpy(*buf + pos, &buflen_sel, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("buflen_sel = %u\n", buflen_sel); + + memcpy(*buf + pos, buf_sel, buflen_sel); + + #ifdef DEBUG + i = 0; + for(i = 0; i < buflen_sel; i++) + { + DEBUGP("pos = %u -- buf_sel[%u] = %u\n", pos, i, *(*buf + pos + i)); + } + #endif + pos += buflen_sel; + + free(buf_sel); + + // dumping vals_rems + memcpy(*buf + pos, cr->vals_rems, vals_rems_size); + #ifdef DEBUG + for(i = 0; i < vals_rems_size; i++) + { + DEBUGP("pos = %u -- vals_rems_size = %u -- vals_rems[%u] = %u\n", pos, vals_rems_size, i, *(*buf + pos + i)); + } + #endif + pos += vals_rems_size; + + DEBUGP("Dumped compressed rank structure with size %u bytes\n", *buflen); +} + +void compressed_rank_load(compressed_rank_t * cr, const char *buf, cmph_uint32 buflen) +{ + register cmph_uint32 pos = 0; + cmph_uint32 buflen_sel = 0; + register cmph_uint32 vals_rems_size = 0; +#ifdef DEBUG + cmph_uint32 i; +#endif + + // loading max_val, n, and rem_r + memcpy(&(cr->max_val), buf, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("max_val = %u\n", cr->max_val); + + memcpy(&(cr->n), buf + pos, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("n = %u\n", cr->n); + + memcpy(&(cr->rem_r), buf + pos, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("rem_r = %u\n", cr->rem_r); + + // loading sel + memcpy(&buflen_sel, buf + pos, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("buflen_sel = %u\n", buflen_sel); + + select_load(&cr->sel, buf + pos, buflen_sel); + #ifdef DEBUG + i = 0; + for(i = 0; i < buflen_sel; i++) + { + DEBUGP("pos = %u -- buf_sel[%u] = %u\n", pos, i, *(buf + pos + i)); + } + #endif + pos += buflen_sel; + + // loading vals_rems + if(cr->vals_rems) + { + free(cr->vals_rems); + } + vals_rems_size = BITS_TABLE_SIZE(cr->n, cr->rem_r); + cr->vals_rems = (cmph_uint32 *) calloc(vals_rems_size, sizeof(cmph_uint32)); + vals_rems_size *= 4; + memcpy(cr->vals_rems, buf + pos, vals_rems_size); + + #ifdef DEBUG + for(i = 0; i < vals_rems_size; i++) + { + DEBUGP("pos = %u -- vals_rems_size = %u -- vals_rems[%u] = %u\n", pos, vals_rems_size, i, *(buf + pos + i)); + } + #endif + pos += vals_rems_size; + + DEBUGP("Loaded compressed rank structure with size %u bytes\n", buflen); +} + + + +void compressed_rank_pack(compressed_rank_t *cr, void *cr_packed) +{ + if (cr && cr_packed) + { + char *buf = NULL; + cmph_uint32 buflen = 0; + compressed_rank_dump(cr, &buf, &buflen); + memcpy(cr_packed, buf, buflen); + free(buf); + } +} + +cmph_uint32 compressed_rank_packed_size(compressed_rank_t *cr) +{ + register cmph_uint32 sel_size = select_packed_size(&cr->sel); + register cmph_uint32 vals_rems_size = BITS_TABLE_SIZE(cr->n, cr->rem_r) * (cmph_uint32)sizeof(cmph_uint32); + return 4 * (cmph_uint32)sizeof(cmph_uint32) + sel_size + vals_rems_size; +} + +cmph_uint32 compressed_rank_query_packed(void * cr_packed, cmph_uint32 idx) +{ + // unpacking cr_packed + register cmph_uint32 *ptr = (cmph_uint32 *)cr_packed; + register cmph_uint32 max_val = *ptr++; + register cmph_uint32 n = *ptr++; + register cmph_uint32 rem_r = *ptr++; + register cmph_uint32 buflen_sel = *ptr++; + register cmph_uint32 * sel_packed = ptr; + + register cmph_uint32 * bits_vec = sel_packed + 2; // skipping n and m + + register cmph_uint32 * vals_rems = (ptr += (buflen_sel >> 2)); + + // compressed sequence query computation + register cmph_uint32 rems_mask; + register cmph_uint32 val_quot, val_rem; + register cmph_uint32 sel_res, rank; + + if(idx > max_val) + { + return n; + } + + val_quot = idx >> rem_r; + rems_mask = (1U << rem_r) - 1U; + val_rem = idx & rems_mask; + if(val_quot == 0) + { + rank = sel_res = 0; + } + else + { + sel_res = select_query_packed(sel_packed, val_quot - 1) + 1; + rank = sel_res - val_quot; + } + + do + { + if(GETBIT32(bits_vec, sel_res)) + { + break; + } + if(get_bits_value(vals_rems, rank, rem_r, rems_mask) >= val_rem) + { + break; + } + sel_res++; + rank++; + } while(1); + + return rank; +} + + + diff --git a/girepository/cmph/compressed_rank.h b/girepository/cmph/compressed_rank.h new file mode 100644 index 000000000..bfe930dd3 --- /dev/null +++ b/girepository/cmph/compressed_rank.h @@ -0,0 +1,55 @@ +#ifndef __CMPH_COMPRESSED_RANK_H__ +#define __CMPH_COMPRESSED_RANK_H__ + +#include "select.h" + +struct _compressed_rank_t +{ + cmph_uint32 max_val; + cmph_uint32 n; // number of values stored in vals_rems + // The length in bits of each value is decomposed into two compnents: the lg(n) MSBs are stored in rank_select data structure + // the remaining LSBs are stored in a table of n cells, each one of rem_r bits. + cmph_uint32 rem_r; + select_t sel; + cmph_uint32 * vals_rems; +}; + +typedef struct _compressed_rank_t compressed_rank_t; + +void compressed_rank_init(compressed_rank_t * cr); + +void compressed_rank_destroy(compressed_rank_t * cr); + +void compressed_rank_generate(compressed_rank_t * cr, cmph_uint32 * vals_table, cmph_uint32 n); + +cmph_uint32 compressed_rank_query(compressed_rank_t * cr, cmph_uint32 idx); + +cmph_uint32 compressed_rank_get_space_usage(compressed_rank_t * cr); + +void compressed_rank_dump(compressed_rank_t * cr, char **buf, cmph_uint32 *buflen); + +void compressed_rank_load(compressed_rank_t * cr, const char *buf, cmph_uint32 buflen); + + +/** \fn void compressed_rank_pack(compressed_rank_t *cr, void *cr_packed); + * \brief Support the ability to pack a compressed_rank structure into a preallocated contiguous memory space pointed by cr_packed. + * \param cr points to the compressed_rank structure + * \param cr_packed pointer to the contiguous memory area used to store the compressed_rank structure. The size of cr_packed must be at least @see compressed_rank_packed_size + */ +void compressed_rank_pack(compressed_rank_t *cr, void *cr_packed); + +/** \fn cmph_uint32 compressed_rank_packed_size(compressed_rank_t *cr); + * \brief Return the amount of space needed to pack a compressed_rank structure. + * \return the size of the packed compressed_rank structure or zero for failures + */ +cmph_uint32 compressed_rank_packed_size(compressed_rank_t *cr); + + +/** \fn cmph_uint32 compressed_rank_query_packed(void * cr_packed, cmph_uint32 idx); + * \param cr_packed is a pointer to a contiguous memory area + * \param idx is an index to compute the rank + * \return an integer that represents the compressed_rank value. + */ +cmph_uint32 compressed_rank_query_packed(void * cr_packed, cmph_uint32 idx); + +#endif diff --git a/girepository/cmph/compressed_seq.c b/girepository/cmph/compressed_seq.c new file mode 100644 index 000000000..e5191fd5a --- /dev/null +++ b/girepository/cmph/compressed_seq.c @@ -0,0 +1,384 @@ +#include "compressed_seq.h" +#include +#include +#include +#include +#include + +#include "bitbool.h" + +// #define DEBUG +#include "debug.h" + +static inline cmph_uint32 compressed_seq_i_log2(cmph_uint32 x) +{ + register cmph_uint32 res = 0; + + while(x > 1) + { + x >>= 1; + res++; + } + return res; +}; + +void compressed_seq_init(compressed_seq_t * cs) +{ + select_init(&cs->sel); + cs->n = 0; + cs->rem_r = 0; + cs->length_rems = 0; + cs->total_length = 0; + cs->store_table = 0; +} + +void compressed_seq_destroy(compressed_seq_t * cs) +{ + free(cs->store_table); + cs->store_table = 0; + free(cs->length_rems); + cs->length_rems = 0; + select_destroy(&cs->sel); +}; + + +void compressed_seq_generate(compressed_seq_t * cs, cmph_uint32 * vals_table, cmph_uint32 n) +{ + register cmph_uint32 i; + // lengths: represents lengths of encoded values + register cmph_uint32 * lengths = (cmph_uint32 *)calloc(n, sizeof(cmph_uint32)); + register cmph_uint32 rems_mask; + register cmph_uint32 stored_value; + + cs->n = n; + cs->total_length = 0; + + for(i = 0; i < cs->n; i++) + { + if(vals_table[i] == 0) + { + lengths[i] = 0; + } + else + { + lengths[i] = compressed_seq_i_log2(vals_table[i] + 1); + cs->total_length += lengths[i]; + }; + }; + + if(cs->store_table) + { + free(cs->store_table); + } + cs->store_table = (cmph_uint32 *) calloc(((cs->total_length + 31) >> 5), sizeof(cmph_uint32)); + cs->total_length = 0; + + for(i = 0; i < cs->n; i++) + { + if(vals_table[i] == 0) + continue; + stored_value = vals_table[i] - ((1U << lengths[i]) - 1U); + set_bits_at_pos(cs->store_table, cs->total_length, stored_value, lengths[i]); + cs->total_length += lengths[i]; + }; + + cs->rem_r = compressed_seq_i_log2(cs->total_length/cs->n); + + if(cs->rem_r == 0) + { + cs->rem_r = 1; + } + + if(cs->length_rems) + { + free(cs->length_rems); + } + + cs->length_rems = (cmph_uint32 *) calloc(BITS_TABLE_SIZE(cs->n, cs->rem_r), sizeof(cmph_uint32)); + + rems_mask = (1U << cs->rem_r) - 1U; + cs->total_length = 0; + + for(i = 0; i < cs->n; i++) + { + cs->total_length += lengths[i]; + set_bits_value(cs->length_rems, i, cs->total_length & rems_mask, cs->rem_r, rems_mask); + lengths[i] = cs->total_length >> cs->rem_r; + }; + + select_init(&cs->sel); + + // FABIANO: before it was (cs->total_length >> cs->rem_r) + 1. But I wiped out the + 1 because + // I changed the select structure to work up to m, instead of up to m - 1. + select_generate(&cs->sel, lengths, cs->n, (cs->total_length >> cs->rem_r)); + + free(lengths); +}; + +cmph_uint32 compressed_seq_get_space_usage(compressed_seq_t * cs) +{ + register cmph_uint32 space_usage = select_get_space_usage(&cs->sel); + space_usage += ((cs->total_length + 31) >> 5) * (cmph_uint32)sizeof(cmph_uint32) * 8; + space_usage += BITS_TABLE_SIZE(cs->n, cs->rem_r) * (cmph_uint32)sizeof(cmph_uint32) * 8; + return 4 * (cmph_uint32)sizeof(cmph_uint32) * 8 + space_usage; +} + +cmph_uint32 compressed_seq_query(compressed_seq_t * cs, cmph_uint32 idx) +{ + register cmph_uint32 enc_idx, enc_length; + register cmph_uint32 rems_mask; + register cmph_uint32 stored_value; + register cmph_uint32 sel_res; + + assert(idx < cs->n); // FABIANO ADDED + + rems_mask = (1U << cs->rem_r) - 1U; + + if(idx == 0) + { + enc_idx = 0; + sel_res = select_query(&cs->sel, idx); + } + else + { + sel_res = select_query(&cs->sel, idx - 1); + + enc_idx = (sel_res - (idx - 1)) << cs->rem_r; + enc_idx += get_bits_value(cs->length_rems, idx-1, cs->rem_r, rems_mask); + + sel_res = select_next_query(&cs->sel, sel_res); + }; + + enc_length = (sel_res - idx) << cs->rem_r; + enc_length += get_bits_value(cs->length_rems, idx, cs->rem_r, rems_mask); + enc_length -= enc_idx; + if(enc_length == 0) + return 0; + + stored_value = get_bits_at_pos(cs->store_table, enc_idx, enc_length); + return stored_value + ((1U << enc_length) - 1U); +}; + +void compressed_seq_dump(compressed_seq_t * cs, char ** buf, cmph_uint32 * buflen) +{ + register cmph_uint32 sel_size = select_packed_size(&(cs->sel)); + register cmph_uint32 length_rems_size = BITS_TABLE_SIZE(cs->n, cs->rem_r) * 4; + register cmph_uint32 store_table_size = ((cs->total_length + 31) >> 5) * 4; + register cmph_uint32 pos = 0; + char * buf_sel = 0; + cmph_uint32 buflen_sel = 0; +#ifdef DEBUG + cmph_uint32 i; +#endif + + *buflen = 4*(cmph_uint32)sizeof(cmph_uint32) + sel_size + length_rems_size + store_table_size; + + DEBUGP("sel_size = %u\n", sel_size); + DEBUGP("length_rems_size = %u\n", length_rems_size); + DEBUGP("store_table_size = %u\n", store_table_size); + *buf = (char *)calloc(*buflen, sizeof(char)); + + if (!*buf) + { + *buflen = UINT_MAX; + return; + } + + // dumping n, rem_r and total_length + memcpy(*buf, &(cs->n), sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("n = %u\n", cs->n); + + memcpy(*buf + pos, &(cs->rem_r), sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("rem_r = %u\n", cs->rem_r); + + memcpy(*buf + pos, &(cs->total_length), sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("total_length = %u\n", cs->total_length); + + + // dumping sel + select_dump(&cs->sel, &buf_sel, &buflen_sel); + memcpy(*buf + pos, &buflen_sel, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("buflen_sel = %u\n", buflen_sel); + + memcpy(*buf + pos, buf_sel, buflen_sel); + #ifdef DEBUG + i = 0; + for(i = 0; i < buflen_sel; i++) + { + DEBUGP("pos = %u -- buf_sel[%u] = %u\n", pos, i, *(*buf + pos + i)); + } + #endif + pos += buflen_sel; + + free(buf_sel); + + // dumping length_rems + memcpy(*buf + pos, cs->length_rems, length_rems_size); + #ifdef DEBUG + for(i = 0; i < length_rems_size; i++) + { + DEBUGP("pos = %u -- length_rems_size = %u -- length_rems[%u] = %u\n", pos, length_rems_size, i, *(*buf + pos + i)); + } + #endif + pos += length_rems_size; + + // dumping store_table + memcpy(*buf + pos, cs->store_table, store_table_size); + + #ifdef DEBUG + for(i = 0; i < store_table_size; i++) + { + DEBUGP("pos = %u -- store_table_size = %u -- store_table[%u] = %u\n", pos, store_table_size, i, *(*buf + pos + i)); + } + #endif + DEBUGP("Dumped compressed sequence structure with size %u bytes\n", *buflen); +} + +void compressed_seq_load(compressed_seq_t * cs, const char * buf, cmph_uint32 buflen) +{ + register cmph_uint32 pos = 0; + cmph_uint32 buflen_sel = 0; + register cmph_uint32 length_rems_size = 0; + register cmph_uint32 store_table_size = 0; +#ifdef DEBUG + cmph_uint32 i; +#endif + + // loading n, rem_r and total_length + memcpy(&(cs->n), buf, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("n = %u\n", cs->n); + + memcpy(&(cs->rem_r), buf + pos, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("rem_r = %u\n", cs->rem_r); + + memcpy(&(cs->total_length), buf + pos, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("total_length = %u\n", cs->total_length); + + // loading sel + memcpy(&buflen_sel, buf + pos, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("buflen_sel = %u\n", buflen_sel); + + select_load(&cs->sel, buf + pos, buflen_sel); + #ifdef DEBUG + i = 0; + for(i = 0; i < buflen_sel; i++) + { + DEBUGP("pos = %u -- buf_sel[%u] = %u\n", pos, i, *(buf + pos + i)); + } + #endif + pos += buflen_sel; + + // loading length_rems + if(cs->length_rems) + { + free(cs->length_rems); + } + length_rems_size = BITS_TABLE_SIZE(cs->n, cs->rem_r); + cs->length_rems = (cmph_uint32 *) calloc(length_rems_size, sizeof(cmph_uint32)); + length_rems_size *= 4; + memcpy(cs->length_rems, buf + pos, length_rems_size); + + #ifdef DEBUG + for(i = 0; i < length_rems_size; i++) + { + DEBUGP("pos = %u -- length_rems_size = %u -- length_rems[%u] = %u\n", pos, length_rems_size, i, *(buf + pos + i)); + } + #endif + pos += length_rems_size; + + // loading store_table + store_table_size = ((cs->total_length + 31) >> 5); + if(cs->store_table) + { + free(cs->store_table); + } + cs->store_table = (cmph_uint32 *) calloc(store_table_size, sizeof(cmph_uint32)); + store_table_size *= 4; + memcpy(cs->store_table, buf + pos, store_table_size); + + #ifdef DEBUG + for(i = 0; i < store_table_size; i++) + { + DEBUGP("pos = %u -- store_table_size = %u -- store_table[%u] = %u\n", pos, store_table_size, i, *(buf + pos + i)); + } + #endif + + DEBUGP("Loaded compressed sequence structure with size %u bytes\n", buflen); +} + +void compressed_seq_pack(compressed_seq_t *cs, void *cs_packed) +{ + if (cs && cs_packed) + { + char *buf = NULL; + cmph_uint32 buflen = 0; + compressed_seq_dump(cs, &buf, &buflen); + memcpy(cs_packed, buf, buflen); + free(buf); + } + +} + +cmph_uint32 compressed_seq_packed_size(compressed_seq_t *cs) +{ + register cmph_uint32 sel_size = select_packed_size(&cs->sel); + register cmph_uint32 store_table_size = ((cs->total_length + 31) >> 5) * (cmph_uint32)sizeof(cmph_uint32); + register cmph_uint32 length_rems_size = BITS_TABLE_SIZE(cs->n, cs->rem_r) * (cmph_uint32)sizeof(cmph_uint32); + return 4 * (cmph_uint32)sizeof(cmph_uint32) + sel_size + store_table_size + length_rems_size; +} + + +cmph_uint32 compressed_seq_query_packed(void * cs_packed, cmph_uint32 idx) +{ + // unpacking cs_packed + register cmph_uint32 *ptr = (cmph_uint32 *)cs_packed; + register cmph_uint32 n = *ptr++; + register cmph_uint32 rem_r = *ptr++; + register cmph_uint32 buflen_sel, length_rems_size, enc_idx, enc_length; + // compressed sequence query computation + register cmph_uint32 rems_mask, stored_value, sel_res; + register cmph_uint32 *sel_packed, *length_rems, *store_table; + + ptr++; // skipping total_length +// register cmph_uint32 total_length = *ptr++; + buflen_sel = *ptr++; + sel_packed = ptr; + length_rems = (ptr += (buflen_sel >> 2)); + length_rems_size = BITS_TABLE_SIZE(n, rem_r); + store_table = (ptr += length_rems_size); + + + rems_mask = (1U << rem_r) - 1U; + + if(idx == 0) + { + enc_idx = 0; + sel_res = select_query_packed(sel_packed, idx); + } + else + { + sel_res = select_query_packed(sel_packed, idx - 1); + + enc_idx = (sel_res - (idx - 1)) << rem_r; + enc_idx += get_bits_value(length_rems, idx-1, rem_r, rems_mask); + + sel_res = select_next_query_packed(sel_packed, sel_res); + }; + + enc_length = (sel_res - idx) << rem_r; + enc_length += get_bits_value(length_rems, idx, rem_r, rems_mask); + enc_length -= enc_idx; + if(enc_length == 0) + return 0; + + stored_value = get_bits_at_pos(store_table, enc_idx, enc_length); + return stored_value + ((1U << enc_length) - 1U); +} diff --git a/girepository/cmph/compressed_seq.h b/girepository/cmph/compressed_seq.h new file mode 100644 index 000000000..8d87fc700 --- /dev/null +++ b/girepository/cmph/compressed_seq.h @@ -0,0 +1,84 @@ +#ifndef __CMPH_COMPRESSED_SEQ_H__ +#define __CMPH_COMPRESSED_SEQ_H__ + +#include"select.h" + +struct _compressed_seq_t +{ + cmph_uint32 n; // number of values stored in store_table + // The length in bits of each value is decomposed into two compnents: the lg(n) MSBs are stored in rank_select data structure + // the remaining LSBs are stored in a table of n cells, each one of rem_r bits. + cmph_uint32 rem_r; + cmph_uint32 total_length; // total length in bits of stored_table + select_t sel; + cmph_uint32 * length_rems; + cmph_uint32 * store_table; +}; + +typedef struct _compressed_seq_t compressed_seq_t; + +/** \fn void compressed_seq_init(compressed_seq_t * cs); + * \brief Initialize a compressed sequence structure. + * \param cs points to the compressed sequence structure to be initialized + */ +void compressed_seq_init(compressed_seq_t * cs); + +/** \fn void compressed_seq_destroy(compressed_seq_t * cs); + * \brief Destroy a compressed sequence given as input. + * \param cs points to the compressed sequence structure to be destroyed + */ +void compressed_seq_destroy(compressed_seq_t * cs); + +/** \fn void compressed_seq_generate(compressed_seq_t * cs, cmph_uint32 * vals_table, cmph_uint32 n); + * \brief Generate a compressed sequence from an input array with n values. + * \param cs points to the compressed sequence structure + * \param vals_table poiter to the array given as input + * \param n number of values in @see vals_table + */ +void compressed_seq_generate(compressed_seq_t * cs, cmph_uint32 * vals_table, cmph_uint32 n); + + +/** \fn cmph_uint32 compressed_seq_query(compressed_seq_t * cs, cmph_uint32 idx); + * \brief Returns the value stored at index @see idx of the compressed sequence structure. + * \param cs points to the compressed sequence structure + * \param idx index to retrieve the value from + * \return the value stored at index @see idx of the compressed sequence structure + */ +cmph_uint32 compressed_seq_query(compressed_seq_t * cs, cmph_uint32 idx); + + +/** \fn cmph_uint32 compressed_seq_get_space_usage(compressed_seq_t * cs); + * \brief Returns amount of space (in bits) to store the compressed sequence. + * \param cs points to the compressed sequence structure + * \return the amount of space (in bits) to store @see cs + */ +cmph_uint32 compressed_seq_get_space_usage(compressed_seq_t * cs); + +void compressed_seq_dump(compressed_seq_t * cs, char ** buf, cmph_uint32 * buflen); + +void compressed_seq_load(compressed_seq_t * cs, const char * buf, cmph_uint32 buflen); + + +/** \fn void compressed_seq_pack(compressed_seq_t *cs, void *cs_packed); + * \brief Support the ability to pack a compressed sequence structure into a preallocated contiguous memory space pointed by cs_packed. + * \param cs points to the compressed sequence structure + * \param cs_packed pointer to the contiguous memory area used to store the compressed sequence structure. The size of cs_packed must be at least @see compressed_seq_packed_size + */ +void compressed_seq_pack(compressed_seq_t *cs, void *cs_packed); + +/** \fn cmph_uint32 compressed_seq_packed_size(compressed_seq_t *cs); + * \brief Return the amount of space needed to pack a compressed sequence structure. + * \return the size of the packed compressed sequence structure or zero for failures + */ +cmph_uint32 compressed_seq_packed_size(compressed_seq_t *cs); + + +/** \fn cmph_uint32 compressed_seq_query_packed(void * cs_packed, cmph_uint32 idx); + * \brief Returns the value stored at index @see idx of the packed compressed sequence structure. + * \param cs_packed is a pointer to a contiguous memory area + * \param idx is the index to retrieve the value from + * \return the value stored at index @see idx of the packed compressed sequence structure + */ +cmph_uint32 compressed_seq_query_packed(void * cs_packed, cmph_uint32 idx); + +#endif diff --git a/girepository/cmph/debug.h b/girepository/cmph/debug.h new file mode 100644 index 000000000..0f7ddb139 --- /dev/null +++ b/girepository/cmph/debug.h @@ -0,0 +1,53 @@ +#ifdef DEBUGP +#undef DEBUGP +#endif + +#ifdef __cplusplus +#include +#ifdef WIN32 +#include +#endif +#else +#include +#ifdef WIN32 +#include +#endif +#endif + +#ifndef __GNUC__ +#ifndef __DEBUG_H__ +#define __DEBUG_H__ +#include +static void debugprintf(const char *format, ...) +{ + va_list ap; + char *f = NULL; + const char *p="%s:%d "; + size_t plen = strlen(p); + va_start(ap, format); + f = (char *)malloc(plen + strlen(format) + 1); + if (!f) return; + memcpy(f, p, plen); + memcpy(f + plen, format, strlen(format) + 1); + vfprintf(stderr, f, ap); + va_end(ap); + free(f); +} +static void dummyprintf(const char *format, ...) +{} +#endif +#endif + +#ifdef DEBUG +#ifndef __GNUC__ +#define DEBUGP debugprintf +#else +#define DEBUGP(args...) do { fprintf(stderr, "%s:%d ", __FILE__, __LINE__); fprintf(stderr, ## args); } while(0) +#endif +#else +#ifndef __GNUC__ +#define DEBUGP dummyprintf +#else +#define DEBUGP(args...) +#endif +#endif diff --git a/girepository/cmph/djb2_hash.c b/girepository/cmph/djb2_hash.c new file mode 100644 index 000000000..d3b4330ab --- /dev/null +++ b/girepository/cmph/djb2_hash.c @@ -0,0 +1,49 @@ +#include "djb2_hash.h" +#include + +djb2_state_t *djb2_state_new() +{ + djb2_state_t *state = (djb2_state_t *)malloc(sizeof(djb2_state_t)); + state->hashfunc = CMPH_HASH_DJB2; + return state; +} + +void djb2_state_destroy(djb2_state_t *state) +{ + free(state); +} + +cmph_uint32 djb2_hash(djb2_state_t *state, const char *k, cmph_uint32 keylen) +{ + register cmph_uint32 hash = 5381; + const unsigned char *ptr = (unsigned char *)k; + cmph_uint32 i = 0; + while (i < keylen) + { + hash = hash*33 ^ *ptr; + ++ptr, ++i; + } + return hash; +} + + +void djb2_state_dump(djb2_state_t *state, char **buf, cmph_uint32 *buflen) +{ + *buf = NULL; + *buflen = 0; + return; +} + +djb2_state_t *djb2_state_copy(djb2_state_t *src_state) +{ + djb2_state_t *dest_state = (djb2_state_t *)malloc(sizeof(djb2_state_t)); + dest_state->hashfunc = src_state->hashfunc; + return dest_state; +} + +djb2_state_t *djb2_state_load(const char *buf, cmph_uint32 buflen) +{ + djb2_state_t *state = (djb2_state_t *)malloc(sizeof(djb2_state_t)); + state->hashfunc = CMPH_HASH_DJB2; + return state; +} diff --git a/girepository/cmph/djb2_hash.h b/girepository/cmph/djb2_hash.h new file mode 100644 index 000000000..dda97e317 --- /dev/null +++ b/girepository/cmph/djb2_hash.h @@ -0,0 +1,18 @@ +#ifndef __DJB2_HASH_H__ +#define __DJB2_HASH_H__ + +#include "hash.h" + +typedef struct __djb2_state_t +{ + CMPH_HASH hashfunc; +} djb2_state_t; + +djb2_state_t *djb2_state_new(); +cmph_uint32 djb2_hash(djb2_state_t *state, const char *k, cmph_uint32 keylen); +void djb2_state_dump(djb2_state_t *state, char **buf, cmph_uint32 *buflen); +djb2_state_t *djb2_state_copy(djb2_state_t *src_state); +djb2_state_t *djb2_state_load(const char *buf, cmph_uint32 buflen); +void djb2_state_destroy(djb2_state_t *state); + +#endif diff --git a/girepository/cmph/fch.c b/girepository/cmph/fch.c new file mode 100644 index 000000000..f12b6fcd2 --- /dev/null +++ b/girepository/cmph/fch.c @@ -0,0 +1,539 @@ +#include "fch.h" +#include "cmph_structs.h" +#include "fch_structs.h" +#include "hash.h" +#include "bitbool.h" +#include "fch_buckets.h" +#include +#include +#include +#include +#include +#include + +#define INDEX 0 /* alignment index within a bucket */ +//#define DEBUG +#include "debug.h" + +static fch_buckets_t * mapping(cmph_config_t *mph); +static cmph_uint32 * ordering(fch_buckets_t * buckets); +static cmph_uint8 check_for_collisions_h2(fch_config_data_t *fch, fch_buckets_t * buckets, cmph_uint32 *sorted_indexes); +static void permut(cmph_uint32 * vector, cmph_uint32 n); +static cmph_uint8 searching(fch_config_data_t *fch, fch_buckets_t *buckets, cmph_uint32 *sorted_indexes); + +fch_config_data_t *fch_config_new(void) +{ + fch_config_data_t *fch; + fch = (fch_config_data_t *)malloc(sizeof(fch_config_data_t)); + assert(fch); + memset(fch, 0, sizeof(fch_config_data_t)); + fch->hashfuncs[0] = CMPH_HASH_JENKINS; + fch->hashfuncs[1] = CMPH_HASH_JENKINS; + fch->m = fch->b = 0; + fch->c = fch->p1 = fch->p2 = 0.0; + fch->g = NULL; + fch->h1 = NULL; + fch->h2 = NULL; + return fch; +} + +void fch_config_destroy(cmph_config_t *mph) +{ + fch_config_data_t *data = (fch_config_data_t *)mph->data; + //DEBUGP("Destroying algorithm dependent data\n"); + free(data); +} + +void fch_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + fch_config_data_t *fch = (fch_config_data_t *)mph->data; + CMPH_HASH *hashptr = hashfuncs; + cmph_uint32 i = 0; + while(*hashptr != CMPH_HASH_COUNT) + { + if (i >= 2) break; //fch only uses two hash functions + fch->hashfuncs[i] = *hashptr; + ++i, ++hashptr; + } +} + +cmph_uint32 mixh10h11h12(cmph_uint32 b, double p1, double p2, cmph_uint32 initial_index) +{ + register cmph_uint32 int_p2 = (cmph_uint32)p2; + if (initial_index < p1) initial_index %= int_p2; /* h11 o h10 */ + else { /* h12 o h10 */ + initial_index %= b; + if(initial_index < p2) initial_index += int_p2; + } + return initial_index; +} + + +cmph_uint32 fch_calc_b(double c, cmph_uint32 m) +{ + return (cmph_uint32)ceil((c*m)/(log((double)m)/log(2.0) + 1)); +} + +double fch_calc_p1(cmph_uint32 m) +{ + return ceil(0.55*m); +} + +double fch_calc_p2(cmph_uint32 b) +{ + return ceil(0.3*b); +} + +static fch_buckets_t * mapping(cmph_config_t *mph) +{ + cmph_uint32 i = 0; + fch_buckets_t *buckets = NULL; + fch_config_data_t *fch = (fch_config_data_t *)mph->data; + if (fch->h1) hash_state_destroy(fch->h1); + fch->h1 = hash_state_new(fch->hashfuncs[0], fch->m); + fch->b = fch_calc_b(fch->c, fch->m); + fch->p1 = fch_calc_p1(fch->m); + fch->p2 = fch_calc_p2(fch->b); + //DEBUGP("b:%u p1:%f p2:%f\n", fch->b, fch->p1, fch->p2); + buckets = fch_buckets_new(fch->b); + + mph->key_source->rewind(mph->key_source->data); + for(i = 0; i < fch->m; i++) + { + cmph_uint32 h1, keylen; + char *key = NULL; + mph->key_source->read(mph->key_source->data, &key, &keylen); + h1 = hash(fch->h1, key, keylen) % fch->m; + h1 = mixh10h11h12 (fch->b, fch->p1, fch->p2, h1); + fch_buckets_insert(buckets, h1, key, keylen); + key = NULL; // transger memory ownership + + } + return buckets; +} + + +// returns the buckets indexes sorted by their sizes. +static cmph_uint32 * ordering(fch_buckets_t * buckets) +{ + return fch_buckets_get_indexes_sorted_by_size(buckets); +} + +/* Check whether function h2 causes collisions among the keys of each bucket */ +static cmph_uint8 check_for_collisions_h2(fch_config_data_t *fch, fch_buckets_t * buckets, cmph_uint32 *sorted_indexes) +{ + //cmph_uint32 max_size = fch_buckets_get_max_size(buckets); + cmph_uint8 * hashtable = (cmph_uint8 *)calloc((size_t)fch->m, sizeof(cmph_uint8)); + cmph_uint32 nbuckets = fch_buckets_get_nbuckets(buckets); + cmph_uint32 i = 0, index = 0, j =0; + for (i = 0; i < nbuckets; i++) + { + cmph_uint32 nkeys = fch_buckets_get_size(buckets, sorted_indexes[i]); + memset(hashtable, 0, (size_t)fch->m); + //DEBUGP("bucket %u -- nkeys: %u\n", i, nkeys); + for (j = 0; j < nkeys; j++) + { + char * key = fch_buckets_get_key(buckets, sorted_indexes[i], j); + cmph_uint32 keylen = fch_buckets_get_keylength(buckets, sorted_indexes[i], j); + index = hash(fch->h2, key, keylen) % fch->m; + if(hashtable[index]) { // collision detected + free(hashtable); + return 1; + } + hashtable[index] = 1; + } + } + free(hashtable); + return 0; +} + +static void permut(cmph_uint32 * vector, cmph_uint32 n) +{ + cmph_uint32 i, j, b; + for (i = 0; i < n; i++) { + j = (cmph_uint32) rand() % n; + b = vector[i]; + vector[i] = vector[j]; + vector[j] = b; + } +} + +static cmph_uint8 searching(fch_config_data_t *fch, fch_buckets_t *buckets, cmph_uint32 *sorted_indexes) +{ + cmph_uint32 * random_table = (cmph_uint32 *) calloc((size_t)fch->m, sizeof(cmph_uint32)); + cmph_uint32 * map_table = (cmph_uint32 *) calloc((size_t)fch->m, sizeof(cmph_uint32)); + cmph_uint32 iteration_to_generate_h2 = 0; + cmph_uint32 searching_iterations = 0; + cmph_uint8 restart = 0; + cmph_uint32 nbuckets = fch_buckets_get_nbuckets(buckets); + cmph_uint32 i, j, z, counter = 0, filled_count = 0; + if (fch->g) free (fch->g); + fch->g = (cmph_uint32 *) calloc((size_t)fch->b, sizeof(cmph_uint32)); + + //DEBUGP("max bucket size: %u\n", fch_buckets_get_max_size(buckets)); + + for(i = 0; i < fch->m; i++) + { + random_table[i] = i; + } + permut(random_table, fch->m); + for(i = 0; i < fch->m; i++) + { + map_table[random_table[i]] = i; + } + do { + if (fch->h2) hash_state_destroy(fch->h2); + fch->h2 = hash_state_new(fch->hashfuncs[1], fch->m); + restart = check_for_collisions_h2(fch, buckets, sorted_indexes); + filled_count = 0; + if (!restart) + { + searching_iterations++; iteration_to_generate_h2 = 0; + //DEBUGP("searching_iterations: %u\n", searching_iterations); + } + else { + iteration_to_generate_h2++; + //DEBUGP("iteration_to_generate_h2: %u\n", iteration_to_generate_h2); + } + for(i = 0; (i < nbuckets) && !restart; i++) { + cmph_uint32 bucketsize = fch_buckets_get_size(buckets, sorted_indexes[i]); + if (bucketsize == 0) + { + restart = 0; // false + break; + } + else restart = 1; // true + for(z = 0; (z < (fch->m - filled_count)) && restart; z++) { + char * key = fch_buckets_get_key(buckets, sorted_indexes[i], INDEX); + cmph_uint32 keylen = fch_buckets_get_keylength(buckets, sorted_indexes[i], INDEX); + cmph_uint32 h2 = hash(fch->h2, key, keylen) % fch->m; + counter = 0; + restart = 0; // false + fch->g[sorted_indexes[i]] = (fch->m + random_table[filled_count + z] - h2) % fch->m; + //DEBUGP("g[%u]: %u\n", sorted_indexes[i], fch->g[sorted_indexes[i]]); + j = INDEX; + do { + cmph_uint32 index = 0; + key = fch_buckets_get_key(buckets, sorted_indexes[i], j); + keylen = fch_buckets_get_keylength(buckets, sorted_indexes[i], j); + h2 = hash(fch->h2, key, keylen) % fch->m; + index = (h2 + fch->g[sorted_indexes[i]]) % fch->m; + //DEBUGP("key:%s keylen:%u index: %u h2:%u bucketsize:%u\n", key, keylen, index, h2, bucketsize); + if (map_table[index] >= filled_count) { + cmph_uint32 y = map_table[index]; + cmph_uint32 ry = random_table[y]; + random_table[y] = random_table[filled_count]; + random_table[filled_count] = ry; + map_table[random_table[y]] = y; + map_table[random_table[filled_count]] = filled_count; + filled_count++; + counter ++; + } + else { + restart = 1; // true + filled_count = filled_count - counter; + counter = 0; + break; + } + j = (j + 1) % bucketsize; + } while(j % bucketsize != INDEX); + } + //getchar(); + } + } while(restart && (searching_iterations < 10) && (iteration_to_generate_h2 < 1000)); + free(map_table); + free(random_table); + return restart; +} + + + +cmph_t *fch_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + fch_data_t *fchf = NULL; + cmph_uint32 iterations = 100; + cmph_uint8 restart_mapping = 0; + fch_buckets_t * buckets = NULL; + cmph_uint32 * sorted_indexes = NULL; + fch_config_data_t *fch = (fch_config_data_t *)mph->data; + fch->m = mph->key_source->nkeys; + //DEBUGP("m: %f\n", fch->m); + if (c <= 2) c = 2.6; // validating restrictions over parameter c. + fch->c = c; + //DEBUGP("c: %f\n", fch->c); + fch->h1 = NULL; + fch->h2 = NULL; + fch->g = NULL; + do + { + if (mph->verbosity) + { + fprintf(stderr, "Entering mapping step for mph creation of %u keys\n", fch->m); + } + if (buckets) fch_buckets_destroy(buckets); + buckets = mapping(mph); + if (mph->verbosity) + { + fprintf(stderr, "Starting ordering step\n"); + } + if (sorted_indexes) free (sorted_indexes); + sorted_indexes = ordering(buckets); + if (mph->verbosity) + { + fprintf(stderr, "Starting searching step.\n"); + } + restart_mapping = searching(fch, buckets, sorted_indexes); + iterations--; + + } while(restart_mapping && iterations > 0); + if (buckets) fch_buckets_destroy(buckets); + if (sorted_indexes) free (sorted_indexes); + if (iterations == 0) return NULL; + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + fchf = (fch_data_t *)malloc(sizeof(fch_data_t)); + fchf->g = fch->g; + fch->g = NULL; //transfer memory ownership + fchf->h1 = fch->h1; + fch->h1 = NULL; //transfer memory ownership + fchf->h2 = fch->h2; + fch->h2 = NULL; //transfer memory ownership + fchf->p2 = fch->p2; + fchf->p1 = fch->p1; + fchf->b = fch->b; + fchf->c = fch->c; + fchf->m = fch->m; + mphf->data = fchf; + mphf->size = fch->m; + //DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + return mphf; +} + +int fch_dump(cmph_t *mphf, FILE *fd) +{ + char *buf = NULL; + cmph_uint32 buflen; + register size_t nbytes; + + fch_data_t *data = (fch_data_t *)mphf->data; + +#ifdef DEBUG + cmph_uint32 i; +#endif + __cmph_dump(mphf, fd); + + hash_state_dump(data->h1, &buf, &buflen); + //DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + hash_state_dump(data->h2, &buf, &buflen); + //DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + nbytes = fwrite(&(data->m), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->c), sizeof(double), (size_t)1, fd); + nbytes = fwrite(&(data->b), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->p1), sizeof(double), (size_t)1, fd); + nbytes = fwrite(&(data->p2), sizeof(double), (size_t)1, fd); + nbytes = fwrite(data->g, sizeof(cmph_uint32)*(data->b), (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < data->b; ++i) fprintf(stderr, "%u ", data->g[i]); + fprintf(stderr, "\n"); + #endif + return 1; +} + +void fch_load(FILE *f, cmph_t *mphf) +{ + char *buf = NULL; + cmph_uint32 buflen; + register size_t nbytes; + fch_data_t *fch = (fch_data_t *)malloc(sizeof(fch_data_t)); +#ifdef DEBUG + cmph_uint32 i; +#endif + + //DEBUGP("Loading fch mphf\n"); + mphf->data = fch; + //DEBUGP("Reading h1\n"); + fch->h1 = NULL; + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + //DEBUGP("Hash state of h1 has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + fch->h1 = hash_state_load(buf, buflen); + free(buf); + + //DEBUGP("Loading fch mphf\n"); + mphf->data = fch; + //DEBUGP("Reading h2\n"); + fch->h2 = NULL; + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + //DEBUGP("Hash state of h2 has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + fch->h2 = hash_state_load(buf, buflen); + free(buf); + + + //DEBUGP("Reading m and n\n"); + nbytes = fread(&(fch->m), sizeof(cmph_uint32), (size_t)1, f); + nbytes = fread(&(fch->c), sizeof(double), (size_t)1, f); + nbytes = fread(&(fch->b), sizeof(cmph_uint32), (size_t)1, f); + nbytes = fread(&(fch->p1), sizeof(double), (size_t)1, f); + nbytes = fread(&(fch->p2), sizeof(double), (size_t)1, f); + + fch->g = (cmph_uint32 *)malloc(sizeof(cmph_uint32)*fch->b); + nbytes = fread(fch->g, fch->b*sizeof(cmph_uint32), (size_t)1, f); + if (nbytes == 0 && ferror(f)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return; + } + + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < fch->b; ++i) fprintf(stderr, "%u ", fch->g[i]); + fprintf(stderr, "\n"); + #endif + return; +} + +cmph_uint32 fch_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + fch_data_t *fch = mphf->data; + cmph_uint32 h1 = hash(fch->h1, key, keylen) % fch->m; + cmph_uint32 h2 = hash(fch->h2, key, keylen) % fch->m; + h1 = mixh10h11h12 (fch->b, fch->p1, fch->p2, h1); + //DEBUGP("key: %s h1: %u h2: %u g[h1]: %u\n", key, h1, h2, fch->g[h1]); + return (h2 + fch->g[h1]) % fch->m; +} +void fch_destroy(cmph_t *mphf) +{ + fch_data_t *data = (fch_data_t *)mphf->data; + free(data->g); + hash_state_destroy(data->h1); + hash_state_destroy(data->h2); + free(data); + free(mphf); +} + +/** \fn void fch_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void fch_pack(cmph_t *mphf, void *packed_mphf) +{ + fch_data_t *data = (fch_data_t *)mphf->data; + cmph_uint8 * ptr = packed_mphf; + + // packing h1 type + CMPH_HASH h1_type = hash_get_type(data->h1); + CMPH_HASH h2_type; + *((cmph_uint32 *) ptr) = h1_type; + ptr += sizeof(cmph_uint32); + + // packing h1 + hash_state_pack(data->h1, ptr); + ptr += hash_state_packed_size(h1_type); + + // packing h2 type + h2_type = hash_get_type(data->h2); + *((cmph_uint32 *) ptr) = h2_type; + ptr += sizeof(cmph_uint32); + + // packing h2 + hash_state_pack(data->h2, ptr); + ptr += hash_state_packed_size(h2_type); + + // packing m + *((cmph_uint32 *) ptr) = data->m; + ptr += sizeof(data->m); + + // packing b + *((cmph_uint32 *) ptr) = data->b; + ptr += sizeof(data->b); + + // packing p1 + *((cmph_uint64 *)ptr) = (cmph_uint64)data->p1; + ptr += sizeof(data->p1); + + // packing p2 + *((cmph_uint64 *)ptr) = (cmph_uint64)data->p2; + ptr += sizeof(data->p2); + + // packing g + memcpy(ptr, data->g, sizeof(cmph_uint32)*(data->b)); +} + +/** \fn cmph_uint32 fch_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 fch_packed_size(cmph_t *mphf) +{ + fch_data_t *data = (fch_data_t *)mphf->data; + CMPH_HASH h1_type = hash_get_type(data->h1); + CMPH_HASH h2_type = hash_get_type(data->h2); + + return (cmph_uint32)(sizeof(CMPH_ALGO) + hash_state_packed_size(h1_type) + hash_state_packed_size(h2_type) + + 4*sizeof(cmph_uint32) + 2*sizeof(double) + sizeof(cmph_uint32)*(data->b)); +} + + +/** cmph_uint32 fch_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 fch_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + register cmph_uint8 *h1_ptr = packed_mphf; + register CMPH_HASH h1_type = *((cmph_uint32 *)h1_ptr); + register cmph_uint8 *h2_ptr; + register CMPH_HASH h2_type; + register cmph_uint32 *g_ptr; + register cmph_uint32 m, b, h1, h2; + register double p1, p2; + + h1_ptr += 4; + + h2_ptr = h1_ptr + hash_state_packed_size(h1_type); + h2_type = *((cmph_uint32 *)h2_ptr); + h2_ptr += 4; + + g_ptr = (cmph_uint32 *)(h2_ptr + hash_state_packed_size(h2_type)); + + m = *g_ptr++; + + b = *g_ptr++; + + p1 = (double)(*((cmph_uint64 *)g_ptr)); + g_ptr += 2; + + p2 = (double)(*((cmph_uint64 *)g_ptr)); + g_ptr += 2; + + h1 = hash_packed(h1_ptr, h1_type, key, keylen) % m; + h2 = hash_packed(h2_ptr, h2_type, key, keylen) % m; + h1 = mixh10h11h12 (b, p1, p2, h1); + return (h2 + g_ptr[h1]) % m; +} + diff --git a/girepository/cmph/fch.h b/girepository/cmph/fch.h new file mode 100644 index 000000000..9d13a1c1b --- /dev/null +++ b/girepository/cmph/fch.h @@ -0,0 +1,48 @@ +#ifndef __CMPH_FCH_H__ +#define __CMPH_FCH_H__ + +#include "cmph.h" + +typedef struct __fch_data_t fch_data_t; +typedef struct __fch_config_data_t fch_config_data_t; + +/* Parameters calculation */ +cmph_uint32 fch_calc_b(double c, cmph_uint32 m); +double fch_calc_p1(cmph_uint32 m); +double fch_calc_p2(cmph_uint32 b); +cmph_uint32 mixh10h11h12(cmph_uint32 b, double p1, double p2, cmph_uint32 initial_index); + +fch_config_data_t *fch_config_new(void); +void fch_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); +void fch_config_destroy(cmph_config_t *mph); +cmph_t *fch_new(cmph_config_t *mph, double c); + +void fch_load(FILE *f, cmph_t *mphf); +int fch_dump(cmph_t *mphf, FILE *f); +void fch_destroy(cmph_t *mphf); +cmph_uint32 fch_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +/** \fn void fch_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void fch_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 fch_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 fch_packed_size(cmph_t *mphf); + +/** cmph_uint32 fch_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 fch_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +#endif diff --git a/girepository/cmph/fch_buckets.c b/girepository/cmph/fch_buckets.c new file mode 100644 index 000000000..a588f147e --- /dev/null +++ b/girepository/cmph/fch_buckets.c @@ -0,0 +1,214 @@ +#include "vqueue.h" +#include "fch_buckets.h" +#include +#include +#include +//#define DEBUG +#include "debug.h" + +typedef struct __fch_bucket_entry_t +{ + char * value; + cmph_uint32 length; +} fch_bucket_entry_t; + +typedef struct __fch_bucket_t +{ + fch_bucket_entry_t * entries; + cmph_uint32 capacity, size; +} fch_bucket_t; + + + +static void fch_bucket_new(fch_bucket_t *bucket) +{ + assert(bucket); + bucket->size = 0; + bucket->entries = NULL; + bucket->capacity = 0; +} + +static void fch_bucket_destroy(fch_bucket_t *bucket) +{ + cmph_uint32 i; + assert(bucket); + for (i = 0; i < bucket->size; i++) + { + free((bucket->entries + i)->value); + } + free(bucket->entries); +} + + +static void fch_bucket_reserve(fch_bucket_t *bucket, cmph_uint32 size) +{ + assert(bucket); + if (bucket->capacity < size) + { + cmph_uint32 new_capacity = bucket->capacity + 1; + DEBUGP("Increasing current capacity %u to %u\n", bucket->capacity, size); + while (new_capacity < size) + { + new_capacity *= 2; + } + bucket->entries = (fch_bucket_entry_t *)realloc(bucket->entries, sizeof(fch_bucket_entry_t)*new_capacity); + assert(bucket->entries); + bucket->capacity = new_capacity; + DEBUGP("Increased\n"); + } +} + +static void fch_bucket_insert(fch_bucket_t *bucket, char *val, cmph_uint32 val_length) +{ + assert(bucket); + fch_bucket_reserve(bucket, bucket->size + 1); + (bucket->entries + bucket->size)->value = val; + (bucket->entries + bucket->size)->length = val_length; + ++(bucket->size); +} + + +static cmph_uint8 fch_bucket_is_empty(fch_bucket_t *bucket) +{ + assert(bucket); + return (cmph_uint8)(bucket->size == 0); +} + +static cmph_uint32 fch_bucket_size(fch_bucket_t *bucket) +{ + assert(bucket); + return bucket->size; +} + +static char * fch_bucket_get_key(fch_bucket_t *bucket, cmph_uint32 index_key) +{ + assert(bucket); assert(index_key < bucket->size); + return (bucket->entries + index_key)->value; +} + +static cmph_uint32 fch_bucket_get_length(fch_bucket_t *bucket, cmph_uint32 index_key) +{ + assert(bucket); assert(index_key < bucket->size); + return (bucket->entries + index_key)->length; +} + +static void fch_bucket_print(fch_bucket_t * bucket, cmph_uint32 index) +{ + cmph_uint32 i; + assert(bucket); + fprintf(stderr, "Printing bucket %u ...\n", index); + for (i = 0; i < bucket->size; i++) + { + fprintf(stderr, " key: %s\n", (bucket->entries + i)->value); + } +} + +////////////////////////////////////////////////////////////////////////////////////// + +struct __fch_buckets_t +{ + fch_bucket_t * values; + cmph_uint32 nbuckets, max_size; + +}; + +fch_buckets_t * fch_buckets_new(cmph_uint32 nbuckets) +{ + cmph_uint32 i; + fch_buckets_t *buckets = (fch_buckets_t *)malloc(sizeof(fch_buckets_t)); + assert(buckets); + buckets->values = (fch_bucket_t *)calloc((size_t)nbuckets, sizeof(fch_bucket_t)); + for (i = 0; i < nbuckets; i++) fch_bucket_new(buckets->values + i); + assert(buckets->values); + buckets->nbuckets = nbuckets; + buckets->max_size = 0; + return buckets; +} + +cmph_uint8 fch_buckets_is_empty(fch_buckets_t * buckets, cmph_uint32 index) +{ + assert(index < buckets->nbuckets); + return fch_bucket_is_empty(buckets->values + index); +} + +void fch_buckets_insert(fch_buckets_t * buckets, cmph_uint32 index, char * key, cmph_uint32 length) +{ + assert(index < buckets->nbuckets); + fch_bucket_insert(buckets->values + index, key, length); + if (fch_bucket_size(buckets->values + index) > buckets->max_size) + { + buckets->max_size = fch_bucket_size(buckets->values + index); + } +} + +cmph_uint32 fch_buckets_get_size(fch_buckets_t * buckets, cmph_uint32 index) +{ + assert(index < buckets->nbuckets); + return fch_bucket_size(buckets->values + index); +} + + +char * fch_buckets_get_key(fch_buckets_t * buckets, cmph_uint32 index, cmph_uint32 index_key) +{ + assert(index < buckets->nbuckets); + return fch_bucket_get_key(buckets->values + index, index_key); +} + +cmph_uint32 fch_buckets_get_keylength(fch_buckets_t * buckets, cmph_uint32 index, cmph_uint32 index_key) +{ + assert(index < buckets->nbuckets); + return fch_bucket_get_length(buckets->values + index, index_key); +} + +cmph_uint32 fch_buckets_get_max_size(fch_buckets_t * buckets) +{ + return buckets->max_size; +} + +cmph_uint32 fch_buckets_get_nbuckets(fch_buckets_t * buckets) +{ + return buckets->nbuckets; +} + +cmph_uint32 * fch_buckets_get_indexes_sorted_by_size(fch_buckets_t * buckets) +{ + cmph_uint32 i = 0; + cmph_uint32 sum = 0, value; + cmph_uint32 *nbuckets_size = (cmph_uint32 *) calloc((size_t)buckets->max_size + 1, sizeof(cmph_uint32)); + cmph_uint32 * sorted_indexes = (cmph_uint32 *) calloc((size_t)buckets->nbuckets, sizeof(cmph_uint32)); + + // collect how many buckets for each size. + for(i = 0; i < buckets->nbuckets; i++) nbuckets_size[fch_bucket_size(buckets->values + i)] ++; + + // calculating offset considering a decreasing order of buckets size. + value = nbuckets_size[buckets->max_size]; + nbuckets_size[buckets->max_size] = sum; + for(i = (int)buckets->max_size - 1; i >= 0; i--) + { + sum += value; + value = nbuckets_size[i]; + nbuckets_size[i] = sum; + + } + for(i = 0; i < buckets->nbuckets; i++) + { + sorted_indexes[nbuckets_size[fch_bucket_size(buckets->values + i)]] = (cmph_uint32)i; + nbuckets_size[fch_bucket_size(buckets->values + i)] ++; + } + free(nbuckets_size); + return sorted_indexes; +} + +void fch_buckets_print(fch_buckets_t * buckets) +{ + cmph_uint32 i; + for (i = 0; i < buckets->nbuckets; i++) fch_bucket_print(buckets->values + i, i); +} + +void fch_buckets_destroy(fch_buckets_t * buckets) +{ + cmph_uint32 i; + for (i = 0; i < buckets->nbuckets; i++) fch_bucket_destroy(buckets->values + i); + free(buckets->values); + free(buckets); +} diff --git a/girepository/cmph/fch_buckets.h b/girepository/cmph/fch_buckets.h new file mode 100644 index 000000000..2a1b8b2a6 --- /dev/null +++ b/girepository/cmph/fch_buckets.h @@ -0,0 +1,30 @@ +#ifndef __CMPH_FCH_BUCKETS_H__ +#define __CMPH_FCH_BUCKETS_H__ + +#include "cmph_types.h" +typedef struct __fch_buckets_t fch_buckets_t; + +fch_buckets_t * fch_buckets_new(cmph_uint32 nbuckets); + +cmph_uint8 fch_buckets_is_empty(fch_buckets_t * buckets, cmph_uint32 index); + +void fch_buckets_insert(fch_buckets_t * buckets, cmph_uint32 index, char * key, cmph_uint32 length); + +cmph_uint32 fch_buckets_get_size(fch_buckets_t * buckets, cmph_uint32 index); + +char * fch_buckets_get_key(fch_buckets_t * buckets, cmph_uint32 index, cmph_uint32 index_key); + +cmph_uint32 fch_buckets_get_keylength(fch_buckets_t * buckets, cmph_uint32 index, cmph_uint32 index_key); + +// returns the size of biggest bucket. +cmph_uint32 fch_buckets_get_max_size(fch_buckets_t * buckets); + +// returns the number of buckets. +cmph_uint32 fch_buckets_get_nbuckets(fch_buckets_t * buckets); + +cmph_uint32 * fch_buckets_get_indexes_sorted_by_size(fch_buckets_t * buckets); + +void fch_buckets_print(fch_buckets_t * buckets); + +void fch_buckets_destroy(fch_buckets_t * buckets); +#endif diff --git a/girepository/cmph/fch_structs.h b/girepository/cmph/fch_structs.h new file mode 100644 index 000000000..fcd1555ea --- /dev/null +++ b/girepository/cmph/fch_structs.h @@ -0,0 +1,30 @@ +#ifndef __CMPH_FCH_STRUCTS_H__ +#define __CMPH_FCH_STRUCTS_H__ + +#include "hash_state.h" + +struct __fch_data_t +{ + cmph_uint32 m; // words count + double c; // constant c + cmph_uint32 b; // parameter b = ceil(c*m/(log(m)/log(2) + 1)). Don't need to be stored + double p1; // constant p1 = ceil(0.6*m). Don't need to be stored + double p2; // constant p2 = ceil(0.3*b). Don't need to be stored + cmph_uint32 *g; // g function. + hash_state_t *h1; // h10 function. + hash_state_t *h2; // h20 function. +}; + +struct __fch_config_data_t +{ + CMPH_HASH hashfuncs[2]; + cmph_uint32 m; // words count + double c; // constant c + cmph_uint32 b; // parameter b = ceil(c*m/(log(m)/log(2) + 1)). Don't need to be stored + double p1; // constant p1 = ceil(0.6*m). Don't need to be stored + double p2; // constant p2 = ceil(0.3*b). Don't need to be stored + cmph_uint32 *g; // g function. + hash_state_t *h1; // h10 function. + hash_state_t *h2; // h20 function. +}; +#endif diff --git a/girepository/cmph/fnv_hash.c b/girepository/cmph/fnv_hash.c new file mode 100644 index 000000000..aeaca8ff4 --- /dev/null +++ b/girepository/cmph/fnv_hash.c @@ -0,0 +1,53 @@ +#include "fnv_hash.h" +#include + +fnv_state_t *fnv_state_new() +{ + fnv_state_t *state = (fnv_state_t *)malloc(sizeof(fnv_state_t)); + state->hashfunc = CMPH_HASH_FNV; + return state; +} + +void fnv_state_destroy(fnv_state_t *state) +{ + free(state); +} + +cmph_uint32 fnv_hash(fnv_state_t *state, const char *k, cmph_uint32 keylen) +{ + const unsigned char *bp = (const unsigned char *)k; + const unsigned char *be = bp + keylen; + static unsigned int hval = 0; + + while (bp < be) + { + + //hval *= 0x01000193; good for non-gcc compiler + hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24); //good for gcc + + hval ^= *bp++; + } + return hval; +} + + +void fnv_state_dump(fnv_state_t *state, char **buf, cmph_uint32 *buflen) +{ + *buf = NULL; + *buflen = 0; + return; +} + +fnv_state_t * fnv_state_copy(fnv_state_t *src_state) +{ + fnv_state_t *dest_state = (fnv_state_t *)malloc(sizeof(fnv_state_t)); + dest_state->hashfunc = src_state->hashfunc; + return dest_state; +} + +fnv_state_t *fnv_state_load(const char *buf, cmph_uint32 buflen) +{ + fnv_state_t *state = (fnv_state_t *)malloc(sizeof(fnv_state_t)); + state->hashfunc = CMPH_HASH_FNV; + return state; +} diff --git a/girepository/cmph/fnv_hash.h b/girepository/cmph/fnv_hash.h new file mode 100644 index 000000000..7f5794657 --- /dev/null +++ b/girepository/cmph/fnv_hash.h @@ -0,0 +1,18 @@ +#ifndef __FNV_HASH_H__ +#define __FNV_HASH_H__ + +#include "hash.h" + +typedef struct __fnv_state_t +{ + CMPH_HASH hashfunc; +} fnv_state_t; + +fnv_state_t *fnv_state_new(); +cmph_uint32 fnv_hash(fnv_state_t *state, const char *k, cmph_uint32 keylen); +void fnv_state_dump(fnv_state_t *state, char **buf, cmph_uint32 *buflen); +fnv_state_t *fnv_state_copy(fnv_state_t *src_state); +fnv_state_t *fnv_state_load(const char *buf, cmph_uint32 buflen); +void fnv_state_destroy(fnv_state_t *state); + +#endif diff --git a/girepository/cmph/graph.c b/girepository/cmph/graph.c new file mode 100644 index 000000000..c29fd8b9c --- /dev/null +++ b/girepository/cmph/graph.c @@ -0,0 +1,338 @@ +#include "graph.h" + +#include +#include +#include +#include +#include +#include "vstack.h" +#include "bitbool.h" + +//#define DEBUG +#include "debug.h" + +/* static const cmph_uint8 bitmask[8] = { 1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7 }; */ +/* #define GETBIT(array, i) (array[(i) / 8] & bitmask[(i) % 8]) */ +/* #define SETBIT(array, i) (array[(i) / 8] |= bitmask[(i) % 8]) */ +/* #define UNSETBIT(array, i) (array[(i) / 8] &= (~(bitmask[(i) % 8]))) */ + +#define abs_edge(e, i) (e % g->nedges + i * g->nedges) + +struct __graph_t +{ + cmph_uint32 nnodes; + cmph_uint32 nedges; + cmph_uint32 *edges; + cmph_uint32 *first; + cmph_uint32 *next; + cmph_uint8 *critical_nodes; /* included -- Fabiano*/ + cmph_uint32 ncritical_nodes; /* included -- Fabiano*/ + cmph_uint32 cedges; + int shrinking; +}; + +static cmph_uint32 EMPTY = UINT_MAX; + +graph_t *graph_new(cmph_uint32 nnodes, cmph_uint32 nedges) +{ + graph_t *graph = (graph_t *)malloc(sizeof(graph_t)); + if (!graph) return NULL; + + graph->edges = (cmph_uint32 *)malloc(sizeof(cmph_uint32) * 2 * nedges); + graph->next = (cmph_uint32 *)malloc(sizeof(cmph_uint32) * 2 * nedges); + graph->first = (cmph_uint32 *)malloc(sizeof(cmph_uint32) * nnodes); + graph->critical_nodes = NULL; /* included -- Fabiano*/ + graph->ncritical_nodes = 0; /* included -- Fabiano*/ + graph->nnodes = nnodes; + graph->nedges = nedges; + + graph_clear_edges(graph); + return graph; +} + + +void graph_destroy(graph_t *graph) +{ + DEBUGP("Destroying graph\n"); + free(graph->edges); + free(graph->first); + free(graph->next); + free(graph->critical_nodes); /* included -- Fabiano*/ + free(graph); + return; +} + +void graph_print(graph_t *g) +{ + cmph_uint32 i, e; + for (i = 0; i < g->nnodes; ++i) + { + DEBUGP("Printing edges connected to %u\n", i); + e = g->first[i]; + if (e != EMPTY) + { + printf("%u -> %u\n", g->edges[abs_edge(e, 0)], g->edges[abs_edge(e, 1)]); + while ((e = g->next[e]) != EMPTY) + { + printf("%u -> %u\n", g->edges[abs_edge(e, 0)], g->edges[abs_edge(e, 1)]); + } + } + + } + return; +} + +void graph_add_edge(graph_t *g, cmph_uint32 v1, cmph_uint32 v2) +{ + cmph_uint32 e = g->cedges; + + assert(v1 < g->nnodes); + assert(v2 < g->nnodes); + assert(e < g->nedges); + assert(!g->shrinking); + + g->next[e] = g->first[v1]; + g->first[v1] = e; + g->edges[e] = v2; + + g->next[e + g->nedges] = g->first[v2]; + g->first[v2] = e + g->nedges; + g->edges[e + g->nedges] = v1; + + ++(g->cedges); +} + +static int check_edge(graph_t *g, cmph_uint32 e, cmph_uint32 v1, cmph_uint32 v2) +{ + DEBUGP("Checking edge %u %u looking for %u %u\n", g->edges[abs_edge(e, 0)], g->edges[abs_edge(e, 1)], v1, v2); + if (g->edges[abs_edge(e, 0)] == v1 && g->edges[abs_edge(e, 1)] == v2) return 1; + if (g->edges[abs_edge(e, 0)] == v2 && g->edges[abs_edge(e, 1)] == v1) return 1; + return 0; +} + +cmph_uint32 graph_edge_id(graph_t *g, cmph_uint32 v1, cmph_uint32 v2) +{ + cmph_uint32 e; + e = g->first[v1]; + assert(e != EMPTY); + if (check_edge(g, e, v1, v2)) return abs_edge(e, 0); + do + { + e = g->next[e]; + assert(e != EMPTY); + } + while (!check_edge(g, e, v1, v2)); + return abs_edge(e, 0); +} +static void del_edge_point(graph_t *g, cmph_uint32 v1, cmph_uint32 v2) +{ + cmph_uint32 e, prev; + + DEBUGP("Deleting edge point %u %u\n", v1, v2); + e = g->first[v1]; + if (check_edge(g, e, v1, v2)) + { + g->first[v1] = g->next[e]; + //g->edges[e] = EMPTY; + DEBUGP("Deleted\n"); + return; + } + DEBUGP("Checking linked list\n"); + do + { + prev = e; + e = g->next[e]; + assert(e != EMPTY); + } + while (!check_edge(g, e, v1, v2)); + + g->next[prev] = g->next[e]; + //g->edges[e] = EMPTY; + DEBUGP("Deleted\n"); +} + + +void graph_del_edge(graph_t *g, cmph_uint32 v1, cmph_uint32 v2) +{ + g->shrinking = 1; + del_edge_point(g, v1, v2); + del_edge_point(g, v2, v1); +} + +void graph_clear_edges(graph_t *g) +{ + cmph_uint32 i; + for (i = 0; i < g->nnodes; ++i) g->first[i] = EMPTY; + for (i = 0; i < g->nedges*2; ++i) + { + g->edges[i] = EMPTY; + g->next[i] = EMPTY; + } + g->cedges = 0; + g->shrinking = 0; +} + +static cmph_uint8 find_degree1_edge(graph_t *g, cmph_uint32 v, cmph_uint8 *deleted, cmph_uint32 *e) +{ + cmph_uint32 edge = g->first[v]; + cmph_uint8 found = 0; + DEBUGP("Checking degree of vertex %u\n", v); + if (edge == EMPTY) return 0; + else if (!(GETBIT(deleted, abs_edge(edge, 0)))) + { + found = 1; + *e = edge; + } + while(1) + { + edge = g->next[edge]; + if (edge == EMPTY) break; + if (GETBIT(deleted, abs_edge(edge, 0))) continue; + if (found) return 0; + DEBUGP("Found first edge\n"); + *e = edge; + found = 1; + } + return found; +} + +static void cyclic_del_edge(graph_t *g, cmph_uint32 v, cmph_uint8 *deleted) +{ + + cmph_uint32 e = 0; + cmph_uint8 degree1; + cmph_uint32 v1 = v; + cmph_uint32 v2 = 0; + + degree1 = find_degree1_edge(g, v1, deleted, &e); + if (!degree1) return; + while(1) + { + DEBUGP("Deleting edge %u (%u->%u)\n", e, g->edges[abs_edge(e, 0)], g->edges[abs_edge(e, 1)]); + SETBIT(deleted, abs_edge(e, 0)); + + v2 = g->edges[abs_edge(e, 0)]; + if (v2 == v1) v2 = g->edges[abs_edge(e, 1)]; + + DEBUGP("Checking if second endpoint %u has degree 1\n", v2); + degree1 = find_degree1_edge(g, v2, deleted, &e); + if (degree1) + { + DEBUGP("Inspecting vertex %u\n", v2); + v1 = v2; + } + else break; + } +} + +int graph_is_cyclic(graph_t *g) +{ + cmph_uint32 i; + cmph_uint32 v; + cmph_uint8 *deleted = (cmph_uint8 *)malloc((g->nedges*sizeof(cmph_uint8))/8 + 1); + size_t deleted_len = g->nedges/8 + 1; + memset(deleted, 0, deleted_len); + + DEBUGP("Looking for cycles in graph with %u vertices and %u edges\n", g->nnodes, g->nedges); + for (v = 0; v < g->nnodes; ++v) + { + cyclic_del_edge(g, v, deleted); + } + for (i = 0; i < g->nedges; ++i) + { + if (!(GETBIT(deleted, i))) + { + DEBUGP("Edge %u %u->%u was not deleted\n", i, g->edges[i], g->edges[i + g->nedges]); + free(deleted); + return 1; + } + } + free(deleted); + return 0; +} + +cmph_uint8 graph_node_is_critical(graph_t * g, cmph_uint32 v) /* included -- Fabiano */ +{ + return (cmph_uint8)GETBIT(g->critical_nodes,v); +} + +void graph_obtain_critical_nodes(graph_t *g) /* included -- Fabiano*/ +{ + cmph_uint32 i; + cmph_uint32 v; + cmph_uint8 *deleted = (cmph_uint8 *)malloc((g->nedges*sizeof(cmph_uint8))/8+1); + size_t deleted_len = g->nedges/8 + 1; + memset(deleted, 0, deleted_len); + free(g->critical_nodes); + g->critical_nodes = (cmph_uint8 *)malloc((g->nnodes*sizeof(cmph_uint8))/8 + 1); + g->ncritical_nodes = 0; + memset(g->critical_nodes, 0, (g->nnodes*sizeof(cmph_uint8))/8 + 1); + DEBUGP("Looking for the 2-core in graph with %u vertices and %u edges\n", g->nnodes, g->nedges); + for (v = 0; v < g->nnodes; ++v) + { + cyclic_del_edge(g, v, deleted); + } + + for (i = 0; i < g->nedges; ++i) + { + if (!(GETBIT(deleted,i))) + { + DEBUGP("Edge %u %u->%u belongs to the 2-core\n", i, g->edges[i], g->edges[i + g->nedges]); + if(!(GETBIT(g->critical_nodes,g->edges[i]))) + { + g->ncritical_nodes ++; + SETBIT(g->critical_nodes,g->edges[i]); + } + if(!(GETBIT(g->critical_nodes,g->edges[i + g->nedges]))) + { + g->ncritical_nodes ++; + SETBIT(g->critical_nodes,g->edges[i + g->nedges]); + } + } + } + free(deleted); +} + +cmph_uint8 graph_contains_edge(graph_t *g, cmph_uint32 v1, cmph_uint32 v2) /* included -- Fabiano*/ +{ + cmph_uint32 e; + e = g->first[v1]; + if(e == EMPTY) return 0; + if (check_edge(g, e, v1, v2)) return 1; + do + { + e = g->next[e]; + if(e == EMPTY) return 0; + } + while (!check_edge(g, e, v1, v2)); + return 1; +} + +cmph_uint32 graph_vertex_id(graph_t *g, cmph_uint32 e, cmph_uint32 id) /* included -- Fabiano*/ +{ + return (g->edges[e + id*g->nedges]); +} + +cmph_uint32 graph_ncritical_nodes(graph_t *g) /* included -- Fabiano*/ +{ + return g->ncritical_nodes; +} + +graph_iterator_t graph_neighbors_it(graph_t *g, cmph_uint32 v) +{ + graph_iterator_t it; + it.vertex = v; + it.edge = g->first[v]; + return it; +} +cmph_uint32 graph_next_neighbor(graph_t *g, graph_iterator_t* it) +{ + cmph_uint32 ret; + if(it->edge == EMPTY) return GRAPH_NO_NEIGHBOR; + if (g->edges[it->edge] == it->vertex) ret = g->edges[it->edge + g->nedges]; + else ret = g->edges[it->edge]; + it->edge = g->next[it->edge]; + return ret; +} + + diff --git a/girepository/cmph/graph.h b/girepository/cmph/graph.h new file mode 100644 index 000000000..e1b5de6f6 --- /dev/null +++ b/girepository/cmph/graph.h @@ -0,0 +1,40 @@ +#ifndef _CMPH_GRAPH_H__ +#define _CMPH_GRAPH_H__ + +#include +#include "cmph_types.h" + +#define GRAPH_NO_NEIGHBOR UINT_MAX + +typedef struct __graph_t graph_t; +typedef struct __graph_iterator_t graph_iterator_t; +struct __graph_iterator_t +{ + cmph_uint32 vertex; + cmph_uint32 edge; +}; + + + +graph_t *graph_new(cmph_uint32 nnodes, cmph_uint32 nedges); +void graph_destroy(graph_t *graph); + +void graph_add_edge(graph_t *g, cmph_uint32 v1, cmph_uint32 v2); +void graph_del_edge(graph_t *g, cmph_uint32 v1, cmph_uint32 v2); +void graph_clear_edges(graph_t *g); +cmph_uint32 graph_edge_id(graph_t *g, cmph_uint32 v1, cmph_uint32 v2); +cmph_uint8 graph_contains_edge(graph_t *g, cmph_uint32 v1, cmph_uint32 v2); + +graph_iterator_t graph_neighbors_it(graph_t *g, cmph_uint32 v); +cmph_uint32 graph_next_neighbor(graph_t *g, graph_iterator_t* it); + +void graph_obtain_critical_nodes(graph_t *g); /* included -- Fabiano*/ +cmph_uint8 graph_node_is_critical(graph_t * g, cmph_uint32 v); /* included -- Fabiano */ +cmph_uint32 graph_ncritical_nodes(graph_t *g); /* included -- Fabiano*/ +cmph_uint32 graph_vertex_id(graph_t *g, cmph_uint32 e, cmph_uint32 id); /* included -- Fabiano*/ + +int graph_is_cyclic(graph_t *g); + +void graph_print(graph_t *); + +#endif diff --git a/girepository/cmph/hash.c b/girepository/cmph/hash.c new file mode 100644 index 000000000..be86d6e75 --- /dev/null +++ b/girepository/cmph/hash.c @@ -0,0 +1,216 @@ +#include "hash_state.h" +#include +#include +#include +#include + +//#define DEBUG +#include "debug.h" + +const char *cmph_hash_names[] = { "jenkins", NULL }; + +hash_state_t *hash_state_new(CMPH_HASH hashfunc, cmph_uint32 hashsize) +{ + hash_state_t *state = NULL; + switch (hashfunc) + { + case CMPH_HASH_JENKINS: + DEBUGP("Jenkins function - %u\n", hashsize); + state = (hash_state_t *)jenkins_state_new(hashsize); + DEBUGP("Jenkins function created\n"); + break; + default: + assert(0); + } + state->hashfunc = hashfunc; + return state; +} +cmph_uint32 hash(hash_state_t *state, const char *key, cmph_uint32 keylen) +{ + switch (state->hashfunc) + { + case CMPH_HASH_JENKINS: + return jenkins_hash((jenkins_state_t *)state, key, keylen); + default: + assert(0); + } + assert(0); + return 0; +} + +void hash_vector(hash_state_t *state, const char *key, cmph_uint32 keylen, cmph_uint32 * hashes) +{ + switch (state->hashfunc) + { + case CMPH_HASH_JENKINS: + jenkins_hash_vector_((jenkins_state_t *)state, key, keylen, hashes); + break; + default: + assert(0); + } +} + + +void hash_state_dump(hash_state_t *state, char **buf, cmph_uint32 *buflen) +{ + char *algobuf; + size_t len; + switch (state->hashfunc) + { + case CMPH_HASH_JENKINS: + jenkins_state_dump((jenkins_state_t *)state, &algobuf, buflen); + if (*buflen == UINT_MAX) return; + break; + default: + assert(0); + } + *buf = (char *)malloc(strlen(cmph_hash_names[state->hashfunc]) + 1 + *buflen); + memcpy(*buf, cmph_hash_names[state->hashfunc], strlen(cmph_hash_names[state->hashfunc]) + 1); + DEBUGP("Algobuf is %u\n", *(cmph_uint32 *)algobuf); + len = *buflen; + memcpy(*buf + strlen(cmph_hash_names[state->hashfunc]) + 1, algobuf, len); + *buflen = (cmph_uint32)strlen(cmph_hash_names[state->hashfunc]) + 1 + *buflen; + free(algobuf); + return; +} + +hash_state_t * hash_state_copy(hash_state_t *src_state) +{ + hash_state_t *dest_state = NULL; + switch (src_state->hashfunc) + { + case CMPH_HASH_JENKINS: + dest_state = (hash_state_t *)jenkins_state_copy((jenkins_state_t *)src_state); + break; + default: + assert(0); + } + dest_state->hashfunc = src_state->hashfunc; + return dest_state; +} + +hash_state_t *hash_state_load(const char *buf, cmph_uint32 buflen) +{ + cmph_uint32 i; + cmph_uint32 offset; + CMPH_HASH hashfunc = CMPH_HASH_COUNT; + for (i = 0; i < CMPH_HASH_COUNT; ++i) + { + if (strcmp(buf, cmph_hash_names[i]) == 0) + { + hashfunc = i; + break; + } + } + if (hashfunc == CMPH_HASH_COUNT) return NULL; + offset = (cmph_uint32)strlen(cmph_hash_names[hashfunc]) + 1; + switch (hashfunc) + { + case CMPH_HASH_JENKINS: + return (hash_state_t *)jenkins_state_load(buf + offset, buflen - offset); + default: + return NULL; + } + return NULL; +} +void hash_state_destroy(hash_state_t *state) +{ + switch (state->hashfunc) + { + case CMPH_HASH_JENKINS: + jenkins_state_destroy((jenkins_state_t *)state); + break; + default: + assert(0); + } + return; +} + +/** \fn void hash_state_pack(hash_state_t *state, void *hash_packed) + * \brief Support the ability to pack a hash function into a preallocated contiguous memory space pointed by hash_packed. + * \param state points to the hash function + * \param hash_packed pointer to the contiguous memory area used to store the hash function. The size of hash_packed must be at least hash_state_packed_size() + * + * Support the ability to pack a hash function into a preallocated contiguous memory space pointed by hash_packed. + * However, the hash function type must be packed outside. + */ +void hash_state_pack(hash_state_t *state, void *hash_packed) +{ + switch (state->hashfunc) + { + case CMPH_HASH_JENKINS: + // pack the jenkins hash function + jenkins_state_pack((jenkins_state_t *)state, hash_packed); + break; + default: + assert(0); + } + return; +} + +/** \fn cmph_uint32 hash_state_packed_size(CMPH_HASH hashfunc) + * \brief Return the amount of space needed to pack a hash function. + * \param hashfunc function type + * \return the size of the packed function or zero for failures + */ +cmph_uint32 hash_state_packed_size(CMPH_HASH hashfunc) +{ + cmph_uint32 size = 0; + switch (hashfunc) + { + case CMPH_HASH_JENKINS: + size += jenkins_state_packed_size(); + break; + default: + assert(0); + } + return size; +} + +/** \fn cmph_uint32 hash_packed(void *hash_packed, CMPH_HASH hashfunc, const char *k, cmph_uint32 keylen) + * \param hash_packed is a pointer to a contiguous memory area + * \param hashfunc is the type of the hash function packed in hash_packed + * \param key is a pointer to a key + * \param keylen is the key length + * \return an integer that represents a hash value of 32 bits. + */ +cmph_uint32 hash_packed(void *hash_packed, CMPH_HASH hashfunc, const char *k, cmph_uint32 keylen) +{ + switch (hashfunc) + { + case CMPH_HASH_JENKINS: + return jenkins_hash_packed(hash_packed, k, keylen); + default: + assert(0); + } + assert(0); + return 0; +} + +/** \fn hash_vector_packed(void *hash_packed, CMPH_HASH hashfunc, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes) + * \param hash_packed is a pointer to a contiguous memory area + * \param key is a pointer to a key + * \param keylen is the key length + * \param hashes is a pointer to a memory large enough to fit three 32-bit integers. + */ +void hash_vector_packed(void *hash_packed, CMPH_HASH hashfunc, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes) +{ + switch (hashfunc) + { + case CMPH_HASH_JENKINS: + jenkins_hash_vector_packed(hash_packed, k, keylen, hashes); + break; + default: + assert(0); + } +} + + +/** \fn CMPH_HASH hash_get_type(hash_state_t *state); + * \param state is a pointer to a hash_state_t structure + * \return the hash function type pointed by state + */ +CMPH_HASH hash_get_type(hash_state_t *state) +{ + return state->hashfunc; +} diff --git a/girepository/cmph/hash.h b/girepository/cmph/hash.h new file mode 100644 index 000000000..0ec4ce1c7 --- /dev/null +++ b/girepository/cmph/hash.h @@ -0,0 +1,76 @@ +#ifndef __CMPH_HASH_H__ +#define __CMPH_HASH_H__ + +#include "cmph_types.h" + +typedef union __hash_state_t hash_state_t; + +hash_state_t *hash_state_new(CMPH_HASH, cmph_uint32 hashsize); + +/** \fn cmph_uint32 hash(hash_state_t *state, const char *key, cmph_uint32 keylen); + * \param state is a pointer to a hash_state_t structure + * \param key is a pointer to a key + * \param keylen is the key length + * \return an integer that represents a hash value of 32 bits. + */ +cmph_uint32 hash(hash_state_t *state, const char *key, cmph_uint32 keylen); + +/** \fn void hash_vector(hash_state_t *state, const char *key, cmph_uint32 keylen, cmph_uint32 * hashes); + * \param state is a pointer to a hash_state_t structure + * \param key is a pointer to a key + * \param keylen is the key length + * \param hashes is a pointer to a memory large enough to fit three 32-bit integers. + */ +void hash_vector(hash_state_t *state, const char *key, cmph_uint32 keylen, cmph_uint32 * hashes); + +void hash_state_dump(hash_state_t *state, char **buf, cmph_uint32 *buflen); + +hash_state_t * hash_state_copy(hash_state_t *src_state); + +hash_state_t *hash_state_load(const char *buf, cmph_uint32 buflen); + +void hash_state_destroy(hash_state_t *state); + +/** \fn void hash_state_pack(hash_state_t *state, void *hash_packed); + * \brief Support the ability to pack a hash function into a preallocated contiguous memory space pointed by hash_packed. + * \param state points to the hash function + * \param hash_packed pointer to the contiguous memory area used to store the hash function. The size of hash_packed must be at least hash_state_packed_size() + * + * Support the ability to pack a hash function into a preallocated contiguous memory space pointed by hash_packed. + * However, the hash function type must be packed outside. + */ +void hash_state_pack(hash_state_t *state, void *hash_packed); + +/** \fn cmph_uint32 hash_packed(void *hash_packed, CMPH_HASH hashfunc, const char *k, cmph_uint32 keylen); + * \param hash_packed is a pointer to a contiguous memory area + * \param hashfunc is the type of the hash function packed in hash_packed + * \param key is a pointer to a key + * \param keylen is the key length + * \return an integer that represents a hash value of 32 bits. + */ +cmph_uint32 hash_packed(void *hash_packed, CMPH_HASH hashfunc, const char *k, cmph_uint32 keylen); + +/** \fn cmph_uint32 hash_state_packed_size(CMPH_HASH hashfunc) + * \brief Return the amount of space needed to pack a hash function. + * \param hashfunc function type + * \return the size of the packed function or zero for failures + */ +cmph_uint32 hash_state_packed_size(CMPH_HASH hashfunc); + + +/** \fn hash_vector_packed(void *hash_packed, CMPH_HASH hashfunc, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes); + * \param hash_packed is a pointer to a contiguous memory area + * \param key is a pointer to a key + * \param keylen is the key length + * \param hashes is a pointer to a memory large enough to fit three 32-bit integers. + */ +void hash_vector_packed(void *hash_packed, CMPH_HASH hashfunc, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes); + + +/** \fn CMPH_HASH hash_get_type(hash_state_t *state); + * \param state is a pointer to a hash_state_t structure + * \return the hash function type pointed by state + */ +CMPH_HASH hash_get_type(hash_state_t *state); + +#endif diff --git a/girepository/cmph/hash_state.h b/girepository/cmph/hash_state.h new file mode 100644 index 000000000..1b567dca1 --- /dev/null +++ b/girepository/cmph/hash_state.h @@ -0,0 +1,12 @@ +#ifndef __HASH_STATE_H__ +#define __HASH_STATE_H__ + +#include "hash.h" +#include "jenkins_hash.h" +union __hash_state_t +{ + CMPH_HASH hashfunc; + jenkins_state_t jenkins; +}; + +#endif diff --git a/girepository/cmph/hashtree.c b/girepository/cmph/hashtree.c new file mode 100644 index 000000000..2f3567e55 --- /dev/null +++ b/girepository/cmph/hashtree.c @@ -0,0 +1,289 @@ +#include "graph.h" +#include "hashtree.h" +#include "cmph_structs.h" +#include "hastree_structs.h" +#include "hash.h" +#include "bitbool.h" + +#include +#include +#include +#include +#include + +//#define DEBUG +#include "debug.h" + +hashtree_config_data_t *hashtree_config_new() +{ + hashtree_config_data_t *hashtree; + hashtree = (hashtree_config_data_t *)malloc(sizeof(hashtree_config_data_t)); + if (!hashtree) return NULL; + memset(hashtree, 0, sizeof(hashtree_config_data_t)); + hashtree->hashfuncs[0] = CMPH_HASH_JENKINS; + hashtree->hashfuncs[1] = CMPH_HASH_JENKINS; + hashtree->hashfuncs[2] = CMPH_HASH_JENKINS; + hashtree->memory = 32 * 1024 * 1024; + return hashtree; +} +void hashtree_config_destroy(cmph_config_t *mph) +{ + hashtree_config_data_t *data = (hashtree_config_data_t *)mph->data; + DEBUGP("Destroying algorithm dependent data\n"); + free(data); +} + +void hashtree_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + hashtree_config_data_t *hashtree = (hashtree_config_data_t *)mph->data; + CMPH_HASH *hashptr = hashfuncs; + cmph_uint32 i = 0; + while(*hashptr != CMPH_HASH_COUNT) + { + if (i >= 3) break; //hashtree only uses three hash functions + hashtree->hashfuncs[i] = *hashptr; + ++i, ++hashptr; + } +} + +cmph_t *hashtree_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + hashtree_data_t *hashtreef = NULL; + + cmph_uint32 i; + cmph_uint32 iterations = 20; + cmph_uint8 *visited = NULL; + hashtree_config_data_t *hashtree = (hashtree_config_data_t *)mph->data; + hashtree->m = mph->key_source->nkeys; + hashtree->n = ceil(c * mph->key_source->nkeys); + DEBUGP("m (edges): %u n (vertices): %u c: %f\n", hashtree->m, hashtree->n, c); + hashtree->graph = graph_new(hashtree->n, hashtree->m); + DEBUGP("Created graph\n"); + + hashtree->hashes = (hash_state_t **)malloc(sizeof(hash_state_t *)*3); + for(i = 0; i < 3; ++i) hashtree->hashes[i] = NULL; + //Mapping step + if (mph->verbosity) + { + fprintf(stderr, "Entering mapping step for mph creation of %u keys with graph sized %u\n", hashtree->m, hashtree->n); + } + while(1) + { + int ok; + hashtree->hashes[0] = hash_state_new(hashtree->hashfuncs[0], hashtree->n); + hashtree->hashes[1] = hash_state_new(hashtree->hashfuncs[1], hashtree->n); + ok = hashtree_gen_edges(mph); + if (!ok) + { + --iterations; + hash_state_destroy(hashtree->hashes[0]); + hashtree->hashes[0] = NULL; + hash_state_destroy(hashtree->hashes[1]); + hashtree->hashes[1] = NULL; + DEBUGP("%u iterations remaining\n", iterations); + if (mph->verbosity) + { + fprintf(stderr, "Acyclic graph creation failure - %u iterations remaining\n", iterations); + } + if (iterations == 0) break; + } + else break; + } + if (iterations == 0) + { + graph_destroy(hashtree->graph); + return NULL; + } + + //Assignment step + if (mph->verbosity) + { + fprintf(stderr, "Starting assignment step\n"); + } + DEBUGP("Assignment step\n"); + visited = (char *)malloc(hashtree->n/8 + 1); + memset(visited, 0, hashtree->n/8 + 1); + free(hashtree->g); + hashtree->g = (cmph_uint32 *)malloc(hashtree->n * sizeof(cmph_uint32)); + assert(hashtree->g); + for (i = 0; i < hashtree->n; ++i) + { + if (!GETBIT(visited,i)) + { + hashtree->g[i] = 0; + hashtree_traverse(hashtree, visited, i); + } + } + graph_destroy(hashtree->graph); + free(visited); + hashtree->graph = NULL; + + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + hashtreef = (hashtree_data_t *)malloc(sizeof(hashtree_data_t)); + hashtreef->g = hashtree->g; + hashtree->g = NULL; //transfer memory ownership + hashtreef->hashes = hashtree->hashes; + hashtree->hashes = NULL; //transfer memory ownership + hashtreef->n = hashtree->n; + hashtreef->m = hashtree->m; + mphf->data = hashtreef; + mphf->size = hashtree->m; + DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + return mphf; +} + +static void hashtree_traverse(hashtree_config_data_t *hashtree, cmph_uint8 *visited, cmph_uint32 v) +{ + + graph_iterator_t it = graph_neighbors_it(hashtree->graph, v); + cmph_uint32 neighbor = 0; + SETBIT(visited,v); + + DEBUGP("Visiting vertex %u\n", v); + while((neighbor = graph_next_neighbor(hashtree->graph, &it)) != GRAPH_NO_NEIGHBOR) + { + DEBUGP("Visiting neighbor %u\n", neighbor); + if(GETBIT(visited,neighbor)) continue; + DEBUGP("Visiting neighbor %u\n", neighbor); + DEBUGP("Visiting edge %u->%u with id %u\n", v, neighbor, graph_edge_id(hashtree->graph, v, neighbor)); + hashtree->g[neighbor] = graph_edge_id(hashtree->graph, v, neighbor) - hashtree->g[v]; + DEBUGP("g is %u (%u - %u mod %u)\n", hashtree->g[neighbor], graph_edge_id(hashtree->graph, v, neighbor), hashtree->g[v], hashtree->m); + hashtree_traverse(hashtree, visited, neighbor); + } +} + +static int hashtree_gen_edges(cmph_config_t *mph) +{ + cmph_uint32 e; + hashtree_config_data_t *hashtree = (hashtree_config_data_t *)mph->data; + int cycles = 0; + + DEBUGP("Generating edges for %u vertices with hash functions %s and %s\n", hashtree->n, cmph_hash_names[hashtree->hashfuncs[0]], cmph_hash_names[hashtree->hashfuncs[1]]); + graph_clear_edges(hashtree->graph); + mph->key_source->rewind(mph->key_source->data); + for (e = 0; e < mph->key_source->nkeys; ++e) + { + cmph_uint32 h1, h2; + cmph_uint32 keylen; + char *key; + mph->key_source->read(mph->key_source->data, &key, &keylen); + h1 = hash(hashtree->hashes[0], key, keylen) % hashtree->n; + h2 = hash(hashtree->hashes[1], key, keylen) % hashtree->n; + if (h1 == h2) if (++h2 >= hashtree->n) h2 = 0; + if (h1 == h2) + { + if (mph->verbosity) fprintf(stderr, "Self loop for key %u\n", e); + mph->key_source->dispose(mph->key_source->data, key, keylen); + return 0; + } + DEBUGP("Adding edge: %u -> %u for key %s\n", h1, h2, key); + mph->key_source->dispose(mph->key_source->data, key, keylen); + graph_add_edge(hashtree->graph, h1, h2); + } + cycles = graph_is_cyclic(hashtree->graph); + if (mph->verbosity && cycles) fprintf(stderr, "Cyclic graph generated\n"); + DEBUGP("Looking for cycles: %u\n", cycles); + + return ! cycles; +} + +int hashtree_dump(cmph_t *mphf, FILE *fd) +{ + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint32 two = 2; //number of hash functions + hashtree_data_t *data = (hashtree_data_t *)mphf->data; + __cmph_dump(mphf, fd); + + fwrite(&two, sizeof(cmph_uint32), 1, fd); + hash_state_dump(data->hashes[0], &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + fwrite(&buflen, sizeof(cmph_uint32), 1, fd); + fwrite(buf, buflen, 1, fd); + free(buf); + + hash_state_dump(data->hashes[1], &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + fwrite(&buflen, sizeof(cmph_uint32), 1, fd); + fwrite(buf, buflen, 1, fd); + free(buf); + + fwrite(&(data->n), sizeof(cmph_uint32), 1, fd); + fwrite(&(data->m), sizeof(cmph_uint32), 1, fd); + + fwrite(data->g, sizeof(cmph_uint32)*data->n, 1, fd); + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < data->n; ++i) fprintf(stderr, "%u ", data->g[i]); + fprintf(stderr, "\n"); + #endif + return 1; +} + +void hashtree_load(FILE *f, cmph_t *mphf) +{ + cmph_uint32 nhashes; + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint32 i; + hashtree_data_t *hashtree = (hashtree_data_t *)malloc(sizeof(hashtree_data_t)); + + DEBUGP("Loading hashtree mphf\n"); + mphf->data = hashtree; + fread(&nhashes, sizeof(cmph_uint32), 1, f); + hashtree->hashes = (hash_state_t **)malloc(sizeof(hash_state_t *)*(nhashes + 1)); + hashtree->hashes[nhashes] = NULL; + DEBUGP("Reading %u hashes\n", nhashes); + for (i = 0; i < nhashes; ++i) + { + hash_state_t *state = NULL; + fread(&buflen, sizeof(cmph_uint32), 1, f); + DEBUGP("Hash state has %u bytes\n", buflen); + buf = (char *)malloc(buflen); + fread(buf, buflen, 1, f); + state = hash_state_load(buf, buflen); + hashtree->hashes[i] = state; + free(buf); + } + + DEBUGP("Reading m and n\n"); + fread(&(hashtree->n), sizeof(cmph_uint32), 1, f); + fread(&(hashtree->m), sizeof(cmph_uint32), 1, f); + + hashtree->g = (cmph_uint32 *)malloc(sizeof(cmph_uint32)*hashtree->n); + fread(hashtree->g, hashtree->n*sizeof(cmph_uint32), 1, f); + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < hashtree->n; ++i) fprintf(stderr, "%u ", hashtree->g[i]); + fprintf(stderr, "\n"); + #endif + return; +} + + +cmph_uint32 hashtree_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + hashtree_data_t *hashtree = mphf->data; + cmph_uint32 h1 = hash(hashtree->hashes[0], key, keylen) % hashtree->n; + cmph_uint32 h2 = hash(hashtree->hashes[1], key, keylen) % hashtree->n; + DEBUGP("key: %s h1: %u h2: %u\n", key, h1, h2); + if (h1 == h2 && ++h2 >= hashtree->n) h2 = 0; + DEBUGP("key: %s g[h1]: %u g[h2]: %u edges: %u\n", key, hashtree->g[h1], hashtree->g[h2], hashtree->m); + return (hashtree->g[h1] + hashtree->g[h2]) % hashtree->m; +} +void hashtree_destroy(cmph_t *mphf) +{ + hashtree_data_t *data = (hashtree_data_t *)mphf->data; + free(data->g); + hash_state_destroy(data->hashes[0]); + hash_state_destroy(data->hashes[1]); + free(data->hashes); + free(data); + free(mphf); +} diff --git a/girepository/cmph/hashtree.h b/girepository/cmph/hashtree.h new file mode 100644 index 000000000..8bff67462 --- /dev/null +++ b/girepository/cmph/hashtree.h @@ -0,0 +1,19 @@ +#ifndef __CMPH_HASHTREE_H__ +#define __CMPH_HASHTREE_H__ + +#include "cmph.h" + +typedef struct __hashtree_data_t hashtree_data_t; +typedef struct __hashtree_config_data_t hashtree_config_data_t; + +hashtree_config_data_t *hashtree_config_new(); +void hashtree_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); +void hashtree_config_set_leaf_algo(cmph_config_t *mph, CMPH_ALGO leaf_algo); +void hashtree_config_destroy(cmph_config_t *mph); +cmph_t *hashtree_new(cmph_config_t *mph, double c); + +void hashtree_load(FILE *f, cmph_t *mphf); +int hashtree_dump(cmph_t *mphf, FILE *f); +void hashtree_destroy(cmph_t *mphf); +cmph_uint32 hashtree_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); +#endif diff --git a/girepository/cmph/hashtree_structs.h b/girepository/cmph/hashtree_structs.h new file mode 100644 index 000000000..7258cd399 --- /dev/null +++ b/girepository/cmph/hashtree_structs.h @@ -0,0 +1,32 @@ +#ifndef __CMPH_HASHTREE_STRUCTS_H__ +#define __CMPH_HASHTREE_STRUCTS_H__ + +#include "hash_state.h" + +struct __hashtree_data_t +{ + cmph_uint32 m; //edges (words) count + double c; //constant c + cmph_uint8 *size; //size[i] stores the number of edges represented by g[i] + cmph_uint32 **g; + cmph_uint32 k; //number of components + hash_state_t **h1; + hash_state_t **h2; + hash_state_t *h3; +}; + +struct __hashtree_config_data_t +{ + CMPH_ALGO leaf_algo; + CMPH_HASH hashfuncs[3]; + cmph_uint32 m; //edges (words) count + cmph_uint8 *size; //size[i] stores the number of edges represented by g[i] + cmph_uint32 *offset; //offset[i] stores the sum size[0] + ... size[i - 1] + cmph_uint32 k; //number of components + cmph_uint32 memory; + hash_state_t **h1; + hash_state_t **h2; + hash_state_t *h3; +}; + +#endif diff --git a/girepository/cmph/jenkins_hash.c b/girepository/cmph/jenkins_hash.c new file mode 100644 index 000000000..65cdff959 --- /dev/null +++ b/girepository/cmph/jenkins_hash.c @@ -0,0 +1,297 @@ +#include "jenkins_hash.h" +#include +#ifdef WIN32 +#define _USE_MATH_DEFINES //For M_LOG2E +#endif +#include +#include +#include + +//#define DEBUG +#include "debug.h" + +#define hashsize(n) ((cmph_uint32)1<<(n)) +#define hashmask(n) (hashsize(n)-1) + + + +//#define NM2 /* Define this if you do not want power of 2 table sizes*/ + + +/* + -------------------------------------------------------------------- + mix -- mix 3 32-bit values reversibly. + For every delta with one or two bits set, and the deltas of all three + high bits or all three low bits, whether the original value of a,b,c + is almost all zero or is uniformly distributed, + * If mix() is run forward or backward, at least 32 bits in a,b,c + have at least 1/4 probability of changing. + * If mix() is run forward, every bit of c will change between 1/3 and + 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.) + mix() was built out of 36 single-cycle latency instructions in a + structure that could supported 2x parallelism, like so: + a -= b; + a -= c; x = (c>>13); + b -= c; a ^= x; + b -= a; x = (a<<8); + c -= a; b ^= x; + c -= b; x = (b>>13); + ... + Unfortunately, superscalar Pentiums and Sparcs can't take advantage + of that parallelism. They've also turned some of those single-cycle + latency instructions into multi-cycle latency instructions. Still, + this is the fastest good hash I could find. There were about 2^^68 + to choose from. I only looked at a billion or so. + -------------------------------------------------------------------- + */ +#define mix(a,b,c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ +} + +/* + -------------------------------------------------------------------- + hash() -- hash a variable-length key into a 32-bit value +k : the key (the unaligned variable-length array of bytes) +len : the length of the key, counting by bytes +initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Every 1-bit and 2-bit delta achieves avalanche. +About 6*len+35 instructions. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do +h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (cmph_uint8 **)k, do it like this: +for (i=0, h=0; iseed = ((cmph_uint32)rand() % size); + return state; +} +void jenkins_state_destroy(jenkins_state_t *state) +{ + free(state); +} + + +static inline void __jenkins_hash_vector(cmph_uint32 seed, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes) +{ + register cmph_uint32 len, length; + + /* Set up the internal state */ + length = keylen; + len = length; + hashes[0] = hashes[1] = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + hashes[2] = seed; /* the previous hash value - seed in our case */ + + /*---------------------------------------- handle most of the key */ + while (len >= 12) + { + hashes[0] += ((cmph_uint32)k[0] +((cmph_uint32)k[1]<<8) +((cmph_uint32)k[2]<<16) +((cmph_uint32)k[3]<<24)); + hashes[1] += ((cmph_uint32)k[4] +((cmph_uint32)k[5]<<8) +((cmph_uint32)k[6]<<16) +((cmph_uint32)k[7]<<24)); + hashes[2] += ((cmph_uint32)k[8] +((cmph_uint32)k[9]<<8) +((cmph_uint32)k[10]<<16)+((cmph_uint32)k[11]<<24)); + mix(hashes[0],hashes[1],hashes[2]); + k += 12; len -= 12; + } + + /*------------------------------------- handle the last 11 bytes */ + hashes[2] += length; + switch(len) /* all the case statements fall through */ + { + case 11: + hashes[2] +=((cmph_uint32)k[10]<<24); + case 10: + hashes[2] +=((cmph_uint32)k[9]<<16); + case 9 : + hashes[2] +=((cmph_uint32)k[8]<<8); + /* the first byte of hashes[2] is reserved for the length */ + case 8 : + hashes[1] +=((cmph_uint32)k[7]<<24); + case 7 : + hashes[1] +=((cmph_uint32)k[6]<<16); + case 6 : + hashes[1] +=((cmph_uint32)k[5]<<8); + case 5 : + hashes[1] +=(cmph_uint8) k[4]; + case 4 : + hashes[0] +=((cmph_uint32)k[3]<<24); + case 3 : + hashes[0] +=((cmph_uint32)k[2]<<16); + case 2 : + hashes[0] +=((cmph_uint32)k[1]<<8); + case 1 : + hashes[0] +=(cmph_uint8)k[0]; + /* case 0: nothing left to add */ + } + + mix(hashes[0],hashes[1],hashes[2]); +} + +cmph_uint32 jenkins_hash(jenkins_state_t *state, const char *k, cmph_uint32 keylen) +{ + cmph_uint32 hashes[3]; + __jenkins_hash_vector(state->seed, k, keylen, hashes); + return hashes[2]; +/* cmph_uint32 a, b, c; + cmph_uint32 len, length; + + // Set up the internal state + length = keylen; + len = length; + a = b = 0x9e3779b9; // the golden ratio; an arbitrary value + c = state->seed; // the previous hash value - seed in our case + + // handle most of the key + while (len >= 12) + { + a += (k[0] +((cmph_uint32)k[1]<<8) +((cmph_uint32)k[2]<<16) +((cmph_uint32)k[3]<<24)); + b += (k[4] +((cmph_uint32)k[5]<<8) +((cmph_uint32)k[6]<<16) +((cmph_uint32)k[7]<<24)); + c += (k[8] +((cmph_uint32)k[9]<<8) +((cmph_uint32)k[10]<<16)+((cmph_uint32)k[11]<<24)); + mix(a,b,c); + k += 12; len -= 12; + } + + // handle the last 11 bytes + c += length; + switch(len) /// all the case statements fall through + { + case 11: + c +=((cmph_uint32)k[10]<<24); + case 10: + c +=((cmph_uint32)k[9]<<16); + case 9 : + c +=((cmph_uint32)k[8]<<8); + // the first byte of c is reserved for the length + case 8 : + b +=((cmph_uint32)k[7]<<24); + case 7 : + b +=((cmph_uint32)k[6]<<16); + case 6 : + b +=((cmph_uint32)k[5]<<8); + case 5 : + b +=k[4]; + case 4 : + a +=((cmph_uint32)k[3]<<24); + case 3 : + a +=((cmph_uint32)k[2]<<16); + case 2 : + a +=((cmph_uint32)k[1]<<8); + case 1 : + a +=k[0]; + // case 0: nothing left to add + } + + mix(a,b,c); + + /// report the result + + return c; + */ +} + +void jenkins_hash_vector_(jenkins_state_t *state, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes) +{ + __jenkins_hash_vector(state->seed, k, keylen, hashes); +} + +void jenkins_state_dump(jenkins_state_t *state, char **buf, cmph_uint32 *buflen) +{ + *buflen = sizeof(cmph_uint32); + *buf = (char *)malloc(sizeof(cmph_uint32)); + if (!*buf) + { + *buflen = UINT_MAX; + return; + } + memcpy(*buf, &(state->seed), sizeof(cmph_uint32)); + DEBUGP("Dumped jenkins state with seed %u\n", state->seed); + return; +} + +jenkins_state_t *jenkins_state_copy(jenkins_state_t *src_state) +{ + jenkins_state_t *dest_state = (jenkins_state_t *)malloc(sizeof(jenkins_state_t)); + dest_state->hashfunc = src_state->hashfunc; + dest_state->seed = src_state->seed; + return dest_state; +} + +jenkins_state_t *jenkins_state_load(const char *buf, cmph_uint32 buflen) +{ + jenkins_state_t *state = (jenkins_state_t *)malloc(sizeof(jenkins_state_t)); + state->seed = *(cmph_uint32 *)buf; + state->hashfunc = CMPH_HASH_JENKINS; + DEBUGP("Loaded jenkins state with seed %u\n", state->seed); + return state; +} + + +/** \fn void jenkins_state_pack(jenkins_state_t *state, void *jenkins_packed); + * \brief Support the ability to pack a jenkins function into a preallocated contiguous memory space pointed by jenkins_packed. + * \param state points to the jenkins function + * \param jenkins_packed pointer to the contiguous memory area used to store the jenkins function. The size of jenkins_packed must be at least jenkins_state_packed_size() + */ +void jenkins_state_pack(jenkins_state_t *state, void *jenkins_packed) +{ + if (state && jenkins_packed) + { + memcpy(jenkins_packed, &(state->seed), sizeof(cmph_uint32)); + } +} + +/** \fn cmph_uint32 jenkins_state_packed_size(jenkins_state_t *state); + * \brief Return the amount of space needed to pack a jenkins function. + * \return the size of the packed function or zero for failures + */ +cmph_uint32 jenkins_state_packed_size(void) +{ + return sizeof(cmph_uint32); +} + + +/** \fn cmph_uint32 jenkins_hash_packed(void *jenkins_packed, const char *k, cmph_uint32 keylen); + * \param jenkins_packed is a pointer to a contiguous memory area + * \param key is a pointer to a key + * \param keylen is the key length + * \return an integer that represents a hash value of 32 bits. + */ +cmph_uint32 jenkins_hash_packed(void *jenkins_packed, const char *k, cmph_uint32 keylen) +{ + cmph_uint32 hashes[3]; + __jenkins_hash_vector(*((cmph_uint32 *)jenkins_packed), k, keylen, hashes); + return hashes[2]; +} + +/** \fn jenkins_hash_vector_packed(void *jenkins_packed, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes); + * \param jenkins_packed is a pointer to a contiguous memory area + * \param key is a pointer to a key + * \param keylen is the key length + * \param hashes is a pointer to a memory large enough to fit three 32-bit integers. + */ +void jenkins_hash_vector_packed(void *jenkins_packed, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes) +{ + __jenkins_hash_vector(*((cmph_uint32 *)jenkins_packed), k, keylen, hashes); +} diff --git a/girepository/cmph/jenkins_hash.h b/girepository/cmph/jenkins_hash.h new file mode 100644 index 000000000..39626e204 --- /dev/null +++ b/girepository/cmph/jenkins_hash.h @@ -0,0 +1,65 @@ +#ifndef __JEKINS_HASH_H__ +#define __JEKINS_HASH_H__ + +#include "hash.h" + +typedef struct __jenkins_state_t +{ + CMPH_HASH hashfunc; + cmph_uint32 seed; +} jenkins_state_t; + +jenkins_state_t *jenkins_state_new(cmph_uint32 size); //size of hash table + +/** \fn cmph_uint32 jenkins_hash(jenkins_state_t *state, const char *k, cmph_uint32 keylen); + * \param state is a pointer to a jenkins_state_t structure + * \param key is a pointer to a key + * \param keylen is the key length + * \return an integer that represents a hash value of 32 bits. + */ +cmph_uint32 jenkins_hash(jenkins_state_t *state, const char *k, cmph_uint32 keylen); + +/** \fn void jenkins_hash_vector_(jenkins_state_t *state, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes); + * \param state is a pointer to a jenkins_state_t structure + * \param key is a pointer to a key + * \param keylen is the key length + * \param hashes is a pointer to a memory large enough to fit three 32-bit integers. + */ +void jenkins_hash_vector_(jenkins_state_t *state, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes); + +void jenkins_state_dump(jenkins_state_t *state, char **buf, cmph_uint32 *buflen); +jenkins_state_t *jenkins_state_copy(jenkins_state_t *src_state); +jenkins_state_t *jenkins_state_load(const char *buf, cmph_uint32 buflen); +void jenkins_state_destroy(jenkins_state_t *state); + +/** \fn void jenkins_state_pack(jenkins_state_t *state, void *jenkins_packed); + * \brief Support the ability to pack a jenkins function into a preallocated contiguous memory space pointed by jenkins_packed. + * \param state points to the jenkins function + * \param jenkins_packed pointer to the contiguous memory area used to store the jenkins function. The size of jenkins_packed must be at least jenkins_state_packed_size() + */ +void jenkins_state_pack(jenkins_state_t *state, void *jenkins_packed); + +/** \fn cmph_uint32 jenkins_state_packed_size(); + * \brief Return the amount of space needed to pack a jenkins function. + * \return the size of the packed function or zero for failures + */ +cmph_uint32 jenkins_state_packed_size(void); + + +/** \fn cmph_uint32 jenkins_hash_packed(void *jenkins_packed, const char *k, cmph_uint32 keylen); + * \param jenkins_packed is a pointer to a contiguous memory area + * \param key is a pointer to a key + * \param keylen is the key length + * \return an integer that represents a hash value of 32 bits. + */ +cmph_uint32 jenkins_hash_packed(void *jenkins_packed, const char *k, cmph_uint32 keylen); + +/** \fn jenkins_hash_vector_packed(void *jenkins_packed, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes); + * \param jenkins_packed is a pointer to a contiguous memory area + * \param key is a pointer to a key + * \param keylen is the key length + * \param hashes is a pointer to a memory large enough to fit three 32-bit integers. + */ +void jenkins_hash_vector_packed(void *jenkins_packed, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes); + +#endif diff --git a/girepository/cmph/main.c b/girepository/cmph/main.c new file mode 100644 index 000000000..f739b325f --- /dev/null +++ b/girepository/cmph/main.c @@ -0,0 +1,342 @@ +#ifdef WIN32 +#include "wingetopt.h" +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include "cmph.h" +#include "hash.h" + +#ifdef WIN32 +#define VERSION "0.8" +#else +#include "config.h" +#endif + + +void usage(const char *prg) +{ + fprintf(stderr, "usage: %s [-v] [-h] [-V] [-k nkeys] [-f hash_function] [-g [-c algorithm_dependent_value][-s seed] ] [-a algorithm] [-M memory_in_MB] [-b algorithm_dependent_value] [-t keys_per_bin] [-d tmp_dir] [-m file.mph] keysfile\n", prg); +} +void usage_long(const char *prg) +{ + cmph_uint32 i; + fprintf(stderr, "usage: %s [-v] [-h] [-V] [-k nkeys] [-f hash_function] [-g [-c algorithm_dependent_value][-s seed] ] [-a algorithm] [-M memory_in_MB] [-b algorithm_dependent_value] [-t keys_per_bin] [-d tmp_dir] [-m file.mph] keysfile\n", prg); + fprintf(stderr, "Minimum perfect hashing tool\n\n"); + fprintf(stderr, " -h\t print this help message\n"); + fprintf(stderr, " -c\t c value determines:\n"); + fprintf(stderr, " \t * the number of vertices in the graph for the algorithms BMZ and CHM\n"); + fprintf(stderr, " \t * the number of bits per key required in the FCH algorithm\n"); + fprintf(stderr, " \t * the load factor in the CHD_PH algorithm\n"); + fprintf(stderr, " -a\t algorithm - valid values are\n"); + for (i = 0; i < CMPH_COUNT; ++i) fprintf(stderr, " \t * %s\n", cmph_names[i]); + fprintf(stderr, " -f\t hash function (may be used multiple times) - valid values are\n"); + for (i = 0; i < CMPH_HASH_COUNT; ++i) fprintf(stderr, " \t * %s\n", cmph_hash_names[i]); + fprintf(stderr, " -V\t print version number and exit\n"); + fprintf(stderr, " -v\t increase verbosity (may be used multiple times)\n"); + fprintf(stderr, " -k\t number of keys\n"); + fprintf(stderr, " -g\t generation mode\n"); + fprintf(stderr, " -s\t random seed\n"); + fprintf(stderr, " -m\t minimum perfect hash function file \n"); + fprintf(stderr, " -M\t main memory availability (in MB) used in BRZ algorithm \n"); + fprintf(stderr, " -d\t temporary directory used in BRZ algorithm \n"); + fprintf(stderr, " -b\t the meaning of this parameter depends on the algorithm selected in the -a option:\n"); + fprintf(stderr, " \t * For BRZ it is used to make the maximal number of keys in a bucket lower than 256.\n"); + fprintf(stderr, " \t In this case its value should be an integer in the range [64,175]. Default is 128.\n\n"); + fprintf(stderr, " \t * For BDZ it is used to determine the size of some precomputed rank\n"); + fprintf(stderr, " \t information and its value should be an integer in the range [3,10]. Default\n"); + fprintf(stderr, " \t is 7. The larger is this value, the more compact are the resulting functions\n"); + fprintf(stderr, " \t and the slower are them at evaluation time.\n\n"); + fprintf(stderr, " \t * For CHD and CHD_PH it is used to set the average number of keys per bucket\n"); + fprintf(stderr, " \t and its value should be an integer in the range [1,32]. Default is 4. The\n"); + fprintf(stderr, " \t larger is this value, the slower is the construction of the functions.\n"); + fprintf(stderr, " \t This parameter has no effect for other algorithms.\n\n"); + fprintf(stderr, " -t\t set the number of keys per bin for a t-perfect hashing function. A t-perfect\n"); + fprintf(stderr, " \t hash function allows at most t collisions in a given bin. This parameter applies\n"); + fprintf(stderr, " \t only to the CHD and CHD_PH algorithms. Its value should be an integer in the\n"); + fprintf(stderr, " \t range [1,128]. Defaul is 1\n"); + fprintf(stderr, " keysfile\t line separated file with keys\n"); +} + +int main(int argc, char **argv) +{ + cmph_uint32 verbosity = 0; + char generate = 0; + char *mphf_file = NULL; + FILE *mphf_fd = stdout; + const char *keys_file = NULL; + FILE *keys_fd; + cmph_uint32 nkeys = UINT_MAX; + cmph_uint32 seed = UINT_MAX; + CMPH_HASH *hashes = NULL; + cmph_uint32 nhashes = 0; + cmph_uint32 i; + CMPH_ALGO mph_algo = CMPH_CHM; + double c = 0; + cmph_config_t *config = NULL; + cmph_t *mphf = NULL; + char * tmp_dir = NULL; + cmph_io_adapter_t *source; + cmph_uint32 memory_availability = 0; + cmph_uint32 b = 0; + cmph_uint32 keys_per_bin = 1; + while (1) + { + char ch = (char)getopt(argc, argv, "hVvgc:k:a:M:b:t:f:m:d:s:"); + if (ch == -1) break; + switch (ch) + { + case 's': + { + char *cptr; + seed = (cmph_uint32)strtoul(optarg, &cptr, 10); + if(*cptr != 0) { + fprintf(stderr, "Invalid seed %s\n", optarg); + exit(1); + } + } + break; + case 'c': + { + char *endptr; + c = strtod(optarg, &endptr); + if(*endptr != 0) { + fprintf(stderr, "Invalid c value %s\n", optarg); + exit(1); + } + } + break; + case 'g': + generate = 1; + break; + case 'k': + { + char *endptr; + nkeys = (cmph_uint32)strtoul(optarg, &endptr, 10); + if(*endptr != 0) { + fprintf(stderr, "Invalid number of keys %s\n", optarg); + exit(1); + } + } + break; + case 'm': + mphf_file = strdup(optarg); + break; + case 'd': + tmp_dir = strdup(optarg); + break; + case 'M': + { + char *cptr; + memory_availability = (cmph_uint32)strtoul(optarg, &cptr, 10); + if(*cptr != 0) { + fprintf(stderr, "Invalid memory availability %s\n", optarg); + exit(1); + } + } + break; + case 'b': + { + char *cptr; + b = (cmph_uint32)strtoul(optarg, &cptr, 10); + if(*cptr != 0) { + fprintf(stderr, "Parameter b was not found: %s\n", optarg); + exit(1); + } + } + break; + case 't': + { + char *cptr; + keys_per_bin = (cmph_uint32)strtoul(optarg, &cptr, 10); + if(*cptr != 0) { + fprintf(stderr, "Parameter t was not found: %s\n", optarg); + exit(1); + } + } + break; + case 'v': + ++verbosity; + break; + case 'V': + printf("%s\n", VERSION); + return 0; + case 'h': + usage_long(argv[0]); + return 0; + case 'a': + { + char valid = 0; + for (i = 0; i < CMPH_COUNT; ++i) + { + if (strcmp(cmph_names[i], optarg) == 0) + { + mph_algo = i; + valid = 1; + break; + } + } + if (!valid) + { + fprintf(stderr, "Invalid mph algorithm: %s. It is not available in version %s\n", optarg, VERSION); + return -1; + } + } + break; + case 'f': + { + char valid = 0; + for (i = 0; i < CMPH_HASH_COUNT; ++i) + { + if (strcmp(cmph_hash_names[i], optarg) == 0) + { + hashes = (CMPH_HASH *)realloc(hashes, sizeof(CMPH_HASH) * ( nhashes + 2 )); + hashes[nhashes] = i; + hashes[nhashes + 1] = CMPH_HASH_COUNT; + ++nhashes; + valid = 1; + break; + } + } + if (!valid) + { + fprintf(stderr, "Invalid hash function: %s\n", optarg); + return -1; + } + } + break; + default: + usage(argv[0]); + return 1; + } + } + + if (optind != argc - 1) + { + usage(argv[0]); + return 1; + } + keys_file = argv[optind]; + + if (seed == UINT_MAX) seed = (cmph_uint32)time(NULL); + srand(seed); + int ret = 0; + if (mphf_file == NULL) + { + mphf_file = (char *)malloc(strlen(keys_file) + 5); + memcpy(mphf_file, keys_file, strlen(keys_file)); + memcpy(mphf_file + strlen(keys_file), ".mph\0", (size_t)5); + } + + keys_fd = fopen(keys_file, "r"); + + if (keys_fd == NULL) + { + fprintf(stderr, "Unable to open file %s: %s\n", keys_file, strerror(errno)); + return -1; + } + + if (seed == UINT_MAX) seed = (cmph_uint32)time(NULL); + if(nkeys == UINT_MAX) source = cmph_io_nlfile_adapter(keys_fd); + else source = cmph_io_nlnkfile_adapter(keys_fd, nkeys); + if (generate) + { + //Create mphf + mphf_fd = fopen(mphf_file, "w"); + config = cmph_config_new(source); + cmph_config_set_algo(config, mph_algo); + if (nhashes) cmph_config_set_hashfuncs(config, hashes); + cmph_config_set_verbosity(config, verbosity); + cmph_config_set_tmp_dir(config, (cmph_uint8 *) tmp_dir); + cmph_config_set_mphf_fd(config, mphf_fd); + cmph_config_set_memory_availability(config, memory_availability); + cmph_config_set_b(config, b); + cmph_config_set_keys_per_bin(config, keys_per_bin); + + //if((mph_algo == CMPH_BMZ || mph_algo == CMPH_BRZ) && c >= 2.0) c=1.15; + if(mph_algo == CMPH_BMZ && c >= 2.0) c=1.15; + if (c != 0) cmph_config_set_graphsize(config, c); + mphf = cmph_new(config); + + cmph_config_destroy(config); + if (mphf == NULL) + { + fprintf(stderr, "Unable to create minimum perfect hashing function\n"); + //cmph_config_destroy(config); + free(mphf_file); + return -1; + } + + if (mphf_fd == NULL) + { + fprintf(stderr, "Unable to open output file %s: %s\n", mphf_file, strerror(errno)); + free(mphf_file); + return -1; + } + cmph_dump(mphf, mphf_fd); + cmph_destroy(mphf); + fclose(mphf_fd); + } + else + { + cmph_uint8 * hashtable = NULL; + mphf_fd = fopen(mphf_file, "r"); + if (mphf_fd == NULL) + { + fprintf(stderr, "Unable to open input file %s: %s\n", mphf_file, strerror(errno)); + free(mphf_file); + return -1; + } + mphf = cmph_load(mphf_fd); + fclose(mphf_fd); + if (!mphf) + { + fprintf(stderr, "Unable to parser input file %s\n", mphf_file); + free(mphf_file); + return -1; + } + cmph_uint32 siz = cmph_size(mphf); + hashtable = (cmph_uint8*)calloc(siz, sizeof(cmph_uint8)); + memset(hashtable, 0,(size_t) siz); + //check all keys + for (i = 0; i < source->nkeys; ++i) + { + cmph_uint32 h; + char *buf; + cmph_uint32 buflen = 0; + source->read(source->data, &buf, &buflen); + h = cmph_search(mphf, buf, buflen); + if (!(h < siz)) + { + fprintf(stderr, "Unknown key %*s in the input.\n", buflen, buf); + ret = 1; + } else if(hashtable[h] >= keys_per_bin) + { + fprintf(stderr, "More than %u keys were mapped to bin %u\n", keys_per_bin, h); + fprintf(stderr, "Duplicated or unknown key %*s in the input\n", buflen, buf); + ret = 1; + } else hashtable[h]++; + + if (verbosity) + { + printf("%s -> %u\n", buf, h); + } + source->dispose(source->data, buf, buflen); + } + + cmph_destroy(mphf); + free(hashtable); + } + fclose(keys_fd); + free(mphf_file); + free(tmp_dir); + cmph_io_nlfile_adapter_destroy(source); + return ret; + +} diff --git a/girepository/cmph/meson.build b/girepository/cmph/meson.build new file mode 100644 index 000000000..e0d5b27f1 --- /dev/null +++ b/girepository/cmph/meson.build @@ -0,0 +1,77 @@ +cmph_sources = [ + 'bdz.c', + 'bdz_ph.c', + 'bmz8.c', + 'bmz.c', + 'brz.c', + 'buffer_entry.c', + 'buffer_manager.c', + 'chd.c', + 'chd_ph.c', + 'chm.c', + 'cmph.c', + 'cmph_structs.c', + 'compressed_rank.c', + 'compressed_seq.c', + 'fch_buckets.c', + 'fch.c', + 'graph.c', + 'hash.c', + 'jenkins_hash.c', + 'miller_rabin.c', + 'select.c', + 'vqueue.c', + 'vstack.c', +] + +cmph_deps = [ + libglib_dep, + libgobject_dep, + libm, +] + +custom_c_args = [] + +if cc.get_id() != 'msvc' + custom_c_args = cc.get_supported_arguments([ + '-Wno-implicit-fallthrough', + '-Wno-old-style-definition', + '-Wno-suggest-attribute=noreturn', + '-Wno-type-limits', + '-Wno-undef', + '-Wno-unused-parameter', + '-Wno-cast-align', + '-Wno-unused-function', + '-Wno-return-type', + '-Wno-sometimes-uninitialized', + ]) +endif + +cmph = static_library('cmph', + sources: cmph_sources, + c_args: custom_c_args, + dependencies: cmph_deps, +) + +cmph_dep = declare_dependency( + link_with: cmph, + include_directories: include_directories('.'), +) + +if cc.get_id() != 'msvc' + custom_c_args = cc.get_supported_arguments([ + '-Wno-old-style-definition', + '-Wno-type-limits', + ]) +endif + +cmph_test = executable('cmph-bdz-test', '../cmph-bdz-test.c', + dependencies: [ + cmph_dep, + libglib_dep, + libgobject_dep, + ], + c_args: custom_c_args, +) + +test('cmph-bdz-test', cmph_test) diff --git a/girepository/cmph/miller_rabin.c b/girepository/cmph/miller_rabin.c new file mode 100644 index 000000000..17d0ed344 --- /dev/null +++ b/girepository/cmph/miller_rabin.c @@ -0,0 +1,67 @@ +#include "miller_rabin.h" + +static inline cmph_uint64 int_pow(cmph_uint64 a, cmph_uint64 d, cmph_uint64 n) +{ + cmph_uint64 a_pow = a; + cmph_uint64 res = 1; + while(d > 0) + { + if((d & 1) == 1) + res =(((cmph_uint64)res) * a_pow) % n; + a_pow = (((cmph_uint64)a_pow) * a_pow) % n; + d /= 2; + }; + return res; +}; + +static inline cmph_uint8 check_witness(cmph_uint64 a_exp_d, cmph_uint64 n, cmph_uint64 s) +{ + cmph_uint64 i; + cmph_uint64 a_exp = a_exp_d; + if(a_exp == 1 || a_exp == (n - 1)) + return 1; + for(i = 1; i < s; i++) + { + a_exp = (((cmph_uint64)a_exp) * a_exp) % n; + if(a_exp == (n - 1)) + return 1; + }; + return 0; +}; + +cmph_uint8 check_primality(cmph_uint64 n) +{ + cmph_uint64 a, d, s, a_exp_d; + if((n % 2) == 0) + return 0; + if((n % 3) == 0) + return 0; + if((n % 5) == 0) + return 0; + if((n % 7 ) == 0) + return 0; + //we decompoe the number n - 1 into 2^s*d + s = 0; + d = n - 1; + do + { + s++; + d /= 2; + }while((d % 2) == 0); + + a = 2; + a_exp_d = int_pow(a, d, n); + if(check_witness(a_exp_d, n, s) == 0) + return 0; + a = 7; + a_exp_d = int_pow(a, d, n); + if(check_witness(a_exp_d, n, s) == 0) + return 0; + a = 61; + a_exp_d = int_pow(a, d, n); + if(check_witness(a_exp_d, n, s) == 0) + return 0; + return 1; +}; + + diff --git a/girepository/cmph/miller_rabin.h b/girepository/cmph/miller_rabin.h new file mode 100644 index 000000000..42dc6ce5e --- /dev/null +++ b/girepository/cmph/miller_rabin.h @@ -0,0 +1,5 @@ +#ifndef _CMPH_MILLER_RABIN_H__ +#define _CMPH_MILLER_RABIN_H__ +#include "cmph_types.h" +cmph_uint8 check_primality(cmph_uint64 n); +#endif diff --git a/girepository/cmph/sdbm_hash.c b/girepository/cmph/sdbm_hash.c new file mode 100644 index 000000000..2f706c9ff --- /dev/null +++ b/girepository/cmph/sdbm_hash.c @@ -0,0 +1,49 @@ +#include "sdbm_hash.h" +#include + +sdbm_state_t *sdbm_state_new() +{ + sdbm_state_t *state = (sdbm_state_t *)malloc(sizeof(sdbm_state_t)); + state->hashfunc = CMPH_HASH_SDBM; + return state; +} + +void sdbm_state_destroy(sdbm_state_t *state) +{ + free(state); +} + +cmph_uint32 sdbm_hash(sdbm_state_t *state, const char *k, cmph_uint32 keylen) +{ + register cmph_uint32 hash = 0; + const unsigned char *ptr = (unsigned char *)k; + cmph_uint32 i = 0; + + while(i < keylen) { + hash = *ptr + (hash << 6) + (hash << 16) - hash; + ++ptr, ++i; + } + return hash; +} + + +void sdbm_state_dump(sdbm_state_t *state, char **buf, cmph_uint32 *buflen) +{ + *buf = NULL; + *buflen = 0; + return; +} + +sdbm_state_t *sdbm_state_copy(sdbm_state_t *src_state) +{ + sdbm_state_t *dest_state = (sdbm_state_t *)malloc(sizeof(sdbm_state_t)); + dest_state->hashfunc = src_state->hashfunc; + return dest_state; +} + +sdbm_state_t *sdbm_state_load(const char *buf, cmph_uint32 buflen) +{ + sdbm_state_t *state = (sdbm_state_t *)malloc(sizeof(sdbm_state_t)); + state->hashfunc = CMPH_HASH_SDBM; + return state; +} diff --git a/girepository/cmph/sdbm_hash.h b/girepository/cmph/sdbm_hash.h new file mode 100644 index 000000000..f44b2f15a --- /dev/null +++ b/girepository/cmph/sdbm_hash.h @@ -0,0 +1,18 @@ +#ifndef __SDBM_HASH_H__ +#define __SDBM_HASH_H__ + +#include "hash.h" + +typedef struct __sdbm_state_t +{ + CMPH_HASH hashfunc; +} sdbm_state_t; + +sdbm_state_t *sdbm_state_new(); +cmph_uint32 sdbm_hash(sdbm_state_t *state, const char *k, cmph_uint32 keylen); +void sdbm_state_dump(sdbm_state_t *state, char **buf, cmph_uint32 *buflen); +sdbm_state_t *sdbm_state_copy(sdbm_state_t *src_state); +sdbm_state_t *sdbm_state_load(const char *buf, cmph_uint32 buflen); +void sdbm_state_destroy(sdbm_state_t *state); + +#endif diff --git a/girepository/cmph/select.c b/girepository/cmph/select.c new file mode 100644 index 000000000..fec4b7ada --- /dev/null +++ b/girepository/cmph/select.c @@ -0,0 +1,337 @@ +#include +#include +#include +#include +#include +#include "select_lookup_tables.h" +#include "select.h" + +//#define DEBUG +#include "debug.h" + +#ifndef STEP_SELECT_TABLE +#define STEP_SELECT_TABLE 128 +#endif + +#ifndef NBITS_STEP_SELECT_TABLE +#define NBITS_STEP_SELECT_TABLE 7 +#endif + +#ifndef MASK_STEP_SELECT_TABLE +#define MASK_STEP_SELECT_TABLE 0x7f // 0x7f = 127 +#endif + +static inline void select_insert_0(cmph_uint32 * buffer) +{ + (*buffer) >>= 1; +}; + +static inline void select_insert_1(cmph_uint32 * buffer) +{ + (*buffer) >>= 1; + (*buffer) |= 0x80000000; +}; + +void select_init(select_t * sel) +{ + sel->n = 0; + sel->m = 0; + sel->bits_vec = 0; + sel->select_table = 0; +}; + +cmph_uint32 select_get_space_usage(select_t * sel) +{ + register cmph_uint32 nbits; + register cmph_uint32 vec_size; + register cmph_uint32 sel_table_size; + register cmph_uint32 space_usage; + + nbits = sel->n + sel->m; + vec_size = (nbits + 31) >> 5; + sel_table_size = (sel->n >> NBITS_STEP_SELECT_TABLE) + 1; // (sel->n >> NBITS_STEP_SELECT_TABLE) = (sel->n/STEP_SELECT_TABLE) + + space_usage = 2 * sizeof(cmph_uint32) * 8; // n and m + space_usage += vec_size * (cmph_uint32) sizeof(cmph_uint32) * 8; + space_usage += sel_table_size * (cmph_uint32)sizeof(cmph_uint32) * 8; + return space_usage; +} + +void select_destroy(select_t * sel) +{ + free(sel->bits_vec); + free(sel->select_table); + sel->bits_vec = 0; + sel->select_table = 0; +}; + +static inline void select_generate_sel_table(select_t * sel) +{ + register cmph_uint8 * bits_table = (cmph_uint8 *)sel->bits_vec; + register cmph_uint32 part_sum, old_part_sum; + register cmph_uint32 vec_idx, one_idx, sel_table_idx; + + part_sum = vec_idx = one_idx = sel_table_idx = 0; + + for(;;) + { + // FABIANO: Should'n it be one_idx >= sel->n + if(one_idx >= sel->n) + break; + do + { + old_part_sum = part_sum; + part_sum += rank_lookup_table[bits_table[vec_idx]]; + vec_idx++; + } while (part_sum <= one_idx); + + sel->select_table[sel_table_idx] = select_lookup_table[bits_table[vec_idx - 1]][one_idx - old_part_sum] + ((vec_idx - 1) << 3); // ((vec_idx - 1) << 3) = ((vec_idx - 1) * 8) + one_idx += STEP_SELECT_TABLE ; + sel_table_idx++; + }; +}; + +void select_generate(select_t * sel, cmph_uint32 * keys_vec, cmph_uint32 n, cmph_uint32 m) +{ + register cmph_uint32 i, j, idx; + cmph_uint32 buffer = 0; + + register cmph_uint32 nbits; + register cmph_uint32 vec_size; + register cmph_uint32 sel_table_size; + sel->n = n; + sel->m = m; // n values in the range [0,m-1] + + nbits = sel->n + sel->m; + vec_size = (nbits + 31) >> 5; // (nbits + 31) >> 5 = (nbits + 31)/32 + + sel_table_size = (sel->n >> NBITS_STEP_SELECT_TABLE) + 1; // (sel->n >> NBITS_STEP_SELECT_TABLE) = (sel->n/STEP_SELECT_TABLE) + + if(sel->bits_vec) + { + free(sel->bits_vec); + } + sel->bits_vec = (cmph_uint32 *)calloc(vec_size, sizeof(cmph_uint32)); + + if(sel->select_table) + { + free(sel->select_table); + } + sel->select_table = (cmph_uint32 *)calloc(sel_table_size, sizeof(cmph_uint32)); + + + + idx = i = j = 0; + + for(;;) + { + while(keys_vec[j]==i) + { + select_insert_1(&buffer); + idx++; + + if((idx & 0x1f) == 0 ) // (idx & 0x1f) = idx % 32 + sel->bits_vec[(idx >> 5) - 1] = buffer; // (idx >> 5) = idx/32 + j++; + + if(j == sel->n) + goto loop_end; + + //assert(keys_vec[j] < keys_vec[j-1]); + } + + if(i == sel->m) + break; + + while(keys_vec[j] > i) + { + select_insert_0(&buffer); + idx++; + + if((idx & 0x1f) == 0 ) // (idx & 0x1f) = idx % 32 + sel->bits_vec[(idx >> 5) - 1] = buffer; // (idx >> 5) = idx/32 + i++; + }; + + }; + loop_end: + if((idx & 0x1f) != 0 ) // (idx & 0x1f) = idx % 32 + { + buffer >>= 32 - (idx & 0x1f); + sel->bits_vec[ (idx - 1) >> 5 ] = buffer; + }; + + select_generate_sel_table(sel); +}; + +static inline cmph_uint32 _select_query(cmph_uint8 * bits_table, cmph_uint32 * select_table, cmph_uint32 one_idx) +{ + register cmph_uint32 vec_bit_idx ,vec_byte_idx; + register cmph_uint32 part_sum, old_part_sum; + + vec_bit_idx = select_table[one_idx >> NBITS_STEP_SELECT_TABLE]; // one_idx >> NBITS_STEP_SELECT_TABLE = one_idx/STEP_SELECT_TABLE + vec_byte_idx = vec_bit_idx >> 3; // vec_bit_idx / 8 + + one_idx &= MASK_STEP_SELECT_TABLE; // one_idx %= STEP_SELECT_TABLE == one_idx &= MASK_STEP_SELECT_TABLE + one_idx += rank_lookup_table[bits_table[vec_byte_idx] & ((1 << (vec_bit_idx & 0x7)) - 1)]; + part_sum = 0; + + do + { + old_part_sum = part_sum; + part_sum += rank_lookup_table[bits_table[vec_byte_idx]]; + vec_byte_idx++; + + }while (part_sum <= one_idx); + + return select_lookup_table[bits_table[vec_byte_idx - 1]][one_idx - old_part_sum] + ((vec_byte_idx-1) << 3); +} + +cmph_uint32 select_query(select_t * sel, cmph_uint32 one_idx) +{ + return _select_query((cmph_uint8 *)sel->bits_vec, sel->select_table, one_idx); +}; + + +static inline cmph_uint32 _select_next_query(cmph_uint8 * bits_table, cmph_uint32 vec_bit_idx) +{ + register cmph_uint32 vec_byte_idx, one_idx; + register cmph_uint32 part_sum, old_part_sum; + + vec_byte_idx = vec_bit_idx >> 3; + + one_idx = rank_lookup_table[bits_table[vec_byte_idx] & ((1U << (vec_bit_idx & 0x7)) - 1U)] + 1U; + part_sum = 0; + + do + { + old_part_sum = part_sum; + part_sum += rank_lookup_table[bits_table[vec_byte_idx]]; + vec_byte_idx++; + + }while (part_sum <= one_idx); + + return select_lookup_table[bits_table[(vec_byte_idx - 1)]][(one_idx - old_part_sum)] + ((vec_byte_idx - 1) << 3); +} + +cmph_uint32 select_next_query(select_t * sel, cmph_uint32 vec_bit_idx) +{ + return _select_next_query((cmph_uint8 *)sel->bits_vec, vec_bit_idx); +}; + +void select_dump(select_t *sel, char **buf, cmph_uint32 *buflen) +{ + register cmph_uint32 nbits = sel->n + sel->m; + register cmph_uint32 vec_size = ((nbits + 31) >> 5) * (cmph_uint32)sizeof(cmph_uint32); // (nbits + 31) >> 5 = (nbits + 31)/32 + register cmph_uint32 sel_table_size = ((sel->n >> NBITS_STEP_SELECT_TABLE) + 1) * (cmph_uint32)sizeof(cmph_uint32); // (sel->n >> NBITS_STEP_SELECT_TABLE) = (sel->n/STEP_SELECT_TABLE) + register cmph_uint32 pos = 0; + + *buflen = 2*(cmph_uint32)sizeof(cmph_uint32) + vec_size + sel_table_size; + + *buf = (char *)calloc(*buflen, sizeof(char)); + + if (!*buf) + { + *buflen = UINT_MAX; + return; + } + + memcpy(*buf, &(sel->n), sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + memcpy(*buf + pos, &(sel->m), sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + memcpy(*buf + pos, sel->bits_vec, vec_size); + pos += vec_size; + memcpy(*buf + pos, sel->select_table, sel_table_size); + + DEBUGP("Dumped select structure with size %u bytes\n", *buflen); +} + +void select_load(select_t * sel, const char *buf, cmph_uint32 buflen) +{ + register cmph_uint32 pos = 0; + register cmph_uint32 nbits = 0; + register cmph_uint32 vec_size = 0; + register cmph_uint32 sel_table_size = 0; + + memcpy(&(sel->n), buf, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + memcpy(&(sel->m), buf + pos, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + + nbits = sel->n + sel->m; + vec_size = ((nbits + 31) >> 5) * (cmph_uint32)sizeof(cmph_uint32); // (nbits + 31) >> 5 = (nbits + 31)/32 + sel_table_size = ((sel->n >> NBITS_STEP_SELECT_TABLE) + 1) * (cmph_uint32)sizeof(cmph_uint32); // (sel->n >> NBITS_STEP_SELECT_TABLE) = (sel->n/STEP_SELECT_TABLE) + + if(sel->bits_vec) + { + free(sel->bits_vec); + } + sel->bits_vec = (cmph_uint32 *)calloc(vec_size/sizeof(cmph_uint32), sizeof(cmph_uint32)); + + if(sel->select_table) + { + free(sel->select_table); + } + sel->select_table = (cmph_uint32 *)calloc(sel_table_size/sizeof(cmph_uint32), sizeof(cmph_uint32)); + + memcpy(sel->bits_vec, buf + pos, vec_size); + pos += vec_size; + memcpy(sel->select_table, buf + pos, sel_table_size); + + DEBUGP("Loaded select structure with size %u bytes\n", buflen); +} + + +/** \fn void select_pack(select_t *sel, void *sel_packed); + * \brief Support the ability to pack a select structure function into a preallocated contiguous memory space pointed by sel_packed. + * \param sel points to the select structure + * \param sel_packed pointer to the contiguous memory area used to store the select structure. The size of sel_packed must be at least @see select_packed_size + */ +void select_pack(select_t *sel, void *sel_packed) +{ + if (sel && sel_packed) + { + char *buf = NULL; + cmph_uint32 buflen = 0; + select_dump(sel, &buf, &buflen); + memcpy(sel_packed, buf, buflen); + free(buf); + } +} + + +/** \fn cmph_uint32 select_packed_size(select_t *sel); + * \brief Return the amount of space needed to pack a select structure. + * \return the size of the packed select structure or zero for failures + */ +cmph_uint32 select_packed_size(select_t *sel) +{ + register cmph_uint32 nbits = sel->n + sel->m; + register cmph_uint32 vec_size = ((nbits + 31) >> 5) * (cmph_uint32)sizeof(cmph_uint32); // (nbits + 31) >> 5 = (nbits + 31)/32 + register cmph_uint32 sel_table_size = ((sel->n >> NBITS_STEP_SELECT_TABLE) + 1) * (cmph_uint32)sizeof(cmph_uint32); // (sel->n >> NBITS_STEP_SELECT_TABLE) = (sel->n/STEP_SELECT_TABLE) + return 2*(cmph_uint32)sizeof(cmph_uint32) + vec_size + sel_table_size; +} + + + +cmph_uint32 select_query_packed(void * sel_packed, cmph_uint32 one_idx) +{ + register cmph_uint32 *ptr = (cmph_uint32 *)sel_packed; + register cmph_uint32 n = *ptr++; + register cmph_uint32 m = *ptr++; + register cmph_uint32 nbits = n + m; + register cmph_uint32 vec_size = (nbits + 31) >> 5; // (nbits + 31) >> 5 = (nbits + 31)/32 + register cmph_uint8 * bits_vec = (cmph_uint8 *)ptr; + register cmph_uint32 * select_table = ptr + vec_size; + + return _select_query(bits_vec, select_table, one_idx); +} + + +cmph_uint32 select_next_query_packed(void * sel_packed, cmph_uint32 vec_bit_idx) +{ + register cmph_uint8 * bits_vec = (cmph_uint8 *)sel_packed; + bits_vec += 8; // skipping n and m + return _select_next_query(bits_vec, vec_bit_idx); +} diff --git a/girepository/cmph/select.h b/girepository/cmph/select.h new file mode 100644 index 000000000..a31eb0f22 --- /dev/null +++ b/girepository/cmph/select.h @@ -0,0 +1,61 @@ +#ifndef __CMPH_SELECT_H__ +#define __CMPH_SELECT_H__ + +#include "cmph_types.h" + +struct _select_t +{ + cmph_uint32 n,m; + cmph_uint32 * bits_vec; + cmph_uint32 * select_table; +}; + +typedef struct _select_t select_t; + +void select_init(select_t * sel); + +void select_destroy(select_t * sel); + +void select_generate(select_t * sel, cmph_uint32 * keys_vec, cmph_uint32 n, cmph_uint32 m); + +cmph_uint32 select_query(select_t * sel, cmph_uint32 one_idx); + +cmph_uint32 select_next_query(select_t * sel, cmph_uint32 vec_bit_idx); + +cmph_uint32 select_get_space_usage(select_t * sel); + +void select_dump(select_t *sel, char **buf, cmph_uint32 *buflen); + +void select_load(select_t * sel, const char *buf, cmph_uint32 buflen); + + +/** \fn void select_pack(select_t *sel, void *sel_packed); + * \brief Support the ability to pack a select structure into a preallocated contiguous memory space pointed by sel_packed. + * \param sel points to the select structure + * \param sel_packed pointer to the contiguous memory area used to store the select structure. The size of sel_packed must be at least @see select_packed_size + */ +void select_pack(select_t *sel, void *sel_packed); + +/** \fn cmph_uint32 select_packed_size(select_t *sel); + * \brief Return the amount of space needed to pack a select structure. + * \return the size of the packed select structure or zero for failures + */ +cmph_uint32 select_packed_size(select_t *sel); + + +/** \fn cmph_uint32 select_query_packed(void * sel_packed, cmph_uint32 one_idx); + * \param sel_packed is a pointer to a contiguous memory area + * \param one_idx is the rank for which we want to calculate the inverse function select + * \return an integer that represents the select value of rank idx. + */ +cmph_uint32 select_query_packed(void * sel_packed, cmph_uint32 one_idx); + + +/** \fn cmph_uint32 select_next_query_packed(void * sel_packed, cmph_uint32 vec_bit_idx); + * \param sel_packed is a pointer to a contiguous memory area + * \param vec_bit_idx is a value prior computed by @see select_query_packed + * \return an integer that represents the next select value greater than @see vec_bit_idx. + */ +cmph_uint32 select_next_query_packed(void * sel_packed, cmph_uint32 vec_bit_idx); + +#endif diff --git a/girepository/cmph/select_lookup_tables.h b/girepository/cmph/select_lookup_tables.h new file mode 100644 index 000000000..efd595ed5 --- /dev/null +++ b/girepository/cmph/select_lookup_tables.h @@ -0,0 +1,170 @@ +#ifndef SELECT_LOOKUP_TABLES +#define SELECT_LOOKUP_TABLES + +#include "cmph_types.h" + +/* +rank_lookup_table[i] simply gives the number of bits set to one in the byte of value i. +For example if i = 01010101 in binary then we have : +rank_lookup_table[i] = 4 +*/ + +static cmph_uint8 rank_lookup_table[256] ={ + 0 , 1 , 1 , 2 , 1 , 2 , 2 , 3 , 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 +, 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 +, 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 +, 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 +, 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 +, 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 +, 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 +, 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7 +, 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 +, 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 +, 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 +, 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7 +, 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 +, 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7 +, 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7 +, 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7 , 5 , 6 , 6 , 7 , 6 , 7 , 7 , 8 + }; + +/* +select_lookup_table[i][j] simply gives the index of the j'th bit set to one in the byte of value i. +For example if i=01010101 in binary then we have : +select_lookup_table[i][0] = 0, the first bit set to one is at position 0 +select_lookup_table[i][1] = 2, the second bit set to one is at position 2 +select_lookup_table[i][2] = 4, the third bit set to one is at position 4 +select_lookup_table[i][3] = 6, the fourth bit set to one is at position 6 +select_lookup_table[i][4] = 255, there is no more than 4 bits set to one in i, so we return escape value 255. +*/ +static cmph_uint8 select_lookup_table[256][8]={ +{ 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 255 , 255 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 255 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 255 , 255 , 255 , 255 , 255 , 255 } , +{ 2 , 255 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 255 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 255 , 255 , 255 , 255 , 255 } , +{ 3 , 255 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 255 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 255 , 255 , 255 , 255 , 255 } , +{ 2 , 3 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 255 , 255 , 255 , 255 } , +{ 4 , 255 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 4 , 255 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 4 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 4 , 255 , 255 , 255 , 255 , 255 } , +{ 2 , 4 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 4 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 4 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 4 , 255 , 255 , 255 , 255 } , +{ 3 , 4 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 4 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 4 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 4 , 255 , 255 , 255 , 255 } , +{ 2 , 3 , 4 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 4 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 4 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 4 , 255 , 255 , 255 } , +{ 5 , 255 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 5 , 255 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 5 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 5 , 255 , 255 , 255 , 255 , 255 } , +{ 2 , 5 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 5 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 5 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 5 , 255 , 255 , 255 , 255 } , +{ 3 , 5 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 5 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 5 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 5 , 255 , 255 , 255 , 255 } , +{ 2 , 3 , 5 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 5 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 5 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 5 , 255 , 255 , 255 } , +{ 4 , 5 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 4 , 5 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 4 , 5 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 4 , 5 , 255 , 255 , 255 , 255 } , +{ 2 , 4 , 5 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 4 , 5 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 4 , 5 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 4 , 5 , 255 , 255 , 255 } , +{ 3 , 4 , 5 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 4 , 5 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 4 , 5 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 4 , 5 , 255 , 255 , 255 } , +{ 2 , 3 , 4 , 5 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 4 , 5 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 4 , 5 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 4 , 5 , 255 , 255 } , +{ 6 , 255 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 6 , 255 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 6 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 6 , 255 , 255 , 255 , 255 , 255 } , +{ 2 , 6 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 6 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 6 , 255 , 255 , 255 , 255 } , +{ 3 , 6 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 6 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 6 , 255 , 255 , 255 , 255 } , +{ 2 , 3 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 6 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 6 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 6 , 255 , 255 , 255 } , +{ 4 , 6 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 4 , 6 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 4 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 4 , 6 , 255 , 255 , 255 , 255 } , +{ 2 , 4 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 4 , 6 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 4 , 6 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 4 , 6 , 255 , 255 , 255 } , +{ 3 , 4 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 4 , 6 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 4 , 6 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 4 , 6 , 255 , 255 , 255 } , +{ 2 , 3 , 4 , 6 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 4 , 6 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 4 , 6 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 4 , 6 , 255 , 255 } , +{ 5 , 6 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 5 , 6 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 5 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 5 , 6 , 255 , 255 , 255 , 255 } , +{ 2 , 5 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 5 , 6 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 5 , 6 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 5 , 6 , 255 , 255 , 255 } , +{ 3 , 5 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 5 , 6 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 5 , 6 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 5 , 6 , 255 , 255 , 255 } , +{ 2 , 3 , 5 , 6 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 5 , 6 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 5 , 6 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 5 , 6 , 255 , 255 } , +{ 4 , 5 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 4 , 5 , 6 , 255 , 255 , 255 , 255 } , +{ 1 , 4 , 5 , 6 , 255 , 255 , 255 , 255 } , { 0 , 1 , 4 , 5 , 6 , 255 , 255 , 255 } , +{ 2 , 4 , 5 , 6 , 255 , 255 , 255 , 255 } , { 0 , 2 , 4 , 5 , 6 , 255 , 255 , 255 } , +{ 1 , 2 , 4 , 5 , 6 , 255 , 255 , 255 } , { 0 , 1 , 2 , 4 , 5 , 6 , 255 , 255 } , +{ 3 , 4 , 5 , 6 , 255 , 255 , 255 , 255 } , { 0 , 3 , 4 , 5 , 6 , 255 , 255 , 255 } , +{ 1 , 3 , 4 , 5 , 6 , 255 , 255 , 255 } , { 0 , 1 , 3 , 4 , 5 , 6 , 255 , 255 } , +{ 2 , 3 , 4 , 5 , 6 , 255 , 255 , 255 } , { 0 , 2 , 3 , 4 , 5 , 6 , 255 , 255 } , +{ 1 , 2 , 3 , 4 , 5 , 6 , 255 , 255 } , { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 255 } , +{ 7 , 255 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 7 , 255 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 7 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 7 , 255 , 255 , 255 , 255 , 255 } , +{ 2 , 7 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 7 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 7 , 255 , 255 , 255 , 255 } , +{ 3 , 7 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 7 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 7 , 255 , 255 , 255 , 255 } , +{ 2 , 3 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 7 , 255 , 255 , 255 } , +{ 4 , 7 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 4 , 7 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 4 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 4 , 7 , 255 , 255 , 255 , 255 } , +{ 2 , 4 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 4 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 4 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 4 , 7 , 255 , 255 , 255 } , +{ 3 , 4 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 4 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 4 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 4 , 7 , 255 , 255 , 255 } , +{ 2 , 3 , 4 , 7 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 4 , 7 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 4 , 7 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 4 , 7 , 255 , 255 } , +{ 5 , 7 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 5 , 7 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 5 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 5 , 7 , 255 , 255 , 255 , 255 } , +{ 2 , 5 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 5 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 5 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 5 , 7 , 255 , 255 , 255 } , +{ 3 , 5 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 5 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 5 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 5 , 7 , 255 , 255 , 255 } , +{ 2 , 3 , 5 , 7 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 5 , 7 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 5 , 7 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 5 , 7 , 255 , 255 } , +{ 4 , 5 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 4 , 5 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 4 , 5 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 4 , 5 , 7 , 255 , 255 , 255 } , +{ 2 , 4 , 5 , 7 , 255 , 255 , 255 , 255 } , { 0 , 2 , 4 , 5 , 7 , 255 , 255 , 255 } , +{ 1 , 2 , 4 , 5 , 7 , 255 , 255 , 255 } , { 0 , 1 , 2 , 4 , 5 , 7 , 255 , 255 } , +{ 3 , 4 , 5 , 7 , 255 , 255 , 255 , 255 } , { 0 , 3 , 4 , 5 , 7 , 255 , 255 , 255 } , +{ 1 , 3 , 4 , 5 , 7 , 255 , 255 , 255 } , { 0 , 1 , 3 , 4 , 5 , 7 , 255 , 255 } , +{ 2 , 3 , 4 , 5 , 7 , 255 , 255 , 255 } , { 0 , 2 , 3 , 4 , 5 , 7 , 255 , 255 } , +{ 1 , 2 , 3 , 4 , 5 , 7 , 255 , 255 } , { 0 , 1 , 2 , 3 , 4 , 5 , 7 , 255 } , +{ 6 , 7 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 6 , 7 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 6 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 6 , 7 , 255 , 255 , 255 , 255 } , +{ 2 , 6 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 6 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 6 , 7 , 255 , 255 , 255 } , +{ 3 , 6 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 6 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 6 , 7 , 255 , 255 , 255 } , +{ 2 , 3 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 6 , 7 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 6 , 7 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 6 , 7 , 255 , 255 } , +{ 4 , 6 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 4 , 6 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 4 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 4 , 6 , 7 , 255 , 255 , 255 } , +{ 2 , 4 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 2 , 4 , 6 , 7 , 255 , 255 , 255 } , +{ 1 , 2 , 4 , 6 , 7 , 255 , 255 , 255 } , { 0 , 1 , 2 , 4 , 6 , 7 , 255 , 255 } , +{ 3 , 4 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 3 , 4 , 6 , 7 , 255 , 255 , 255 } , +{ 1 , 3 , 4 , 6 , 7 , 255 , 255 , 255 } , { 0 , 1 , 3 , 4 , 6 , 7 , 255 , 255 } , +{ 2 , 3 , 4 , 6 , 7 , 255 , 255 , 255 } , { 0 , 2 , 3 , 4 , 6 , 7 , 255 , 255 } , +{ 1 , 2 , 3 , 4 , 6 , 7 , 255 , 255 } , { 0 , 1 , 2 , 3 , 4 , 6 , 7 , 255 } , +{ 5 , 6 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 5 , 6 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 5 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 5 , 6 , 7 , 255 , 255 , 255 } , +{ 2 , 5 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 2 , 5 , 6 , 7 , 255 , 255 , 255 } , +{ 1 , 2 , 5 , 6 , 7 , 255 , 255 , 255 } , { 0 , 1 , 2 , 5 , 6 , 7 , 255 , 255 } , +{ 3 , 5 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 3 , 5 , 6 , 7 , 255 , 255 , 255 } , +{ 1 , 3 , 5 , 6 , 7 , 255 , 255 , 255 } , { 0 , 1 , 3 , 5 , 6 , 7 , 255 , 255 } , +{ 2 , 3 , 5 , 6 , 7 , 255 , 255 , 255 } , { 0 , 2 , 3 , 5 , 6 , 7 , 255 , 255 } , +{ 1 , 2 , 3 , 5 , 6 , 7 , 255 , 255 } , { 0 , 1 , 2 , 3 , 5 , 6 , 7 , 255 } , +{ 4 , 5 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 4 , 5 , 6 , 7 , 255 , 255 , 255 } , +{ 1 , 4 , 5 , 6 , 7 , 255 , 255 , 255 } , { 0 , 1 , 4 , 5 , 6 , 7 , 255 , 255 } , +{ 2 , 4 , 5 , 6 , 7 , 255 , 255 , 255 } , { 0 , 2 , 4 , 5 , 6 , 7 , 255 , 255 } , +{ 1 , 2 , 4 , 5 , 6 , 7 , 255 , 255 } , { 0 , 1 , 2 , 4 , 5 , 6 , 7 , 255 } , +{ 3 , 4 , 5 , 6 , 7 , 255 , 255 , 255 } , { 0 , 3 , 4 , 5 , 6 , 7 , 255 , 255 } , +{ 1 , 3 , 4 , 5 , 6 , 7 , 255 , 255 } , { 0 , 1 , 3 , 4 , 5 , 6 , 7 , 255 } , +{ 2 , 3 , 4 , 5 , 6 , 7 , 255 , 255 } , { 0 , 2 , 3 , 4 , 5 , 6 , 7 , 255 } , +{ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 255 } , { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 } }; + +#endif diff --git a/girepository/cmph/vqueue.c b/girepository/cmph/vqueue.c new file mode 100644 index 000000000..0619dd7cc --- /dev/null +++ b/girepository/cmph/vqueue.c @@ -0,0 +1,51 @@ +#include "vqueue.h" +#include +#include +#include +struct __vqueue_t +{ + cmph_uint32 * values; + cmph_uint32 beg, end, capacity; +}; + +vqueue_t * vqueue_new(cmph_uint32 capacity) +{ + size_t capacity_plus_one = capacity + 1; + vqueue_t *q = (vqueue_t *)malloc(sizeof(vqueue_t)); + assert(q); + q->values = (cmph_uint32 *)calloc(capacity_plus_one, sizeof(cmph_uint32)); + q->beg = q->end = 0; + q->capacity = (cmph_uint32) capacity_plus_one; + return q; +} + +cmph_uint8 vqueue_is_empty(vqueue_t * q) +{ + return (cmph_uint8)(q->beg == q->end); +} + +void vqueue_insert(vqueue_t * q, cmph_uint32 val) +{ + assert((q->end + 1)%q->capacity != q->beg); // Is queue full? + q->end = (q->end + 1)%q->capacity; + q->values[q->end] = val; +} + +cmph_uint32 vqueue_remove(vqueue_t * q) +{ + assert(!vqueue_is_empty(q)); // Is queue empty? + q->beg = (q->beg + 1)%q->capacity; + return q->values[q->beg]; +} + +void vqueue_print(vqueue_t * q) +{ + cmph_uint32 i; + for (i = q->beg; i != q->end; i = (i + 1)%q->capacity) + fprintf(stderr, "%u\n", q->values[(i + 1)%q->capacity]); +} + +void vqueue_destroy(vqueue_t *q) +{ + free(q->values); q->values = NULL; free(q); +} diff --git a/girepository/cmph/vqueue.h b/girepository/cmph/vqueue.h new file mode 100644 index 000000000..86fccab68 --- /dev/null +++ b/girepository/cmph/vqueue.h @@ -0,0 +1,18 @@ +#ifndef __CMPH_VQUEUE_H__ +#define __CMPH_VQUEUE_H__ + +#include "cmph_types.h" +typedef struct __vqueue_t vqueue_t; + +vqueue_t * vqueue_new(cmph_uint32 capacity); + +cmph_uint8 vqueue_is_empty(vqueue_t * q); + +void vqueue_insert(vqueue_t * q, cmph_uint32 val); + +cmph_uint32 vqueue_remove(vqueue_t * q); + +void vqueue_print(vqueue_t * q); + +void vqueue_destroy(vqueue_t * q); +#endif diff --git a/girepository/cmph/vstack.c b/girepository/cmph/vstack.c new file mode 100644 index 000000000..96f5380ab --- /dev/null +++ b/girepository/cmph/vstack.c @@ -0,0 +1,79 @@ +#include "vstack.h" + +#include +#include + +//#define DEBUG +#include "debug.h" + +struct __vstack_t +{ + cmph_uint32 pointer; + cmph_uint32 *values; + cmph_uint32 capacity; +}; + +vstack_t *vstack_new(void) +{ + vstack_t *stack = (vstack_t *)malloc(sizeof(vstack_t)); + assert(stack); + stack->pointer = 0; + stack->values = NULL; + stack->capacity = 0; + return stack; +} + +void vstack_destroy(vstack_t *stack) +{ + assert(stack); + free(stack->values); + free(stack); +} + +void vstack_push(vstack_t *stack, cmph_uint32 val) +{ + assert(stack); + vstack_reserve(stack, stack->pointer + 1); + stack->values[stack->pointer] = val; + ++(stack->pointer); +} +void vstack_pop(vstack_t *stack) +{ + assert(stack); + assert(stack->pointer > 0); + --(stack->pointer); +} + +cmph_uint32 vstack_top(vstack_t *stack) +{ + assert(stack); + assert(stack->pointer > 0); + return stack->values[(stack->pointer - 1)]; +} +int vstack_empty(vstack_t *stack) +{ + assert(stack); + return stack->pointer == 0; +} +cmph_uint32 vstack_size(vstack_t *stack) +{ + return stack->pointer; +} +void vstack_reserve(vstack_t *stack, cmph_uint32 size) +{ + assert(stack); + if (stack->capacity < size) + { + cmph_uint32 new_capacity = stack->capacity + 1; + DEBUGP("Increasing current capacity %u to %u\n", stack->capacity, size); + while (new_capacity < size) + { + new_capacity *= 2; + } + stack->values = (cmph_uint32 *)realloc(stack->values, sizeof(cmph_uint32)*new_capacity); + assert(stack->values); + stack->capacity = new_capacity; + DEBUGP("Increased\n"); + } +} + diff --git a/girepository/cmph/vstack.h b/girepository/cmph/vstack.h new file mode 100644 index 000000000..fecc7d55d --- /dev/null +++ b/girepository/cmph/vstack.h @@ -0,0 +1,18 @@ +#ifndef __CMPH_VSTACK_H__ +#define __CMPH_VSTACK_H__ + +#include "cmph_types.h" +typedef struct __vstack_t vstack_t; + +vstack_t *vstack_new(void); +void vstack_destroy(vstack_t *stack); + +void vstack_push(vstack_t *stack, cmph_uint32 val); +cmph_uint32 vstack_top(vstack_t *stack); +void vstack_pop(vstack_t *stack); +int vstack_empty(vstack_t *stack); +cmph_uint32 vstack_size(vstack_t *stack); + +void vstack_reserve(vstack_t *stack, cmph_uint32 size); + +#endif diff --git a/girepository/cmph/wingetopt.c b/girepository/cmph/wingetopt.c new file mode 100644 index 000000000..c981d0f04 --- /dev/null +++ b/girepository/cmph/wingetopt.c @@ -0,0 +1,179 @@ +#ifdef WIN32 +/***************************************************************************** + * + * MODULE NAME : GETOPT.C + * + * COPYRIGHTS: + * This module contains code made available by IBM + * Corporation on an AS IS basis. Any one receiving the + * module is considered to be licensed under IBM copyrights + * to use the IBM-provided source code in any way he or she + * deems fit, including copying it, compiling it, modifying + * it, and redistributing it, with or without + * modifications. No license under any IBM patents or + * patent applications is to be implied from this copyright + * license. + * + * A user of the module should understand that IBM cannot + * provide technical support for the module and will not be + * responsible for any consequences of use of the program. + * + * Any notices, including this one, are not to be removed + * from the module without the prior written consent of + * IBM. + * + * AUTHOR: Original author: + * G. R. Blair (BOBBLAIR at AUSVM1) + * Internet: bobblair@bobblair.austin.ibm.com + * + * Extensively revised by: + * John Q. Walker II, Ph.D. (JOHHQ at RALVM6) + * Internet: johnq@ralvm6.vnet.ibm.com + * + *****************************************************************************/ + +/****************************************************************************** + * getopt() + * + * The getopt() function is a command line parser. It returns the next + * option character in argv that matches an option character in opstring. + * + * The argv argument points to an array of argc+1 elements containing argc + * pointers to character strings followed by a null pointer. + * + * The opstring argument points to a string of option characters; if an + * option character is followed by a colon, the option is expected to have + * an argument that may or may not be separated from it by white space. + * The external variable optarg is set to point to the start of the option + * argument on return from getopt(). + * + * The getopt() function places in optind the argv index of the next argument + * to be processed. The system initializes the external variable optind to + * 1 before the first call to getopt(). + * + * When all options have been processed (that is, up to the first nonoption + * argument), getopt() returns EOF. The special option "--" may be used to + * delimit the end of the options; EOF will be returned, and "--" will be + * skipped. + * + * The getopt() function returns a question mark (?) when it encounters an + * option character not included in opstring. This error message can be + * disabled by setting opterr to zero. Otherwise, it returns the option + * character that was detected. + * + * If the special option "--" is detected, or all options have been + * processed, EOF is returned. + * + * Options are marked by either a minus sign (-) or a slash (/). + * + * No errors are defined. + *****************************************************************************/ + +#include /* for EOF */ +#include /* for strchr() */ + +/* static (global) variables that are specified as exported by getopt() */ +extern char *optarg; /* pointer to the start of the option argument */ +extern int optind; /* number of the next argv[] to be evaluated */ +extern int opterr; /* non-zero if a question mark should be returned + when a non-valid option character is detected */ + +/* handle possible future character set concerns by putting this in a macro */ +#define _next_char(string) (char)(*(string+1)) + +int getopt(int argc, char *argv[], char *opstring) +{ + static char *pIndexPosition = NULL; /* place inside current argv string */ + char *pArgString = NULL; /* where to start from next */ + char *pOptString; /* the string in our program */ + + + if (pIndexPosition != NULL) { + /* we last left off inside an argv string */ + if (*(++pIndexPosition)) { + /* there is more to come in the most recent argv */ + pArgString = pIndexPosition; + } + } + + if (pArgString == NULL) { + /* we didn't leave off in the middle of an argv string */ + if (optind >= argc) { + /* more command-line arguments than the argument count */ + pIndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* used up all command-line arguments */ + } + + /*--------------------------------------------------------------------- + * If the next argv[] is not an option, there can be no more options. + *-------------------------------------------------------------------*/ + pArgString = argv[optind++]; /* set this to the next argument ptr */ + + if (('/' != *pArgString) && /* doesn't start with a slash or a dash? */ + ('-' != *pArgString)) { + --optind; /* point to current arg once we're done */ + optarg = NULL; /* no argument follows the option */ + pIndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* used up all the command-line flags */ + } + + /* check for special end-of-flags markers */ + if ((strcmp(pArgString, "-") == 0) || + (strcmp(pArgString, "--") == 0)) { + optarg = NULL; /* no argument follows the option */ + pIndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* encountered the special flag */ + } + + pArgString++; /* look past the / or - */ + } + + if (':' == *pArgString) { /* is it a colon? */ + /*--------------------------------------------------------------------- + * Rare case: if opterr is non-zero, return a question mark; + * otherwise, just return the colon we're on. + *-------------------------------------------------------------------*/ + return (opterr ? (int)'?' : (int)':'); + } + else if ((pOptString = strchr(opstring, *pArgString)) == 0) { + /*--------------------------------------------------------------------- + * The letter on the command-line wasn't any good. + *-------------------------------------------------------------------*/ + optarg = NULL; /* no argument follows the option */ + pIndexPosition = NULL; /* not in the middle of anything */ + return (opterr ? (int)'?' : (int)*pArgString); + } + else { + /*--------------------------------------------------------------------- + * The letter on the command-line matches one we expect to see + *-------------------------------------------------------------------*/ + if (':' == _next_char(pOptString)) { /* is the next letter a colon? */ + /* It is a colon. Look for an argument string. */ + if ('\0' != _next_char(pArgString)) { /* argument in this argv? */ + optarg = &pArgString[1]; /* Yes, it is */ + } + else { + /*------------------------------------------------------------- + * The argument string must be in the next argv. + * But, what if there is none (bad input from the user)? + * In that case, return the letter, and optarg as NULL. + *-----------------------------------------------------------*/ + if (optind < argc) + optarg = argv[optind++]; + else { + optarg = NULL; + return (opterr ? (int)'?' : (int)*pArgString); + } + } + pIndexPosition = NULL; /* not in the middle of anything */ + } + else { + /* it's not a colon, so just return the letter */ + optarg = NULL; /* no argument follows the option */ + pIndexPosition = pArgString; /* point to the letter we're on */ + } + return (int)*pArgString; /* return the letter that matched */ + } +} + +#endif //WIN32 diff --git a/girepository/cmph/wingetopt.h b/girepository/cmph/wingetopt.h new file mode 100644 index 000000000..9596853d9 --- /dev/null +++ b/girepository/cmph/wingetopt.h @@ -0,0 +1,25 @@ +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef WIN32 + #include +#else + #ifndef _GETOPT_ + #define _GETOPT_ + + #include /* for EOF */ + #include /* for strchr() */ + + char *optarg = NULL; /* pointer to the start of the option argument */ + int optind = 1; /* number of the next argv[] to be evaluated */ + int opterr = 1; /* non-zero if a question mark should be returned */ + + int getopt(int argc, char *argv[], char *opstring); + #endif //_GETOPT_ +#endif //WIN32 + +#ifdef __cplusplus +} +#endif + diff --git a/girepository/docs.c b/girepository/docs.c new file mode 100644 index 000000000..5373c1db3 --- /dev/null +++ b/girepository/docs.c @@ -0,0 +1,35 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Dump introspection data + * + * Copyright (C) 2013 Dieter Verfaillie + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +/* This file collects documentation for macros, typedefs and + * the like, which have no good home in any of the 'real' source + * files. + */ + +/** + * SECTION:gicommontypes + * @title: Common Types + * @short_description: TODO + * + * TODO + */ diff --git a/girepository/gdump.c b/girepository/gdump.c new file mode 100644 index 000000000..a805427f8 --- /dev/null +++ b/girepository/gdump.c @@ -0,0 +1,684 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Dump introspection data + * + * Copyright (C) 2008 Colin Walters + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +/* This file is both compiled into libgirepository.so, and installed + * on the filesystem. But for the dumper, we want to avoid linking + * to libgirepository; see + * https://bugzilla.gnome.org/show_bug.cgi?id=630342 + */ +#ifdef GI_COMPILATION +#include "config.h" +#include "girepository.h" +#endif + +#include +#include +#include + +#include +#include + +static void +escaped_printf (GOutputStream *out, const char *fmt, ...) G_GNUC_PRINTF (2, 3); + +static void +escaped_printf (GOutputStream *out, const char *fmt, ...) +{ + char *str; + va_list args; + gsize written; + GError *error = NULL; + + va_start (args, fmt); + + str = g_markup_vprintf_escaped (fmt, args); + if (!g_output_stream_write_all (out, str, strlen (str), &written, NULL, &error)) + { + g_critical ("failed to write to iochannel: %s", error->message); + g_clear_error (&error); + } + g_free (str); + + va_end (args); +} + +static void +goutput_write (GOutputStream *out, const char *str) +{ + gsize written; + GError *error = NULL; + if (!g_output_stream_write_all (out, str, strlen (str), &written, NULL, &error)) + { + g_critical ("failed to write to iochannel: %s", error->message); + g_clear_error (&error); + } +} + +typedef GType (*GetTypeFunc)(void); +typedef GQuark (*ErrorQuarkFunc)(void); + +static GType +invoke_get_type (GModule *self, const char *symbol, GError **error) +{ + GetTypeFunc sym; + GType ret; + + if (!g_module_symbol (self, symbol, (void**)&sym)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Failed to find symbol '%s'", symbol); + return G_TYPE_INVALID; + } + + ret = sym (); + if (ret == G_TYPE_INVALID) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Function '%s' returned G_TYPE_INVALID", symbol); + } + return ret; +} + +static GQuark +invoke_error_quark (GModule *self, const char *symbol, GError **error) +{ + ErrorQuarkFunc sym; + + if (!g_module_symbol (self, symbol, (void**)&sym)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Failed to find symbol '%s'", symbol); + return G_TYPE_INVALID; + } + + return sym (); +} + +static char * +value_transform_to_string (const GValue *value) +{ + GValue tmp = G_VALUE_INIT; + char *s = NULL; + + g_value_init (&tmp, G_TYPE_STRING); + + if (g_value_transform (value, &tmp)) + { + const char *str = g_value_get_string (&tmp); + + if (str != NULL) + s = g_strescape (str, NULL); + } + + g_value_unset (&tmp); + + return s; +} + +/* A simpler version of g_strdup_value_contents(), but with stable + * output and less complex semantics + */ +static char * +value_to_string (const GValue *value) +{ + if (value == NULL) + return NULL; + + if (G_VALUE_HOLDS_STRING (value)) + { + const char *s = g_value_get_string (value); + + if (s == NULL) + return g_strdup ("NULL"); + + return g_strescape (s, NULL); + } + else + { + GType value_type = G_VALUE_TYPE (value); + + switch (G_TYPE_FUNDAMENTAL (value_type)) + { + case G_TYPE_BOXED: + if (g_value_get_boxed (value) == NULL) + return NULL; + else + return value_transform_to_string (value); + break; + + case G_TYPE_OBJECT: + if (g_value_get_object (value) == NULL) + return NULL; + else + return value_transform_to_string (value); + break; + + case G_TYPE_POINTER: + return NULL; + + default: + return value_transform_to_string (value); + } + } + + return NULL; +} + +static void +dump_properties (GType type, GOutputStream *out) +{ + guint i; + guint n_properties; + GParamSpec **props; + + if (G_TYPE_FUNDAMENTAL (type) == G_TYPE_OBJECT) + { + GObjectClass *klass; + klass = g_type_class_ref (type); + props = g_object_class_list_properties (klass, &n_properties); + } + else + { + void *klass; + klass = g_type_default_interface_ref (type); + props = g_object_interface_list_properties (klass, &n_properties); + } + + for (i = 0; i < n_properties; i++) + { + GParamSpec *prop; + + prop = props[i]; + if (prop->owner_type != type) + continue; + + const GValue *v = g_param_spec_get_default_value (prop); + char *default_value = value_to_string (v); + + if (v != NULL && default_value != NULL) + { + escaped_printf (out, " \n", + prop->name, + g_type_name (prop->value_type), + prop->flags, + default_value); + } + else + { + escaped_printf (out, " \n", + prop->name, + g_type_name (prop->value_type), + prop->flags); + } + + g_free (default_value); + } + + g_free (props); +} + +static void +dump_signals (GType type, GOutputStream *out) +{ + guint i; + guint n_sigs; + guint *sig_ids; + + sig_ids = g_signal_list_ids (type, &n_sigs); + for (i = 0; i < n_sigs; i++) + { + guint sigid; + GSignalQuery query; + guint j; + + sigid = sig_ids[i]; + g_signal_query (sigid, &query); + + escaped_printf (out, " \n"); + + for (j = 0; j < query.n_params; j++) + { + escaped_printf (out, " \n", + g_type_name (query.param_types[j])); + } + goutput_write (out, " \n"); + } + g_free (sig_ids); +} + +static void +dump_object_type (GType type, const char *symbol, GOutputStream *out) +{ + guint n_interfaces; + guint i; + GType *interfaces; + + escaped_printf (out, " str); + + g_string_free (parent_str, TRUE); + } + + if (G_TYPE_IS_ABSTRACT (type)) + escaped_printf (out, " abstract=\"1\""); + +#if GLIB_CHECK_VERSION (2, 70, 0) + if (G_TYPE_IS_FINAL (type)) + escaped_printf (out, " final=\"1\""); +#endif + + goutput_write (out, ">\n"); + + interfaces = g_type_interfaces (type, &n_interfaces); + for (i = 0; i < n_interfaces; i++) + { + GType itype = interfaces[i]; + escaped_printf (out, " \n", + g_type_name (itype)); + } + g_free (interfaces); + + dump_properties (type, out); + dump_signals (type, out); + goutput_write (out, " \n"); +} + +static void +dump_interface_type (GType type, const char *symbol, GOutputStream *out) +{ + guint n_interfaces; + guint i; + GType *interfaces; + + escaped_printf (out, " \n", + g_type_name (type), symbol); + + interfaces = g_type_interface_prerequisites (type, &n_interfaces); + for (i = 0; i < n_interfaces; i++) + { + GType itype = interfaces[i]; + if (itype == G_TYPE_OBJECT) + { + /* Treat this as implicit for now; in theory GInterfaces are + * supported on things like GstMiniObject, but right now + * the introspection system only supports GObject. + * http://bugzilla.gnome.org/show_bug.cgi?id=559706 + */ + continue; + } + escaped_printf (out, " \n", + g_type_name (itype)); + } + g_free (interfaces); + + dump_properties (type, out); + dump_signals (type, out); + goutput_write (out, " \n"); +} + +static void +dump_boxed_type (GType type, const char *symbol, GOutputStream *out) +{ + escaped_printf (out, " \n", + g_type_name (type), symbol); +} + +static void +dump_flags_type (GType type, const char *symbol, GOutputStream *out) +{ + guint i; + GFlagsClass *klass; + + klass = g_type_class_ref (type); + escaped_printf (out, " \n", + g_type_name (type), symbol); + + for (i = 0; i < klass->n_values; i++) + { + GFlagsValue *value = &(klass->values[i]); + + escaped_printf (out, " \n", + value->value_name, value->value_nick, value->value); + } + goutput_write (out, " \n"); +} + +static void +dump_enum_type (GType type, const char *symbol, GOutputStream *out) +{ + guint i; + GEnumClass *klass; + + klass = g_type_class_ref (type); + escaped_printf (out, " \n", + g_type_name (type), symbol); + + for (i = 0; i < klass->n_values; i++) + { + GEnumValue *value = &(klass->values[i]); + + escaped_printf (out, " \n", + value->value_name, value->value_nick, value->value); + } + goutput_write (out, " "); +} + +static void +dump_fundamental_type (GType type, const char *symbol, GOutputStream *out) +{ + guint n_interfaces; + guint i; + GType *interfaces; + GString *parent_str; + GType parent; + gboolean first = TRUE; + + + escaped_printf (out, " len > 0) + escaped_printf (out, " parents=\"%s\"", parent_str->str); + g_string_free (parent_str, TRUE); + + goutput_write (out, ">\n"); + + interfaces = g_type_interfaces (type, &n_interfaces); + for (i = 0; i < n_interfaces; i++) + { + GType itype = interfaces[i]; + escaped_printf (out, " \n", + g_type_name (itype)); + } + g_free (interfaces); + goutput_write (out, " \n"); +} + +static void +dump_type (GType type, const char *symbol, GOutputStream *out) +{ + switch (g_type_fundamental (type)) + { + case G_TYPE_OBJECT: + dump_object_type (type, symbol, out); + break; + case G_TYPE_INTERFACE: + dump_interface_type (type, symbol, out); + break; + case G_TYPE_BOXED: + dump_boxed_type (type, symbol, out); + break; + case G_TYPE_FLAGS: + dump_flags_type (type, symbol, out); + break; + case G_TYPE_ENUM: + dump_enum_type (type, symbol, out); + break; + case G_TYPE_POINTER: + /* GValue, etc. Just skip them. */ + break; + default: + dump_fundamental_type (type, symbol, out); + break; + } +} + +static void +dump_error_quark (GQuark quark, const char *symbol, GOutputStream *out) +{ + escaped_printf (out, " \n", + symbol, g_quark_to_string (quark)); +} + +/** + * g_irepository_dump: + * @arg: Comma-separated pair of input and output filenames + * @error: a %GError + * + * Argument specified is a comma-separated pair of filenames; i.e. of + * the form "input.txt,output.xml". The input file should be a + * UTF-8 Unix-line-ending text file, with each line containing either + * "get-type:" followed by the name of a GType _get_type function, or + * "error-quark:" followed by the name of an error quark function. No + * extra whitespace is allowed. + * + * The output file should already exist, but be empty. This function will + * overwrite its contents. + * + * Returns: %TRUE on success, %FALSE on error + */ +#ifndef GI_COMPILATION +static gboolean +dump_irepository (const char *arg, GError **error) G_GNUC_UNUSED; +static gboolean +dump_irepository (const char *arg, GError **error) +#else +gboolean +g_irepository_dump (const char *arg, GError **error) +#endif +{ + GHashTable *output_types; + char **args; + GFile *input_file; + GFile *output_file; + GFileInputStream *input; + GFileOutputStream *output; + GDataInputStream *in; + GModule *self; + gboolean caught_error = FALSE; + + self = g_module_open (NULL, 0); + if (!self) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to open self: %s", + g_module_error ()); + return FALSE; + } + + args = g_strsplit (arg, ",", 2); + + input_file = g_file_new_for_path (args[0]); + output_file = g_file_new_for_path (args[1]); + + g_strfreev (args); + + input = g_file_read (input_file, NULL, error); + g_object_unref (input_file); + + if (input == NULL) + { + g_object_unref (output_file); + return FALSE; + } + + output = g_file_replace (output_file, NULL, FALSE, 0, NULL, error); + g_object_unref (output_file); + + if (output == NULL) + { + g_input_stream_close (G_INPUT_STREAM (input), NULL, NULL); + g_object_unref (input); + return FALSE; + } + + goutput_write (G_OUTPUT_STREAM (output), "\n"); + goutput_write (G_OUTPUT_STREAM (output), "\n"); + + output_types = g_hash_table_new (NULL, NULL); + + in = g_data_input_stream_new (G_INPUT_STREAM (input)); + g_object_unref (input); + + while (TRUE) + { + gsize len; + char *line = g_data_input_stream_read_line (in, &len, NULL, NULL); + const char *function; + + if (line == NULL || *line == '\0') + { + g_free (line); + break; + } + + g_strchomp (line); + + if (strncmp (line, "get-type:", strlen ("get-type:")) == 0) + { + GType type; + + function = line + strlen ("get-type:"); + + type = invoke_get_type (self, function, error); + + if (type == G_TYPE_INVALID) + { + g_printerr ("Invalid GType function: '%s'\n", function); + caught_error = TRUE; + g_free (line); + break; + } + + if (g_hash_table_lookup (output_types, (gpointer) type)) + goto next; + g_hash_table_insert (output_types, (gpointer) type, (gpointer) type); + + dump_type (type, function, G_OUTPUT_STREAM (output)); + } + else if (strncmp (line, "error-quark:", strlen ("error-quark:")) == 0) + { + GQuark quark; + function = line + strlen ("error-quark:"); + quark = invoke_error_quark (self, function, error); + + if (quark == 0) + { + g_printerr ("Invalid error quark function: '%s'\n", function); + caught_error = TRUE; + g_free (line); + break; + } + + dump_error_quark (quark, function, G_OUTPUT_STREAM (output)); + } + + + next: + g_free (line); + } + + g_hash_table_destroy (output_types); + + goutput_write (G_OUTPUT_STREAM (output), "\n"); + + { + /* Avoid overwriting an earlier set error */ + caught_error |= !g_input_stream_close (G_INPUT_STREAM (in), NULL, + caught_error ? NULL : error); + caught_error |= !g_output_stream_close (G_OUTPUT_STREAM (output), NULL, + caught_error ? NULL : error); + } + + g_object_unref (in); + g_object_unref (output); + + return !caught_error; +} diff --git a/girepository/gi-dump-types.c b/girepository/gi-dump-types.c new file mode 100644 index 000000000..ca3735665 --- /dev/null +++ b/girepository/gi-dump-types.c @@ -0,0 +1,70 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: typelib validation, auxiliary functions + * related to the binary typelib format + * + * Copyright (C) 2011 Colin Walters + * Copyright (C) 2020 Gisle Vanem + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "gdump.c" +#ifdef G_OS_WIN32 + #include + #include /* For _get_osfhandle() */ + #include +#else + #include +#endif + +int +main (int argc, + char **argv) +{ + int i; + GOutputStream *Stdout; + GModule *self; + +#if defined(G_OS_WIN32) + HANDLE *hnd = (HANDLE) _get_osfhandle (1); + + g_return_val_if_fail (hnd && hnd != INVALID_HANDLE_VALUE, 1); + Stdout = g_win32_output_stream_new (hnd, FALSE); +#else + Stdout = g_unix_output_stream_new (1, FALSE); +#endif + + self = g_module_open (NULL, 0); + + for (i = 1; i < argc; i++) + { + GError *error = NULL; + GType type; + + type = invoke_get_type (self, argv[i], &error); + if (!type) + { + g_printerr ("%s\n", error->message); + g_clear_error (&error); + } + else + dump_type (type, argv[i], Stdout); + } + + return 0; +} diff --git a/girepository/giarginfo.c b/girepository/giarginfo.c new file mode 100644 index 000000000..4e4e4560f --- /dev/null +++ b/girepository/giarginfo.c @@ -0,0 +1,335 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Argument implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include + +#include "gitypelib-internal.h" +#include "girepository-private.h" +#include "giarginfo.h" + + +/* GIArgInfo functions */ + +/** + * SECTION:giarginfo + * @title: GIArgInfo + * @short_description: Struct representing an argument + * + * GIArgInfo represents an argument of a callable. + * + * An argument is always part of a #GICallableInfo. + */ + +/** + * g_arg_info_get_direction: + * @info: a #GIArgInfo + * + * Obtain the direction of the argument. Check #GIDirection for possible + * direction values. + * + * Returns: the direction + */ +GIDirection +g_arg_info_get_direction (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_ARG_INFO (info), -1); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->in && blob->out) + return GI_DIRECTION_INOUT; + else if (blob->out) + return GI_DIRECTION_OUT; + else + return GI_DIRECTION_IN; +} + +/** + * g_arg_info_is_return_value: + * @info: a #GIArgInfo + * + * Obtain if the argument is a return value. It can either be a + * parameter or a return value. + * + * Returns: %TRUE if it is a return value + */ +gboolean +g_arg_info_is_return_value (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_ARG_INFO (info), FALSE); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->return_value; +} + +/** + * g_arg_info_is_caller_allocates: + * @info: a #GIArgInfo + * + * Obtain if the argument is a pointer to a struct or object that will + * receive an output of a function. The default assumption for + * %GI_DIRECTION_OUT arguments which have allocation is that the + * callee allocates; if this is %TRUE, then the caller must allocate. + * + * Returns: %TRUE if caller is required to have allocated the argument + */ +gboolean +g_arg_info_is_caller_allocates (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_ARG_INFO (info), FALSE); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->caller_allocates; +} + +/** + * g_arg_info_is_optional: + * @info: a #GIArgInfo + * + * Obtain if the argument is optional. For 'out' arguments this means + * that you can pass %NULL in order to ignore the result. + * + * Returns: %TRUE if it is an optional argument + */ +gboolean +g_arg_info_is_optional (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_ARG_INFO (info), FALSE); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->optional; +} + +/** + * g_arg_info_may_be_null: + * @info: a #GIArgInfo + * + * Obtain if the type of the argument includes the possibility of %NULL. + * For 'in' values this means that %NULL is a valid value. For 'out' + * values, this means that %NULL may be returned. + * + * See also g_arg_info_is_optional(). + * + * Returns: %TRUE if the value may be %NULL + */ +gboolean +g_arg_info_may_be_null (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_ARG_INFO (info), FALSE); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->nullable; +} + +/** + * g_arg_info_is_skip: + * @info: a #GIArgInfo + * + * Obtain if an argument is only useful in C. + * + * Returns: %TRUE if argument is only useful in C. + * Since: 1.30 + */ +gboolean +g_arg_info_is_skip (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_ARG_INFO (info), FALSE); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->skip; +} + +/** + * g_arg_info_get_ownership_transfer: + * @info: a #GIArgInfo + * + * Obtain the ownership transfer for this argument. + * #GITransfer contains a list of possible values. + * + * Returns: the transfer + */ +GITransfer +g_arg_info_get_ownership_transfer (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_ARG_INFO (info), -1); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->transfer_ownership) + return GI_TRANSFER_EVERYTHING; + else if (blob->transfer_container_ownership) + return GI_TRANSFER_CONTAINER; + else + return GI_TRANSFER_NOTHING; +} + +/** + * g_arg_info_get_scope: + * @info: a #GIArgInfo + * + * Obtain the scope type for this argument. The scope type explains + * how a callback is going to be invoked, most importantly when + * the resources required to invoke it can be freed. + * #GIScopeType contains a list of possible values. + * + * Returns: the scope type + */ +GIScopeType +g_arg_info_get_scope (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_ARG_INFO (info), -1); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->scope; +} + +/** + * g_arg_info_get_closure: + * @info: a #GIArgInfo + * + * Obtain the index of the user data argument. This is only valid + * for arguments which are callbacks. + * + * Returns: index of the user data argument or -1 if there is none + */ +gint +g_arg_info_get_closure (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_ARG_INFO (info), -1); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->closure; +} + +/** + * g_arg_info_get_destroy: + * @info: a #GIArgInfo + * + * Obtains the index of the #GDestroyNotify argument. This is only valid + * for arguments which are callbacks. + * + * Returns: index of the #GDestroyNotify argument or -1 if there is none + */ +gint +g_arg_info_get_destroy (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_ARG_INFO (info), -1); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->destroy; +} + +/** + * g_arg_info_get_type: + * @info: a #GIArgInfo + * + * Obtain the type information for @info. + * + * Returns: (transfer full): the #GITypeInfo holding the type + * information for @info, free it with g_base_info_unref() + * when done. + */ +GITypeInfo * +g_arg_info_get_type (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_ARG_INFO (info), NULL); + + return _g_type_info_new ((GIBaseInfo*)info, rinfo->typelib, rinfo->offset + G_STRUCT_OFFSET (ArgBlob, arg_type)); +} + +/** + * g_arg_info_load_type: + * @info: a #GIArgInfo + * @type: (out caller-allocates): Initialized with information about type of @info + * + * Obtain information about a the type of given argument @info; this + * function is a variant of g_arg_info_get_type() designed for stack + * allocation. + * + * The initialized @type must not be referenced after @info is deallocated. + */ +void +g_arg_info_load_type (GIArgInfo *info, + GITypeInfo *type) +{ + GIRealInfo *rinfo = (GIRealInfo*) info; + + g_return_if_fail (info != NULL); + g_return_if_fail (GI_IS_ARG_INFO (info)); + + _g_type_info_init (type, (GIBaseInfo*)info, rinfo->typelib, rinfo->offset + G_STRUCT_OFFSET (ArgBlob, arg_type)); +} diff --git a/girepository/giarginfo.h b/girepository/giarginfo.h new file mode 100644 index 000000000..395535277 --- /dev/null +++ b/girepository/giarginfo.h @@ -0,0 +1,81 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Argument + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_ARG_INFO + * @info: an info structure + * + * Checks if @info is a GIArgInfo. + */ +#define GI_IS_ARG_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_ARG) + + +GI_AVAILABLE_IN_ALL +GIDirection g_arg_info_get_direction (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_arg_info_is_return_value (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_arg_info_is_optional (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_arg_info_is_caller_allocates (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_arg_info_may_be_null (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_arg_info_is_skip (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +GITransfer g_arg_info_get_ownership_transfer (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +GIScopeType g_arg_info_get_scope (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_arg_info_get_closure (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_arg_info_get_destroy (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +GITypeInfo * g_arg_info_get_type (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +void g_arg_info_load_type (GIArgInfo *info, + GITypeInfo *type); +G_END_DECLS diff --git a/girepository/gibaseinfo.c b/girepository/gibaseinfo.c new file mode 100644 index 000000000..9103a4415 --- /dev/null +++ b/girepository/gibaseinfo.c @@ -0,0 +1,677 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Base struct implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include +#include + +#include +#include + +#include "gitypelib-internal.h" +#include "girepository-private.h" +#include "gibaseinfo.h" + +#define INVALID_REFCOUNT 0x7FFFFFFF + +/* GBoxed registration of BaseInfo. */ +GType +g_base_info_gtype_get_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + our_type = + g_boxed_type_register_static ("GIBaseInfo", + (GBoxedCopyFunc) g_base_info_ref, + (GBoxedFreeFunc) g_base_info_unref); + + return our_type; +} + +/* info creation */ +GIBaseInfo * +_g_info_new_full (GIInfoType type, + GIRepository *repository, + GIBaseInfo *container, + GITypelib *typelib, + guint32 offset) +{ + GIRealInfo *info; + + g_return_val_if_fail (container != NULL || repository != NULL, NULL); + + info = g_slice_new (GIRealInfo); + + _g_info_init (info, type, repository, container, typelib, offset); + info->ref_count = 1; + + if (container && ((GIRealInfo *) container)->ref_count != INVALID_REFCOUNT) + g_base_info_ref (info->container); + + g_object_ref (info->repository); + + return (GIBaseInfo*)info; +} + +/** + * g_info_new: + * @type: TODO + * @container: TODO + * @typelib: TODO + * @offset: TODO + * + * TODO + * + * Returns: TODO + */ +GIBaseInfo * +g_info_new (GIInfoType type, + GIBaseInfo *container, + GITypelib *typelib, + guint32 offset) +{ + return _g_info_new_full (type, ((GIRealInfo*)container)->repository, container, typelib, offset); +} + +void +_g_info_init (GIRealInfo *info, + GIInfoType type, + GIRepository *repository, + GIBaseInfo *container, + GITypelib *typelib, + guint32 offset) +{ + memset (info, 0, sizeof (GIRealInfo)); + + /* Invalid refcount used to flag stack-allocated infos */ + info->ref_count = INVALID_REFCOUNT; + info->type = type; + + info->typelib = typelib; + info->offset = offset; + + if (container) + info->container = container; + + g_assert (G_IS_IREPOSITORY (repository)); + info->repository = repository; +} + +GIBaseInfo * +_g_info_from_entry (GIRepository *repository, + GITypelib *typelib, + guint16 index) +{ + GIBaseInfo *result; + DirEntry *entry = g_typelib_get_dir_entry (typelib, index); + + if (entry->local) + result = _g_info_new_full (entry->blob_type, repository, NULL, typelib, entry->offset); + else + { + const gchar *namespace = g_typelib_get_string (typelib, entry->offset); + const gchar *name = g_typelib_get_string (typelib, entry->name); + + result = g_irepository_find_by_name (repository, namespace, name); + if (result == NULL) + { + GIUnresolvedInfo *unresolved; + + unresolved = g_slice_new0 (GIUnresolvedInfo); + + unresolved->type = GI_INFO_TYPE_UNRESOLVED; + unresolved->ref_count = 1; + unresolved->repository = g_object_ref (repository); + unresolved->container = NULL; + unresolved->name = name; + unresolved->namespace = namespace; + + return (GIBaseInfo *)unresolved; + } + return (GIBaseInfo *)result; + } + + return (GIBaseInfo *)result; +} + +GITypeInfo * +_g_type_info_new (GIBaseInfo *container, + GITypelib *typelib, + guint32 offset) +{ + SimpleTypeBlob *type = (SimpleTypeBlob *)&typelib->data[offset]; + + return (GITypeInfo *) g_info_new (GI_INFO_TYPE_TYPE, container, typelib, + (type->flags.reserved == 0 && type->flags.reserved2 == 0) ? offset : type->offset); +} + +void +_g_type_info_init (GIBaseInfo *info, + GIBaseInfo *container, + GITypelib *typelib, + guint32 offset) +{ + GIRealInfo *rinfo = (GIRealInfo*)container; + SimpleTypeBlob *type = (SimpleTypeBlob *)&typelib->data[offset]; + + _g_info_init ((GIRealInfo*)info, GI_INFO_TYPE_TYPE, rinfo->repository, container, typelib, + (type->flags.reserved == 0 && type->flags.reserved2 == 0) ? offset : type->offset); +} + +/* GIBaseInfo functions */ + +/** + * SECTION:gibaseinfo + * @title: GIBaseInfo + * @short_description: Base struct for all GITypelib structs + * + * GIBaseInfo is the common base struct of all other Info structs + * accessible through the #GIRepository API. + * + * All info structures can be cast to a #GIBaseInfo, for instance: + * + * |[ + * GIFunctionInfo *function_info = ...; + * GIBaseInfo *info = (GIBaseInfo *) function_info; + * ]| + * + * Most #GIRepository APIs returning a #GIBaseInfo is actually + * creating a new struct; in other words, g_base_info_unref() has to + * be called when done accessing the data. + * + * #GIBaseInfo structuress are normally accessed by calling either + * g_irepository_find_by_name(), g_irepository_find_by_gtype() or + * g_irepository_get_info(). + * + * |[ + * GIBaseInfo *button_info = + * g_irepository_find_by_name (NULL, "Gtk", "Button"); + * + * // ... use button_info ... + * + * g_base_info_unref (button_info); + * ]| + * + * ## Hierarchy + * + * |[ + * GIBaseInfo + * +---- GIArgInfo + * +---- GICallableInfo + * +---- GIConstantInfo + * +---- GIFieldInfo + * +---- GIPropertyInfo + * +---- GIRegisteredTypeInfo + * +---- GITypeInfo + * ]| + */ + +/** + * g_base_info_ref: (skip) + * @info: a #GIBaseInfo + * + * Increases the reference count of @info. + * + * Returns: the same @info. + */ +GIBaseInfo * +g_base_info_ref (GIBaseInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*)info; + + g_assert (rinfo->ref_count != INVALID_REFCOUNT); + g_atomic_int_inc (&rinfo->ref_count); + + return info; +} + +/** + * g_base_info_unref: (skip) + * @info: a #GIBaseInfo + * + * Decreases the reference count of @info. When its reference count + * drops to 0, the info is freed. + */ +void +g_base_info_unref (GIBaseInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*)info; + + g_assert (rinfo->ref_count > 0 && rinfo->ref_count != INVALID_REFCOUNT); + + if (!g_atomic_int_dec_and_test (&rinfo->ref_count)) + return; + + if (rinfo->container && ((GIRealInfo *) rinfo->container)->ref_count != INVALID_REFCOUNT) + g_base_info_unref (rinfo->container); + + if (rinfo->repository) + g_object_unref (rinfo->repository); + + if (rinfo->type == GI_INFO_TYPE_UNRESOLVED) + g_slice_free (GIUnresolvedInfo, (GIUnresolvedInfo *) rinfo); + else + g_slice_free (GIRealInfo, rinfo); +} + +/** + * g_base_info_get_type: + * @info: a #GIBaseInfo + * + * Obtain the info type of the GIBaseInfo. + * + * Returns: the info type of @info + */ +GIInfoType +g_base_info_get_type (GIBaseInfo *info) +{ + + return ((GIRealInfo*)info)->type; +} + +/** + * g_base_info_get_name: + * @info: a #GIBaseInfo + * + * Obtain the name of the @info. What the name represents depends on + * the #GIInfoType of the @info. For instance for #GIFunctionInfo it is + * the name of the function. + * + * Returns: the name of @info or %NULL if it lacks a name. + */ +const gchar * +g_base_info_get_name (GIBaseInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*)info; + g_assert (rinfo->ref_count > 0); + switch (rinfo->type) + { + case GI_INFO_TYPE_FUNCTION: + case GI_INFO_TYPE_CALLBACK: + case GI_INFO_TYPE_STRUCT: + case GI_INFO_TYPE_BOXED: + case GI_INFO_TYPE_ENUM: + case GI_INFO_TYPE_FLAGS: + case GI_INFO_TYPE_OBJECT: + case GI_INFO_TYPE_INTERFACE: + case GI_INFO_TYPE_CONSTANT: + case GI_INFO_TYPE_INVALID_0: + case GI_INFO_TYPE_UNION: + { + CommonBlob *blob = (CommonBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->name); + } + break; + + case GI_INFO_TYPE_VALUE: + { + ValueBlob *blob = (ValueBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->name); + } + break; + + case GI_INFO_TYPE_SIGNAL: + { + SignalBlob *blob = (SignalBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->name); + } + break; + + case GI_INFO_TYPE_PROPERTY: + { + PropertyBlob *blob = (PropertyBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->name); + } + break; + + case GI_INFO_TYPE_VFUNC: + { + VFuncBlob *blob = (VFuncBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->name); + } + break; + + case GI_INFO_TYPE_FIELD: + { + FieldBlob *blob = (FieldBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->name); + } + break; + + case GI_INFO_TYPE_ARG: + { + ArgBlob *blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->name); + } + break; + case GI_INFO_TYPE_UNRESOLVED: + { + GIUnresolvedInfo *unresolved = (GIUnresolvedInfo *)info; + + return unresolved->name; + } + break; + case GI_INFO_TYPE_TYPE: + return NULL; + default: ; + g_assert_not_reached (); + /* unnamed */ + } + + return NULL; +} + +/** + * g_base_info_get_namespace: + * @info: a #GIBaseInfo + * + * Obtain the namespace of @info. + * + * Returns: the namespace + */ +const gchar * +g_base_info_get_namespace (GIBaseInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*) info; + Header *header = (Header *)rinfo->typelib->data; + + g_assert (rinfo->ref_count > 0); + + if (rinfo->type == GI_INFO_TYPE_UNRESOLVED) + { + GIUnresolvedInfo *unresolved = (GIUnresolvedInfo *)info; + + return unresolved->namespace; + } + + return g_typelib_get_string (rinfo->typelib, header->namespace); +} + +/** + * g_base_info_is_deprecated: + * @info: a #GIBaseInfo + * + * Obtain whether the @info is represents a metadata which is + * deprecated or not. + * + * Returns: %TRUE if deprecated + */ +gboolean +g_base_info_is_deprecated (GIBaseInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*) info; + switch (rinfo->type) + { + case GI_INFO_TYPE_FUNCTION: + case GI_INFO_TYPE_CALLBACK: + case GI_INFO_TYPE_STRUCT: + case GI_INFO_TYPE_BOXED: + case GI_INFO_TYPE_ENUM: + case GI_INFO_TYPE_FLAGS: + case GI_INFO_TYPE_OBJECT: + case GI_INFO_TYPE_INTERFACE: + case GI_INFO_TYPE_CONSTANT: + case GI_INFO_TYPE_INVALID_0: + { + CommonBlob *blob = (CommonBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->deprecated; + } + break; + + case GI_INFO_TYPE_VALUE: + { + ValueBlob *blob = (ValueBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->deprecated; + } + break; + + case GI_INFO_TYPE_SIGNAL: + { + SignalBlob *blob = (SignalBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->deprecated; + } + break; + + case GI_INFO_TYPE_PROPERTY: + { + PropertyBlob *blob = (PropertyBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->deprecated; + } + break; + + case GI_INFO_TYPE_VFUNC: + case GI_INFO_TYPE_FIELD: + case GI_INFO_TYPE_ARG: + case GI_INFO_TYPE_TYPE: + default: ; + /* no deprecation flag for these */ + } + + return FALSE; +} + +/** + * g_base_info_get_attribute: + * @info: a #GIBaseInfo + * @name: a freeform string naming an attribute + * + * Retrieve an arbitrary attribute associated with this node. + * + * Returns: The value of the attribute, or %NULL if no such attribute exists + */ +const gchar * +g_base_info_get_attribute (GIBaseInfo *info, + const gchar *name) +{ + GIAttributeIter iter = { 0, }; + gchar *curname, *curvalue; + while (g_base_info_iterate_attributes (info, &iter, &curname, &curvalue)) + { + if (strcmp (name, curname) == 0) + return (const gchar*) curvalue; + } + + return NULL; +} + +static int +cmp_attribute (const void *av, + const void *bv) +{ + const AttributeBlob *a = av; + const AttributeBlob *b = bv; + + if (a->offset < b->offset) + return -1; + else if (a->offset == b->offset) + return 0; + else + return 1; +} + +/* + * _attribute_blob_find_first: + * @GIBaseInfo: A #GIBaseInfo. + * @blob_offset: The offset for the blob to find the first attribute for. + * + * Searches for the first #AttributeBlob for @blob_offset and returns + * it if found. + * + * Returns: A pointer to #AttributeBlob or %NULL if not found. + */ +AttributeBlob * +_attribute_blob_find_first (GIBaseInfo *info, + guint32 blob_offset) +{ + GIRealInfo *rinfo = (GIRealInfo *) info; + Header *header = (Header *)rinfo->typelib->data; + AttributeBlob blob, *first, *res, *previous; + + blob.offset = blob_offset; + + first = (AttributeBlob *) &rinfo->typelib->data[header->attributes]; + + res = bsearch (&blob, first, header->n_attributes, + header->attribute_blob_size, cmp_attribute); + + if (res == NULL) + return NULL; + + previous = res - 1; + while (previous >= first && previous->offset == blob_offset) + { + res = previous; + previous = res - 1; + } + + return res; +} + +/** + * g_base_info_iterate_attributes: + * @info: a #GIBaseInfo + * @iterator: (inout): a #GIAttributeIter structure, must be initialized; see below + * @name: (out) (transfer none): Returned name, must not be freed + * @value: (out) (transfer none): Returned name, must not be freed + * + * Iterate over all attributes associated with this node. The iterator + * structure is typically stack allocated, and must have its first + * member initialized to %NULL. Attributes are arbitrary namespaced key–value + * pairs which can be attached to almost any item. They are intended for use + * by software higher in the toolchain than bindings, and are distinct from + * normal GIR annotations. + * + * Both the @name and @value should be treated as constants + * and must not be freed. + * + * |[ + * void + * print_attributes (GIBaseInfo *info) + * { + * GIAttributeIter iter = { 0, }; + * char *name; + * char *value; + * while (g_base_info_iterate_attributes (info, &iter, &name, &value)) + * { + * g_print ("attribute name: %s value: %s", name, value); + * } + * } + * ]| + * + * Returns: %TRUE if there are more attributes + */ +gboolean +g_base_info_iterate_attributes (GIBaseInfo *info, + GIAttributeIter *iterator, + gchar **name, + gchar **value) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header = (Header *)rinfo->typelib->data; + AttributeBlob *next, *after; + + after = (AttributeBlob *) &rinfo->typelib->data[header->attributes + + header->n_attributes * header->attribute_blob_size]; + + if (iterator->data != NULL) + next = (AttributeBlob *) iterator->data; + else + next = _attribute_blob_find_first (info, rinfo->offset); + + if (next == NULL || next->offset != rinfo->offset || next >= after) + return FALSE; + + *name = (gchar*) g_typelib_get_string (rinfo->typelib, next->name); + *value = (gchar*) g_typelib_get_string (rinfo->typelib, next->value); + iterator->data = next + 1; + + return TRUE; +} + +/** + * g_base_info_get_container: + * @info: a #GIBaseInfo + * + * Obtain the container of the @info. The container is the parent + * GIBaseInfo. For instance, the parent of a #GIFunctionInfo is an + * #GIObjectInfo or #GIInterfaceInfo. + * + * Returns: (transfer none): the container + */ +GIBaseInfo * +g_base_info_get_container (GIBaseInfo *info) +{ + return ((GIRealInfo*)info)->container; +} + +/** + * g_base_info_get_typelib: + * @info: a #GIBaseInfo + * + * Obtain the typelib this @info belongs to + * + * Returns: (transfer none): the typelib. + */ +GITypelib * +g_base_info_get_typelib (GIBaseInfo *info) +{ + return ((GIRealInfo*)info)->typelib; +} + +/** + * g_base_info_equal: + * @info1: a #GIBaseInfo + * @info2: a #GIBaseInfo + * + * Compare two #GIBaseInfo. + * + * Using pointer comparison is not practical since many functions return + * different instances of #GIBaseInfo that refers to the same part of the + * TypeLib; use this function instead to do #GIBaseInfo comparisons. + * + * Returns: %TRUE if and only if @info1 equals @info2. + */ +gboolean +g_base_info_equal (GIBaseInfo *info1, GIBaseInfo *info2) +{ + /* Compare the TypeLib pointers, which are mmapped. */ + GIRealInfo *rinfo1 = (GIRealInfo*)info1; + GIRealInfo *rinfo2 = (GIRealInfo*)info2; + return rinfo1->typelib->data + rinfo1->offset == rinfo2->typelib->data + rinfo2->offset; +} + + diff --git a/girepository/gibaseinfo.h b/girepository/gibaseinfo.h new file mode 100644 index 000000000..2a2012528 --- /dev/null +++ b/girepository/gibaseinfo.h @@ -0,0 +1,101 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: GIBaseInfo + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include + +G_BEGIN_DECLS + +/** + * GIAttributeIter: + * + * An opaque structure used to iterate over attributes + * in a #GIBaseInfo struct. + */ +typedef struct { + /*< private >*/ + gpointer data; + gpointer data2; + gpointer data3; + gpointer data4; +} GIAttributeIter; + +#define GI_TYPE_BASE_INFO (g_base_info_gtype_get_type ()) + + +GI_AVAILABLE_IN_ALL +GType g_base_info_gtype_get_type (void) G_GNUC_CONST; + +GI_AVAILABLE_IN_ALL +GIBaseInfo * g_base_info_ref (GIBaseInfo *info); + +GI_AVAILABLE_IN_ALL +void g_base_info_unref (GIBaseInfo *info); + +GI_AVAILABLE_IN_ALL +GIInfoType g_base_info_get_type (GIBaseInfo *info); + +GI_AVAILABLE_IN_ALL +const gchar * g_base_info_get_name (GIBaseInfo *info); + +GI_AVAILABLE_IN_ALL +const gchar * g_base_info_get_namespace (GIBaseInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_base_info_is_deprecated (GIBaseInfo *info); + +GI_AVAILABLE_IN_ALL +const gchar * g_base_info_get_attribute (GIBaseInfo *info, + const gchar *name); + +GI_AVAILABLE_IN_ALL +gboolean g_base_info_iterate_attributes (GIBaseInfo *info, + GIAttributeIter *iterator, + char **name, + char **value); + +GI_AVAILABLE_IN_ALL +GIBaseInfo * g_base_info_get_container (GIBaseInfo *info); + +GI_AVAILABLE_IN_ALL +GITypelib * g_base_info_get_typelib (GIBaseInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_base_info_equal (GIBaseInfo *info1, + GIBaseInfo *info2); + +GI_AVAILABLE_IN_ALL +GIBaseInfo * g_info_new (GIInfoType type, + GIBaseInfo *container, + GITypelib *typelib, + guint32 offset); + +G_END_DECLS diff --git a/girepository/gicallableinfo.c b/girepository/gicallableinfo.c new file mode 100644 index 000000000..88dd367aa --- /dev/null +++ b/girepository/gicallableinfo.c @@ -0,0 +1,793 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Callable implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" +#include "girffi.h" +#include "gicallableinfo.h" + +/* GICallableInfo functions */ + +/** + * SECTION:gicallableinfo + * @title: GICallableInfo + * @short_description: Struct representing a callable + * + * GICallableInfo represents an entity which is callable. + * + * Examples of callable are: + * + * - functions (#GIFunctionInfo) + * - virtual functions (#GIVFuncInfo) + * - callbacks (#GICallbackInfo). + * + * A callable has a list of arguments (#GIArgInfo), a return type, + * direction and a flag which decides if it returns null. + */ + +static guint32 +signature_offset (GICallableInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*)info; + int sigoff = -1; + + switch (rinfo->type) + { + case GI_INFO_TYPE_FUNCTION: + sigoff = G_STRUCT_OFFSET (FunctionBlob, signature); + break; + case GI_INFO_TYPE_VFUNC: + sigoff = G_STRUCT_OFFSET (VFuncBlob, signature); + break; + case GI_INFO_TYPE_CALLBACK: + sigoff = G_STRUCT_OFFSET (CallbackBlob, signature); + break; + case GI_INFO_TYPE_SIGNAL: + sigoff = G_STRUCT_OFFSET (SignalBlob, signature); + break; + default: + g_assert_not_reached (); + } + if (sigoff >= 0) + return *(guint32 *)&rinfo->typelib->data[rinfo->offset + sigoff]; + return 0; +} + +/** + * g_callable_info_can_throw_gerror: + * @info: a #GICallableInfo + * + * TODO + * + * Since: 1.34 + * Returns: %TRUE if this #GICallableInfo can throw a #GError + */ +gboolean +g_callable_info_can_throw_gerror (GICallableInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*)info; + SignatureBlob *signature; + + signature = (SignatureBlob *)&rinfo->typelib->data[signature_offset (info)]; + if (signature->throws) + return TRUE; + + /* Functions and VFuncs store "throws" in their own blobs. + * This info was additionally added to the SignatureBlob + * to support the other callables. For Functions and VFuncs, + * also check their legacy flag for compatibility. + */ + switch (rinfo->type) { + case GI_INFO_TYPE_FUNCTION: + { + FunctionBlob *blob; + blob = (FunctionBlob *)&rinfo->typelib->data[rinfo->offset]; + return blob->throws; + } + case GI_INFO_TYPE_VFUNC: + { + VFuncBlob *blob; + blob = (VFuncBlob *)&rinfo->typelib->data[rinfo->offset]; + return blob->throws; + } + case GI_INFO_TYPE_CALLBACK: + case GI_INFO_TYPE_SIGNAL: + return FALSE; + default: + g_assert_not_reached (); + } +} + +/** + * g_callable_info_is_method: + * @info: a #GICallableInfo + * + * Determines if the callable info is a method. For #GIVFuncInfos, + * #GICallbackInfos, and #GISignalInfos, + * this is always true. Otherwise, this looks at the %GI_FUNCTION_IS_METHOD + * flag on the #GIFunctionInfo. + * + * Concretely, this function returns whether g_callable_info_get_n_args() + * matches the number of arguments in the raw C method. For methods, there + * is one more C argument than is exposed by introspection: the "self" + * or "this" object. + * + * Returns: %TRUE if @info is a method, %FALSE otherwise + * Since: 1.34 + */ +gboolean +g_callable_info_is_method (GICallableInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*)info; + switch (rinfo->type) { + case GI_INFO_TYPE_FUNCTION: + { + FunctionBlob *blob; + blob = (FunctionBlob *)&rinfo->typelib->data[rinfo->offset]; + return (!blob->constructor && !blob->is_static); + } + case GI_INFO_TYPE_VFUNC: + case GI_INFO_TYPE_SIGNAL: + return TRUE; + case GI_INFO_TYPE_CALLBACK: + return FALSE; + default: + g_assert_not_reached (); + } +} + +/** + * g_callable_info_get_return_type: + * @info: a #GICallableInfo + * + * Obtain the return type of a callable item as a #GITypeInfo. + * + * Returns: (transfer full): the #GITypeInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GITypeInfo * +g_callable_info_get_return_type (GICallableInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + guint32 offset; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), NULL); + + offset = signature_offset (info); + + return _g_type_info_new ((GIBaseInfo*)info, rinfo->typelib, offset); +} + + +/** + * g_callable_info_load_return_type: + * @info: a #GICallableInfo + * @type: (out caller-allocates): Initialized with return type of @info + * + * Obtain information about a return value of callable; this + * function is a variant of g_callable_info_get_return_type() designed for stack + * allocation. + * + * The initialized @type must not be referenced after @info is deallocated. + */ +void +g_callable_info_load_return_type (GICallableInfo *info, + GITypeInfo *type) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + guint32 offset; + + g_return_if_fail (info != NULL); + g_return_if_fail (GI_IS_CALLABLE_INFO (info)); + + offset = signature_offset (info); + + _g_type_info_init (type, (GIBaseInfo*)info, rinfo->typelib, offset); +} + +/** + * g_callable_info_may_return_null: + * @info: a #GICallableInfo + * + * See if a callable could return %NULL. + * + * Returns: %TRUE if callable could return %NULL + */ +gboolean +g_callable_info_may_return_null (GICallableInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SignatureBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), FALSE); + + blob = (SignatureBlob *)&rinfo->typelib->data[signature_offset (info)]; + + return blob->may_return_null; +} + +/** + * g_callable_info_skip_return: + * @info: a #GICallableInfo + * + * See if a callable's return value is only useful in C. + * + * Returns: %TRUE if return value is only useful in C. + */ +gboolean +g_callable_info_skip_return (GICallableInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SignatureBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), FALSE); + + blob = (SignatureBlob *)&rinfo->typelib->data[signature_offset (info)]; + + return blob->skip_return; +} + +/** + * g_callable_info_get_caller_owns: + * @info: a #GICallableInfo + * + * See whether the caller owns the return value of this callable. + * #GITransfer contains a list of possible transfer values. + * + * Returns: the transfer mode for the return value of the callable + */ +GITransfer +g_callable_info_get_caller_owns (GICallableInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*) info; + SignatureBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), -1); + + blob = (SignatureBlob *)&rinfo->typelib->data[signature_offset (info)]; + + if (blob->caller_owns_return_value) + return GI_TRANSFER_EVERYTHING; + else if (blob->caller_owns_return_container) + return GI_TRANSFER_CONTAINER; + else + return GI_TRANSFER_NOTHING; +} + +/** + * g_callable_info_get_instance_ownership_transfer: + * @info: a #GICallableInfo + * + * Obtains the ownership transfer for the instance argument. + * #GITransfer contains a list of possible transfer values. + * + * Since: 1.42 + * Returns: the transfer mode of the instance argument + */ +GITransfer +g_callable_info_get_instance_ownership_transfer (GICallableInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*) info; + SignatureBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), -1); + + blob = (SignatureBlob *)&rinfo->typelib->data[signature_offset (info)]; + + if (blob->instance_transfer_ownership) + return GI_TRANSFER_EVERYTHING; + else + return GI_TRANSFER_NOTHING; +} + +/** + * g_callable_info_get_n_args: + * @info: a #GICallableInfo + * + * Obtain the number of arguments (both IN and OUT) for this callable. + * + * Returns: The number of arguments this callable expects. + */ +gint +g_callable_info_get_n_args (GICallableInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + gint offset; + SignatureBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), -1); + + offset = signature_offset (info); + blob = (SignatureBlob *)&rinfo->typelib->data[offset]; + + return blob->n_arguments; +} + +/** + * g_callable_info_get_arg: + * @info: a #GICallableInfo + * @n: the argument index to fetch + * + * Obtain information about a particular argument of this callable. + * + * Returns: (transfer full): the #GIArgInfo. Free it with + * g_base_info_unref() when done. + */ +GIArgInfo * +g_callable_info_get_arg (GICallableInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + gint offset; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), NULL); + + offset = signature_offset (info); + header = (Header *)rinfo->typelib->data; + + return (GIArgInfo *) g_info_new (GI_INFO_TYPE_ARG, (GIBaseInfo*)info, rinfo->typelib, + offset + header->signature_blob_size + n * header->arg_blob_size); +} + +/** + * g_callable_info_load_arg: + * @info: a #GICallableInfo + * @n: the argument index to fetch + * @arg: (out caller-allocates): Initialize with argument number @n + * + * Obtain information about a particular argument of this callable; this + * function is a variant of g_callable_info_get_arg() designed for stack + * allocation. + * + * The initialized @arg must not be referenced after @info is deallocated. + */ +void +g_callable_info_load_arg (GICallableInfo *info, + gint n, + GIArgInfo *arg) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + gint offset; + + g_return_if_fail (info != NULL); + g_return_if_fail (GI_IS_CALLABLE_INFO (info)); + + offset = signature_offset (info); + header = (Header *)rinfo->typelib->data; + + _g_info_init ((GIRealInfo*)arg, GI_INFO_TYPE_ARG, rinfo->repository, (GIBaseInfo*)info, rinfo->typelib, + offset + header->signature_blob_size + n * header->arg_blob_size); +} + +/** + * g_callable_info_get_return_attribute: + * @info: a #GICallableInfo + * @name: a freeform string naming an attribute + * + * Retrieve an arbitrary attribute associated with the return value. + * + * Returns: The value of the attribute, or %NULL if no such attribute exists + */ +const gchar * +g_callable_info_get_return_attribute (GICallableInfo *info, + const gchar *name) +{ + GIAttributeIter iter = { 0, }; + gchar *curname, *curvalue; + while (g_callable_info_iterate_return_attributes (info, &iter, &curname, &curvalue)) + { + if (g_strcmp0 (name, curname) == 0) + return (const gchar*) curvalue; + } + + return NULL; +} + +/** + * g_callable_info_iterate_return_attributes: + * @info: a #GICallableInfo + * @iterator: (inout): a #GIAttributeIter structure, must be initialized; see below + * @name: (out) (transfer none): Returned name, must not be freed + * @value: (out) (transfer none): Returned name, must not be freed + * + * Iterate over all attributes associated with the return value. The + * iterator structure is typically stack allocated, and must have its + * first member initialized to %NULL. + * + * Both the @name and @value should be treated as constants + * and must not be freed. + * + * See g_base_info_iterate_attributes() for an example of how to use a + * similar API. + * + * Returns: %TRUE if there are more attributes + */ +gboolean +g_callable_info_iterate_return_attributes (GICallableInfo *info, + GIAttributeIter *iterator, + char **name, + char **value) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header = (Header *)rinfo->typelib->data; + AttributeBlob *next, *after; + guint32 blob_offset; + + after = (AttributeBlob *) &rinfo->typelib->data[header->attributes + + header->n_attributes * header->attribute_blob_size]; + + blob_offset = signature_offset (info); + + if (iterator->data != NULL) + next = (AttributeBlob *) iterator->data; + else + next = _attribute_blob_find_first (info, blob_offset); + + if (next == NULL || next->offset != blob_offset || next >= after) + return FALSE; + + *name = (gchar*) g_typelib_get_string (rinfo->typelib, next->name); + *value = (gchar*) g_typelib_get_string (rinfo->typelib, next->value); + iterator->data = next + 1; + + return TRUE; +} + +/** + * gi_type_tag_extract_ffi_return_value: + * @return_tag: #GITypeTag of the return value + * @interface_type: #GIInfoType of the underlying interface type + * @ffi_value: pointer to #GIFFIReturnValue union containing the return value + * from `ffi_call()` + * @arg: (out caller-allocates): pointer to an allocated #GIArgument + * + * Extract the correct bits from an `ffi_arg` return value into + * GIArgument. + * + * See: https://bugzilla.gnome.org/show_bug.cgi?id=665152 + * + * Also see `ffi_call(3)`: the storage requirements for return values + * are "special". + * + * The @interface_type argument only applies if @return_tag is + * %GI_TYPE_TAG_INTERFACE. Otherwise it is ignored. + * + * Since: 1.72 + */ +void +gi_type_tag_extract_ffi_return_value (GITypeTag return_tag, + GIInfoType interface_type, + GIFFIReturnValue *ffi_value, + GIArgument *arg) +{ + switch (return_tag) { + case GI_TYPE_TAG_INT8: + arg->v_int8 = (gint8) ffi_value->v_long; + break; + case GI_TYPE_TAG_UINT8: + arg->v_uint8 = (guint8) ffi_value->v_ulong; + break; + case GI_TYPE_TAG_INT16: + arg->v_int16 = (gint16) ffi_value->v_long; + break; + case GI_TYPE_TAG_UINT16: + arg->v_uint16 = (guint16) ffi_value->v_ulong; + break; + case GI_TYPE_TAG_INT32: + arg->v_int32 = (gint32) ffi_value->v_long; + break; + case GI_TYPE_TAG_UINT32: + case GI_TYPE_TAG_BOOLEAN: + case GI_TYPE_TAG_UNICHAR: + arg->v_uint32 = (guint32) ffi_value->v_ulong; + break; + case GI_TYPE_TAG_INT64: + arg->v_int64 = (gint64) ffi_value->v_int64; + break; + case GI_TYPE_TAG_UINT64: + arg->v_uint64 = (guint64) ffi_value->v_uint64; + break; + case GI_TYPE_TAG_FLOAT: + arg->v_float = ffi_value->v_float; + break; + case GI_TYPE_TAG_DOUBLE: + arg->v_double = ffi_value->v_double; + break; + case GI_TYPE_TAG_INTERFACE: + switch(interface_type) { + case GI_INFO_TYPE_ENUM: + case GI_INFO_TYPE_FLAGS: + arg->v_int32 = (gint32) ffi_value->v_long; + break; + default: + arg->v_pointer = (gpointer) ffi_value->v_pointer; + break; + } + break; + default: + arg->v_pointer = (gpointer) ffi_value->v_pointer; + break; + } +} + +/** + * gi_type_info_extract_ffi_return_value: + * @return_info: #GITypeInfo describing the return type + * @ffi_value: pointer to #GIFFIReturnValue union containing the return value + * from `ffi_call()` + * @arg: (out caller-allocates): pointer to an allocated #GIArgument + * + * Extract the correct bits from an `ffi_arg` return value into + * #GIArgument. + * + * See: https://bugzilla.gnome.org/show_bug.cgi?id=665152 + * + * Also see `ffi_call(3)`: the storage requirements for return values + * are "special". + */ +void +gi_type_info_extract_ffi_return_value (GITypeInfo *return_info, + GIFFIReturnValue *ffi_value, + GIArgument *arg) +{ + GITypeTag return_tag = g_type_info_get_tag (return_info); + GIInfoType interface_type = GI_INFO_TYPE_INVALID; + + if (return_tag == GI_TYPE_TAG_INTERFACE) + { + GIBaseInfo *interface_info = g_type_info_get_interface (return_info); + interface_type = g_base_info_get_type (interface_info); + g_base_info_unref (interface_info); + } + + gi_type_tag_extract_ffi_return_value (return_tag, interface_type, + ffi_value, arg); +} + +/** + * g_callable_info_invoke: + * @info: TODO + * @function: TODO + * @in_args: (array length=n_in_args): TODO + * @n_in_args: TODO + * @out_args: (array length=n_out_args): TODO + * @n_out_args: TODO + * @return_value: TODO + * @is_method: TODO + * @throws: TODO + * @error: TODO + * + * TODO + */ +gboolean +g_callable_info_invoke (GIFunctionInfo *info, + gpointer function, + const GIArgument *in_args, + int n_in_args, + const GIArgument *out_args, + int n_out_args, + GIArgument *return_value, + gboolean is_method, + gboolean throws, + GError **error) +{ + ffi_cif cif; + ffi_type *rtype; + ffi_type **atypes; + GITypeInfo *tinfo; + GITypeInfo *rinfo; + GITypeTag rtag; + GIArgInfo *ainfo; + gint n_args, n_invoke_args, in_pos, out_pos, i; + gpointer *args; + gboolean success = FALSE; + GError *local_error = NULL; + gpointer error_address = &local_error; + GIFFIReturnValue ffi_return_value; + gpointer return_value_p; /* Will point inside the union return_value */ + + rinfo = g_callable_info_get_return_type ((GICallableInfo *)info); + rtype = g_type_info_get_ffi_type (rinfo); + rtag = g_type_info_get_tag(rinfo); + + in_pos = 0; + out_pos = 0; + + n_args = g_callable_info_get_n_args ((GICallableInfo *)info); + if (is_method) + { + if (n_in_args == 0) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too few \"in\" arguments (handling this)"); + goto out; + } + n_invoke_args = n_args+1; + in_pos++; + } + else + n_invoke_args = n_args; + + if (throws) + /* Add an argument for the GError */ + n_invoke_args ++; + + atypes = g_alloca (sizeof (ffi_type*) * n_invoke_args); + args = g_alloca (sizeof (gpointer) * n_invoke_args); + + if (is_method) + { + atypes[0] = &ffi_type_pointer; + args[0] = (gpointer) &in_args[0]; + } + for (i = 0; i < n_args; i++) + { + int offset = (is_method ? 1 : 0); + ainfo = g_callable_info_get_arg ((GICallableInfo *)info, i); + switch (g_arg_info_get_direction (ainfo)) + { + case GI_DIRECTION_IN: + tinfo = g_arg_info_get_type (ainfo); + atypes[i+offset] = g_type_info_get_ffi_type (tinfo); + g_base_info_unref ((GIBaseInfo *)ainfo); + g_base_info_unref ((GIBaseInfo *)tinfo); + + if (in_pos >= n_in_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too few \"in\" arguments (handling in)"); + goto out; + } + + args[i+offset] = (gpointer)&in_args[in_pos]; + in_pos++; + + break; + case GI_DIRECTION_OUT: + atypes[i+offset] = &ffi_type_pointer; + g_base_info_unref ((GIBaseInfo *)ainfo); + + if (out_pos >= n_out_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too few \"out\" arguments (handling out)"); + goto out; + } + + args[i+offset] = (gpointer)&out_args[out_pos]; + out_pos++; + break; + case GI_DIRECTION_INOUT: + atypes[i+offset] = &ffi_type_pointer; + g_base_info_unref ((GIBaseInfo *)ainfo); + + if (in_pos >= n_in_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too few \"in\" arguments (handling inout)"); + goto out; + } + + if (out_pos >= n_out_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too few \"out\" arguments (handling inout)"); + goto out; + } + + args[i+offset] = (gpointer)&in_args[in_pos]; + in_pos++; + out_pos++; + break; + default: + g_base_info_unref ((GIBaseInfo *)ainfo); + g_assert_not_reached (); + } + } + + if (throws) + { + args[n_invoke_args - 1] = &error_address; + atypes[n_invoke_args - 1] = &ffi_type_pointer; + } + + if (in_pos < n_in_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too many \"in\" arguments (at end)"); + goto out; + } + if (out_pos < n_out_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too many \"out\" arguments (at end)"); + goto out; + } + + if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, n_invoke_args, rtype, atypes) != FFI_OK) + goto out; + + g_return_val_if_fail (return_value, FALSE); + /* See comment for GIFFIReturnValue above */ + switch (rtag) + { + case GI_TYPE_TAG_FLOAT: + return_value_p = &ffi_return_value.v_float; + break; + case GI_TYPE_TAG_DOUBLE: + return_value_p = &ffi_return_value.v_double; + break; + case GI_TYPE_TAG_INT64: + case GI_TYPE_TAG_UINT64: + return_value_p = &ffi_return_value.v_uint64; + break; + default: + return_value_p = &ffi_return_value.v_long; + } + ffi_call (&cif, function, return_value_p, args); + + if (local_error) + { + g_propagate_error (error, local_error); + success = FALSE; + } + else + { + gi_type_info_extract_ffi_return_value (rinfo, &ffi_return_value, return_value); + success = TRUE; + } + out: + g_base_info_unref ((GIBaseInfo *)rinfo); + return success; +} diff --git a/girepository/gicallableinfo.h b/girepository/gicallableinfo.h new file mode 100644 index 000000000..0a1a4999e --- /dev/null +++ b/girepository/gicallableinfo.h @@ -0,0 +1,107 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Callable + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_CALLABLE_INFO + * @info: an info structure + * + * Checks if @info is a #GICallableInfo or derived from it. + */ +#define GI_IS_CALLABLE_INFO(info) \ + ((g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_FUNCTION) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_CALLBACK) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_SIGNAL) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_VFUNC)) + + +GI_AVAILABLE_IN_ALL +gboolean g_callable_info_is_method (GICallableInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_callable_info_can_throw_gerror (GICallableInfo *info); + +GI_AVAILABLE_IN_ALL +GITypeInfo * g_callable_info_get_return_type (GICallableInfo *info); + +GI_AVAILABLE_IN_ALL +void g_callable_info_load_return_type (GICallableInfo *info, + GITypeInfo *type); + +GI_AVAILABLE_IN_ALL +const gchar * g_callable_info_get_return_attribute (GICallableInfo *info, + const gchar *name); + +GI_AVAILABLE_IN_ALL +gboolean g_callable_info_iterate_return_attributes (GICallableInfo *info, + GIAttributeIter *iterator, + char **name, + char **value); + +GI_AVAILABLE_IN_ALL +GITransfer g_callable_info_get_caller_owns (GICallableInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_callable_info_may_return_null (GICallableInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_callable_info_skip_return (GICallableInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_callable_info_get_n_args (GICallableInfo *info); + +GI_AVAILABLE_IN_ALL +GIArgInfo * g_callable_info_get_arg (GICallableInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +void g_callable_info_load_arg (GICallableInfo *info, + gint n, + GIArgInfo *arg); + +GI_AVAILABLE_IN_ALL +gboolean g_callable_info_invoke (GICallableInfo *info, + gpointer function, + const GIArgument *in_args, + int n_in_args, + const GIArgument *out_args, + int n_out_args, + GIArgument *return_value, + gboolean is_method, + gboolean throws, + GError **error); + +GI_AVAILABLE_IN_ALL +GITransfer g_callable_info_get_instance_ownership_transfer (GICallableInfo *info); + +G_END_DECLS diff --git a/girepository/giconstantinfo.c b/girepository/giconstantinfo.c new file mode 100644 index 000000000..8025d93fd --- /dev/null +++ b/girepository/giconstantinfo.c @@ -0,0 +1,182 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Constant implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include +#include // memcpy + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" +#include "giconstantinfo.h" + +/** + * SECTION:giconstantinfo + * @title: GIConstantInfo + * @short_description: Struct representing a constant + * + * GIConstantInfo represents a constant. + * + * A constant has a type associated which can be obtained by calling + * g_constant_info_get_type() and a value, which can be obtained by + * calling g_constant_info_get_value(). + */ + + +/** + * g_constant_info_get_type: + * @info: a #GIConstantInfo + * + * Obtain the type of the constant as a #GITypeInfo. + * + * Returns: (transfer full): the #GITypeInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GITypeInfo * +g_constant_info_get_type (GIConstantInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_CONSTANT_INFO (info), NULL); + + return _g_type_info_new ((GIBaseInfo*)info, rinfo->typelib, rinfo->offset + 8); +} + +#define DO_ALIGNED_COPY(dest_addr, src_addr, type) \ + memcpy((dest_addr), (src_addr), sizeof(type)) + +/** + * g_constant_info_free_value: (skip) + * @info: a #GIConstantInfo + * @value: the argument + * + * Free the value returned from g_constant_info_get_value(). + * + * Since: 1.32 + */ +void +g_constant_info_free_value (GIConstantInfo *info, + GIArgument *value) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ConstantBlob *blob; + + g_return_if_fail (info != NULL); + g_return_if_fail (GI_IS_CONSTANT_INFO (info)); + + blob = (ConstantBlob *)&rinfo->typelib->data[rinfo->offset]; + + /* FIXME non-basic types ? */ + if (blob->type.flags.reserved == 0 && blob->type.flags.reserved2 == 0) + { + if (blob->type.flags.pointer) + g_free (value->v_pointer); + } +} + +/** + * g_constant_info_get_value: (skip) + * @info: a #GIConstantInfo + * @value: (out): an argument + * + * Obtain the value associated with the #GIConstantInfo and store it in the + * @value parameter. @argument needs to be allocated before passing it in. + * The size of the constant value stored in @argument will be returned. + * Free the value with g_constant_info_free_value(). + * + * Returns: size of the constant + */ +gint +g_constant_info_get_value (GIConstantInfo *info, + GIArgument *value) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ConstantBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_CONSTANT_INFO (info), 0); + + blob = (ConstantBlob *)&rinfo->typelib->data[rinfo->offset]; + + /* FIXME non-basic types ? */ + if (blob->type.flags.reserved == 0 && blob->type.flags.reserved2 == 0) + { + if (blob->type.flags.pointer) + { +#if GLIB_CHECK_VERSION (2, 67, 5) + gsize blob_size = blob->size; + + value->v_pointer = g_memdup2 (&rinfo->typelib->data[blob->offset], blob_size); +#else + value->v_pointer = g_memdup (&rinfo->typelib->data[blob->offset], blob->size); +#endif + } + else + { + switch (blob->type.flags.tag) + { + case GI_TYPE_TAG_BOOLEAN: + value->v_boolean = *(gboolean*)&rinfo->typelib->data[blob->offset]; + break; + case GI_TYPE_TAG_INT8: + value->v_int8 = *(gint8*)&rinfo->typelib->data[blob->offset]; + break; + case GI_TYPE_TAG_UINT8: + value->v_uint8 = *(guint8*)&rinfo->typelib->data[blob->offset]; + break; + case GI_TYPE_TAG_INT16: + value->v_int16 = *(gint16*)&rinfo->typelib->data[blob->offset]; + break; + case GI_TYPE_TAG_UINT16: + value->v_uint16 = *(guint16*)&rinfo->typelib->data[blob->offset]; + break; + case GI_TYPE_TAG_INT32: + value->v_int32 = *(gint32*)&rinfo->typelib->data[blob->offset]; + break; + case GI_TYPE_TAG_UINT32: + value->v_uint32 = *(guint32*)&rinfo->typelib->data[blob->offset]; + break; + case GI_TYPE_TAG_INT64: + DO_ALIGNED_COPY(&value->v_int64, &rinfo->typelib->data[blob->offset], gint64); + break; + case GI_TYPE_TAG_UINT64: + DO_ALIGNED_COPY(&value->v_uint64, &rinfo->typelib->data[blob->offset], guint64); + break; + case GI_TYPE_TAG_FLOAT: + DO_ALIGNED_COPY(&value->v_float, &rinfo->typelib->data[blob->offset], gfloat); + break; + case GI_TYPE_TAG_DOUBLE: + DO_ALIGNED_COPY(&value->v_double, &rinfo->typelib->data[blob->offset], gdouble); + break; + default: + g_assert_not_reached (); + } + } + } + + return blob->size; +} + diff --git a/girepository/giconstantinfo.h b/girepository/giconstantinfo.h new file mode 100644 index 000000000..7a0cfb12b --- /dev/null +++ b/girepository/giconstantinfo.h @@ -0,0 +1,55 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Constant + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_CONSTANT_INFO + * @info: an info structure + * + * Checks if @info is a #GIConstantInfo. + */ +#define GI_IS_CONSTANT_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_CONSTANT) + + +GI_AVAILABLE_IN_ALL +GITypeInfo * g_constant_info_get_type (GIConstantInfo *info); + +GI_AVAILABLE_IN_ALL +void g_constant_info_free_value(GIConstantInfo *info, + GIArgument *value); + +GI_AVAILABLE_IN_ALL +gint g_constant_info_get_value(GIConstantInfo *info, + GIArgument *value); +G_END_DECLS diff --git a/girepository/gienuminfo.c b/girepository/gienuminfo.c new file mode 100644 index 000000000..d7b4ef7f3 --- /dev/null +++ b/girepository/gienuminfo.c @@ -0,0 +1,235 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Enum implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" +#include "gienuminfo.h" + +/** + * SECTION:gienuminfo + * @title: GIEnumInfo + * @short_description: Structs representing an enumeration and its values + * + * A GIEnumInfo represents an enumeration, and a GIValueInfo represents + * a value in the enumeration. + * + * The GIEnumInfo contains a set of values and a type. + * + * The GIValueInfo is fetched by calling g_enum_info_get_value() on + * a GIEnumInfo. + */ + +/** + * g_enum_info_get_n_values: + * @info: a #GIEnumInfo + * + * Obtain the number of values this enumeration contains. + * + * Returns: the number of enumeration values + */ +gint +g_enum_info_get_n_values (GIEnumInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + EnumBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_ENUM_INFO (info), 0); + + blob = (EnumBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_values; +} + +/** + * g_enum_info_get_error_domain: + * @info: a #GIEnumInfo + * + * Obtain the string form of the quark for the error domain associated with + * this enum, if any. + * + * Returns: (transfer none): the string form of the error domain associated + * with this enum, or %NULL. + * Since: 1.30 + */ +const gchar * +g_enum_info_get_error_domain (GIEnumInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + EnumBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_ENUM_INFO (info), 0); + + blob = (EnumBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->error_domain) + return g_typelib_get_string (rinfo->typelib, blob->error_domain); + else + return NULL; +} + +/** + * g_enum_info_get_value: + * @info: a #GIEnumInfo + * @n: index of value to fetch + * + * Obtain a value for this enumeration. + * + * Returns: (transfer full): the enumeration value or %NULL if type tag is wrong, + * free the struct with g_base_info_unref() when done. + */ +GIValueInfo * +g_enum_info_get_value (GIEnumInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + gint offset; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_ENUM_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + offset = rinfo->offset + header->enum_blob_size + + n * header->value_blob_size; + + return (GIValueInfo *) g_info_new (GI_INFO_TYPE_VALUE, (GIBaseInfo*)info, rinfo->typelib, offset); +} + +/** + * g_enum_info_get_n_methods: + * @info: a #GIEnumInfo + * + * Obtain the number of methods that this enum type has. + * + * Returns: number of methods + * Since: 1.30 + */ +gint +g_enum_info_get_n_methods (GIEnumInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + EnumBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_ENUM_INFO (info), 0); + + blob = (EnumBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_methods; +} + +/** + * g_enum_info_get_method: + * @info: a #GIEnumInfo + * @n: index of method to get + * + * Obtain an enum type method at index @n. + * + * Returns: (transfer full): the #GIFunctionInfo. Free the struct by calling + * g_base_info_unref() when done. + * Since: 1.30 + */ +GIFunctionInfo * +g_enum_info_get_method (GIEnumInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + EnumBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_ENUM_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (EnumBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->enum_blob_size + + blob->n_values * header->value_blob_size + + n * header->function_blob_size; + + return (GIFunctionInfo *) g_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_enum_info_get_storage_type: + * @info: a #GIEnumInfo + * + * Obtain the tag of the type used for the enum in the C ABI. This will + * will be a signed or unsigned integral type. + * + * Note that in the current implementation the width of the type is + * computed correctly, but the signed or unsigned nature of the type + * may not match the sign of the type used by the C compiler. + * + * Returns: the storage type for the enumeration + */ +GITypeTag +g_enum_info_get_storage_type (GIEnumInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + EnumBlob *blob; + + g_return_val_if_fail (info != NULL, GI_TYPE_TAG_BOOLEAN); + g_return_val_if_fail (GI_IS_ENUM_INFO (info), GI_TYPE_TAG_BOOLEAN); + + blob = (EnumBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->storage_type; +} + +/** + * g_value_info_get_value: + * @info: a #GIValueInfo + * + * Obtain the enumeration value of the #GIValueInfo. + * + * Returns: the enumeration value. This will always be representable + * as a 32-bit signed or unsigned value. The use of gint64 as the + * return type is to allow both. + */ +gint64 +g_value_info_get_value (GIValueInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ValueBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_VALUE_INFO (info), -1); + + blob = (ValueBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->unsigned_value) + return (gint64)(guint32)blob->value; + else + return (gint64)blob->value; +} diff --git a/girepository/gienuminfo.h b/girepository/gienuminfo.h new file mode 100644 index 000000000..fd8a11b82 --- /dev/null +++ b/girepository/gienuminfo.h @@ -0,0 +1,79 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Enum and Enum values + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_ENUM_INFO + * @info: an info structure + * + * Checks if @info is a #GIEnumInfo. + */ +#define GI_IS_ENUM_INFO(info) \ + ((g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_ENUM) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_FLAGS)) + +/** + * GI_IS_VALUE_INFO + * @info: an info structure + * + * Checks if @info is a #GIValueInfo. + */ +#define GI_IS_VALUE_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_VALUE) + + +GI_AVAILABLE_IN_ALL +gint g_enum_info_get_n_values (GIEnumInfo *info); + +GI_AVAILABLE_IN_ALL +GIValueInfo * g_enum_info_get_value (GIEnumInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +gint g_enum_info_get_n_methods (GIEnumInfo *info); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_enum_info_get_method (GIEnumInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GITypeTag g_enum_info_get_storage_type (GIEnumInfo *info); + +GI_AVAILABLE_IN_ALL +const gchar * g_enum_info_get_error_domain (GIEnumInfo *info); + + +GI_AVAILABLE_IN_ALL +gint64 g_value_info_get_value (GIValueInfo *info); + +G_END_DECLS diff --git a/girepository/gifieldinfo.c b/girepository/gifieldinfo.c new file mode 100644 index 000000000..c61c62478 --- /dev/null +++ b/girepository/gifieldinfo.c @@ -0,0 +1,556 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Field implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" +#include "config.h" +#include "gifieldinfo.h" + +/** + * SECTION:gifieldinfo + * @title: GIFieldInfo + * @short_description: Struct representing a struct or union field + * + * A GIFieldInfo struct represents a field of a struct, union, or object. + * + * The GIFieldInfo is fetched by calling g_struct_info_get_field(), + * g_union_info_get_field() or g_object_info_get_field(). + * + * A field has a size, type and a struct offset asssociated and a set of flags, + * which are currently #GI_FIELD_IS_READABLE or #GI_FIELD_IS_WRITABLE. + * + * See also: #GIStructInfo, #GIUnionInfo, #GIObjectInfo + */ + +/** + * g_field_info_get_flags: + * @info: a #GIFieldInfo + * + * Obtain the flags for this #GIFieldInfo. See #GIFieldInfoFlags for possible + * flag values. + * + * Returns: the flags + */ +GIFieldInfoFlags +g_field_info_get_flags (GIFieldInfo *info) +{ + GIFieldInfoFlags flags; + GIRealInfo *rinfo = (GIRealInfo *)info; + FieldBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_FIELD_INFO (info), 0); + + blob = (FieldBlob *)&rinfo->typelib->data[rinfo->offset]; + + flags = 0; + + if (blob->readable) + flags = flags | GI_FIELD_IS_READABLE; + + if (blob->writable) + flags = flags | GI_FIELD_IS_WRITABLE; + + return flags; +} + +/** + * g_field_info_get_size: + * @info: a #GIFieldInfo + * + * Obtain the size in bits of the field member, this is how + * much space you need to allocate to store the field. + * + * Returns: the field size + */ +gint +g_field_info_get_size (GIFieldInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + FieldBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_FIELD_INFO (info), 0); + + blob = (FieldBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->bits; +} + +/** + * g_field_info_get_offset: + * @info: a #GIFieldInfo + * + * Obtain the offset in bytes of the field member, this is relative + * to the beginning of the struct or union. + * + * Returns: the field offset + */ +gint +g_field_info_get_offset (GIFieldInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + FieldBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_FIELD_INFO (info), 0); + + blob = (FieldBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->struct_offset; +} + +/** + * g_field_info_get_type: + * @info: a #GIFieldInfo + * + * Obtain the type of a field as a #GITypeInfo. + * + * Returns: (transfer full): the #GITypeInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GITypeInfo * +g_field_info_get_type (GIFieldInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header = (Header *)rinfo->typelib->data; + FieldBlob *blob; + GIRealInfo *type_info; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_FIELD_INFO (info), NULL); + + blob = (FieldBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->has_embedded_type) + { + type_info = (GIRealInfo *) g_info_new (GI_INFO_TYPE_TYPE, + (GIBaseInfo*)info, rinfo->typelib, + rinfo->offset + header->field_blob_size); + type_info->type_is_embedded = TRUE; + } + else + return _g_type_info_new ((GIBaseInfo*)info, rinfo->typelib, rinfo->offset + G_STRUCT_OFFSET (FieldBlob, type)); + + return (GIBaseInfo*)type_info; +} + +/** + * g_field_info_get_field: (skip) + * @field_info: a #GIFieldInfo + * @mem: pointer to a block of memory representing a C structure or union + * @value: a #GIArgument into which to store the value retrieved + * + * Reads a field identified by a #GIFieldInfo from a C structure or + * union. This only handles fields of simple C types. It will fail + * for a field of a composite type like a nested structure or union + * even if that is actually readable. + * + * Returns: %TRUE if reading the field succeeded, otherwise %FALSE + */ +gboolean +g_field_info_get_field (GIFieldInfo *field_info, + gpointer mem, + GIArgument *value) +{ + int offset; + GITypeInfo *type_info; + gboolean result = FALSE; + + g_return_val_if_fail (field_info != NULL, FALSE); + g_return_val_if_fail (GI_IS_FIELD_INFO (field_info), FALSE); + + if ((g_field_info_get_flags (field_info) & GI_FIELD_IS_READABLE) == 0) + return FALSE; + + offset = g_field_info_get_offset (field_info); + type_info = g_field_info_get_type (field_info); + + if (g_type_info_is_pointer (type_info)) + { + value->v_pointer = G_STRUCT_MEMBER (gpointer, mem, offset); + result = TRUE; + } + else + { + switch (g_type_info_get_tag (type_info)) + { + case GI_TYPE_TAG_VOID: + g_warning("Field %s: should not be have void type", + g_base_info_get_name ((GIBaseInfo *)field_info)); + break; + case GI_TYPE_TAG_BOOLEAN: + value->v_boolean = G_STRUCT_MEMBER (gboolean, mem, offset) != FALSE; + result = TRUE; + break; + case GI_TYPE_TAG_INT8: + case GI_TYPE_TAG_UINT8: + value->v_uint8 = G_STRUCT_MEMBER (guint8, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_INT16: + case GI_TYPE_TAG_UINT16: + value->v_uint16 = G_STRUCT_MEMBER (guint16, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_INT32: + case GI_TYPE_TAG_UINT32: + case GI_TYPE_TAG_UNICHAR: + value->v_uint32 = G_STRUCT_MEMBER (guint32, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_INT64: + case GI_TYPE_TAG_UINT64: + value->v_uint64 = G_STRUCT_MEMBER (guint64, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_GTYPE: + value->v_size = G_STRUCT_MEMBER (gsize, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_FLOAT: + value->v_float = G_STRUCT_MEMBER (gfloat, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_DOUBLE: + value->v_double = G_STRUCT_MEMBER (gdouble, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_ARRAY: + /* We don't check the array type and that it is fixed-size, + we trust g-ir-compiler to do the right thing */ + value->v_pointer = G_STRUCT_MEMBER_P (mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_UTF8: + case GI_TYPE_TAG_FILENAME: + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + case GI_TYPE_TAG_GHASH: + g_warning("Field %s: type %s should have is_pointer set", + g_base_info_get_name ((GIBaseInfo *)field_info), + g_type_tag_to_string (g_type_info_get_tag (type_info))); + break; + case GI_TYPE_TAG_ERROR: + /* Needs to be handled by the language binding directly */ + break; + case GI_TYPE_TAG_INTERFACE: + { + GIBaseInfo *interface = g_type_info_get_interface (type_info); + switch (g_base_info_get_type (interface)) + { + case GI_INFO_TYPE_STRUCT: + case GI_INFO_TYPE_UNION: + case GI_INFO_TYPE_BOXED: + /* Needs to be handled by the language binding directly */ + break; + case GI_INFO_TYPE_OBJECT: + break; + case GI_INFO_TYPE_ENUM: + case GI_INFO_TYPE_FLAGS: + { + /* FIXME: there's a mismatch here between the value->v_int we use + * here and the gint64 result returned from g_value_info_get_value(). + * But to switch this to gint64, we'd have to make g_function_info_invoke() + * translate value->v_int64 to the proper ABI for an enum function + * call parameter, which will usually be int, and then fix up language + * bindings. + */ + GITypeTag storage_type = g_enum_info_get_storage_type ((GIEnumInfo *)interface); + switch (storage_type) + { + case GI_TYPE_TAG_INT8: + case GI_TYPE_TAG_UINT8: + value->v_int = (gint)G_STRUCT_MEMBER (guint8, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_INT16: + case GI_TYPE_TAG_UINT16: + value->v_int = (gint)G_STRUCT_MEMBER (guint16, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_INT32: + case GI_TYPE_TAG_UINT32: + value->v_int = (gint)G_STRUCT_MEMBER (guint32, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_INT64: + case GI_TYPE_TAG_UINT64: + value->v_int = (gint)G_STRUCT_MEMBER (guint64, mem, offset); + result = TRUE; + break; + default: + g_warning("Field %s: Unexpected enum storage type %s", + g_base_info_get_name ((GIBaseInfo *)field_info), + g_type_tag_to_string (storage_type)); + break; + } + break; + } + case GI_INFO_TYPE_VFUNC: + case GI_INFO_TYPE_CALLBACK: + g_warning("Field %s: Interface type %d should have is_pointer set", + g_base_info_get_name ((GIBaseInfo *)field_info), + g_base_info_get_type (interface)); + break; + case GI_INFO_TYPE_INVALID: + case GI_INFO_TYPE_INTERFACE: + case GI_INFO_TYPE_FUNCTION: + case GI_INFO_TYPE_CONSTANT: + case GI_INFO_TYPE_INVALID_0: + case GI_INFO_TYPE_VALUE: + case GI_INFO_TYPE_SIGNAL: + case GI_INFO_TYPE_PROPERTY: + case GI_INFO_TYPE_FIELD: + case GI_INFO_TYPE_ARG: + case GI_INFO_TYPE_TYPE: + case GI_INFO_TYPE_UNRESOLVED: + g_warning("Field %s: Interface type %d not expected", + g_base_info_get_name ((GIBaseInfo *)field_info), + g_base_info_get_type (interface)); + break; + default: + break; + } + + g_base_info_unref ((GIBaseInfo *)interface); + break; + } + break; + default: + break; + } + } + + g_base_info_unref ((GIBaseInfo *)type_info); + + return result; +} + +/** + * g_field_info_set_field: (skip) + * @field_info: a #GIFieldInfo + * @mem: pointer to a block of memory representing a C structure or union + * @value: a #GIArgument holding the value to store + * + * Writes a field identified by a #GIFieldInfo to a C structure or + * union. This only handles fields of simple C types. It will fail + * for a field of a composite type like a nested structure or union + * even if that is actually writable. Note also that that it will refuse + * to write fields where memory management would by required. A field + * with a type such as 'char *' must be set with a setter function. + * + * Returns: %TRUE if writing the field succeeded, otherwise %FALSE + */ +gboolean +g_field_info_set_field (GIFieldInfo *field_info, + gpointer mem, + const GIArgument *value) +{ + int offset; + GITypeInfo *type_info; + gboolean result = FALSE; + + g_return_val_if_fail (field_info != NULL, FALSE); + g_return_val_if_fail (GI_IS_FIELD_INFO (field_info), FALSE); + + if ((g_field_info_get_flags (field_info) & GI_FIELD_IS_WRITABLE) == 0) + return FALSE; + + offset = g_field_info_get_offset (field_info); + type_info = g_field_info_get_type (field_info); + + if (!g_type_info_is_pointer (type_info)) + { + switch (g_type_info_get_tag (type_info)) + { + case GI_TYPE_TAG_VOID: + g_warning("Field %s: should not be have void type", + g_base_info_get_name ((GIBaseInfo *)field_info)); + break; + case GI_TYPE_TAG_BOOLEAN: + G_STRUCT_MEMBER (gboolean, mem, offset) = value->v_boolean != FALSE; + result = TRUE; + break; + case GI_TYPE_TAG_INT8: + case GI_TYPE_TAG_UINT8: + G_STRUCT_MEMBER (guint8, mem, offset) = value->v_uint8; + result = TRUE; + break; + case GI_TYPE_TAG_INT16: + case GI_TYPE_TAG_UINT16: + G_STRUCT_MEMBER (guint16, mem, offset) = value->v_uint16; + result = TRUE; + break; + case GI_TYPE_TAG_INT32: + case GI_TYPE_TAG_UINT32: + case GI_TYPE_TAG_UNICHAR: + G_STRUCT_MEMBER (guint32, mem, offset) = value->v_uint32; + result = TRUE; + break; + case GI_TYPE_TAG_INT64: + case GI_TYPE_TAG_UINT64: + G_STRUCT_MEMBER (guint64, mem, offset) = value->v_uint64; + result = TRUE; + break; + case GI_TYPE_TAG_GTYPE: + G_STRUCT_MEMBER (gsize, mem, offset) = value->v_size; + result = TRUE; + break; + case GI_TYPE_TAG_FLOAT: + G_STRUCT_MEMBER (gfloat, mem, offset) = value->v_float; + result = TRUE; + break; + case GI_TYPE_TAG_DOUBLE: + G_STRUCT_MEMBER (gdouble, mem, offset)= value->v_double; + result = TRUE; + break; + case GI_TYPE_TAG_UTF8: + case GI_TYPE_TAG_FILENAME: + case GI_TYPE_TAG_ARRAY: + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + case GI_TYPE_TAG_GHASH: + g_warning("Field %s: type %s should have is_pointer set", + g_base_info_get_name ((GIBaseInfo *)field_info), + g_type_tag_to_string (g_type_info_get_tag (type_info))); + break; + case GI_TYPE_TAG_ERROR: + /* Needs to be handled by the language binding directly */ + break; + case GI_TYPE_TAG_INTERFACE: + { + GIBaseInfo *interface = g_type_info_get_interface (type_info); + switch (g_base_info_get_type (interface)) + { + case GI_INFO_TYPE_STRUCT: + case GI_INFO_TYPE_UNION: + case GI_INFO_TYPE_BOXED: + /* Needs to be handled by the language binding directly */ + break; + case GI_INFO_TYPE_OBJECT: + break; + case GI_INFO_TYPE_ENUM: + case GI_INFO_TYPE_FLAGS: + { + /* See FIXME above + */ + GITypeTag storage_type = g_enum_info_get_storage_type ((GIEnumInfo *)interface); + switch (storage_type) + { + case GI_TYPE_TAG_INT8: + case GI_TYPE_TAG_UINT8: + G_STRUCT_MEMBER (guint8, mem, offset) = (guint8)value->v_int; + result = TRUE; + break; + case GI_TYPE_TAG_INT16: + case GI_TYPE_TAG_UINT16: + G_STRUCT_MEMBER (guint16, mem, offset) = (guint16)value->v_int; + result = TRUE; + break; + case GI_TYPE_TAG_INT32: + case GI_TYPE_TAG_UINT32: + G_STRUCT_MEMBER (guint32, mem, offset) = (guint32)value->v_int; + result = TRUE; + break; + case GI_TYPE_TAG_INT64: + case GI_TYPE_TAG_UINT64: + G_STRUCT_MEMBER (guint64, mem, offset) = (guint64)value->v_int; + result = TRUE; + break; + default: + g_warning("Field %s: Unexpected enum storage type %s", + g_base_info_get_name ((GIBaseInfo *)field_info), + g_type_tag_to_string (storage_type)); + break; + } + break; + } + break; + case GI_INFO_TYPE_VFUNC: + case GI_INFO_TYPE_CALLBACK: + g_warning("Field%s: Interface type %d should have is_pointer set", + g_base_info_get_name ((GIBaseInfo *)field_info), + g_base_info_get_type (interface)); + break; + case GI_INFO_TYPE_INVALID: + case GI_INFO_TYPE_INTERFACE: + case GI_INFO_TYPE_FUNCTION: + case GI_INFO_TYPE_CONSTANT: + case GI_INFO_TYPE_INVALID_0: + case GI_INFO_TYPE_VALUE: + case GI_INFO_TYPE_SIGNAL: + case GI_INFO_TYPE_PROPERTY: + case GI_INFO_TYPE_FIELD: + case GI_INFO_TYPE_ARG: + case GI_INFO_TYPE_TYPE: + case GI_INFO_TYPE_UNRESOLVED: + g_warning("Field %s: Interface type %d not expected", + g_base_info_get_name ((GIBaseInfo *)field_info), + g_base_info_get_type (interface)); + break; + default: + break; + } + + g_base_info_unref ((GIBaseInfo *)interface); + break; + } + break; + default: + break; + } + } else { + switch (g_type_info_get_tag (type_info)) + { + case GI_TYPE_TAG_INTERFACE: + { + GIBaseInfo *interface = g_type_info_get_interface (type_info); + switch (g_base_info_get_type (interface)) + { + case GI_INFO_TYPE_OBJECT: + case GI_INFO_TYPE_INTERFACE: + G_STRUCT_MEMBER (gpointer, mem, offset) = (gpointer)value->v_pointer; + result = TRUE; + break; + default: + break; + } + g_base_info_unref ((GIBaseInfo *)interface); + } + break; + default: + break; + } + } + + g_base_info_unref ((GIBaseInfo *)type_info); + + return result; +} diff --git a/girepository/gifieldinfo.h b/girepository/gifieldinfo.h new file mode 100644 index 000000000..83a901d28 --- /dev/null +++ b/girepository/gifieldinfo.h @@ -0,0 +1,68 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Field and Field values + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_FIELD_INFO + * @info: an info structure + * + * Checks if @info is a #GIFieldInfo. + * + */ +#define GI_IS_FIELD_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_FIELD) + + +GI_AVAILABLE_IN_ALL +GIFieldInfoFlags g_field_info_get_flags (GIFieldInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_field_info_get_size (GIFieldInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_field_info_get_offset (GIFieldInfo *info); + +GI_AVAILABLE_IN_ALL +GITypeInfo * g_field_info_get_type (GIFieldInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_field_info_get_field (GIFieldInfo *field_info, + gpointer mem, + GIArgument *value); + +GI_AVAILABLE_IN_ALL +gboolean g_field_info_set_field (GIFieldInfo *field_info, + gpointer mem, + const GIArgument *value); + +G_END_DECLS diff --git a/girepository/gifunctioninfo.c b/girepository/gifunctioninfo.c new file mode 100644 index 000000000..d9ef7931d --- /dev/null +++ b/girepository/gifunctioninfo.c @@ -0,0 +1,297 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Function implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" +#include "gifunctioninfo.h" + +/** + * SECTION:gifunctioninfo + * @title: GIFunctionInfo + * @short_description: Struct representing a function + * + * GIFunctionInfo represents a function, method or constructor. + * + * To find out what kind of entity a #GIFunctionInfo represents, call + * g_function_info_get_flags(). + * + * See also #GICallableInfo for information on how to retreive arguments and + * other metadata. + */ + +GIFunctionInfo * +_g_base_info_find_method (GIBaseInfo *base, + guint32 offset, + gint n_methods, + const gchar *name) +{ + /* FIXME hash */ + GIRealInfo *rinfo = (GIRealInfo*)base; + Header *header = (Header *)rinfo->typelib->data; + gint i; + + for (i = 0; i < n_methods; i++) + { + FunctionBlob *fblob = (FunctionBlob *)&rinfo->typelib->data[offset]; + const gchar *fname = (const gchar *)&rinfo->typelib->data[fblob->name]; + + if (strcmp (name, fname) == 0) + return (GIFunctionInfo *) g_info_new (GI_INFO_TYPE_FUNCTION, base, + rinfo->typelib, offset); + + offset += header->function_blob_size; + } + + return NULL; +} + +/** + * g_function_info_get_symbol: + * @info: a #GIFunctionInfo + * + * Obtain the symbol of the function. The symbol is the name of the + * exported function, suitable to be used as an argument to + * g_module_symbol(). + * + * Returns: the symbol + */ +const gchar * +g_function_info_get_symbol (GIFunctionInfo *info) +{ + GIRealInfo *rinfo; + FunctionBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_FUNCTION_INFO (info), NULL); + + rinfo = (GIRealInfo *)info; + blob = (FunctionBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->symbol); +} + +/** + * g_function_info_get_flags: + * @info: a #GIFunctionInfo + * + * Obtain the #GIFunctionInfoFlags for the @info. + * + * Returns: the flags + */ +GIFunctionInfoFlags +g_function_info_get_flags (GIFunctionInfo *info) +{ + GIFunctionInfoFlags flags; + GIRealInfo *rinfo; + FunctionBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_FUNCTION_INFO (info), -1); + + rinfo = (GIRealInfo *)info; + blob = (FunctionBlob *)&rinfo->typelib->data[rinfo->offset]; + + flags = 0; + + /* Make sure we don't flag Constructors as methods */ + if (!blob->constructor && !blob->is_static) + flags = flags | GI_FUNCTION_IS_METHOD; + + if (blob->constructor) + flags = flags | GI_FUNCTION_IS_CONSTRUCTOR; + + if (blob->getter) + flags = flags | GI_FUNCTION_IS_GETTER; + + if (blob->setter) + flags = flags | GI_FUNCTION_IS_SETTER; + + if (blob->wraps_vfunc) + flags = flags | GI_FUNCTION_WRAPS_VFUNC; + + if (blob->throws) + flags = flags | GI_FUNCTION_THROWS; + + return flags; +} + +/** + * g_function_info_get_property: + * @info: a #GIFunctionInfo + * + * Obtain the property associated with this #GIFunctionInfo. + * Only #GIFunctionInfo with the flag %GI_FUNCTION_IS_GETTER or + * %GI_FUNCTION_IS_SETTER have a property set. For other cases, + * %NULL will be returned. + * + * Returns: (transfer full): the property or %NULL if not set. Free it with + * g_base_info_unref() when done. + */ +GIPropertyInfo * +g_function_info_get_property (GIFunctionInfo *info) +{ + GIRealInfo *rinfo, *container_rinfo; + FunctionBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_FUNCTION_INFO (info), NULL); + + rinfo = (GIRealInfo *)info; + blob = (FunctionBlob *)&rinfo->typelib->data[rinfo->offset]; + container_rinfo = (GIRealInfo *)rinfo->container; + + if (container_rinfo->type == GI_INFO_TYPE_INTERFACE) + { + GIInterfaceInfo *container = (GIInterfaceInfo *)rinfo->container; + + return g_interface_info_get_property (container, blob->index); + } + else if (container_rinfo->type == GI_INFO_TYPE_OBJECT) + { + GIObjectInfo *container = (GIObjectInfo *)rinfo->container; + + return g_object_info_get_property (container, blob->index); + } + else + return NULL; +} + +/** + * g_function_info_get_vfunc: + * @info: a #GIFunctionInfo + * + * Obtain the virtual function associated with this #GIFunctionInfo. + * Only #GIFunctionInfo with the flag %GI_FUNCTION_WRAPS_VFUNC has + * a virtual function set. For other cases, %NULL will be returned. + * + * Returns: (transfer full): the virtual function or %NULL if not set. + * Free it by calling g_base_info_unref() when done. + */ +GIVFuncInfo * +g_function_info_get_vfunc (GIFunctionInfo *info) +{ + GIRealInfo *rinfo; + FunctionBlob *blob; + GIInterfaceInfo *container; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_FUNCTION_INFO (info), NULL); + + rinfo = (GIRealInfo *)info; + blob = (FunctionBlob *)&rinfo->typelib->data[rinfo->offset]; + container = (GIInterfaceInfo *)rinfo->container; + + return g_interface_info_get_vfunc (container, blob->index); +} + +/** + * g_invoke_error_quark: + * + * TODO + * + * Returns: TODO + */ +GQuark +g_invoke_error_quark (void) +{ + static GQuark quark = 0; + if (quark == 0) + quark = g_quark_from_static_string ("g-invoke-error-quark"); + return quark; +} + +/** + * g_function_info_invoke: (skip) + * @info: a #GIFunctionInfo describing the function to invoke + * @in_args: (array length=n_in_args): an array of #GIArguments, one for each in + * parameter of @info. If there are no in parameter, @in_args + * can be %NULL + * @n_in_args: the length of the @in_args array + * @out_args: (array length=n_out_args): an array of #GIArguments, one for each out + * parameter of @info. If there are no out parameters, @out_args + * may be %NULL + * @n_out_args: the length of the @out_args array + * @return_value: return location for the return value of the + * function. + * @error: return location for detailed error information, or %NULL + * + * Invokes the function described in @info with the given + * arguments. Note that inout parameters must appear in both + * argument lists. This function uses dlsym() to obtain a pointer + * to the function, so the library or shared object containing the + * described function must either be linked to the caller, or must + * have been g_module_symbol()ed before calling this function. + * + * Returns: %TRUE if the function has been invoked, %FALSE if an + * error occurred. + */ +gboolean +g_function_info_invoke (GIFunctionInfo *info, + const GIArgument *in_args, + int n_in_args, + const GIArgument *out_args, + int n_out_args, + GIArgument *return_value, + GError **error) +{ + const gchar *symbol; + gpointer func; + gboolean is_method; + gboolean throws; + + symbol = g_function_info_get_symbol (info); + + if (!g_typelib_symbol (g_base_info_get_typelib((GIBaseInfo *) info), + symbol, &func)) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_SYMBOL_NOT_FOUND, + "Could not locate %s: %s", symbol, g_module_error ()); + + return FALSE; + } + + is_method = (g_function_info_get_flags (info) & GI_FUNCTION_IS_METHOD) != 0 + && (g_function_info_get_flags (info) & GI_FUNCTION_IS_CONSTRUCTOR) == 0; + throws = g_function_info_get_flags (info) & GI_FUNCTION_THROWS; + + return g_callable_info_invoke ((GICallableInfo*) info, + func, + in_args, + n_in_args, + out_args, + n_out_args, + return_value, + is_method, + throws, + error); +} diff --git a/girepository/gifunctioninfo.h b/girepository/gifunctioninfo.h new file mode 100644 index 000000000..ae5b451da --- /dev/null +++ b/girepository/gifunctioninfo.h @@ -0,0 +1,97 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Function + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_FUNCTION_INFO + * @info: an info structure + * + * Checks if @info is a #GIFunctionInfo. + */ +#define GI_IS_FUNCTION_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_FUNCTION) + + +GI_AVAILABLE_IN_ALL +const gchar * g_function_info_get_symbol (GIFunctionInfo *info); + +GI_AVAILABLE_IN_ALL +GIFunctionInfoFlags g_function_info_get_flags (GIFunctionInfo *info); + +GI_AVAILABLE_IN_ALL +GIPropertyInfo * g_function_info_get_property (GIFunctionInfo *info); + +GI_AVAILABLE_IN_ALL +GIVFuncInfo * g_function_info_get_vfunc (GIFunctionInfo *info); + +/** + * G_INVOKE_ERROR: + * + * TODO + */ +#define G_INVOKE_ERROR (g_invoke_error_quark ()) + +GI_AVAILABLE_IN_ALL +GQuark g_invoke_error_quark (void); + +/** + * GInvokeError: + * @G_INVOKE_ERROR_FAILED: invokation failed, unknown error. + * @G_INVOKE_ERROR_SYMBOL_NOT_FOUND: symbol couldn't be found in any of the + * libraries associated with the typelib of the function. + * @G_INVOKE_ERROR_ARGUMENT_MISMATCH: the arguments provided didn't match + * the expected arguments for the functions type signature. + * + * An error occuring while invoking a function via + * g_function_info_invoke(). + */ + +typedef enum +{ + G_INVOKE_ERROR_FAILED, + G_INVOKE_ERROR_SYMBOL_NOT_FOUND, + G_INVOKE_ERROR_ARGUMENT_MISMATCH +} GInvokeError; + + +GI_AVAILABLE_IN_ALL +gboolean g_function_info_invoke (GIFunctionInfo *info, + const GIArgument *in_args, + int n_in_args, + const GIArgument *out_args, + int n_out_args, + GIArgument *return_value, + GError **error); + + +G_END_DECLS diff --git a/girepository/giinterfaceinfo.c b/girepository/giinterfaceinfo.c new file mode 100644 index 000000000..9e54658fb --- /dev/null +++ b/girepository/giinterfaceinfo.c @@ -0,0 +1,503 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Interface implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" +#include "giinterfaceinfo.h" + +/** + * SECTION:giinterfaceinfo + * @title: GIInterfaceInfo + * @short_description: Struct representing a GInterface + * + * GIInterfaceInfo represents a #GInterface type. + * + * A GInterface has methods, fields, properties, signals, interfaces, constants, + * virtual functions and prerequisites. + */ + +/** + * g_interface_info_get_n_prerequisites: + * @info: a #GIInterfaceInfo + * + * Obtain the number of prerequisites for this interface type. + * A prerequisites is another interface that needs to be implemented for + * interface, similar to an base class for GObjects. + * + * Returns: number of prerequisites + */ +gint +g_interface_info_get_n_prerequisites (GIInterfaceInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), 0); + + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_prerequisites; +} + +/** + * g_interface_info_get_prerequisite: + * @info: a #GIInterfaceInfo + * @n: index of prerequisites to get + * + * Obtain an interface type prerequisites index @n. + * + * Returns: (transfer full): the prerequisites as a #GIBaseInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIBaseInfo * +g_interface_info_get_prerequisite (GIInterfaceInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), NULL); + + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + return _g_info_from_entry (rinfo->repository, + rinfo->typelib, blob->prerequisites[n]); +} + + +/** + * g_interface_info_get_n_properties: + * @info: a #GIInterfaceInfo + * + * Obtain the number of properties that this interface type has. + * + * Returns: number of properties + */ +gint +g_interface_info_get_n_properties (GIInterfaceInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), 0); + + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_properties; +} + +/** + * g_interface_info_get_property: + * @info: a #GIInterfaceInfo + * @n: index of property to get + * + * Obtain an interface type property at index @n. + * + * Returns: (transfer full): the #GIPropertyInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIPropertyInfo * +g_interface_info_get_property (GIInterfaceInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->interface_blob_size + + (blob->n_prerequisites + (blob->n_prerequisites % 2)) * 2 + + n * header->property_blob_size; + + return (GIPropertyInfo *) g_info_new (GI_INFO_TYPE_PROPERTY, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_interface_info_get_n_methods: + * @info: a #GIInterfaceInfo + * + * Obtain the number of methods that this interface type has. + * + * Returns: number of methods + */ +gint +g_interface_info_get_n_methods (GIInterfaceInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), 0); + + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_methods; +} + +/** + * g_interface_info_get_method: + * @info: a #GIInterfaceInfo + * @n: index of method to get + * + * Obtain an interface type method at index @n. + * + * Returns: (transfer full): the #GIFunctionInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIFunctionInfo * +g_interface_info_get_method (GIInterfaceInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->interface_blob_size + + (blob->n_prerequisites + (blob->n_prerequisites % 2)) * 2 + + blob->n_properties * header->property_blob_size + + n * header->function_blob_size; + + return (GIFunctionInfo *) g_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_interface_info_find_method: + * @info: a #GIInterfaceInfo + * @name: name of method to obtain + * + * Obtain a method of the interface type given a @name. %NULL will be + * returned if there's no method available with that name. + * + * Returns: (transfer full): the #GIFunctionInfo or %NULL if none found. + * Free the struct by calling g_base_info_unref() when done. + */ +GIFunctionInfo * +g_interface_info_find_method (GIInterfaceInfo *info, + const gchar *name) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header = (Header *)rinfo->typelib->data; + InterfaceBlob *blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->interface_blob_size + + (blob->n_prerequisites + (blob->n_prerequisites % 2)) * 2 + + blob->n_properties * header->property_blob_size; + + return _g_base_info_find_method ((GIBaseInfo*)info, offset, blob->n_methods, name); +} + +/** + * g_interface_info_get_n_signals: + * @info: a #GIInterfaceInfo + * + * Obtain the number of signals that this interface type has. + * + * Returns: number of signals + */ +gint +g_interface_info_get_n_signals (GIInterfaceInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), 0); + + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_signals; +} + +/** + * g_interface_info_get_signal: + * @info: a #GIInterfaceInfo + * @n: index of signal to get + * + * Obtain an interface type signal at index @n. + * + * Returns: (transfer full): the #GISignalInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GISignalInfo * +g_interface_info_get_signal (GIInterfaceInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->interface_blob_size + + (blob->n_prerequisites + (blob->n_prerequisites % 2)) * 2 + + blob->n_properties * header->property_blob_size + + blob->n_methods * header->function_blob_size + + n * header->signal_blob_size; + + return (GISignalInfo *) g_info_new (GI_INFO_TYPE_SIGNAL, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_interface_info_find_signal: + * @info: a #GIInterfaceInfo + * @name: Name of signal + * + * TODO + * + * Returns: (transfer full): Info for the signal with name @name in @info, or + * %NULL on failure. + * Since: 1.34 + */ +GISignalInfo * +g_interface_info_find_signal (GIInterfaceInfo *info, + const gchar *name) +{ + gint n_signals; + gint i; + + n_signals = g_interface_info_get_n_signals (info); + for (i = 0; i < n_signals; i++) + { + GISignalInfo *siginfo = g_interface_info_get_signal (info, i); + + if (g_strcmp0 (g_base_info_get_name (siginfo), name) != 0) + { + g_base_info_unref ((GIBaseInfo*)siginfo); + continue; + } + + return siginfo; + } + return NULL; +} + +/** + * g_interface_info_get_n_vfuncs: + * @info: a #GIInterfaceInfo + * + * Obtain the number of virtual functions that this interface type has. + * + * Returns: number of virtual functions + */ +gint +g_interface_info_get_n_vfuncs (GIInterfaceInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), 0); + + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_vfuncs; +} + +/** + * g_interface_info_get_vfunc: + * @info: a #GIInterfaceInfo + * @n: index of virtual function to get + * + * Obtain an interface type virtual function at index @n. + * + * Returns: (transfer full): the #GIVFuncInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIVFuncInfo * +g_interface_info_get_vfunc (GIInterfaceInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->interface_blob_size + + (blob->n_prerequisites + (blob->n_prerequisites % 2)) * 2 + + blob->n_properties * header->property_blob_size + + blob->n_methods * header->function_blob_size + + blob->n_signals * header->signal_blob_size + + n * header->vfunc_blob_size; + + return (GIVFuncInfo *) g_info_new (GI_INFO_TYPE_VFUNC, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_interface_info_find_vfunc: + * @info: a #GIInterfaceInfo + * @name: The name of a virtual function to find. + * + * Locate a virtual function slot with name @name. See the documentation + * for g_object_info_find_vfunc() for more information on virtuals. + * + * Returns: (transfer full): the #GIVFuncInfo, or %NULL. Free it with + * g_base_info_unref() when done. + */ +GIVFuncInfo * +g_interface_info_find_vfunc (GIInterfaceInfo *info, + const gchar *name) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->interface_blob_size + + (blob->n_prerequisites + blob->n_prerequisites % 2) * 2 + + blob->n_properties * header->property_blob_size + + blob->n_methods * header->function_blob_size + + blob->n_signals * header->signal_blob_size; + + return _g_base_info_find_vfunc (rinfo, offset, blob->n_vfuncs, name); +} + +/** + * g_interface_info_get_n_constants: + * @info: a #GIInterfaceInfo + * + * Obtain the number of constants that this interface type has. + * + * Returns: number of constants + */ +gint +g_interface_info_get_n_constants (GIInterfaceInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), 0); + + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_constants; +} + +/** + * g_interface_info_get_constant: + * @info: a #GIInterfaceInfo + * @n: index of constant to get + * + * Obtain an interface type constant at index @n. + * + * Returns: (transfer full): the #GIConstantInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIConstantInfo * +g_interface_info_get_constant (GIInterfaceInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->interface_blob_size + + (blob->n_prerequisites + (blob->n_prerequisites % 2)) * 2 + + blob->n_properties * header->property_blob_size + + blob->n_methods * header->function_blob_size + + blob->n_signals * header->signal_blob_size + + blob->n_vfuncs * header->vfunc_blob_size + + n * header->constant_blob_size; + + return (GIConstantInfo *) g_info_new (GI_INFO_TYPE_CONSTANT, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_interface_info_get_iface_struct: + * @info: a #GIInterfaceInfo + * + * Returns the layout C structure associated with this #GInterface. + * + * Returns: (transfer full): the #GIStructInfo or %NULL. Free it with + * g_base_info_unref() when done. + */ +GIStructInfo * +g_interface_info_get_iface_struct (GIInterfaceInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), NULL); + + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->gtype_struct) + return (GIStructInfo *) _g_info_from_entry (rinfo->repository, + rinfo->typelib, blob->gtype_struct); + else + return NULL; +} + diff --git a/girepository/giinterfaceinfo.h b/girepository/giinterfaceinfo.h new file mode 100644 index 000000000..6bc7b1511 --- /dev/null +++ b/girepository/giinterfaceinfo.h @@ -0,0 +1,103 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Interface + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_INTERFACE_INFO + * @info: an info structure + * + * Checks if @info is a #GIInterfaceInfo. + */ +#define GI_IS_INTERFACE_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_INTERFACE) + + +GI_AVAILABLE_IN_ALL +gint g_interface_info_get_n_prerequisites (GIInterfaceInfo *info); + +GI_AVAILABLE_IN_ALL +GIBaseInfo * g_interface_info_get_prerequisite (GIInterfaceInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +gint g_interface_info_get_n_properties (GIInterfaceInfo *info); + +GI_AVAILABLE_IN_ALL +GIPropertyInfo * g_interface_info_get_property (GIInterfaceInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +gint g_interface_info_get_n_methods (GIInterfaceInfo *info); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_interface_info_get_method (GIInterfaceInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_interface_info_find_method (GIInterfaceInfo *info, + const gchar *name); + +GI_AVAILABLE_IN_ALL +gint g_interface_info_get_n_signals (GIInterfaceInfo *info); + +GI_AVAILABLE_IN_ALL +GISignalInfo * g_interface_info_get_signal (GIInterfaceInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GISignalInfo * g_interface_info_find_signal (GIInterfaceInfo *info, + const gchar *name); + +GI_AVAILABLE_IN_ALL +gint g_interface_info_get_n_vfuncs (GIInterfaceInfo *info); + +GI_AVAILABLE_IN_ALL +GIVFuncInfo * g_interface_info_get_vfunc (GIInterfaceInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GIVFuncInfo * g_interface_info_find_vfunc (GIInterfaceInfo *info, + const gchar *name); + +GI_AVAILABLE_IN_ALL +gint g_interface_info_get_n_constants (GIInterfaceInfo *info); + +GI_AVAILABLE_IN_ALL +GIConstantInfo * g_interface_info_get_constant (GIInterfaceInfo *info, + gint n); + + +GI_AVAILABLE_IN_ALL +GIStructInfo * g_interface_info_get_iface_struct (GIInterfaceInfo *info); + +G_END_DECLS diff --git a/girepository/ginvoke.c b/girepository/ginvoke.c new file mode 100644 index 000000000..ad9a5f8c6 --- /dev/null +++ b/girepository/ginvoke.c @@ -0,0 +1,313 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Invoke functionality + * + * Copyright (C) 2005 Matthias Clasen + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include + +#include +#include + +#include +#include "girffi.h" + +/** + * value_to_ffi_type: + * @gvalue: TODO + * @value: TODO + * + * TODO + */ +static ffi_type * +value_to_ffi_type (const GValue *gvalue, gpointer *value) +{ + ffi_type *rettype = NULL; + GType type = g_type_fundamental (G_VALUE_TYPE (gvalue)); + g_assert (type != G_TYPE_INVALID); + + switch (type) + { + case G_TYPE_BOOLEAN: + case G_TYPE_CHAR: + case G_TYPE_INT: + rettype = &ffi_type_sint; + *value = (gpointer)&(gvalue->data[0].v_int); + break; + case G_TYPE_UCHAR: + case G_TYPE_UINT: + rettype = &ffi_type_uint; + *value = (gpointer)&(gvalue->data[0].v_uint); + break; + case G_TYPE_STRING: + case G_TYPE_OBJECT: + case G_TYPE_BOXED: + case G_TYPE_POINTER: + case G_TYPE_PARAM: + rettype = &ffi_type_pointer; + *value = (gpointer)&(gvalue->data[0].v_pointer); + break; + case G_TYPE_FLOAT: + rettype = &ffi_type_float; + *value = (gpointer)&(gvalue->data[0].v_float); + break; + case G_TYPE_DOUBLE: + rettype = &ffi_type_double; + *value = (gpointer)&(gvalue->data[0].v_double); + break; + case G_TYPE_LONG: + rettype = &ffi_type_slong; + *value = (gpointer)&(gvalue->data[0].v_long); + break; + case G_TYPE_ULONG: + rettype = &ffi_type_ulong; + *value = (gpointer)&(gvalue->data[0].v_ulong); + break; + case G_TYPE_INT64: + rettype = &ffi_type_sint64; + *value = (gpointer)&(gvalue->data[0].v_int64); + break; + case G_TYPE_UINT64: + rettype = &ffi_type_uint64; + *value = (gpointer)&(gvalue->data[0].v_uint64); + break; + default: + rettype = &ffi_type_pointer; + *value = NULL; + g_warning ("Unsupported fundamental type: %s", g_type_name (type)); + break; + } + return rettype; +} + +/** + * g_value_to_ffi_return_type: + * @gvalue: TODO + * @ffi_value: TODO + * @value: TODO + * + * TODO + */ +static ffi_type * +g_value_to_ffi_return_type (const GValue *gvalue, + const GIArgument *ffi_value, + gpointer *value) +{ + ffi_type *rettype = NULL; + GType type = g_type_fundamental (G_VALUE_TYPE (gvalue)); + g_assert (type != G_TYPE_INVALID); + + *value = (gpointer)&(ffi_value->v_long); + + switch (type) { + case G_TYPE_CHAR: + rettype = &ffi_type_sint8; + break; + case G_TYPE_UCHAR: + rettype = &ffi_type_uint8; + break; + case G_TYPE_BOOLEAN: + case G_TYPE_INT: + rettype = &ffi_type_sint; + break; + case G_TYPE_UINT: + rettype = &ffi_type_uint; + break; + case G_TYPE_STRING: + case G_TYPE_OBJECT: + case G_TYPE_BOXED: + case G_TYPE_POINTER: + case G_TYPE_PARAM: + rettype = &ffi_type_pointer; + break; + case G_TYPE_FLOAT: + rettype = &ffi_type_float; + *value = (gpointer)&(ffi_value->v_float); + break; + case G_TYPE_DOUBLE: + rettype = &ffi_type_double; + *value = (gpointer)&(ffi_value->v_double); + break; + case G_TYPE_LONG: + rettype = &ffi_type_slong; + break; + case G_TYPE_ULONG: + rettype = &ffi_type_ulong; + break; + case G_TYPE_INT64: + rettype = &ffi_type_sint64; + *value = (gpointer)&(ffi_value->v_int64); + break; + case G_TYPE_UINT64: + rettype = &ffi_type_uint64; + *value = (gpointer)&(ffi_value->v_uint64); + break; + default: + rettype = &ffi_type_pointer; + *value = NULL; + g_warning ("Unsupported fundamental type: %s", g_type_name (type)); + break; + } + return rettype; +} + +/** + * g_value_from_ffi_value: + * @gvalue: TODO + * @value: TODO + * + * TODO + */ +static void +g_value_from_ffi_value (GValue *gvalue, + const GIArgument *value) +{ + switch (g_type_fundamental (G_VALUE_TYPE (gvalue))) { + case G_TYPE_INT: + g_value_set_int (gvalue, (gint)value->v_long); + break; + case G_TYPE_FLOAT: + g_value_set_float (gvalue, (gfloat)value->v_float); + break; + case G_TYPE_DOUBLE: + g_value_set_double (gvalue, (gdouble)value->v_double); + break; + case G_TYPE_BOOLEAN: + g_value_set_boolean (gvalue, (gboolean)value->v_long); + break; + case G_TYPE_STRING: + g_value_set_string (gvalue, (gchar*)value->v_pointer); + break; + case G_TYPE_CHAR: + g_value_set_schar (gvalue, (gchar)value->v_long); + break; + case G_TYPE_UCHAR: + g_value_set_uchar (gvalue, (guchar)value->v_ulong); + break; + case G_TYPE_UINT: + g_value_set_uint (gvalue, (guint)value->v_ulong); + break; + case G_TYPE_POINTER: + g_value_set_pointer (gvalue, (gpointer)value->v_pointer); + break; + case G_TYPE_LONG: + g_value_set_long (gvalue, (glong)value->v_long); + break; + case G_TYPE_ULONG: + g_value_set_ulong (gvalue, (gulong)value->v_ulong); + break; + case G_TYPE_INT64: + g_value_set_int64 (gvalue, (gint64)value->v_int64); + break; + case G_TYPE_UINT64: + g_value_set_uint64 (gvalue, (guint64)value->v_uint64); + break; + case G_TYPE_BOXED: + g_value_set_boxed (gvalue, (gpointer)value->v_pointer); + break; + case G_TYPE_PARAM: + g_value_set_param (gvalue, (gpointer)value->v_pointer); + break; + default: + g_warning ("Unsupported fundamental type: %s", + g_type_name (g_type_fundamental (G_VALUE_TYPE (gvalue)))); + } + +} + +/** + * gi_cclosure_marshal_generic: (skip) + * @closure: TODO + * @return_gvalue: TODO + * @n_param_values: TODO + * @param_values: TODO + * @invocation_hint: TODO + * @marshal_data: TODO + * + * TODO + */ +void +gi_cclosure_marshal_generic (GClosure *closure, + GValue *return_gvalue, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + GIArgument return_ffi_value = { 0, }; + ffi_type *rtype; + void *rvalue; + int n_args; + ffi_type **atypes; + void **args; + int i; + ffi_cif cif; + GCClosure *cc = (GCClosure*) closure; + + if (return_gvalue && G_VALUE_TYPE (return_gvalue)) + { + rtype = g_value_to_ffi_return_type (return_gvalue, &return_ffi_value, + &rvalue); + } + else + { + rtype = &ffi_type_void; + rvalue = &return_ffi_value.v_long; + } + + n_args = n_param_values + 1; + atypes = g_alloca (sizeof (ffi_type *) * n_args); + args = g_alloca (sizeof (gpointer) * n_args); + + if (n_param_values > 0) + { + if (G_CCLOSURE_SWAP_DATA (closure)) + { + atypes[n_args-1] = value_to_ffi_type (param_values + 0, + &args[n_args-1]); + atypes[0] = &ffi_type_pointer; + args[0] = &closure->data; + } + else + { + atypes[0] = value_to_ffi_type (param_values + 0, &args[0]); + atypes[n_args-1] = &ffi_type_pointer; + args[n_args-1] = &closure->data; + } + } + else + { + atypes[0] = &ffi_type_pointer; + args[0] = &closure->data; + } + + for (i = 1; i < n_args - 1; i++) + atypes[i] = value_to_ffi_type (param_values + i, &args[i]); + + if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, n_args, rtype, atypes) != FFI_OK) + return; + + ffi_call (&cif, marshal_data ? marshal_data : cc->callback, rvalue, args); + + if (return_gvalue && G_VALUE_TYPE (return_gvalue)) + g_value_from_ffi_value (return_gvalue, &return_ffi_value); +} diff --git a/girepository/giobjectinfo.c b/girepository/giobjectinfo.c new file mode 100644 index 000000000..f3839ed4a --- /dev/null +++ b/girepository/giobjectinfo.c @@ -0,0 +1,1099 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Object implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" +#include "giobjectinfo.h" + +/** + * SECTION:giobjectinfo + * @title: GIObjectInfo + * @short_description: Struct representing a classed type + * + * GIObjectInfo represents a classed type. + * + * Classed types in GType inherit from #GTypeInstance; the most common + * type is #GObject. + * + * A GIObjectInfo doesn't represent a specific instance of a classed type, + * instead this represent the object type (eg class). + * + * A GIObjectInfo has methods, fields, properties, signals, interfaces, + * constants and virtual functions. + */ + +/** + * g_object_info_get_field_offset: + * @info: a #GIObjectInfo + * @n: index of queried field + * + * Obtain the offset of the specified field. + * + * Returns: field offset in bytes + */ +static gint32 +g_object_info_get_field_offset (GIObjectInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header = (Header *)rinfo->typelib->data; + ObjectBlob *blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + guint32 offset; + gint i; + FieldBlob *field_blob; + + offset = rinfo->offset + header->object_blob_size + + (blob->n_interfaces + blob->n_interfaces % 2) * 2; + + for (i = 0; i < n; i++) + { + field_blob = (FieldBlob *)&rinfo->typelib->data[offset]; + offset += header->field_blob_size; + if (field_blob->has_embedded_type) + offset += header->callback_blob_size; + } + + return offset; +} + +/** + * g_object_info_get_parent: + * @info: a #GIObjectInfo + * + * Obtain the parent of the object type. + * + * Returns: (transfer full) (nullable): the #GIObjectInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIObjectInfo * +g_object_info_get_parent (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->parent) + return (GIObjectInfo *) _g_info_from_entry (rinfo->repository, + rinfo->typelib, blob->parent); + else + return NULL; +} + +/** + * g_object_info_get_abstract: + * @info: a #GIObjectInfo + * + * Obtain if the object type is an abstract type, eg if it cannot be + * instantiated + * + * Returns: %TRUE if the object type is abstract + */ +gboolean +g_object_info_get_abstract (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), FALSE); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->abstract != 0; +} + +/** + * g_object_info_get_final: + * @info: a #GIObjectInfo + * + * Checks whether the object type is a final type, i.e. if it cannot + * be derived + * + * Returns: %TRUE if the object type is final + * + * Since: 1.70 + */ +gboolean +g_object_info_get_final (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *) info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), FALSE); + + blob = (ObjectBlob *) &rinfo->typelib->data[rinfo->offset]; + + return blob->final_ != 0; +} + +/** + * g_object_info_get_fundamental: + * @info: a #GIObjectInfo + * + * Obtain if the object type is of a fundamental type which is not + * G_TYPE_OBJECT. This is mostly for supporting GstMiniObject. + * + * Returns: %TRUE if the object type is a fundamental type + */ +gboolean +g_object_info_get_fundamental (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), FALSE); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->fundamental != 0; +} + +/** + * g_object_info_get_type_name: + * @info: a #GIObjectInfo + * + * Obtain the name of the objects class/type. + * + * Returns: name of the objects type + */ +const gchar * +g_object_info_get_type_name (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->gtype_name); +} + +/** + * g_object_info_get_type_init: + * @info: a #GIObjectInfo + * + * Obtain the function which when called will return the GType + * function for which this object type is registered. + * + * Returns: the type init function + */ +const gchar * +g_object_info_get_type_init (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->gtype_init); +} + +/** + * g_object_info_get_n_interfaces: + * @info: a #GIObjectInfo + * + * Obtain the number of interfaces that this object type has. + * + * Returns: number of interfaces + */ +gint +g_object_info_get_n_interfaces (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), 0); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_interfaces; +} + +/** + * g_object_info_get_interface: + * @info: a #GIObjectInfo + * @n: index of interface to get + * + * Obtain an object type interface at index @n. + * + * Returns: (transfer full): the #GIInterfaceInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIInterfaceInfo * +g_object_info_get_interface (GIObjectInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return (GIInterfaceInfo *) _g_info_from_entry (rinfo->repository, + rinfo->typelib, blob->interfaces[n]); +} + +/** + * g_object_info_get_n_fields: + * @info: a #GIObjectInfo + * + * Obtain the number of fields that this object type has. + * + * Returns: number of fields + */ +gint +g_object_info_get_n_fields (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), 0); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_fields; +} + +/** + * g_object_info_get_field: + * @info: a #GIObjectInfo + * @n: index of field to get + * + * Obtain an object type field at index @n. + * + * Returns: (transfer full): the #GIFieldInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIFieldInfo * +g_object_info_get_field (GIObjectInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + offset = g_object_info_get_field_offset(info, n); + + return (GIFieldInfo *) g_info_new (GI_INFO_TYPE_FIELD, (GIBaseInfo*)info, rinfo->typelib, offset); +} + +/** + * g_object_info_get_n_properties: + * @info: a #GIObjectInfo + * + * Obtain the number of properties that this object type has. + * + * Returns: number of properties + */ +gint +g_object_info_get_n_properties (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), 0); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + return blob->n_properties; +} + +/** + * g_object_info_get_property: + * @info: a #GIObjectInfo + * @n: index of property to get + * + * Obtain an object type property at index @n. + * + * Returns: (transfer full): the #GIPropertyInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIPropertyInfo * +g_object_info_get_property (GIObjectInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->object_blob_size + + (blob->n_interfaces + blob->n_interfaces % 2) * 2 + + blob->n_fields * header->field_blob_size + + blob->n_field_callbacks * header->callback_blob_size + + n * header->property_blob_size; + + return (GIPropertyInfo *) g_info_new (GI_INFO_TYPE_PROPERTY, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_object_info_get_n_methods: + * @info: a #GIObjectInfo + * + * Obtain the number of methods that this object type has. + * + * Returns: number of methods + */ +gint +g_object_info_get_n_methods (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), 0); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_methods; +} + +/** + * g_object_info_get_method: + * @info: a #GIObjectInfo + * @n: index of method to get + * + * Obtain an object type method at index @n. + * + * Returns: (transfer full): the #GIFunctionInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIFunctionInfo * +g_object_info_get_method (GIObjectInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + + offset = rinfo->offset + header->object_blob_size + + (blob->n_interfaces + blob->n_interfaces % 2) * 2 + + blob->n_fields * header->field_blob_size + + blob->n_field_callbacks * header->callback_blob_size + + blob->n_properties * header->property_blob_size + + n * header->function_blob_size; + + return (GIFunctionInfo *) g_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_object_info_find_method: + * @info: a #GIObjectInfo + * @name: name of method to obtain + * + * Obtain a method of the object type given a @name. %NULL will be + * returned if there's no method available with that name. + * + * Returns: (transfer full) (nullable): the #GIFunctionInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIFunctionInfo * +g_object_info_find_method (GIObjectInfo *info, + const gchar *name) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), 0); + + header = (Header *)rinfo->typelib->data; + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->object_blob_size + + (blob->n_interfaces + blob->n_interfaces % 2) * 2 + + blob->n_fields * header->field_blob_size + + + blob->n_field_callbacks * header->callback_blob_size + + blob->n_properties * header->property_blob_size; + + return _g_base_info_find_method ((GIBaseInfo*)info, offset, blob->n_methods, name); +} + +/** + * g_object_info_find_method_using_interfaces: + * @info: a #GIObjectInfo + * @name: name of method to obtain + * @implementor: (out) (transfer full): The implementor of the interface + * + * Obtain a method of the object given a @name, searching both the + * object @info and any interfaces it implements. %NULL will be + * returned if there's no method available with that name. + * + * Note that this function does *not* search parent classes; you will have + * to chain up if that's desired. + * + * Returns: (transfer full) (nullable): the #GIFunctionInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIFunctionInfo * +g_object_info_find_method_using_interfaces (GIObjectInfo *info, + const gchar *name, + GIObjectInfo **implementor) +{ + GIFunctionInfo *result = NULL; + GIObjectInfo *implementor_result = NULL; + + result = g_object_info_find_method (info, name); + if (result) + implementor_result = g_base_info_ref ((GIBaseInfo*) info); + + if (result == NULL) + { + int n_interfaces; + int i; + + n_interfaces = g_object_info_get_n_interfaces (info); + for (i = 0; i < n_interfaces; ++i) + { + GIInterfaceInfo *iface_info; + + iface_info = g_object_info_get_interface (info, i); + + result = g_interface_info_find_method (iface_info, name); + + if (result != NULL) + { + implementor_result = iface_info; + break; + } + g_base_info_unref ((GIBaseInfo*) iface_info); + } + } + if (implementor) + *implementor = implementor_result; + else if (implementor_result != NULL) + g_base_info_unref ((GIBaseInfo*) implementor_result); + return result; +} + +/** + * g_object_info_get_n_signals: + * @info: a #GIObjectInfo + * + * Obtain the number of signals that this object type has. + * + * Returns: number of signals + */ +gint +g_object_info_get_n_signals (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), 0); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_signals; +} + +/** + * g_object_info_get_signal: + * @info: a #GIObjectInfo + * @n: index of signal to get + * + * Obtain an object type signal at index @n. + * + * Returns: (transfer full): the #GISignalInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GISignalInfo * +g_object_info_get_signal (GIObjectInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->object_blob_size + + (blob->n_interfaces + blob->n_interfaces % 2) * 2 + + blob->n_fields * header->field_blob_size + + blob->n_field_callbacks * header->callback_blob_size + + blob->n_properties * header->property_blob_size + + blob->n_methods * header->function_blob_size + + n * header->signal_blob_size; + + return (GISignalInfo *) g_info_new (GI_INFO_TYPE_SIGNAL, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_object_info_find_signal: + * @info: a #GIObjectInfo + * @name: Name of signal + * + * TODO + * + * Returns: (transfer full) (nullable): Info for the signal with name @name in @info, or %NULL on failure. + */ +GISignalInfo * +g_object_info_find_signal (GIObjectInfo *info, + const gchar *name) +{ + gint n_signals; + gint i; + + n_signals = g_object_info_get_n_signals (info); + for (i = 0; i < n_signals; i++) + { + GISignalInfo *siginfo = g_object_info_get_signal (info, i); + + if (g_strcmp0 (g_base_info_get_name (siginfo), name) != 0) + { + g_base_info_unref ((GIBaseInfo*)siginfo); + continue; + } + + return siginfo; + } + return NULL; +} + + +/** + * g_object_info_get_n_vfuncs: + * @info: a #GIObjectInfo + * + * Obtain the number of virtual functions that this object type has. + * + * Returns: number of virtual functions + */ +gint +g_object_info_get_n_vfuncs (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), 0); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_vfuncs; +} + +/** + * g_object_info_get_vfunc: + * @info: a #GIObjectInfo + * @n: index of virtual function to get + * + * Obtain an object type virtual function at index @n. + * + * Returns: (transfer full): the #GIVFuncInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIVFuncInfo * +g_object_info_get_vfunc (GIObjectInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->object_blob_size + + (blob->n_interfaces + blob->n_interfaces % 2) * 2 + + blob->n_fields * header->field_blob_size + + blob->n_field_callbacks * header->callback_blob_size + + blob->n_properties * header->property_blob_size + + blob->n_methods * header->function_blob_size + + blob->n_signals * header->signal_blob_size + + n * header->vfunc_blob_size; + + return (GIVFuncInfo *) g_info_new (GI_INFO_TYPE_VFUNC, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_object_info_find_vfunc: + * @info: a #GIObjectInfo + * @name: The name of a virtual function to find. + * + * Locate a virtual function slot with name @name. Note that the namespace + * for virtuals is distinct from that of methods; there may or may not be + * a concrete method associated for a virtual. If there is one, it may + * be retrieved using g_vfunc_info_get_invoker(), otherwise %NULL will be + * returned. + * See the documentation for g_vfunc_info_get_invoker() for more + * information on invoking virtuals. + * + * Returns: (transfer full) (nullable): the #GIVFuncInfo, or %NULL. Free it with + * g_base_info_unref() when done. + */ +GIVFuncInfo * +g_object_info_find_vfunc (GIObjectInfo *info, + const gchar *name) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->object_blob_size + + (blob->n_interfaces + blob->n_interfaces % 2) * 2 + + blob->n_fields * header->field_blob_size + + blob->n_field_callbacks * header->callback_blob_size + + blob->n_properties * header->property_blob_size + + blob->n_methods * header->function_blob_size + + blob->n_signals * header->signal_blob_size; + + return _g_base_info_find_vfunc (rinfo, offset, blob->n_vfuncs, name); +} + +/** + * g_object_info_find_vfunc_using_interfaces: + * @info: a #GIObjectInfo + * @name: name of vfunc to obtain + * @implementor: (out) (transfer full): The implementor of the interface + * + * Locate a virtual function slot with name @name, searching both the object + * @info and any interfaces it implements. Note that the namespace for + * virtuals is distinct from that of methods; there may or may not be a + * concrete method associated for a virtual. If there is one, it may be + * retrieved using g_vfunc_info_get_invoker(), otherwise %NULL will be + * returned. + * + * Note that this function does *not* search parent classes; you will have + * to chain up if that's desired. + * + * Returns: (transfer full) (nullable): the #GIVFuncInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIVFuncInfo * +g_object_info_find_vfunc_using_interfaces (GIObjectInfo *info, + const gchar *name, + GIObjectInfo **implementor) +{ + GIVFuncInfo *result = NULL; + GIObjectInfo *implementor_result = NULL; + + result = g_object_info_find_vfunc (info, name); + if (result) + implementor_result = g_base_info_ref ((GIBaseInfo*) info); + + if (result == NULL) + { + int n_interfaces; + int i; + + n_interfaces = g_object_info_get_n_interfaces (info); + for (i = 0; i < n_interfaces; ++i) + { + GIInterfaceInfo *iface_info; + + iface_info = g_object_info_get_interface (info, i); + + result = g_interface_info_find_vfunc (iface_info, name); + + if (result != NULL) + { + implementor_result = iface_info; + break; + } + g_base_info_unref ((GIBaseInfo*) iface_info); + } + } + if (implementor) + *implementor = implementor_result; + else if (implementor_result != NULL) + g_base_info_unref ((GIBaseInfo*) implementor_result); + return result; +} + +/** + * g_object_info_get_n_constants: + * @info: a #GIObjectInfo + * + * Obtain the number of constants that this object type has. + * + * Returns: number of constants + */ +gint +g_object_info_get_n_constants (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), 0); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_constants; +} + +/** + * g_object_info_get_constant: + * @info: a #GIObjectInfo + * @n: index of constant to get + * + * Obtain an object type constant at index @n. + * + * Returns: (transfer full): the #GIConstantInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIConstantInfo * +g_object_info_get_constant (GIObjectInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->object_blob_size + + (blob->n_interfaces + blob->n_interfaces % 2) * 2 + + blob->n_fields * header->field_blob_size + + blob->n_field_callbacks * header->callback_blob_size + + blob->n_properties * header->property_blob_size + + blob->n_methods * header->function_blob_size + + blob->n_signals * header->signal_blob_size + + blob->n_vfuncs * header->vfunc_blob_size + + n * header->constant_blob_size; + + return (GIConstantInfo *) g_info_new (GI_INFO_TYPE_CONSTANT, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_object_info_get_class_struct: + * @info: a #GIObjectInfo + * + * Every #GObject has two structures; an instance structure and a class + * structure. This function returns the metadata for the class structure. + * + * Returns: (transfer full) (nullable): the #GIStructInfo or %NULL. Free with + * g_base_info_unref() when done. + */ +GIStructInfo * +g_object_info_get_class_struct (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->gtype_struct) + return (GIStructInfo *) _g_info_from_entry (rinfo->repository, + rinfo->typelib, blob->gtype_struct); + else + return NULL; +} + +typedef const char* (*SymbolGetter) (GIObjectInfo *info); + +static void * +_get_func(GIObjectInfo *info, + SymbolGetter getter) +{ + const char* symbol; + GSList *parents = NULL, *l; + GIObjectInfo *parent_info; + gpointer func = NULL; + + parent_info = g_base_info_ref (info); + while (parent_info != NULL) + { + parents = g_slist_prepend (parents, parent_info); + parent_info = g_object_info_get_parent (parent_info); + } + + for (l = parents; l; l = l->next) + { + parent_info = l->data; + symbol = getter (parent_info); + if (symbol == NULL) + continue; + + g_typelib_symbol (((GIRealInfo *)parent_info)->typelib, symbol, (gpointer*) &func); + if (func) + break; + } + + g_slist_free_full (parents, (GDestroyNotify) g_base_info_unref); + return func; +} + +/** + * g_object_info_get_ref_function: + * @info: a #GIObjectInfo + * + * Obtain the symbol name of the function that should be called to ref this + * object type. It's mainly used fundamental types. The type signature for + * the symbol is %GIObjectInfoRefFunction, to fetch the function pointer + * see g_object_info_get_ref_function(). + * + * Returns: (nullable): the symbol or %NULL + */ +const char * +g_object_info_get_ref_function (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->ref_func) + return g_typelib_get_string (rinfo->typelib, blob->ref_func); + + return NULL; +} + +/** + * g_object_info_get_ref_function_pointer: (skip) + * @info: a #GIObjectInfo + * + * Obtain a pointer to a function which can be used to + * increase the reference count an instance of this object type. + * This takes derivation into account and will reversely traverse + * the base classes of this type, starting at the top type. + * + * Returns: (nullable): the function pointer or %NULL + */ +GIObjectInfoRefFunction +g_object_info_get_ref_function_pointer (GIObjectInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + return (GIObjectInfoRefFunction)_get_func(info, (SymbolGetter)g_object_info_get_ref_function); +} + +/** + * g_object_info_get_unref_function: + * @info: a #GIObjectInfo + * + * Obtain the symbol name of the function that should be called to unref this + * object type. It's mainly used fundamental types. The type signature for + * the symbol is %GIObjectInfoUnrefFunction, to fetch the function pointer + * see g_object_info_get_unref_function(). + * + * Returns: (nullable): the symbol or %NULL + */ +const char * +g_object_info_get_unref_function (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->unref_func) + return g_typelib_get_string (rinfo->typelib, blob->unref_func); + + return NULL; +} + +/** + * g_object_info_get_unref_function_pointer: (skip) + * @info: a #GIObjectInfo + * + * Obtain a pointer to a function which can be used to + * decrease the reference count an instance of this object type. + * This takes derivation into account and will reversely traverse + * the base classes of this type, starting at the top type. + * + * Returns: (nullable): the function pointer or %NULL + */ +GIObjectInfoUnrefFunction +g_object_info_get_unref_function_pointer (GIObjectInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + return (GIObjectInfoUnrefFunction)_get_func(info, (SymbolGetter)g_object_info_get_unref_function); +} + +/** + * g_object_info_get_set_value_function: + * @info: a #GIObjectInfo + * + * Obtain the symbol name of the function that should be called to convert + * set a GValue giving an object instance pointer of this object type. + * I's mainly used fundamental types. The type signature for the symbol + * is %GIObjectInfoSetValueFunction, to fetch the function pointer + * see g_object_info_get_set_value_function(). + * + * Returns: (nullable): the symbol or %NULL + */ +const char * +g_object_info_get_set_value_function (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->set_value_func) + return g_typelib_get_string (rinfo->typelib, blob->set_value_func); + + return NULL; +} + +/** + * g_object_info_get_set_value_function_pointer: (skip) + * @info: a #GIObjectInfo + * + * Obtain a pointer to a function which can be used to + * set a GValue given an instance of this object type. + * This takes derivation into account and will reversely traverse + * the base classes of this type, starting at the top type. + * + * Returns: (nullable): the function pointer or %NULL + */ +GIObjectInfoSetValueFunction +g_object_info_get_set_value_function_pointer (GIObjectInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + return (GIObjectInfoSetValueFunction)_get_func(info, (SymbolGetter)g_object_info_get_set_value_function); +} + +/** + * g_object_info_get_get_value_function: + * @info: a #GIObjectInfo + * + * Obtain the symbol name of the function that should be called to convert + * an object instance pointer of this object type to a GValue. + * I's mainly used fundamental types. The type signature for the symbol + * is %GIObjectInfoGetValueFunction, to fetch the function pointer + * see g_object_info_get_get_value_function(). + * + * Returns: (nullable): the symbol or %NULL + */ +const char * +g_object_info_get_get_value_function (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->get_value_func) + return g_typelib_get_string (rinfo->typelib, blob->get_value_func); + + return NULL; +} + +/** + * g_object_info_get_get_value_function_pointer: (skip) + * @info: a #GIObjectInfo + * + * Obtain a pointer to a function which can be used to + * extract an instance of this object type out of a GValue. + * This takes derivation into account and will reversely traverse + * the base classes of this type, starting at the top type. + * + * Returns: (nullable): the function pointer or %NULL + */ +GIObjectInfoGetValueFunction +g_object_info_get_get_value_function_pointer (GIObjectInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + return (GIObjectInfoGetValueFunction)_get_func(info, (SymbolGetter)g_object_info_get_get_value_function); +} diff --git a/girepository/giobjectinfo.h b/girepository/giobjectinfo.h new file mode 100644 index 000000000..e12ea62f1 --- /dev/null +++ b/girepository/giobjectinfo.h @@ -0,0 +1,207 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Object + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GIObjectInfoRefFunction: (skip) + * @object: object instance pointer + * + * Increases the reference count of an object instance. + * + * Returns: (transfer full): the object instance + */ +typedef void * (*GIObjectInfoRefFunction) (void *object); + +/** + * GIObjectInfoUnrefFunction: (skip) + * @object: object instance pointer + * + * Decreases the reference count of an object instance. + */ +typedef void (*GIObjectInfoUnrefFunction) (void *object); + +/** + * GIObjectInfoSetValueFunction: (skip) + * @value: a #GValue + * @object: object instance pointer + * + * Update @value and attach the object instance pointer @object to it. + */ +typedef void (*GIObjectInfoSetValueFunction) (GValue *value, void *object); + +/** + * GIObjectInfoGetValueFunction: (skip) + * @value: a #GValue + * + * Extract an object instance out of @value + * + * Returns: (transfer full): the object instance + */ +typedef void * (*GIObjectInfoGetValueFunction) (const GValue *value); + +/** + * GI_IS_OBJECT_INFO + * @info: an info structure + * + * Checks if @info is a #GIObjectInfo. + */ +#define GI_IS_OBJECT_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_OBJECT) + + +GI_AVAILABLE_IN_ALL +const gchar * g_object_info_get_type_name (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +const gchar * g_object_info_get_type_init (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_object_info_get_abstract (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_object_info_get_final (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_object_info_get_fundamental (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIObjectInfo * g_object_info_get_parent (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_object_info_get_n_interfaces (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIInterfaceInfo * g_object_info_get_interface (GIObjectInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +gint g_object_info_get_n_fields (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIFieldInfo * g_object_info_get_field (GIObjectInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +gint g_object_info_get_n_properties (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIPropertyInfo * g_object_info_get_property (GIObjectInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +gint g_object_info_get_n_methods (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_object_info_get_method (GIObjectInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_object_info_find_method (GIObjectInfo *info, + const gchar *name); + + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_object_info_find_method_using_interfaces (GIObjectInfo *info, + const gchar *name, + GIObjectInfo **implementor); + + +GI_AVAILABLE_IN_ALL +gint g_object_info_get_n_signals (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GISignalInfo * g_object_info_get_signal (GIObjectInfo *info, + gint n); + + +GI_AVAILABLE_IN_ALL +GISignalInfo * g_object_info_find_signal (GIObjectInfo *info, + const gchar *name); + + +GI_AVAILABLE_IN_ALL +gint g_object_info_get_n_vfuncs (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIVFuncInfo * g_object_info_get_vfunc (GIObjectInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GIVFuncInfo * g_object_info_find_vfunc (GIObjectInfo *info, + const gchar *name); + +GI_AVAILABLE_IN_ALL +GIVFuncInfo * g_object_info_find_vfunc_using_interfaces (GIObjectInfo *info, + const gchar *name, + GIObjectInfo **implementor); + +GI_AVAILABLE_IN_ALL +gint g_object_info_get_n_constants (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIConstantInfo * g_object_info_get_constant (GIObjectInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GIStructInfo * g_object_info_get_class_struct (GIObjectInfo *info); + + +GI_AVAILABLE_IN_ALL +const char * g_object_info_get_ref_function (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIObjectInfoRefFunction g_object_info_get_ref_function_pointer (GIObjectInfo *info); + + +GI_AVAILABLE_IN_ALL +const char * g_object_info_get_unref_function (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIObjectInfoUnrefFunction g_object_info_get_unref_function_pointer (GIObjectInfo *info); + + +GI_AVAILABLE_IN_ALL +const char * g_object_info_get_set_value_function (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIObjectInfoSetValueFunction g_object_info_get_set_value_function_pointer (GIObjectInfo *info); + + +GI_AVAILABLE_IN_ALL +const char * g_object_info_get_get_value_function (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIObjectInfoGetValueFunction g_object_info_get_get_value_function_pointer (GIObjectInfo *info); + + +G_END_DECLS diff --git a/girepository/gipropertyinfo.c b/girepository/gipropertyinfo.c new file mode 100644 index 000000000..8436ec437 --- /dev/null +++ b/girepository/gipropertyinfo.c @@ -0,0 +1,209 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Property implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" +#include "gipropertyinfo.h" + +/** + * SECTION:gipropertyinfo + * @title: GIPropertyInfo + * @short_description: Struct representing a property + * + * GIPropertyInfo represents a property in a #GObject. + * + * A property belongs to either a #GIObjectInfo or a #GIInterfaceInfo. + */ + +/** + * g_property_info_get_flags: + * @info: a #GIPropertyInfo + * + * Obtain the flags for this property info. See #GParamFlags for + * more information about possible flag values. + * + * Returns: the flags + */ +GParamFlags +g_property_info_get_flags (GIPropertyInfo *info) +{ + GParamFlags flags; + GIRealInfo *rinfo = (GIRealInfo *)info; + PropertyBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_PROPERTY_INFO (info), 0); + + blob = (PropertyBlob *)&rinfo->typelib->data[rinfo->offset]; + + flags = 0; + + if (blob->readable) + flags = flags | G_PARAM_READABLE; + + if (blob->writable) + flags = flags | G_PARAM_WRITABLE; + + if (blob->construct) + flags = flags | G_PARAM_CONSTRUCT; + + if (blob->construct_only) + flags = flags | G_PARAM_CONSTRUCT_ONLY; + + return flags; +} + +/** + * g_property_info_get_type: + * @info: a #GIPropertyInfo + * + * Obtain the type information for the property @info. + * + * Returns: (transfer full): the #GITypeInfo, free it with + * g_base_info_unref() when done. + */ +GITypeInfo * +g_property_info_get_type (GIPropertyInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_PROPERTY_INFO (info), NULL); + + return _g_type_info_new ((GIBaseInfo*)info, + rinfo->typelib, + rinfo->offset + G_STRUCT_OFFSET (PropertyBlob, type)); +} + +/** + * g_property_info_get_ownership_transfer: + * @info: a #GIPropertyInfo + * + * Obtain the ownership transfer for this property. See #GITransfer for more + * information about transfer values. + * + * Returns: the transfer + */ +GITransfer +g_property_info_get_ownership_transfer (GIPropertyInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + PropertyBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_PROPERTY_INFO (info), -1); + + blob = (PropertyBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->transfer_ownership) + return GI_TRANSFER_EVERYTHING; + else if (blob->transfer_container_ownership) + return GI_TRANSFER_CONTAINER; + else + return GI_TRANSFER_NOTHING; +} + +/** + * g_property_info_get_setter: + * @info: a #GIPropertyInfo + * + * Obtains the setter function associated with this #GIPropertyInfo. + * + * The setter is only available for %G_PARAM_WRITABLE properties that + * are also not %G_PARAM_CONSTRUCT_ONLY. + * + * Returns: (transfer full) (nullable): the function info or %NULL if not set. + * Free it with g_base_info_unref() when done. + */ +GIFunctionInfo * +g_property_info_get_setter (GIPropertyInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + PropertyBlob *blob; + GIBaseInfo *container; + GIInfoType parent_type; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_PROPERTY_INFO (info), NULL); + + blob = (PropertyBlob *)&rinfo->typelib->data[rinfo->offset]; + if (!blob->writable || blob->construct_only) + return NULL; + + if (blob->setter == ACCESSOR_SENTINEL) + return NULL; + + container = rinfo->container; + parent_type = g_base_info_get_type (container); + if (parent_type == GI_INFO_TYPE_OBJECT) + return g_object_info_get_method ((GIObjectInfo *) container, blob->setter); + else if (parent_type == GI_INFO_TYPE_INTERFACE) + return g_interface_info_get_method ((GIInterfaceInfo *) container, blob->setter); + else + return NULL; +} + +/** + * g_property_info_get_getter: + * @info: a #GIPropertyInfo + * + * Obtains the getter function associated with this #GIPropertyInfo. + * + * The setter is only available for %G_PARAM_READABLE properties. + * + * Returns: (transfer full) (nullable): the function info or %NULL if not set. + * Free it with g_base_info_unref() when done. + */ +GIFunctionInfo * +g_property_info_get_getter (GIPropertyInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + PropertyBlob *blob; + GIBaseInfo *container; + GIInfoType parent_type; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_PROPERTY_INFO (info), NULL); + + blob = (PropertyBlob *)&rinfo->typelib->data[rinfo->offset]; + if (!blob->readable) + return NULL; + + if (blob->getter == ACCESSOR_SENTINEL) + return NULL; + + container = rinfo->container; + parent_type = g_base_info_get_type (container); + if (parent_type == GI_INFO_TYPE_OBJECT) + return g_object_info_get_method ((GIObjectInfo *) container, blob->getter); + else if (parent_type == GI_INFO_TYPE_INTERFACE) + return g_interface_info_get_method ((GIInterfaceInfo *) container, blob->getter); + else + return NULL; +} diff --git a/girepository/gipropertyinfo.h b/girepository/gipropertyinfo.h new file mode 100644 index 000000000..a20f2918b --- /dev/null +++ b/girepository/gipropertyinfo.h @@ -0,0 +1,60 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Property + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_PROPERTY_INFO + * @info: an info structure + * + * Checks if @info is a #GIPropertyInfo. + */ +#define GI_IS_PROPERTY_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_PROPERTY) + + +GI_AVAILABLE_IN_ALL +GParamFlags g_property_info_get_flags (GIPropertyInfo *info); + +GI_AVAILABLE_IN_ALL +GITypeInfo * g_property_info_get_type (GIPropertyInfo *info); + +GI_AVAILABLE_IN_ALL +GITransfer g_property_info_get_ownership_transfer (GIPropertyInfo *info); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo *g_property_info_get_setter (GIPropertyInfo *info); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo *g_property_info_get_getter (GIPropertyInfo *info); + +G_END_DECLS diff --git a/girepository/giregisteredtypeinfo.c b/girepository/giregisteredtypeinfo.c new file mode 100644 index 000000000..703bf072a --- /dev/null +++ b/girepository/giregisteredtypeinfo.c @@ -0,0 +1,146 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Registered Type implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" +#include "giregisteredtypeinfo.h" + +/** + * SECTION:giregisteredtypeinfo + * @title: GIRegisteredTypeInfo + * @short_description: Struct representing a struct with a GType + * + * GIRegisteredTypeInfo represents an entity with a GType associated. + * + * Could be either a #GIEnumInfo, #GIInterfaceInfo, #GIObjectInfo, + * #GIStructInfo or a #GIUnionInfo. + * + * A registered type info struct has a name and a type function. + * + * To get the name call g_registered_type_info_get_type_name(). + * Most users want to call g_registered_type_info_get_g_type() and don't worry + * about the rest of the details. + */ + +/** + * g_registered_type_info_get_type_name: + * @info: a #GIRegisteredTypeInfo + * + * Obtain the type name of the struct within the GObject type system. + * This type can be passed to g_type_name() to get a #GType. + * + * Returns: the type name + */ +const gchar * +g_registered_type_info_get_type_name (GIRegisteredTypeInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + RegisteredTypeBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_REGISTERED_TYPE_INFO (info), NULL); + + blob = (RegisteredTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->gtype_name) + return g_typelib_get_string (rinfo->typelib, blob->gtype_name); + + return NULL; +} + +/** + * g_registered_type_info_get_type_init: + * @info: a #GIRegisteredTypeInfo + * + * Obtain the type init function for @info. The type init function is the + * function which will register the GType within the GObject type system. + * Usually this is not called by langauge bindings or applications, use + * g_registered_type_info_get_g_type() directly instead. + * + * Returns: the symbol name of the type init function, suitable for + * passing into g_module_symbol(). + */ +const gchar * +g_registered_type_info_get_type_init (GIRegisteredTypeInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + RegisteredTypeBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_REGISTERED_TYPE_INFO (info), NULL); + + blob = (RegisteredTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->gtype_init) + return g_typelib_get_string (rinfo->typelib, blob->gtype_init); + + return NULL; +} + +/** + * g_registered_type_info_get_g_type: + * @info: a #GIRegisteredTypeInfo + * + * Obtain the #GType for this registered type or G_TYPE_NONE which a special meaning. + * It means that either there is no type information associated with this @info or + * that the shared library which provides the type_init function for this + * @info cannot be called. + * + * Returns: the #GType. + */ +GType +g_registered_type_info_get_g_type (GIRegisteredTypeInfo *info) +{ + const char *type_init; + GType (* get_type_func) (void); + GIRealInfo *rinfo = (GIRealInfo*)info; + + g_return_val_if_fail (info != NULL, G_TYPE_INVALID); + g_return_val_if_fail (GI_IS_REGISTERED_TYPE_INFO (info), G_TYPE_INVALID); + + type_init = g_registered_type_info_get_type_init (info); + + if (type_init == NULL) + return G_TYPE_NONE; + else if (!strcmp (type_init, "intern")) + /* The special string "intern" is used for some types exposed by libgobject + (that therefore should be always available) */ + return g_type_from_name (g_registered_type_info_get_type_name (info)); + + get_type_func = NULL; + if (!g_typelib_symbol (rinfo->typelib, + type_init, + (void**) &get_type_func)) + return G_TYPE_NONE; + + return (* get_type_func) (); +} + diff --git a/girepository/giregisteredtypeinfo.h b/girepository/giregisteredtypeinfo.h new file mode 100644 index 000000000..8dceb6dab --- /dev/null +++ b/girepository/giregisteredtypeinfo.h @@ -0,0 +1,61 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Registered Type + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +/** + * GI_IS_REGISTERED_TYPE_INFO + * @info: an info structure + * + * Checks if @info is a #GIRegisteredTypeInfo or derived from it. + */ +#define GI_IS_REGISTERED_TYPE_INFO(info) \ + ((g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_BOXED) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_ENUM) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_FLAGS) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_INTERFACE) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_OBJECT) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_STRUCT) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_UNION) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_BOXED)) + +GI_AVAILABLE_IN_ALL +const gchar * g_registered_type_info_get_type_name (GIRegisteredTypeInfo *info); + +GI_AVAILABLE_IN_ALL +const gchar * g_registered_type_info_get_type_init (GIRegisteredTypeInfo *info); + +GI_AVAILABLE_IN_ALL +GType g_registered_type_info_get_g_type (GIRegisteredTypeInfo *info); + +G_END_DECLS diff --git a/girepository/girepository-private.h b/girepository/girepository-private.h new file mode 100644 index 000000000..bc39b9640 --- /dev/null +++ b/girepository/girepository-private.h @@ -0,0 +1,115 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Private headers + * + * Copyright (C) 2010 Johan Dahlin + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#include +#include + +#define __GIREPOSITORY_H_INSIDE__ + +#include +#include +#include + +typedef struct _GIRealInfo GIRealInfo; + +/* We changed a gint32 -> gint in the structure below, which should be + * valid everywhere we care about. + */ +G_STATIC_ASSERT (sizeof (int) == sizeof (gint32)); + +/* + * We just use one structure for all of the info object + * types; in general, we should be reading data directly + * from the typelib, and not having computed data in + * per-type structures. + */ +struct _GIRealInfo +{ + /* Keep this part in sync with GIUnresolvedInfo below */ + gint32 type; + volatile gint ref_count; + GIRepository *repository; + GIBaseInfo *container; + + /* Resolved specific */ + + GITypelib *typelib; + guint32 offset; + + guint32 type_is_embedded : 1; /* Used by GITypeInfo */ + guint32 reserved : 31; + + gpointer reserved2[4]; +}; + +struct _GIUnresolvedInfo +{ + /* Keep this part in sync with GIBaseInfo above */ + gint32 type; + volatile gint ref_count; + GIRepository *repository; + GIBaseInfo *container; + + /* Unresolved specific */ + + const gchar *name; + const gchar *namespace; +}; + +void _g_info_init (GIRealInfo *info, + GIInfoType type, + GIRepository *repository, + GIBaseInfo *container, + GITypelib *typelib, + guint32 offset); + +GIBaseInfo * _g_info_from_entry (GIRepository *repository, + GITypelib *typelib, + guint16 index); + +GIBaseInfo * _g_info_new_full (GIInfoType type, + GIRepository *repository, + GIBaseInfo *container, + GITypelib *typelib, + guint32 offset); + +GITypeInfo * _g_type_info_new (GIBaseInfo *container, + GITypelib *typelib, + guint32 offset); + +void _g_type_info_init (GIBaseInfo *info, + GIBaseInfo *container, + GITypelib *typelib, + guint32 offset); + +GIFunctionInfo * _g_base_info_find_method (GIBaseInfo *base, + guint32 offset, + gint n_methods, + const gchar *name); + +GIVFuncInfo * _g_base_info_find_vfunc (GIRealInfo *rinfo, + guint32 offset, + gint n_vfuncs, + const gchar *name); diff --git a/girepository/girepository.c b/girepository/girepository.c new file mode 100644 index 000000000..b83410dee --- /dev/null +++ b/girepository/girepository.c @@ -0,0 +1,1882 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Repository implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008 Colin Walters + * Copyright (C) 2008 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include +#include +#include + +#include +#include +#include +#include "girepository.h" +#include "gitypelib-internal.h" +#include "girepository-private.h" + +/** + * SECTION:girepository + * @short_description: GObject Introspection repository manager + * @include: girepository.h + * + * #GIRepository is used to manage repositories of namespaces. Namespaces + * are represented on disk by type libraries (.typelib files). + * + * ### Discovery of type libraries + * + * #GIRepository will typically look for a `girepository-1.0` directory + * under the library directory used when compiling gobject-introspection. + * + * It is possible to control the search paths programmatically, using + * g_irepository_prepend_search_path(). It is also possible to modify + * the search paths by using the `GI_TYPELIB_PATH` environment variable. + * The environment variable takes precedence over the default search path + * and the g_irepository_prepend_search_path() calls. + */ + + +static GIRepository *default_repository = NULL; +static GSList *typelib_search_path = NULL; + +typedef struct { + guint n_interfaces; + GIBaseInfo *interfaces[]; +} GTypeInterfaceCache; + +static void +gtype_interface_cache_free (gpointer data) +{ + GTypeInterfaceCache *cache = data; + guint i; + + for (i = 0; i < cache->n_interfaces; i++) + g_base_info_unref ((GIBaseInfo*) cache->interfaces[i]); + g_free (cache); +} + +struct _GIRepositoryPrivate +{ + GHashTable *typelibs; /* (string) namespace -> GITypelib */ + GHashTable *lazy_typelibs; /* (string) namespace-version -> GITypelib */ + GHashTable *info_by_gtype; /* GType -> GIBaseInfo */ + GHashTable *info_by_error_domain; /* GQuark -> GIBaseInfo */ + GHashTable *interfaces_for_gtype; /* GType -> GTypeInterfaceCache */ + GHashTable *unknown_gtypes; /* hashset of GType */ +}; + +G_DEFINE_TYPE_WITH_CODE (GIRepository, g_irepository, G_TYPE_OBJECT, G_ADD_PRIVATE (GIRepository)); + +#ifdef G_PLATFORM_WIN32 + +#include + +static HMODULE girepository_dll = NULL; + +#ifdef DLL_EXPORT + +BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved); + +BOOL WINAPI +DllMain (HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved) +{ + if (fdwReason == DLL_PROCESS_ATTACH) + girepository_dll = hinstDLL; + + return TRUE; +} + +#endif + +#undef GOBJECT_INTROSPECTION_LIBDIR + +/* GOBJECT_INTROSPECTION_LIBDIR is used only in code called just once, + * so no problem leaking this + */ +#define GOBJECT_INTROSPECTION_LIBDIR \ + g_build_filename (g_win32_get_package_installation_directory_of_module (girepository_dll), \ + "lib", \ + NULL) + +#endif + +static void +g_irepository_init (GIRepository *repository) +{ + repository->priv = g_irepository_get_instance_private (repository); + repository->priv->typelibs + = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) NULL, + (GDestroyNotify) g_typelib_free); + repository->priv->lazy_typelibs + = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) NULL); + repository->priv->info_by_gtype + = g_hash_table_new_full (g_direct_hash, g_direct_equal, + (GDestroyNotify) NULL, + (GDestroyNotify) g_base_info_unref); + repository->priv->info_by_error_domain + = g_hash_table_new_full (g_direct_hash, g_direct_equal, + (GDestroyNotify) NULL, + (GDestroyNotify) g_base_info_unref); + repository->priv->interfaces_for_gtype + = g_hash_table_new_full (g_direct_hash, g_direct_equal, + (GDestroyNotify) NULL, + (GDestroyNotify) gtype_interface_cache_free); + repository->priv->unknown_gtypes = g_hash_table_new (NULL, NULL); +} + +static void +g_irepository_finalize (GObject *object) +{ + GIRepository *repository = G_IREPOSITORY (object); + + g_hash_table_destroy (repository->priv->typelibs); + g_hash_table_destroy (repository->priv->lazy_typelibs); + g_hash_table_destroy (repository->priv->info_by_gtype); + g_hash_table_destroy (repository->priv->info_by_error_domain); + g_hash_table_destroy (repository->priv->interfaces_for_gtype); + g_hash_table_destroy (repository->priv->unknown_gtypes); + + (* G_OBJECT_CLASS (g_irepository_parent_class)->finalize) (G_OBJECT (repository)); +} + +static void +g_irepository_class_init (GIRepositoryClass *class) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (class); + + gobject_class->finalize = g_irepository_finalize; +} + +static void +init_globals (void) +{ + static gsize initialized = 0; + + if (!g_once_init_enter (&initialized)) + return; + + if (default_repository == NULL) + default_repository = g_object_new (G_TYPE_IREPOSITORY, NULL); + + if (typelib_search_path == NULL) + { + const char *libdir; + char *typelib_dir; + const gchar *type_lib_path_env; + + /* This variable is intended to take precedence over both: + * - the default search path; + * - all g_irepository_prepend_search_path() calls. + */ + type_lib_path_env = g_getenv ("GI_TYPELIB_PATH"); + + typelib_search_path = NULL; + if (type_lib_path_env) + { + gchar **custom_dirs; + gchar **d; + + custom_dirs = g_strsplit (type_lib_path_env, G_SEARCHPATH_SEPARATOR_S, 0); + + d = custom_dirs; + while (*d) + { + typelib_search_path = g_slist_prepend (typelib_search_path, *d); + d++; + } + + /* ownership of the array content was passed to the list */ + g_free (custom_dirs); + } + + libdir = GOBJECT_INTROSPECTION_LIBDIR; + + typelib_dir = g_build_filename (libdir, "girepository-1.0", NULL); + + typelib_search_path = g_slist_prepend (typelib_search_path, typelib_dir); + + typelib_search_path = g_slist_reverse (typelib_search_path); + } + + g_once_init_leave (&initialized, 1); +} + +/** + * g_irepository_prepend_search_path: + * @directory: (type filename): directory name to prepend to the typelib + * search path + * + * Prepends @directory to the typelib search path. + * + * See also: g_irepository_get_search_path(). + */ +void +g_irepository_prepend_search_path (const char *directory) +{ + init_globals (); + typelib_search_path = g_slist_prepend (typelib_search_path, g_strdup (directory)); +} + +/** + * g_irepository_get_search_path: + * + * Returns the current search path #GIRepository will use when loading + * typelib files. The list is internal to #GIRepository and should not + * be freed, nor should its string elements. + * + * Returns: (element-type filename) (transfer none): #GSList of strings + */ +GSList * +g_irepository_get_search_path (void) +{ + return typelib_search_path; +} + +static char * +build_typelib_key (const char *name, const char *source) +{ + GString *str = g_string_new (name); + g_string_append_c (str, '\0'); + g_string_append (str, source); + return g_string_free (str, FALSE); +} + +/* Note: Returns %NULL (not an empty %NULL-terminated array) if there are no + * dependencies. */ +static char ** +get_typelib_dependencies (GITypelib *typelib) +{ + Header *header; + const char *dependencies_glob; + + header = (Header *)typelib->data; + + if (header->dependencies == 0) + return NULL; + + dependencies_glob = g_typelib_get_string (typelib, header->dependencies); + return g_strsplit (dependencies_glob, "|", 0); +} + +static GIRepository * +get_repository (GIRepository *repository) +{ + init_globals (); + + if (repository != NULL) + return repository; + else + return default_repository; +} + +static GITypelib * +check_version_conflict (GITypelib *typelib, + const gchar *namespace, + const gchar *expected_version, + char **version_conflict) +{ + Header *header; + const char *loaded_version; + + if (expected_version == NULL) + { + if (version_conflict) + *version_conflict = NULL; + return typelib; + } + + header = (Header*)typelib->data; + loaded_version = g_typelib_get_string (typelib, header->nsversion); + g_assert (loaded_version != NULL); + + if (strcmp (expected_version, loaded_version) != 0) + { + if (version_conflict) + *version_conflict = (char*)loaded_version; + return NULL; + } + if (version_conflict) + *version_conflict = NULL; + return typelib; +} + +static GITypelib * +get_registered_status (GIRepository *repository, + const char *namespace, + const char *version, + gboolean allow_lazy, + gboolean *lazy_status, + char **version_conflict) +{ + GITypelib *typelib; + repository = get_repository (repository); + if (lazy_status) + *lazy_status = FALSE; + typelib = g_hash_table_lookup (repository->priv->typelibs, namespace); + if (typelib) + return check_version_conflict (typelib, namespace, version, version_conflict); + typelib = g_hash_table_lookup (repository->priv->lazy_typelibs, namespace); + if (!typelib) + return NULL; + if (lazy_status) + *lazy_status = TRUE; + if (!allow_lazy) + return NULL; + return check_version_conflict (typelib, namespace, version, version_conflict); +} + +static GITypelib * +get_registered (GIRepository *repository, + const char *namespace, + const char *version) +{ + return get_registered_status (repository, namespace, version, TRUE, NULL, NULL); +} + +static gboolean +load_dependencies_recurse (GIRepository *repository, + GITypelib *typelib, + GError **error) +{ + char **dependencies; + + dependencies = get_typelib_dependencies (typelib); + + if (dependencies != NULL) + { + int i; + + for (i = 0; dependencies[i]; i++) + { + char *dependency = dependencies[i]; + const char *last_dash; + char *dependency_namespace; + const char *dependency_version; + + last_dash = strrchr (dependency, '-'); + dependency_namespace = g_strndup (dependency, last_dash - dependency); + dependency_version = last_dash+1; + + if (!g_irepository_require (repository, dependency_namespace, dependency_version, + 0, error)) + { + g_free (dependency_namespace); + g_strfreev (dependencies); + return FALSE; + } + g_free (dependency_namespace); + } + g_strfreev (dependencies); + } + return TRUE; +} + +static const char * +register_internal (GIRepository *repository, + const char *source, + gboolean lazy, + GITypelib *typelib, + GError **error) +{ + Header *header; + const gchar *namespace; + + g_return_val_if_fail (typelib != NULL, FALSE); + + header = (Header *)typelib->data; + + g_return_val_if_fail (header != NULL, FALSE); + + namespace = g_typelib_get_string (typelib, header->namespace); + + if (lazy) + { + g_assert (!g_hash_table_lookup (repository->priv->lazy_typelibs, + namespace)); + g_hash_table_insert (repository->priv->lazy_typelibs, + build_typelib_key (namespace, source), (void *)typelib); + } + else + { + gpointer value; + char *key; + + /* First, try loading all the dependencies */ + if (!load_dependencies_recurse (repository, typelib, error)) + return NULL; + + /* Check if we are transitioning from lazily loaded state */ + if (g_hash_table_lookup_extended (repository->priv->lazy_typelibs, + namespace, + (gpointer)&key, &value)) + g_hash_table_remove (repository->priv->lazy_typelibs, key); + else + key = build_typelib_key (namespace, source); + + g_hash_table_insert (repository->priv->typelibs, key, (void *)typelib); + } + + /* These types might be resolved now, clear the cache */ + g_hash_table_remove_all (repository->priv->unknown_gtypes); + + return namespace; +} + +/** + * g_irepository_get_immediate_dependencies: + * @repository: (nullable): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: Namespace of interest + * + * Return an array of the immediate versioned dependencies for @namespace_. + * Returned strings are of the form `namespace-version`. + * + * Note: @namespace_ must have already been loaded using a function + * such as g_irepository_require() before calling this function. + * + * To get the transitive closure of dependencies for @namespace_, use + * g_irepository_get_dependencies(). + * + * Returns: (transfer full): Zero-terminated string array of immediate versioned + * dependencies + * + * Since: 1.44 + */ +char ** +g_irepository_get_immediate_dependencies (GIRepository *repository, + const char *namespace) +{ + GITypelib *typelib; + gchar **deps; + + g_return_val_if_fail (namespace != NULL, NULL); + + repository = get_repository (repository); + + typelib = get_registered (repository, namespace, NULL); + g_return_val_if_fail (typelib != NULL, NULL); + + /* Ensure we always return a non-%NULL vector. */ + deps = get_typelib_dependencies (typelib); + if (deps == NULL) + deps = g_strsplit ("", "|", 0); + + return deps; +} + +/* Load the transitive closure of dependency namespace-version strings for the + * given @typelib. @repository must be non-%NULL. @transitive_dependencies must + * be a pre-existing GHashTable set for storing the + * dependencies. */ +static void +get_typelib_dependencies_transitive (GIRepository *repository, + GITypelib *typelib, + GHashTable *transitive_dependencies) +{ + gchar **immediate_dependencies; + guint i; + + immediate_dependencies = get_typelib_dependencies (typelib); + + for (i = 0; immediate_dependencies != NULL && immediate_dependencies[i]; i++) + { + gchar *dependency; + const gchar *last_dash; + gchar *dependency_namespace; + + dependency = immediate_dependencies[i]; + + /* Steal from the strv. */ + g_hash_table_add (transitive_dependencies, dependency); + immediate_dependencies[i] = NULL; + + /* Recurse for this namespace. */ + last_dash = strrchr (dependency, '-'); + dependency_namespace = g_strndup (dependency, last_dash - dependency); + + typelib = get_registered (repository, dependency_namespace, NULL); + g_return_if_fail (typelib != NULL); + get_typelib_dependencies_transitive (repository, typelib, + transitive_dependencies); + + g_free (dependency_namespace); + } + + g_free (immediate_dependencies); +} + +/** + * g_irepository_get_dependencies: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: Namespace of interest + * + * Retrieves all (transitive) versioned dependencies for + * @namespace_. + * + * The strings are of the form `namespace-version`. + * + * Note: @namespace_ must have already been loaded using a function + * such as g_irepository_require() before calling this function. + * + * To get only the immediate dependencies for @namespace_, use + * g_irepository_get_immediate_dependencies(). + * + * Returns: (transfer full) (array zero-terminated=1): all versioned + * dependencies + */ +char ** +g_irepository_get_dependencies (GIRepository *repository, + const char *namespace) +{ + GITypelib *typelib; + GHashTable *transitive_dependencies; /* set of owned utf8 */ + GHashTableIter iter; + gchar *dependency; + GPtrArray *out; /* owned utf8 elements */ + + g_return_val_if_fail (namespace != NULL, NULL); + + repository = get_repository (repository); + + typelib = get_registered (repository, namespace, NULL); + g_return_val_if_fail (typelib != NULL, NULL); + + /* Load the dependencies. */ + transitive_dependencies = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + get_typelib_dependencies_transitive (repository, typelib, + transitive_dependencies); + + /* Convert to a string array. */ + out = g_ptr_array_new_full (g_hash_table_size (transitive_dependencies), + g_free); + g_hash_table_iter_init (&iter, transitive_dependencies); + + while (g_hash_table_iter_next (&iter, (gpointer) &dependency, NULL)) + { + g_ptr_array_add (out, dependency); + g_hash_table_iter_steal (&iter); + } + + g_hash_table_unref (transitive_dependencies); + + /* Add a NULL terminator. */ + g_ptr_array_add (out, NULL); + + return (gchar **) g_ptr_array_free (out, FALSE); +} + +/** + * g_irepository_load_typelib: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @typelib: TODO + * @flags: TODO + * @error: TODO + * + * TODO + */ +const char * +g_irepository_load_typelib (GIRepository *repository, + GITypelib *typelib, + GIRepositoryLoadFlags flags, + GError **error) +{ + Header *header; + const char *namespace; + const char *nsversion; + gboolean allow_lazy = flags & G_IREPOSITORY_LOAD_FLAG_LAZY; + gboolean is_lazy; + char *version_conflict; + + repository = get_repository (repository); + + header = (Header *) typelib->data; + namespace = g_typelib_get_string (typelib, header->namespace); + nsversion = g_typelib_get_string (typelib, header->nsversion); + + if (get_registered_status (repository, namespace, nsversion, allow_lazy, + &is_lazy, &version_conflict)) + { + if (version_conflict != NULL) + { + g_set_error (error, G_IREPOSITORY_ERROR, + G_IREPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT, + "Attempting to load namespace '%s', version '%s', but '%s' is already loaded", + namespace, nsversion, version_conflict); + return NULL; + } + return namespace; + } + return register_internal (repository, "", + allow_lazy, typelib, error); +} + +/** + * g_irepository_is_registered: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: Namespace of interest + * @version: (allow-none): Required version, may be %NULL for latest + * + * Check whether a particular namespace (and optionally, a specific + * version thereof) is currently loaded. This function is likely to + * only be useful in unusual circumstances; in order to act upon + * metadata in the namespace, you should call g_irepository_require() + * instead which will ensure the namespace is loaded, and return as + * quickly as this function will if it has already been loaded. + * + * Returns: %TRUE if namespace-version is loaded, %FALSE otherwise + */ +gboolean +g_irepository_is_registered (GIRepository *repository, + const gchar *namespace, + const gchar *version) +{ + repository = get_repository (repository); + return get_registered (repository, namespace, version) != NULL; +} + +/** + * g_irepository_get_default: + * + * Returns the singleton process-global default #GIRepository. It is + * not currently supported to have multiple repositories in a + * particular process, but this function is provided in the unlikely + * eventuality that it would become possible, and as a convenience for + * higher level language bindings to conform to the GObject method + * call conventions. + * + * All methods on #GIRepository also accept %NULL as an instance + * parameter to mean this default repository, which is usually more + * convenient for C. + * + * Returns: (transfer none): The global singleton #GIRepository + */ +GIRepository * +g_irepository_get_default (void) +{ + return get_repository (NULL); +} + +/** + * g_irepository_get_n_infos: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: Namespace to inspect + * + * This function returns the number of metadata entries in + * given namespace @namespace_. The namespace must have + * already been loaded before calling this function. + * + * Returns: number of metadata entries + */ +gint +g_irepository_get_n_infos (GIRepository *repository, + const gchar *namespace) +{ + GITypelib *typelib; + gint n_interfaces = 0; + + g_return_val_if_fail (namespace != NULL, -1); + + repository = get_repository (repository); + + typelib = get_registered (repository, namespace, NULL); + + g_return_val_if_fail (typelib != NULL, -1); + + n_interfaces = ((Header *)typelib->data)->n_local_entries; + + return n_interfaces; +} + +/** + * g_irepository_get_info: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: Namespace to inspect + * @index: 0-based offset into namespace metadata for entry + * + * This function returns a particular metadata entry in the + * given namespace @namespace_. The namespace must have + * already been loaded before calling this function. + * See g_irepository_get_n_infos() to find the maximum number of + * entries. + * + * Returns: (transfer full): #GIBaseInfo containing metadata + */ +GIBaseInfo * +g_irepository_get_info (GIRepository *repository, + const gchar *namespace, + gint index) +{ + GITypelib *typelib; + DirEntry *entry; + + g_return_val_if_fail (namespace != NULL, NULL); + + repository = get_repository (repository); + + typelib = get_registered (repository, namespace, NULL); + + g_return_val_if_fail (typelib != NULL, NULL); + + entry = g_typelib_get_dir_entry (typelib, index + 1); + if (entry == NULL) + return NULL; + return _g_info_new_full (entry->blob_type, + repository, + NULL, typelib, entry->offset); +} + +typedef struct { + const gchar *gtype_name; + GITypelib *result_typelib; +} FindByGTypeData; + +static DirEntry * +find_by_gtype (GHashTable *table, FindByGTypeData *data, gboolean check_prefix) +{ + GHashTableIter iter; + gpointer key, value; + DirEntry *ret; + + g_hash_table_iter_init (&iter, table); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + GITypelib *typelib = (GITypelib*)value; + if (check_prefix) + { + if (!g_typelib_matches_gtype_name_prefix (typelib, data->gtype_name)) + continue; + } + + ret = g_typelib_get_dir_entry_by_gtype_name (typelib, data->gtype_name); + if (ret) + { + data->result_typelib = typelib; + return ret; + } + } + + return NULL; +} + +/** + * g_irepository_find_by_gtype: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @gtype: GType to search for + * + * Searches all loaded namespaces for a particular #GType. Note that + * in order to locate the metadata, the namespace corresponding to + * the type must first have been loaded. There is currently no + * mechanism for determining the namespace which corresponds to an + * arbitrary GType - thus, this function will operate most reliably + * when you know the GType to originate from be from a loaded namespace. + * + * Returns: (transfer full): #GIBaseInfo representing metadata about @type, or %NULL + */ +GIBaseInfo * +g_irepository_find_by_gtype (GIRepository *repository, + GType gtype) +{ + FindByGTypeData data; + GIBaseInfo *cached; + DirEntry *entry; + + g_return_val_if_fail (gtype != G_TYPE_INVALID, NULL); + + repository = get_repository (repository); + + cached = g_hash_table_lookup (repository->priv->info_by_gtype, + (gpointer)gtype); + + if (cached != NULL) + return g_base_info_ref (cached); + + if (g_hash_table_contains (repository->priv->unknown_gtypes, (gpointer)gtype)) + return NULL; + + data.gtype_name = g_type_name (gtype); + data.result_typelib = NULL; + + /* Inside each typelib, we include the "C prefix" which acts as + * a namespace mechanism. For GtkTreeView, the C prefix is Gtk. + * Given the assumption that GTypes for a library also use the + * C prefix, we know we can skip examining a typelib if our + * target type does not have this typelib's C prefix. Use this + * assumption as our first attempt at locating the DirEntry. + */ + entry = find_by_gtype (repository->priv->typelibs, &data, TRUE); + if (entry == NULL) + entry = find_by_gtype (repository->priv->lazy_typelibs, &data, TRUE); + + /* Not ever class library necessarily specifies a correct c_prefix, + * so take a second pass. This time we will try a global lookup, + * ignoring prefixes. + * See http://bugzilla.gnome.org/show_bug.cgi?id=564016 + */ + if (entry == NULL) + entry = find_by_gtype (repository->priv->typelibs, &data, FALSE); + if (entry == NULL) + entry = find_by_gtype (repository->priv->lazy_typelibs, &data, FALSE); + + if (entry != NULL) + { + cached = _g_info_new_full (entry->blob_type, + repository, + NULL, data.result_typelib, entry->offset); + + g_hash_table_insert (repository->priv->info_by_gtype, + (gpointer) gtype, + g_base_info_ref (cached)); + return cached; + } + else + { + g_hash_table_add (repository->priv->unknown_gtypes, (gpointer) gtype); + return NULL; + } +} + +/** + * g_irepository_find_by_name: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: Namespace which will be searched + * @name: Entry name to find + * + * Searches for a particular entry in a namespace. Before calling + * this function for a particular namespace, you must call + * g_irepository_require() once to load the namespace, or otherwise + * ensure the namespace has already been loaded. + * + * Returns: (transfer full): #GIBaseInfo representing metadata about @name, or %NULL + */ +GIBaseInfo * +g_irepository_find_by_name (GIRepository *repository, + const gchar *namespace, + const gchar *name) +{ + GITypelib *typelib; + DirEntry *entry; + + g_return_val_if_fail (namespace != NULL, NULL); + + repository = get_repository (repository); + typelib = get_registered (repository, namespace, NULL); + g_return_val_if_fail (typelib != NULL, NULL); + + entry = g_typelib_get_dir_entry_by_name (typelib, name); + if (entry == NULL) + return NULL; + return _g_info_new_full (entry->blob_type, + repository, + NULL, typelib, entry->offset); +} + +typedef struct { + GIRepository *repository; + GQuark domain; + + GITypelib *result_typelib; + DirEntry *result; +} FindByErrorDomainData; + +static void +find_by_error_domain_foreach (gpointer key, + gpointer value, + gpointer datap) +{ + GITypelib *typelib = (GITypelib*)value; + FindByErrorDomainData *data = datap; + + if (data->result != NULL) + return; + + data->result = g_typelib_get_dir_entry_by_error_domain (typelib, data->domain); + if (data->result) + data->result_typelib = typelib; +} + +/** + * g_irepository_find_by_error_domain: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @domain: a #GError domain + * + * Searches for the enum type corresponding to the given #GError + * domain. Before calling this function for a particular namespace, + * you must call g_irepository_require() once to load the namespace, or + * otherwise ensure the namespace has already been loaded. + * + * Returns: (transfer full): #GIEnumInfo representing metadata about @domain's + * enum type, or %NULL + * Since: 1.30 + */ +GIEnumInfo * +g_irepository_find_by_error_domain (GIRepository *repository, + GQuark domain) +{ + FindByErrorDomainData data; + GIEnumInfo *cached; + + repository = get_repository (repository); + + cached = g_hash_table_lookup (repository->priv->info_by_error_domain, + GUINT_TO_POINTER (domain)); + + if (cached != NULL) + return g_base_info_ref ((GIBaseInfo *)cached); + + data.repository = repository; + data.domain = domain; + data.result_typelib = NULL; + data.result = NULL; + + g_hash_table_foreach (repository->priv->typelibs, find_by_error_domain_foreach, &data); + if (data.result == NULL) + g_hash_table_foreach (repository->priv->lazy_typelibs, find_by_error_domain_foreach, &data); + + if (data.result != NULL) + { + cached = _g_info_new_full (data.result->blob_type, + repository, + NULL, data.result_typelib, data.result->offset); + + g_hash_table_insert (repository->priv->info_by_error_domain, + GUINT_TO_POINTER (domain), + g_base_info_ref (cached)); + return cached; + } + return NULL; +} + +/** + * g_irepository_get_object_gtype_interfaces: + * @repository: (nullable): a #GIRepository, or %NULL for the default repository + * @gtype: a #GType whose fundamental type is G_TYPE_OBJECT + * @n_interfaces_out: (out): Number of interfaces + * @interfaces_out: (out) (transfer none) (array length=n_interfaces_out): Interfaces for @gtype + * + * Look up the implemented interfaces for @gtype. This function + * cannot fail per se; but for a totally "unknown" #GType, it may + * return 0 implemented interfaces. + * + * The semantics of this function are designed for a dynamic binding, + * where in certain cases (such as a function which returns an + * interface which may have "hidden" implementation classes), not all + * data may be statically known, and will have to be determined from + * the #GType of the object. An example is g_file_new_for_path() + * returning a concrete class of #GLocalFile, which is a #GType we + * see at runtime, but not statically. + * + * Since: 1.62 + */ +void +g_irepository_get_object_gtype_interfaces (GIRepository *repository, + GType gtype, + guint *n_interfaces_out, + GIInterfaceInfo ***interfaces_out) +{ + GTypeInterfaceCache *cache; + + g_return_if_fail (g_type_fundamental (gtype) == G_TYPE_OBJECT); + + repository = get_repository (repository); + + cache = g_hash_table_lookup (repository->priv->interfaces_for_gtype, + (gpointer) gtype); + if (cache == NULL) + { + GType *interfaces; + guint n_interfaces; + guint i; + GList *interface_infos = NULL, *iter; + + interfaces = g_type_interfaces (gtype, &n_interfaces); + for (i = 0; i < n_interfaces; i++) + { + GIBaseInfo *base_info; + + base_info = g_irepository_find_by_gtype (repository, interfaces[i]); + if (base_info == NULL) + continue; + + if (g_base_info_get_type (base_info) != GI_INFO_TYPE_INTERFACE) + { + /* FIXME - could this really happen? */ + g_base_info_unref (base_info); + continue; + } + + if (!g_list_find (interface_infos, base_info)) + interface_infos = g_list_prepend (interface_infos, base_info); + } + + cache = g_malloc (sizeof (GTypeInterfaceCache) + + sizeof (GIBaseInfo*) * g_list_length (interface_infos)); + cache->n_interfaces = g_list_length (interface_infos); + for (iter = interface_infos, i = 0; iter; iter = iter->next, i++) + cache->interfaces[i] = iter->data; + g_list_free (interface_infos); + + g_hash_table_insert (repository->priv->interfaces_for_gtype, (gpointer) gtype, + cache); + + g_free (interfaces); + } + + *n_interfaces_out = cache->n_interfaces; + *interfaces_out = (GIInterfaceInfo**)&cache->interfaces[0]; +} + +static void +collect_namespaces (gpointer key, + gpointer value, + gpointer data) +{ + GList **list = data; + + *list = g_list_append (*list, key); +} + +/** + * g_irepository_get_loaded_namespaces: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * + * Return the list of currently loaded namespaces. + * + * Returns: (element-type utf8) (transfer full): List of namespaces + */ +gchar ** +g_irepository_get_loaded_namespaces (GIRepository *repository) +{ + GList *l, *list = NULL; + gchar **names; + gint i; + + repository = get_repository (repository); + + g_hash_table_foreach (repository->priv->typelibs, collect_namespaces, &list); + g_hash_table_foreach (repository->priv->lazy_typelibs, collect_namespaces, &list); + + names = g_malloc0 (sizeof (gchar *) * (g_list_length (list) + 1)); + i = 0; + for (l = list; l; l = l->next) + names[i++] = g_strdup (l->data); + g_list_free (list); + + return names; +} + +/** + * g_irepository_get_version: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: Namespace to inspect + * + * This function returns the loaded version associated with the given + * namespace @namespace_. + * + * Note: The namespace must have already been loaded using a function + * such as g_irepository_require() before calling this function. + * + * Returns: Loaded version + */ +const gchar * +g_irepository_get_version (GIRepository *repository, + const gchar *namespace) +{ + GITypelib *typelib; + Header *header; + + g_return_val_if_fail (namespace != NULL, NULL); + + repository = get_repository (repository); + + typelib = get_registered (repository, namespace, NULL); + + g_return_val_if_fail (typelib != NULL, NULL); + + header = (Header *) typelib->data; + return g_typelib_get_string (typelib, header->nsversion); +} + +/** + * g_irepository_get_shared_library: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: Namespace to inspect + * + * This function returns a comma-separated list of paths to the + * shared C libraries associated with the given namespace @namespace_. + * There may be no shared library path associated, in which case this + * function will return %NULL. + * + * Note: The namespace must have already been loaded using a function + * such as g_irepository_require() before calling this function. + * + * Returns: (nullable): Comma-separated list of paths to shared libraries, + * or %NULL if none are associated + */ +const gchar * +g_irepository_get_shared_library (GIRepository *repository, + const gchar *namespace) +{ + GITypelib *typelib; + Header *header; + + g_return_val_if_fail (namespace != NULL, NULL); + + repository = get_repository (repository); + + typelib = get_registered (repository, namespace, NULL); + + g_return_val_if_fail (typelib != NULL, NULL); + + header = (Header *) typelib->data; + if (header->shared_library) + return g_typelib_get_string (typelib, header->shared_library); + else + return NULL; +} + +/** + * g_irepository_get_c_prefix: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: Namespace to inspect + * + * This function returns the "C prefix", or the C level namespace + * associated with the given introspection namespace. Each C symbol + * starts with this prefix, as well each #GType in the library. + * + * Note: The namespace must have already been loaded using a function + * such as g_irepository_require() before calling this function. + * + * Returns: C namespace prefix, or %NULL if none associated + */ +const gchar * +g_irepository_get_c_prefix (GIRepository *repository, + const gchar *namespace_) +{ + GITypelib *typelib; + Header *header; + + g_return_val_if_fail (namespace_ != NULL, NULL); + + repository = get_repository (repository); + + typelib = get_registered (repository, namespace_, NULL); + + g_return_val_if_fail (typelib != NULL, NULL); + + header = (Header *) typelib->data; + if (header->c_prefix) + return g_typelib_get_string (typelib, header->c_prefix); + else + return NULL; +} + +/** + * g_irepository_get_typelib_path: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: GI namespace to use, e.g. "Gtk" + * + * If namespace @namespace_ is loaded, return the full path to the + * .typelib file it was loaded from. If the typelib for + * namespace @namespace_ was included in a shared library, return + * the special string "<builtin>". + * + * Returns: Filesystem path (or $lt;builtin$gt;) if successful, %NULL if namespace is not loaded + */ + +const gchar * +g_irepository_get_typelib_path (GIRepository *repository, + const gchar *namespace) +{ + gpointer orig_key, value; + + repository = get_repository (repository); + + if (!g_hash_table_lookup_extended (repository->priv->typelibs, namespace, + &orig_key, &value)) + { + if (!g_hash_table_lookup_extended (repository->priv->lazy_typelibs, namespace, + &orig_key, &value)) + + return NULL; + } + return ((char*)orig_key) + strlen ((char *) orig_key) + 1; +} + +/* This simple search function looks for a specified namespace-version; + it's faster than the full directory listing required for latest version. */ +static GMappedFile * +find_namespace_version (const gchar *namespace, + const gchar *version, + GSList *search_path, + gchar **path_ret) +{ + GSList *ldir; + GError *error = NULL; + GMappedFile *mfile = NULL; + char *fname; + + fname = g_strdup_printf ("%s-%s.typelib", namespace, version); + + for (ldir = search_path; ldir; ldir = ldir->next) + { + char *path = g_build_filename (ldir->data, fname, NULL); + + mfile = g_mapped_file_new (path, FALSE, &error); + if (error) + { + g_free (path); + g_clear_error (&error); + continue; + } + *path_ret = path; + break; + } + g_free (fname); + return mfile; +} + +static gboolean +parse_version (const char *version, + int *major, + int *minor) +{ + const char *dot; + char *end; + + *major = strtol (version, &end, 10); + dot = strchr (version, '.'); + if (dot == NULL) + { + *minor = 0; + return TRUE; + } + if (dot != end) + return FALSE; + *minor = strtol (dot+1, &end, 10); + if (end != (version + strlen (version))) + return FALSE; + return TRUE; +} + +static int +compare_version (const char *v1, + const char *v2) +{ + gboolean success; + int v1_major, v1_minor; + int v2_major, v2_minor; + + success = parse_version (v1, &v1_major, &v1_minor); + g_assert (success); + + success = parse_version (v2, &v2_major, &v2_minor); + g_assert (success); + + if (v1_major > v2_major) + return 1; + else if (v2_major > v1_major) + return -1; + else if (v1_minor > v2_minor) + return 1; + else if (v2_minor > v1_minor) + return -1; + return 0; +} + +struct NamespaceVersionCandidadate +{ + GMappedFile *mfile; + int path_index; + char *path; + char *version; +}; + +static int +compare_candidate_reverse (struct NamespaceVersionCandidadate *c1, + struct NamespaceVersionCandidadate *c2) +{ + int result = compare_version (c1->version, c2->version); + /* First, check the version */ + if (result > 0) + return -1; + else if (result < 0) + return 1; + else + { + /* Now check the path index, which says how early in the search path + * we found it. This ensures that of equal version targets, we + * pick the earlier one. + */ + if (c1->path_index == c2->path_index) + return 0; + else if (c1->path_index > c2->path_index) + return 1; + else + return -1; + } +} + +static void +free_candidate (struct NamespaceVersionCandidadate *candidate) +{ + g_mapped_file_unref (candidate->mfile); + g_free (candidate->path); + g_free (candidate->version); + g_slice_free (struct NamespaceVersionCandidadate, candidate); +} + +static GSList * +enumerate_namespace_versions (const gchar *namespace, + GSList *search_path) +{ + GSList *candidates = NULL; + GHashTable *found_versions = g_hash_table_new (g_str_hash, g_str_equal); + char *namespace_dash; + char *namespace_typelib; + GSList *ldir; + GError *error = NULL; + int index; + + namespace_dash = g_strdup_printf ("%s-", namespace); + namespace_typelib = g_strdup_printf ("%s.typelib", namespace); + + index = 0; + for (ldir = search_path; ldir; ldir = ldir->next) + { + GDir *dir; + const char *dirname; + const char *entry; + + dirname = (const char*)ldir->data; + dir = g_dir_open (dirname, 0, NULL); + if (dir == NULL) + continue; + while ((entry = g_dir_read_name (dir)) != NULL) + { + GMappedFile *mfile; + char *path, *version; + struct NamespaceVersionCandidadate *candidate; + + if (!g_str_has_suffix (entry, ".typelib")) + continue; + + if (g_str_has_prefix (entry, namespace_dash)) + { + const char *last_dash; + const char *name_end; + int major, minor; + + name_end = strrchr (entry, '.'); + last_dash = strrchr (entry, '-'); + version = g_strndup (last_dash+1, name_end-(last_dash+1)); + if (!parse_version (version, &major, &minor)) + { + g_free (version); + continue; + } + } + else + continue; + + if (g_hash_table_lookup (found_versions, version) != NULL) + { + g_free (version); + continue; + } + + path = g_build_filename (dirname, entry, NULL); + mfile = g_mapped_file_new (path, FALSE, &error); + if (mfile == NULL) + { + g_free (path); + g_free (version); + g_clear_error (&error); + continue; + } + candidate = g_slice_new0 (struct NamespaceVersionCandidadate); + candidate->mfile = mfile; + candidate->path_index = index; + candidate->path = path; + candidate->version = version; + candidates = g_slist_prepend (candidates, candidate); + g_hash_table_add (found_versions, version); + } + g_dir_close (dir); + index++; + } + + g_free (namespace_dash); + g_free (namespace_typelib); + g_hash_table_destroy (found_versions); + + return candidates; +} + +static GMappedFile * +find_namespace_latest (const gchar *namespace, + GSList *search_path, + gchar **version_ret, + gchar **path_ret) +{ + GSList *candidates; + GMappedFile *result = NULL; + + *version_ret = NULL; + *path_ret = NULL; + + candidates = enumerate_namespace_versions (namespace, search_path); + + if (candidates != NULL) + { + struct NamespaceVersionCandidadate *elected; + candidates = g_slist_sort (candidates, (GCompareFunc) compare_candidate_reverse); + + elected = (struct NamespaceVersionCandidadate *) candidates->data; + /* Remove the elected one so we don't try to free its contents */ + candidates = g_slist_delete_link (candidates, candidates); + + result = elected->mfile; + *path_ret = elected->path; + *version_ret = elected->version; + g_slice_free (struct NamespaceVersionCandidadate, elected); /* just free the container */ + g_slist_foreach (candidates, (GFunc) (void *) free_candidate, NULL); + g_slist_free (candidates); + } + return result; +} + +/** + * g_irepository_enumerate_versions: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: GI namespace, e.g. "Gtk" + * + * Obtain an unordered list of versions (either currently loaded or + * available) for @namespace_ in this @repository. + * + * Returns: (element-type utf8) (transfer full): the array of versions. + */ +GList * +g_irepository_enumerate_versions (GIRepository *repository, + const gchar *namespace_) +{ + GList *ret = NULL; + GSList *candidates, *link; + const gchar *loaded_version; + + init_globals (); + candidates = enumerate_namespace_versions (namespace_, typelib_search_path); + + for (link = candidates; link; link = link->next) + { + struct NamespaceVersionCandidadate *candidate = link->data; + ret = g_list_prepend (ret, g_strdup (candidate->version)); + free_candidate (candidate); + } + g_slist_free (candidates); + + /* The currently loaded version of a namespace is also part of the + * available versions, as it could have been loaded using + * require_private(). + */ + if (g_irepository_is_registered (repository, namespace_, NULL)) + { + loaded_version = g_irepository_get_version (repository, namespace_); + if (loaded_version && !g_list_find_custom (ret, loaded_version, g_str_equal)) + ret = g_list_prepend (ret, g_strdup (loaded_version)); + } + + return ret; +} + +static GITypelib * +require_internal (GIRepository *repository, + const gchar *namespace, + const gchar *version, + GIRepositoryLoadFlags flags, + GSList *search_path, + GError **error) +{ + GMappedFile *mfile; + GITypelib *ret = NULL; + Header *header; + GITypelib *typelib = NULL; + const gchar *typelib_namespace, *typelib_version; + gboolean allow_lazy = (flags & G_IREPOSITORY_LOAD_FLAG_LAZY) > 0; + gboolean is_lazy; + char *version_conflict = NULL; + char *path = NULL; + char *tmp_version = NULL; + + g_return_val_if_fail (namespace != NULL, FALSE); + + repository = get_repository (repository); + + typelib = get_registered_status (repository, namespace, version, allow_lazy, + &is_lazy, &version_conflict); + if (typelib) + return typelib; + + if (version_conflict != NULL) + { + g_set_error (error, G_IREPOSITORY_ERROR, + G_IREPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT, + "Requiring namespace '%s' version '%s', but '%s' is already loaded", + namespace, version, version_conflict); + return NULL; + } + + if (version != NULL) + { + mfile = find_namespace_version (namespace, version, + search_path, &path); + tmp_version = g_strdup (version); + } + else + { + mfile = find_namespace_latest (namespace, search_path, + &tmp_version, &path); + } + + if (mfile == NULL) + { + if (version != NULL) + g_set_error (error, G_IREPOSITORY_ERROR, + G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND, + "Typelib file for namespace '%s', version '%s' not found", + namespace, version); + else + g_set_error (error, G_IREPOSITORY_ERROR, + G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND, + "Typelib file for namespace '%s' (any version) not found", + namespace); + goto out; + } + + { + GError *temp_error = NULL; + typelib = g_typelib_new_from_mapped_file (mfile, &temp_error); + if (!typelib) + { + g_set_error (error, G_IREPOSITORY_ERROR, + G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND, + "Failed to load typelib file '%s' for namespace '%s': %s", + path, namespace, temp_error->message); + g_clear_error (&temp_error); + goto out; + } + } + header = (Header *) typelib->data; + typelib_namespace = g_typelib_get_string (typelib, header->namespace); + typelib_version = g_typelib_get_string (typelib, header->nsversion); + + if (strcmp (typelib_namespace, namespace) != 0) + { + g_set_error (error, G_IREPOSITORY_ERROR, + G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH, + "Typelib file %s for namespace '%s' contains " + "namespace '%s' which doesn't match the file name", + path, namespace, typelib_namespace); + g_typelib_free (typelib); + goto out; + } + if (version != NULL && strcmp (typelib_version, version) != 0) + { + g_set_error (error, G_IREPOSITORY_ERROR, + G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH, + "Typelib file %s for namespace '%s' contains " + "version '%s' which doesn't match the expected version '%s'", + path, namespace, typelib_version, version); + g_typelib_free (typelib); + goto out; + } + + if (!register_internal (repository, path, allow_lazy, + typelib, error)) + { + g_typelib_free (typelib); + goto out; + } + ret = typelib; + out: + g_free (tmp_version); + g_free (path); + return ret; +} + +/** + * g_irepository_require: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: GI namespace to use, e.g. "Gtk" + * @version: (allow-none): Version of namespace, may be %NULL for latest + * @flags: Set of %GIRepositoryLoadFlags, may be 0 + * @error: a #GError. + * + * Force the namespace @namespace_ to be loaded if it isn't already. + * If @namespace_ is not loaded, this function will search for a + * ".typelib" file using the repository search path. In addition, a + * version @version of namespace may be specified. If @version is + * not specified, the latest will be used. + * + * Returns: (transfer none): a pointer to the #GITypelib if successful, %NULL otherwise + */ +GITypelib * +g_irepository_require (GIRepository *repository, + const gchar *namespace, + const gchar *version, + GIRepositoryLoadFlags flags, + GError **error) +{ + GITypelib *typelib; + + init_globals (); + typelib = require_internal (repository, namespace, version, flags, + typelib_search_path, error); + + return typelib; +} + +/** + * g_irepository_require_private: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @typelib_dir: Private directory where to find the requested typelib + * @namespace_: GI namespace to use, e.g. "Gtk" + * @version: (allow-none): Version of namespace, may be %NULL for latest + * @flags: Set of %GIRepositoryLoadFlags, may be 0 + * @error: a #GError. + * + * Force the namespace @namespace_ to be loaded if it isn't already. + * If @namespace_ is not loaded, this function will search for a + * ".typelib" file within the private directory only. In addition, a + * version @version of namespace should be specified. If @version is + * not specified, the latest will be used. + * + * Returns: (transfer none): a pointer to the #GITypelib if successful, %NULL otherwise + */ +GITypelib * +g_irepository_require_private (GIRepository *repository, + const gchar *typelib_dir, + const gchar *namespace, + const gchar *version, + GIRepositoryLoadFlags flags, + GError **error) +{ + GSList search_path = { (gpointer) typelib_dir, NULL }; + + return require_internal (repository, namespace, version, flags, + &search_path, error); +} + +static gboolean +g_irepository_introspect_cb (const char *option_name, + const char *value, + gpointer data, + GError **error) +{ + GError *tmp_error = NULL; + gboolean ret = g_irepository_dump (value, &tmp_error); + if (!ret) + { + g_error ("Failed to extract GType data: %s", + tmp_error->message); + exit (1); + } + exit (0); +} + +static const GOptionEntry introspection_args[] = { + { "introspect-dump", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, + g_irepository_introspect_cb, "Dump introspection information", + "infile.txt,outfile.xml" }, + G_OPTION_ENTRY_NULL +}; + +/** + * g_irepository_get_option_group: + * + * Obtain the option group for girepository, it's used + * by the dumper and for programs that wants to provide + * introspection information + * + * Returns: (transfer full): the option group + */ +GOptionGroup * +g_irepository_get_option_group (void) +{ + GOptionGroup *group; + group = g_option_group_new ("girepository", "Introspection Options", "Show Introspection Options", NULL, NULL); + + g_option_group_add_entries (group, introspection_args); + return group; +} + +GQuark +g_irepository_error_quark (void) +{ + static GQuark quark = 0; + if (quark == 0) + quark = g_quark_from_static_string ("g-irepository-error-quark"); + return quark; +} + +/** + * g_type_tag_to_string: + * @type: the type_tag + * + * Obtain a string representation of @type + * + * Returns: the string + */ +const gchar* +g_type_tag_to_string (GITypeTag type) +{ + switch (type) + { + case GI_TYPE_TAG_VOID: + return "void"; + case GI_TYPE_TAG_BOOLEAN: + return "gboolean"; + case GI_TYPE_TAG_INT8: + return "gint8"; + case GI_TYPE_TAG_UINT8: + return "guint8"; + case GI_TYPE_TAG_INT16: + return "gint16"; + case GI_TYPE_TAG_UINT16: + return "guint16"; + case GI_TYPE_TAG_INT32: + return "gint32"; + case GI_TYPE_TAG_UINT32: + return "guint32"; + case GI_TYPE_TAG_INT64: + return "gint64"; + case GI_TYPE_TAG_UINT64: + return "guint64"; + case GI_TYPE_TAG_FLOAT: + return "gfloat"; + case GI_TYPE_TAG_DOUBLE: + return "gdouble"; + case GI_TYPE_TAG_UNICHAR: + return "gunichar"; + case GI_TYPE_TAG_GTYPE: + return "GType"; + case GI_TYPE_TAG_UTF8: + return "utf8"; + case GI_TYPE_TAG_FILENAME: + return "filename"; + case GI_TYPE_TAG_ARRAY: + return "array"; + case GI_TYPE_TAG_INTERFACE: + return "interface"; + case GI_TYPE_TAG_GLIST: + return "glist"; + case GI_TYPE_TAG_GSLIST: + return "gslist"; + case GI_TYPE_TAG_GHASH: + return "ghash"; + case GI_TYPE_TAG_ERROR: + return "error"; + default: + return "unknown"; + } +} + +/** + * g_info_type_to_string: + * @type: the info type + * + * Obtain a string representation of @type + * + * Returns: the string + */ +const gchar* +g_info_type_to_string (GIInfoType type) +{ + switch (type) + { + case GI_INFO_TYPE_INVALID: + return "invalid"; + case GI_INFO_TYPE_FUNCTION: + return "function"; + case GI_INFO_TYPE_CALLBACK: + return "callback"; + case GI_INFO_TYPE_STRUCT: + return "struct"; + case GI_INFO_TYPE_BOXED: + return "boxed"; + case GI_INFO_TYPE_ENUM: + return "enum"; + case GI_INFO_TYPE_FLAGS: + return "flags"; + case GI_INFO_TYPE_OBJECT: + return "object"; + case GI_INFO_TYPE_INTERFACE: + return "interface"; + case GI_INFO_TYPE_CONSTANT: + return "constant"; + case GI_INFO_TYPE_UNION: + return "union"; + case GI_INFO_TYPE_VALUE: + return "value"; + case GI_INFO_TYPE_SIGNAL: + return "signal"; + case GI_INFO_TYPE_VFUNC: + return "vfunc"; + case GI_INFO_TYPE_PROPERTY: + return "property"; + case GI_INFO_TYPE_FIELD: + return "field"; + case GI_INFO_TYPE_ARG: + return "arg"; + case GI_INFO_TYPE_TYPE: + return "type"; + case GI_INFO_TYPE_UNRESOLVED: + return "unresolved"; + default: + return "unknown"; + } +} diff --git a/girepository/girepository.h b/girepository/girepository.h new file mode 100644 index 000000000..772a32151 --- /dev/null +++ b/girepository/girepository.h @@ -0,0 +1,247 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Repository + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#include +#include + +#include + +#define __GIREPOSITORY_H_INSIDE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define G_TYPE_IREPOSITORY (g_irepository_get_type ()) +#define G_IREPOSITORY(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_IREPOSITORY, GIRepository)) +#define G_IREPOSITORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_IREPOSITORY, GIRepositoryClass)) +#define G_IS_IREPOSITORY(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_IREPOSITORY)) +#define G_IS_IREPOSITORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_IREPOSITORY)) +#define G_IREPOSITORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_IREPOSITORY, GIRepositoryClass)) + +/** + * GIRepository: + * + * The GIRepository structure contains private data and should only be + * accessed using the provided API. + */ +typedef struct _GIRepository GIRepository; +typedef struct _GIRepositoryClass GIRepositoryClass; +typedef struct _GIRepositoryPrivate GIRepositoryPrivate; + +struct _GIRepository +{ + /*< private >*/ + GObject parent; + GIRepositoryPrivate *priv; +}; + +struct _GIRepositoryClass +{ + /*< private >*/ + GObjectClass parent; +}; + +/** + * GIRepositoryLoadFlags: + * @G_IREPOSITORY_LOAD_FLAG_LAZY: Lazily load the typelib. + * + * Flags that control how a typelib is loaded. + */ +typedef enum +{ + G_IREPOSITORY_LOAD_FLAG_LAZY = 1 << 0 +} GIRepositoryLoadFlags; + +/* Repository */ + +GI_AVAILABLE_IN_ALL +GType g_irepository_get_type (void) G_GNUC_CONST; + +GI_AVAILABLE_IN_ALL +GIRepository *g_irepository_get_default (void); + +GI_AVAILABLE_IN_ALL +void g_irepository_prepend_search_path (const char *directory); + +GI_AVAILABLE_IN_ALL +void g_irepository_prepend_library_path (const char *directory); + +GI_AVAILABLE_IN_ALL +GSList * g_irepository_get_search_path (void); + +GI_AVAILABLE_IN_ALL +const char * g_irepository_load_typelib (GIRepository *repository, + GITypelib *typelib, + GIRepositoryLoadFlags flags, + GError **error); + +GI_AVAILABLE_IN_ALL +gboolean g_irepository_is_registered (GIRepository *repository, + const gchar *namespace_, + const gchar *version); + +GI_AVAILABLE_IN_ALL +GIBaseInfo * g_irepository_find_by_name (GIRepository *repository, + const gchar *namespace_, + const gchar *name); + +GI_AVAILABLE_IN_ALL +GList * g_irepository_enumerate_versions (GIRepository *repository, + const gchar *namespace_); + +GI_AVAILABLE_IN_ALL +GITypelib * g_irepository_require (GIRepository *repository, + const gchar *namespace_, + const gchar *version, + GIRepositoryLoadFlags flags, + GError **error); + +GI_AVAILABLE_IN_ALL +GITypelib * g_irepository_require_private (GIRepository *repository, + const gchar *typelib_dir, + const gchar *namespace_, + const gchar *version, + GIRepositoryLoadFlags flags, + GError **error); + +GI_AVAILABLE_IN_ALL +gchar ** g_irepository_get_immediate_dependencies (GIRepository *repository, + const gchar *namespace_); + +GI_AVAILABLE_IN_ALL +gchar ** g_irepository_get_dependencies (GIRepository *repository, + const gchar *namespace_); + +GI_AVAILABLE_IN_ALL +gchar ** g_irepository_get_loaded_namespaces (GIRepository *repository); + +GI_AVAILABLE_IN_ALL +GIBaseInfo * g_irepository_find_by_gtype (GIRepository *repository, + GType gtype); + +GI_AVAILABLE_IN_ALL +void g_irepository_get_object_gtype_interfaces (GIRepository *repository, + GType gtype, + guint *n_interfaces_out, + GIInterfaceInfo ***interfaces_out); + +GI_AVAILABLE_IN_ALL +gint g_irepository_get_n_infos (GIRepository *repository, + const gchar *namespace_); + +GI_AVAILABLE_IN_ALL +GIBaseInfo * g_irepository_get_info (GIRepository *repository, + const gchar *namespace_, + gint index); + +GI_AVAILABLE_IN_ALL +GIEnumInfo * g_irepository_find_by_error_domain (GIRepository *repository, + GQuark domain); + +GI_AVAILABLE_IN_ALL +const gchar * g_irepository_get_typelib_path (GIRepository *repository, + const gchar *namespace_); +GI_AVAILABLE_IN_ALL +const gchar * g_irepository_get_shared_library (GIRepository *repository, + const gchar *namespace_); +GI_AVAILABLE_IN_ALL +const gchar * g_irepository_get_c_prefix (GIRepository *repository, + const gchar *namespace_); +GI_AVAILABLE_IN_ALL +const gchar * g_irepository_get_version (GIRepository *repository, + const gchar *namespace_); + + +GI_AVAILABLE_IN_ALL +GOptionGroup * g_irepository_get_option_group (void); + + +GI_AVAILABLE_IN_ALL +gboolean g_irepository_dump (const char *arg, GError **error); + +/** + * GIRepositoryError: + * @G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND: the typelib could not be found. + * @G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH: the namespace does not match the + * requested namespace. + * @G_IREPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT: the version of the + * typelib does not match the requested version. + * @G_IREPOSITORY_ERROR_LIBRARY_NOT_FOUND: the library used by the typelib + * could not be found. + * + * An error code used with #G_IREPOSITORY_ERROR in a #GError returned + * from a #GIRepository routine. + */ +typedef enum +{ + G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND, + G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH, + G_IREPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT, + G_IREPOSITORY_ERROR_LIBRARY_NOT_FOUND +} GIRepositoryError; + +/** + * G_IREPOSITORY_ERROR: + * + * Error domain for #GIRepository. Errors in this domain will be from the + * #GIRepositoryError enumeration. See #GError for more information on + * error domains. + */ +#define G_IREPOSITORY_ERROR (g_irepository_error_quark ()) + +GI_AVAILABLE_IN_ALL +GQuark g_irepository_error_quark (void); + + +/* Global utility functions */ + +GI_AVAILABLE_IN_ALL +void gi_cclosure_marshal_generic (GClosure *closure, + GValue *return_gvalue, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +G_END_DECLS diff --git a/girepository/girffi.c b/girepository/girffi.c new file mode 100644 index 000000000..49844e0e3 --- /dev/null +++ b/girepository/girffi.c @@ -0,0 +1,447 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Helper functions for ffi integration + * + * Copyright (C) 2008 Red Hat, Inc + * Copyright (C) 2005 Matthias Clasen + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "girffi.h" +#include "girepository.h" +#include "girepository-private.h" + +/** + * SECTION:girffi + * @short_description: TODO + * @title: girffi + * + * TODO + */ + +static ffi_type * +gi_type_tag_get_ffi_type_internal (GITypeTag tag, + gboolean is_pointer, + gboolean is_enum) +{ + switch (tag) + { + case GI_TYPE_TAG_BOOLEAN: + return &ffi_type_uint; + case GI_TYPE_TAG_INT8: + return &ffi_type_sint8; + case GI_TYPE_TAG_UINT8: + return &ffi_type_uint8; + case GI_TYPE_TAG_INT16: + return &ffi_type_sint16; + case GI_TYPE_TAG_UINT16: + return &ffi_type_uint16; + case GI_TYPE_TAG_INT32: + return &ffi_type_sint32; + case GI_TYPE_TAG_UINT32: + case GI_TYPE_TAG_UNICHAR: + return &ffi_type_uint32; + case GI_TYPE_TAG_INT64: + return &ffi_type_sint64; + case GI_TYPE_TAG_UINT64: + return &ffi_type_uint64; + case GI_TYPE_TAG_GTYPE: +#if GLIB_SIZEOF_SIZE_T == 4 + return &ffi_type_uint32; +#elif GLIB_SIZEOF_SIZE_T == 8 + return &ffi_type_uint64; +#else +# error "Unexpected size for size_t: not 4 or 8" +#endif + case GI_TYPE_TAG_FLOAT: + return &ffi_type_float; + case GI_TYPE_TAG_DOUBLE: + return &ffi_type_double; + case GI_TYPE_TAG_UTF8: + case GI_TYPE_TAG_FILENAME: + case GI_TYPE_TAG_ARRAY: + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + case GI_TYPE_TAG_GHASH: + case GI_TYPE_TAG_ERROR: + return &ffi_type_pointer; + case GI_TYPE_TAG_INTERFACE: + { + /* We need to handle enums specially: + * https://bugzilla.gnome.org/show_bug.cgi?id=665150 + */ + if (!is_enum) + return &ffi_type_pointer; + else + return &ffi_type_sint32; + } + case GI_TYPE_TAG_VOID: + if (is_pointer) + return &ffi_type_pointer; + else + return &ffi_type_void; + default: + break; + } + + g_assert_not_reached (); + + return NULL; +} + +/** + * gi_type_tag_get_ffi_type: + * @type_tag: A #GITypeTag + * @is_pointer: Whether or not this is a pointer type + * + * TODO + * + * Returns: A #ffi_type corresponding to the platform default C ABI for @tag and @is_pointer. + */ +ffi_type * +gi_type_tag_get_ffi_type (GITypeTag type_tag, + gboolean is_pointer) +{ + return gi_type_tag_get_ffi_type_internal (type_tag, is_pointer, FALSE); +} + +/** + * g_type_info_get_ffi_type: + * @info: A #GITypeInfo + * + * TODO + * + * Returns: A #ffi_type corresponding to the platform default C ABI for @info. + */ +ffi_type * +g_type_info_get_ffi_type (GITypeInfo *info) +{ + gboolean is_enum = FALSE; + GIBaseInfo *iinfo; + + if (g_type_info_get_tag (info) == GI_TYPE_TAG_INTERFACE) + { + iinfo = g_type_info_get_interface (info); + switch (g_base_info_get_type (iinfo)) + { + case GI_INFO_TYPE_ENUM: + case GI_INFO_TYPE_FLAGS: + is_enum = TRUE; + break; + default: + break; + } + g_base_info_unref (iinfo); + } + + return gi_type_tag_get_ffi_type_internal (g_type_info_get_tag (info), g_type_info_is_pointer (info), is_enum); +} + +/** + * g_callable_info_get_ffi_arg_types: + * @callable_info: a callable info from a typelib + * @n_args_p: (out): The number of arguments + * + * TODO + * + * Returns: an array of ffi_type*. The array itself + * should be freed using g_free() after use. + */ +static ffi_type ** +g_callable_info_get_ffi_arg_types (GICallableInfo *callable_info, + int *n_args_p) +{ + ffi_type **arg_types; + gboolean is_method, throws; + gint n_args, n_invoke_args, i, offset; + + g_return_val_if_fail (callable_info != NULL, NULL); + + n_args = g_callable_info_get_n_args (callable_info); + is_method = g_callable_info_is_method (callable_info); + throws = g_callable_info_can_throw_gerror (callable_info); + offset = is_method ? 1 : 0; + + n_invoke_args = n_args; + + if (is_method) + n_invoke_args++; + if (throws) + n_invoke_args++; + + if (n_args_p) + *n_args_p = n_invoke_args; + + arg_types = (ffi_type **) g_new0 (ffi_type *, n_invoke_args + 1); + + if (is_method) + arg_types[0] = &ffi_type_pointer; + if (throws) + arg_types[n_invoke_args - 1] = &ffi_type_pointer; + + for (i = 0; i < n_args; ++i) + { + GIArgInfo arg_info; + GITypeInfo arg_type; + + g_callable_info_load_arg (callable_info, i, &arg_info); + g_arg_info_load_type (&arg_info, &arg_type); + switch (g_arg_info_get_direction (&arg_info)) + { + case GI_DIRECTION_IN: + arg_types[i + offset] = g_type_info_get_ffi_type (&arg_type); + break; + case GI_DIRECTION_OUT: + case GI_DIRECTION_INOUT: + arg_types[i + offset] = &ffi_type_pointer; + break; + default: + g_assert_not_reached (); + } + } + + arg_types[n_invoke_args] = NULL; + + return arg_types; +} + +/** + * g_callable_info_get_ffi_return_type: + * @callable_info: a callable info from a typelib + * + * Fetches the ffi_type for a corresponding return value of + * a #GICallableInfo + * + * Returns: the ffi_type for the return value + */ +static ffi_type * +g_callable_info_get_ffi_return_type (GICallableInfo *callable_info) +{ + GITypeInfo *return_type; + ffi_type *return_ffi_type; + + g_return_val_if_fail (callable_info != NULL, NULL); + + return_type = g_callable_info_get_return_type (callable_info); + return_ffi_type = g_type_info_get_ffi_type (return_type); + g_base_info_unref((GIBaseInfo*)return_type); + + return return_ffi_type; +} + +/** + * g_function_info_prep_invoker: + * @info: A #GIFunctionInfo + * @invoker: Output invoker structure + * @error: A #GError + * + * Initialize the caller-allocated @invoker structure with a cache + * of information needed to invoke the C function corresponding to + * @info with the platform's default ABI. + * + * A primary intent of this function is that a dynamic structure allocated + * by a language binding could contain a #GIFunctionInvoker structure + * inside the binding's function mapping. + * + * Returns: %TRUE on success, %FALSE otherwise with @error set. + */ +gboolean +g_function_info_prep_invoker (GIFunctionInfo *info, + GIFunctionInvoker *invoker, + GError **error) +{ + const char *symbol; + gpointer addr; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (invoker != NULL, FALSE); + + symbol = g_function_info_get_symbol ((GIFunctionInfo*) info); + + if (!g_typelib_symbol (g_base_info_get_typelib((GIBaseInfo *) info), + symbol, &addr)) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_SYMBOL_NOT_FOUND, + "Could not locate %s: %s", symbol, g_module_error ()); + + return FALSE; + } + + return g_function_invoker_new_for_address (addr, info, invoker, error); +} + +/** + * g_function_invoker_new_for_address: + * @addr: The address + * @info: A #GICallableInfo + * @invoker: Output invoker structure + * @error: A #GError + * + * Initialize the caller-allocated @invoker structure with a cache + * of information needed to invoke the C function corresponding to + * @info with the platform's default ABI. + * + * A primary intent of this function is that a dynamic structure allocated + * by a language binding could contain a #GIFunctionInvoker structure + * inside the binding's function mapping. + * + * Returns: %TRUE on success, %FALSE otherwise with @error set. + */ +gboolean +g_function_invoker_new_for_address (gpointer addr, + GICallableInfo *info, + GIFunctionInvoker *invoker, + GError **error) +{ + ffi_type **atypes; + gint n_args; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (invoker != NULL, FALSE); + + invoker->native_address = addr; + + atypes = g_callable_info_get_ffi_arg_types (info, &n_args); + + return ffi_prep_cif (&(invoker->cif), FFI_DEFAULT_ABI, n_args, + g_callable_info_get_ffi_return_type (info), + atypes) == FFI_OK; +} + +/** + * g_function_invoker_destroy: + * @invoker: A #GIFunctionInvoker + * + * Release all resources allocated for the internals of @invoker; callers + * are responsible for freeing any resources allocated for the structure + * itself however. + */ +void +g_function_invoker_destroy (GIFunctionInvoker *invoker) +{ + g_free (invoker->cif.arg_types); +} + +typedef struct { + ffi_closure ffi_closure; + gpointer writable_self; + gpointer native_address; +} GIClosureWrapper; + +/** + * g_callable_info_create_closure: + * @callable_info: a callable info from a typelib + * @cif: a ffi_cif structure + * @callback: the ffi callback + * @user_data: data to be passed into the callback + * + * Prepares a callback for ffi invocation. + * + * Returns: the ffi_closure or NULL on error. The return value + * should be freed by calling g_callable_info_destroy_closure(). + */ +ffi_closure * +g_callable_info_create_closure (GICallableInfo *callable_info, + ffi_cif *cif, + GIFFIClosureCallback callback, + gpointer user_data) +{ + gpointer exec_ptr; + int n_args; + ffi_type **atypes; + GIClosureWrapper *closure; + ffi_status status; + + g_return_val_if_fail (callable_info != NULL, FALSE); + g_return_val_if_fail (cif != NULL, FALSE); + g_return_val_if_fail (callback != NULL, FALSE); + + closure = ffi_closure_alloc (sizeof (GIClosureWrapper), &exec_ptr); + if (!closure) + { + g_warning ("could not allocate closure\n"); + return NULL; + } + closure->writable_self = closure; + closure->native_address = exec_ptr; + + + atypes = g_callable_info_get_ffi_arg_types (callable_info, &n_args); + status = ffi_prep_cif (cif, FFI_DEFAULT_ABI, n_args, + g_callable_info_get_ffi_return_type (callable_info), + atypes); + if (status != FFI_OK) + { + g_warning ("ffi_prep_cif failed: %d\n", status); + ffi_closure_free (closure); + return NULL; + } + + status = ffi_prep_closure_loc (&closure->ffi_closure, cif, callback, user_data, exec_ptr); + if (status != FFI_OK) + { + g_warning ("ffi_prep_closure failed: %d\n", status); + ffi_closure_free (closure); + return NULL; + } + + return &closure->ffi_closure; +} + +/** + * g_callable_info_get_closure_native_address: + * @callable_info: a callable info from a typelib + * @closure: ffi closure + * + * Gets callable code from ffi_closure prepared by g_callable_info_create_closure() + */ +gpointer * +g_callable_info_get_closure_native_address (GICallableInfo *callable_info, + ffi_closure *closure) +{ + GIClosureWrapper *wrapper = (GIClosureWrapper *)closure; + return wrapper->native_address; +} + +/** + * g_callable_info_destroy_closure: + * @callable_info: a callable info from a typelib + * @closure: ffi closure + * + * Frees a ffi_closure returned from g_callable_info_create_closure() + */ +void +g_callable_info_destroy_closure (GICallableInfo *callable_info, + ffi_closure *closure) +{ + GIClosureWrapper *wrapper = (GIClosureWrapper *)closure; + + g_free (wrapper->ffi_closure.cif->arg_types); + ffi_closure_free (wrapper->writable_self); +} diff --git a/girepository/girffi.h b/girepository/girffi.h new file mode 100644 index 000000000..a37243feb --- /dev/null +++ b/girepository/girffi.h @@ -0,0 +1,118 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Helper functions for ffi integration + * + * Copyright (C) 2008 Red Hat, Inc + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include "girepository.h" + +G_BEGIN_DECLS + +/** + * GIFFIClosureCallback: + * @Param1: TODO + * @Param2: TODO + * @Param3: TODO + * @Param4: TODO + * + * TODO + */ +typedef void (*GIFFIClosureCallback) (ffi_cif *, + void *, + void **, + void *); + +/** + * GIFunctionInvoker: + * @cif: the cif + * @native_address: the native address + * + * TODO + */ +typedef struct _GIFunctionInvoker GIFunctionInvoker; + +struct _GIFunctionInvoker { + ffi_cif cif; + gpointer native_address; + /*< private >*/ + gpointer padding[3]; +}; + +/** + * GIFFIReturnValue: + * + * TODO + */ +typedef GIArgument GIFFIReturnValue; + +GI_AVAILABLE_IN_ALL +ffi_type * gi_type_tag_get_ffi_type (GITypeTag type_tag, gboolean is_pointer); + +GI_AVAILABLE_IN_ALL +ffi_type * g_type_info_get_ffi_type (GITypeInfo *info); + +GI_AVAILABLE_IN_ALL +void gi_type_info_extract_ffi_return_value (GITypeInfo *return_info, + GIFFIReturnValue *ffi_value, + GIArgument *arg); + +GI_AVAILABLE_IN_ALL +void gi_type_tag_extract_ffi_return_value (GITypeTag return_tag, + GIInfoType interface_type, + GIFFIReturnValue *ffi_value, + GIArgument *arg); + +GI_AVAILABLE_IN_ALL +gboolean g_function_info_prep_invoker (GIFunctionInfo *info, + GIFunctionInvoker *invoker, + GError **error); + +GI_AVAILABLE_IN_ALL +gboolean g_function_invoker_new_for_address (gpointer addr, + GICallableInfo *info, + GIFunctionInvoker *invoker, + GError **error); + +GI_AVAILABLE_IN_ALL +void g_function_invoker_destroy (GIFunctionInvoker *invoker); + + +GI_AVAILABLE_IN_ALL +ffi_closure * g_callable_info_create_closure (GICallableInfo *callable_info, + ffi_cif *cif, + GIFFIClosureCallback callback, + gpointer user_data); + +GI_AVAILABLE_IN_ALL +gpointer * g_callable_info_get_closure_native_address (GICallableInfo *callable_info, + ffi_closure *closure); + +GI_AVAILABLE_IN_ALL +void g_callable_info_destroy_closure (GICallableInfo *callable_info, + ffi_closure *closure); + +G_END_DECLS diff --git a/girepository/girmodule-private.h b/girepository/girmodule-private.h new file mode 100644 index 000000000..98b00d006 --- /dev/null +++ b/girepository/girmodule-private.h @@ -0,0 +1,85 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Parsed IDL + * + * Copyright (C) 2005 Matthias Clasen + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#include +#include "gitypelib-internal.h" + +G_BEGIN_DECLS + +typedef struct _GIrTypelibBuild GIrTypelibBuild; +typedef struct _GIrModule GIrModule; + +struct _GIrTypelibBuild { + GIrModule *module; + GHashTable *strings; + GHashTable *types; + GList *nodes_with_attributes; + guint32 n_attributes; + guchar *data; + GList *stack; +}; + +struct _GIrModule +{ + gchar *name; + gchar *version; + gchar *shared_library; + gchar *c_prefix; + GList *dependencies; + GList *entries; + + /* All modules that are included directly or indirectly */ + GList *include_modules; + + /* Aliases defined in the module or in included modules */ + GHashTable *aliases; + + /* Structures with the 'pointer' flag (typedef struct _X *X) + * in the module or in included modules + */ + GHashTable *pointer_structures; + /* Same as 'pointer' structures, but with the deprecated + * 'disguised' flag + */ + GHashTable *disguised_structures; +}; + +GIrModule *_g_ir_module_new (const gchar *name, + const gchar *nsversion, + const gchar *module_filename, + const gchar *c_prefix); +void _g_ir_module_free (GIrModule *module); + +void _g_ir_module_add_include_module (GIrModule *module, + GIrModule *include_module); + +GITypelib * _g_ir_module_build_typelib (GIrModule *module); + +void _g_ir_module_fatal (GIrTypelibBuild *build, guint line, const char *msg, ...) G_GNUC_PRINTF (3, 4) G_GNUC_NORETURN; + +void _g_irnode_init_stats (void); +void _g_irnode_dump_stats (void); + +G_END_DECLS diff --git a/girepository/girmodule.c b/girepository/girmodule.c new file mode 100644 index 000000000..03c0a0612 --- /dev/null +++ b/girepository/girmodule.c @@ -0,0 +1,582 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Typelib creation + * + * Copyright (C) 2005 Matthias Clasen + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include "girmodule-private.h" + +#include "girnode-private.h" +#include "gitypelib-internal.h" + +#include +#include +#include + +#define ALIGN_VALUE(this, boundary) \ + (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1))) + +#define NUM_SECTIONS 2 + +GIrModule * +_g_ir_module_new (const gchar *name, + const gchar *version, + const gchar *shared_library, + const gchar *c_prefix) +{ + GIrModule *module; + + module = g_slice_new0 (GIrModule); + + module->name = g_strdup (name); + module->version = g_strdup (version); + if (shared_library) + module->shared_library = g_strdup (shared_library); + else + module->shared_library = NULL; + module->c_prefix = g_strdup (c_prefix); + module->dependencies = NULL; + module->entries = NULL; + + module->include_modules = NULL; + module->aliases = NULL; + + return module; +} + +void +_g_ir_module_free (GIrModule *module) +{ + GList *e; + + g_free (module->name); + + for (e = module->entries; e; e = e->next) + _g_ir_node_free ((GIrNode *)e->data); + + g_list_free (module->entries); + /* Don't free dependencies, we inherit that from the parser */ + + g_list_free (module->include_modules); + + g_hash_table_destroy (module->aliases); + g_hash_table_destroy (module->pointer_structures); + g_hash_table_destroy (module->disguised_structures); + + g_slice_free (GIrModule, module); +} + +/** + * _g_ir_module_fatal: + * @build: Current build + * @line: Origin line number, or 0 if unknown + * @msg: printf-format string + * @args: Remaining arguments + * + * Report a fatal error, then exit. + */ +void +_g_ir_module_fatal (GIrTypelibBuild *build, + guint line, + const char *msg, + ...) +{ + GString *context; + char *formatted; + GList *link; + + va_list args; + + va_start (args, msg); + + formatted = g_strdup_vprintf (msg, args); + + context = g_string_new (""); + if (line > 0) + g_string_append_printf (context, "%d: ", line); + if (build->stack) + g_string_append (context, "In "); + for (link = g_list_last (build->stack); link; link = link->prev) + { + GIrNode *node = link->data; + const char *name = node->name; + if (name) + g_string_append (context, name); + if (link->prev) + g_string_append (context, "."); + } + if (build->stack) + g_string_append (context, ": "); + + g_printerr ("%s-%s.gir:%serror: %s\n", build->module->name, + build->module->version, + context->str, formatted); + g_string_free (context, TRUE); + + exit (1); + + va_end (args); +} + +static void +add_alias_foreach (gpointer key, + gpointer value, + gpointer data) +{ + GIrModule *module = data; + + g_hash_table_replace (module->aliases, g_strdup (key), g_strdup (value)); +} + +static void +add_pointer_structure_foreach (gpointer key, + gpointer value, + gpointer data) +{ + GIrModule *module = data; + + g_hash_table_replace (module->pointer_structures, g_strdup (key), value); +} + +static void +add_disguised_structure_foreach (gpointer key, + gpointer value, + gpointer data) +{ + GIrModule *module = data; + + g_hash_table_replace (module->disguised_structures, g_strdup (key), value); +} + +void +_g_ir_module_add_include_module (GIrModule *module, + GIrModule *include_module) +{ + module->include_modules = g_list_prepend (module->include_modules, + include_module); + + g_hash_table_foreach (include_module->aliases, + add_alias_foreach, + module); + + g_hash_table_foreach (include_module->pointer_structures, + add_pointer_structure_foreach, + module); + g_hash_table_foreach (include_module->disguised_structures, + add_disguised_structure_foreach, + module); +} + +struct AttributeWriteData +{ + guint count; + guchar *databuf; + GIrNode *node; + GHashTable *strings; + guint32 *offset; + guint32 *offset2; +}; + +static void +write_attribute (gpointer key, gpointer value, gpointer datap) +{ + struct AttributeWriteData *data = datap; + guint32 old_offset = *(data->offset); + AttributeBlob *blob = (AttributeBlob*)&(data->databuf[old_offset]); + + *(data->offset) += sizeof (AttributeBlob); + + blob->offset = data->node->offset; + blob->name = _g_ir_write_string ((const char*) key, data->strings, data->databuf, data->offset2); + blob->value = _g_ir_write_string ((const char*) value, data->strings, data->databuf, data->offset2); + + data->count++; +} + +static guint +write_attributes (GIrModule *module, + GIrNode *node, + GHashTable *strings, + guchar *data, + guint32 *offset, + guint32 *offset2) +{ + struct AttributeWriteData wdata; + wdata.count = 0; + wdata.databuf = data; + wdata.node = node; + wdata.offset = offset; + wdata.offset2 = offset2; + wdata.strings = strings; + + g_hash_table_foreach (node->attributes, write_attribute, &wdata); + + return wdata.count; +} + +static gint +node_cmp_offset_func (gconstpointer a, + gconstpointer b) +{ + const GIrNode *na = a; + const GIrNode *nb = b; + return na->offset - nb->offset; +} + +static void +alloc_section (guint8 *data, SectionType section_id, guint32 offset) +{ + int i; + Header *header = (Header*)data; + Section *section_data = (Section*)&data[header->sections]; + + g_assert (section_id != GI_SECTION_END); + + for (i = 0; i < NUM_SECTIONS; i++) + { + if (section_data->id == GI_SECTION_END) + { + section_data->id = section_id; + section_data->offset = offset; + return; + } + section_data++; + } + g_assert_not_reached (); +} + +static guint8* +add_directory_index_section (guint8 *data, GIrModule *module, guint32 *offset2) +{ + DirEntry *entry; + Header *header = (Header*)data; + GITypelibHashBuilder *dirindex_builder; + guint i, n_interfaces; + guint16 required_size; + guint32 new_offset; + + dirindex_builder = _gi_typelib_hash_builder_new (); + + n_interfaces = ((Header *)data)->n_local_entries; + + for (i = 0; i < n_interfaces; i++) + { + const char *str; + entry = (DirEntry *)&data[header->directory + (i * header->entry_blob_size)]; + str = (const char *) (&data[entry->name]); + _gi_typelib_hash_builder_add_string (dirindex_builder, str, i); + } + + if (!_gi_typelib_hash_builder_prepare (dirindex_builder)) + { + /* This happens if CMPH couldn't create a perfect hash. So + * we just punt and leave no directory index section. + */ + _gi_typelib_hash_builder_destroy (dirindex_builder); + return data; + } + + alloc_section (data, GI_SECTION_DIRECTORY_INDEX, *offset2); + + required_size = _gi_typelib_hash_builder_get_buffer_size (dirindex_builder); + required_size = ALIGN_VALUE (required_size, 4); + + new_offset = *offset2 + required_size; + + data = g_realloc (data, new_offset); + + _gi_typelib_hash_builder_pack (dirindex_builder, ((guint8*)data) + *offset2, required_size); + + *offset2 = new_offset; + + _gi_typelib_hash_builder_destroy (dirindex_builder); + return data; +} + +GITypelib * +_g_ir_module_build_typelib (GIrModule *module) +{ + GError *error = NULL; + GITypelib *typelib; + gsize length; + guint i; + GList *e; + Header *header; + DirEntry *entry; + guint32 header_size; + guint32 dir_size; + guint32 n_entries; + guint32 n_local_entries; + guint32 size, offset, offset2, old_offset; + GHashTable *strings; + GHashTable *types; + GList *nodes_with_attributes; + char *dependencies; + guchar *data; + Section *section; + + header_size = ALIGN_VALUE (sizeof (Header), 4); + n_local_entries = g_list_length (module->entries); + + /* Serialize dependencies into one string; this is convenient + * and not a major change to the typelib format. */ + { + GString *dependencies_str = g_string_new (""); + GList *link; + for (link = module->dependencies; link; link = link->next) + { + const char *dependency = link->data; + if (!strcmp (dependency, module->name)) + continue; + g_string_append (dependencies_str, dependency); + if (link->next) + g_string_append_c (dependencies_str, '|'); + } + dependencies = g_string_free (dependencies_str, FALSE); + if (!dependencies[0]) + { + g_free (dependencies); + dependencies = NULL; + } + } + + restart: + _g_irnode_init_stats (); + strings = g_hash_table_new (g_str_hash, g_str_equal); + types = g_hash_table_new (g_str_hash, g_str_equal); + nodes_with_attributes = NULL; + n_entries = g_list_length (module->entries); + + g_message ("%d entries (%d local), %d dependencies\n", n_entries, n_local_entries, + g_list_length (module->dependencies)); + + dir_size = n_entries * sizeof (DirEntry); + size = header_size + dir_size; + + size += ALIGN_VALUE (strlen (module->name) + 1, 4); + + for (e = module->entries; e; e = e->next) + { + GIrNode *node = e->data; + + size += _g_ir_node_get_full_size (node); + + /* Also reset the offset here */ + node->offset = 0; + } + + /* Adjust size for strings allocated in header below specially */ + size += ALIGN_VALUE (strlen (module->name) + 1, 4); + if (module->shared_library) + size += ALIGN_VALUE (strlen (module->shared_library) + 1, 4); + if (dependencies != NULL) + size += ALIGN_VALUE (strlen (dependencies) + 1, 4); + if (module->c_prefix != NULL) + size += ALIGN_VALUE (strlen (module->c_prefix) + 1, 4); + + size += sizeof (Section) * NUM_SECTIONS; + + g_message ("allocating %d bytes (%d header, %d directory, %d entries)\n", + size, header_size, dir_size, size - header_size - dir_size); + + data = g_malloc0 (size); + + /* fill in header */ + header = (Header *)data; + memcpy (header, G_IR_MAGIC, 16); + header->major_version = 4; + header->minor_version = 0; + header->reserved = 0; + header->n_entries = n_entries; + header->n_local_entries = n_local_entries; + header->n_attributes = 0; + header->attributes = 0; /* filled in later */ + /* NOTE: When writing strings to the typelib here, you should also update + * the size calculations above. + */ + if (dependencies != NULL) + header->dependencies = _g_ir_write_string (dependencies, strings, data, &header_size); + else + header->dependencies = 0; + header->size = 0; /* filled in later */ + header->namespace = _g_ir_write_string (module->name, strings, data, &header_size); + header->nsversion = _g_ir_write_string (module->version, strings, data, &header_size); + header->shared_library = (module->shared_library? + _g_ir_write_string (module->shared_library, strings, data, &header_size) + : 0); + if (module->c_prefix != NULL) + header->c_prefix = _g_ir_write_string (module->c_prefix, strings, data, &header_size); + else + header->c_prefix = 0; + header->entry_blob_size = sizeof (DirEntry); + header->function_blob_size = sizeof (FunctionBlob); + header->callback_blob_size = sizeof (CallbackBlob); + header->signal_blob_size = sizeof (SignalBlob); + header->vfunc_blob_size = sizeof (VFuncBlob); + header->arg_blob_size = sizeof (ArgBlob); + header->property_blob_size = sizeof (PropertyBlob); + header->field_blob_size = sizeof (FieldBlob); + header->value_blob_size = sizeof (ValueBlob); + header->constant_blob_size = sizeof (ConstantBlob); + header->error_domain_blob_size = 16; /* No longer used */ + header->attribute_blob_size = sizeof (AttributeBlob); + header->signature_blob_size = sizeof (SignatureBlob); + header->enum_blob_size = sizeof (EnumBlob); + header->struct_blob_size = sizeof (StructBlob); + header->object_blob_size = sizeof(ObjectBlob); + header->interface_blob_size = sizeof (InterfaceBlob); + header->union_blob_size = sizeof (UnionBlob); + + offset2 = ALIGN_VALUE (header_size, 4); + header->sections = offset2; + + /* Initialize all the sections to _END/0; we fill them in later using + * alloc_section(). (Right now there's just the directory index + * though, note) + */ + for (i = 0; i < NUM_SECTIONS; i++) + { + section = (Section*) &data[offset2]; + section->id = GI_SECTION_END; + section->offset = 0; + offset2 += sizeof(Section); + } + header->directory = offset2; + + /* fill in directory and content */ + entry = (DirEntry *)&data[header->directory]; + + offset2 += dir_size; + + for (e = module->entries, i = 0; e; e = e->next, i++) + { + GIrTypelibBuild build; + GIrNode *node = e->data; + + if (strchr (node->name, '.')) + { + g_error ("Names may not contain '.'"); + } + + /* we picked up implicit xref nodes, start over */ + if (i == n_entries) + { + GList *link; + g_message ("Found implicit cross references, starting over"); + + g_hash_table_destroy (strings); + g_hash_table_destroy (types); + + /* Reset the cached offsets */ + for (link = nodes_with_attributes; link; link = link->next) + ((GIrNode *) link->data)->offset = 0; + + g_list_free (nodes_with_attributes); + strings = NULL; + + g_free (data); + data = NULL; + + goto restart; + } + + offset = offset2; + + if (node->type == G_IR_NODE_XREF) + { + const char *namespace = ((GIrNodeXRef*)node)->namespace; + + entry->blob_type = 0; + entry->local = FALSE; + entry->offset = _g_ir_write_string (namespace, strings, data, &offset2); + entry->name = _g_ir_write_string (node->name, strings, data, &offset2); + } + else + { + old_offset = offset; + offset2 = offset + _g_ir_node_get_size (node); + + entry->blob_type = node->type; + entry->local = TRUE; + entry->offset = offset; + entry->name = _g_ir_write_string (node->name, strings, data, &offset2); + + memset (&build, 0, sizeof (build)); + build.module = module; + build.strings = strings; + build.types = types; + build.nodes_with_attributes = nodes_with_attributes; + build.n_attributes = header->n_attributes; + build.data = data; + _g_ir_node_build_typelib (node, NULL, &build, &offset, &offset2, NULL); + + nodes_with_attributes = build.nodes_with_attributes; + header->n_attributes = build.n_attributes; + + if (offset2 > old_offset + _g_ir_node_get_full_size (node)) + g_error ("left a hole of %d bytes\n", offset2 - old_offset - _g_ir_node_get_full_size (node)); + } + + entry++; + } + + /* GIBaseInfo expects the AttributeBlob array to be sorted on the field (offset) */ + nodes_with_attributes = g_list_sort (nodes_with_attributes, node_cmp_offset_func); + + g_message ("header: %d entries, %d attributes", header->n_entries, header->n_attributes); + + _g_irnode_dump_stats (); + + /* Write attributes after the blobs */ + offset = offset2; + header->attributes = offset; + offset2 = offset + header->n_attributes * header->attribute_blob_size; + + for (e = nodes_with_attributes; e; e = e->next) + { + GIrNode *node = e->data; + write_attributes (module, node, strings, data, &offset, &offset2); + } + + g_message ("reallocating to %d bytes", offset2); + + data = g_realloc (data, offset2); + header = (Header*) data; + + data = add_directory_index_section (data, module, &offset2); + header = (Header *)data; + + length = header->size = offset2; + typelib = g_typelib_new_from_memory (data, length, &error); + if (!typelib) + { + g_error ("error building typelib: %s", + error->message); + } + + g_hash_table_destroy (strings); + g_hash_table_destroy (types); + g_list_free (nodes_with_attributes); + + return typelib; +} + diff --git a/girepository/girnode-private.h b/girepository/girnode-private.h new file mode 100644 index 000000000..5fbe28916 --- /dev/null +++ b/girepository/girnode-private.h @@ -0,0 +1,400 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Parsed GIR + * + * Copyright (C) 2005 Matthias Clasen + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#include + +#include "girmodule-private.h" + +G_BEGIN_DECLS + +typedef struct _GIrNode GIrNode; +typedef struct _GIrNodeFunction GIrNodeFunction; +typedef struct _GIrNodeParam GIrNodeParam; +typedef struct _GIrNodeType GIrNodeType; +typedef struct _GIrNodeInterface GIrNodeInterface; +typedef struct _GIrNodeSignal GIrNodeSignal; +typedef struct _GIrNodeProperty GIrNodeProperty; +typedef struct _GIrNodeVFunc GIrNodeVFunc; +typedef struct _GIrNodeField GIrNodeField; +typedef struct _GIrNodeValue GIrNodeValue; +typedef struct _GIrNodeEnum GIrNodeEnum; +typedef struct _GIrNodeBoxed GIrNodeBoxed; +typedef struct _GIrNodeStruct GIrNodeStruct; +typedef struct _GIrNodeConstant GIrNodeConstant; +typedef struct _GIrNodeXRef GIrNodeXRef; +typedef struct _GIrNodeUnion GIrNodeUnion; + +typedef enum +{ + G_IR_NODE_INVALID = 0, + G_IR_NODE_FUNCTION = 1, + G_IR_NODE_CALLBACK = 2, + G_IR_NODE_STRUCT = 3, + G_IR_NODE_BOXED = 4, + G_IR_NODE_ENUM = 5, + G_IR_NODE_FLAGS = 6, + G_IR_NODE_OBJECT = 7, + G_IR_NODE_INTERFACE = 8, + G_IR_NODE_CONSTANT = 9, + G_IR_NODE_INVALID_0 = 10, /* DELETED - used to be ERROR_DOMAIN */ + G_IR_NODE_UNION = 11, + G_IR_NODE_PARAM = 12, + G_IR_NODE_TYPE = 13, + G_IR_NODE_PROPERTY = 14, + G_IR_NODE_SIGNAL = 15, + G_IR_NODE_VALUE = 16, + G_IR_NODE_VFUNC = 17, + G_IR_NODE_FIELD = 18, + G_IR_NODE_XREF = 19 +} GIrNodeTypeId; + +struct _GIrNode +{ + GIrNodeTypeId type; + gchar *name; + GIrModule *module; + + guint32 offset; /* Assigned as we build the typelib */ + + GHashTable *attributes; +}; + +struct _GIrNodeXRef +{ + GIrNode node; + + gchar *namespace; +}; + +struct _GIrNodeFunction +{ + GIrNode node; + + gboolean deprecated; + gboolean is_varargs; /* Not in typelib yet */ + + gboolean is_method; + gboolean is_setter; + gboolean is_getter; + gboolean is_constructor; + gboolean wraps_vfunc; + gboolean throws; + gboolean instance_transfer_full; + + gchar *symbol; + char *property; + + GIrNodeParam *result; + GList *parameters; +}; + +struct _GIrNodeType +{ + GIrNode node; + + gboolean is_pointer; + gboolean is_basic; + gboolean is_array; + gboolean is_glist; + gboolean is_gslist; + gboolean is_ghashtable; + gboolean is_interface; + gboolean is_error; + gint tag; + + gchar *unparsed; + + gboolean zero_terminated; + gboolean has_length; + gint length; + gboolean has_size; + gint size; + gint array_type; + + GIrNodeType *parameter_type1; + GIrNodeType *parameter_type2; + + gchar *giinterface; + gchar **errors; +}; + +struct _GIrNodeParam +{ + GIrNode node; + + gboolean in; + gboolean out; + gboolean caller_allocates; + gboolean optional; + gboolean retval; + gboolean nullable; + gboolean skip; + gboolean transfer; + gboolean shallow_transfer; + GIScopeType scope; + + gint8 closure; + gint8 destroy; + + GIrNodeType *type; +}; + +struct _GIrNodeProperty +{ + GIrNode node; + + gboolean deprecated; + + gchar *name; + gboolean readable; + gboolean writable; + gboolean construct; + gboolean construct_only; + gboolean transfer; + gboolean shallow_transfer; + + char *setter; + char *getter; + + GIrNodeType *type; +}; + +struct _GIrNodeSignal +{ + GIrNode node; + + gboolean deprecated; + + gboolean run_first; + gboolean run_last; + gboolean run_cleanup; + gboolean no_recurse; + gboolean detailed; + gboolean action; + gboolean no_hooks; + gboolean instance_transfer_full; + + gboolean has_class_closure; + gboolean true_stops_emit; + + gint class_closure; + + GList *parameters; + GIrNodeParam *result; +}; + +struct _GIrNodeVFunc +{ + GIrNode node; + + gboolean is_varargs; /* Not in typelib yet */ + gboolean must_chain_up; + gboolean must_be_implemented; + gboolean must_not_be_implemented; + gboolean is_class_closure; + gboolean throws; + gboolean instance_transfer_full; + + char *invoker; + + GList *parameters; + GIrNodeParam *result; + + gint offset; +}; + +struct _GIrNodeField +{ + GIrNode node; + + gboolean readable; + gboolean writable; + gint bits; + gint offset; + GIrNodeFunction *callback; + + GIrNodeType *type; +}; + +struct _GIrNodeInterface +{ + GIrNode node; + + gboolean abstract; + gboolean deprecated; + gboolean fundamental; + gboolean final_; + + gchar *gtype_name; + gchar *gtype_init; + + gchar *ref_func; + gchar *unref_func; + gchar *set_value_func; + gchar *get_value_func; + + gchar *parent; + gchar *glib_type_struct; + + GList *interfaces; + GList *prerequisites; + + gint alignment; + gint size; + + GList *members; +}; + +struct _GIrNodeValue +{ + GIrNode node; + + gboolean deprecated; + + gint64 value; +}; + +struct _GIrNodeConstant +{ + GIrNode node; + + gboolean deprecated; + + GIrNodeType *type; + + gchar *value; +}; + +struct _GIrNodeEnum +{ + GIrNode node; + + gboolean deprecated; + gint storage_type; + + gchar *gtype_name; + gchar *gtype_init; + gchar *error_domain; + + GList *values; + GList *methods; +}; + +struct _GIrNodeBoxed +{ + GIrNode node; + + gboolean deprecated; + + gchar *gtype_name; + gchar *gtype_init; + + gint alignment; + gint size; + + GList *members; +}; + +struct _GIrNodeStruct +{ + GIrNode node; + + gboolean deprecated; + gboolean disguised; + gboolean opaque; + gboolean pointer; + gboolean is_gtype_struct; + gboolean foreign; + + gchar *gtype_name; + gchar *gtype_init; + + gchar *copy_func; + gchar *free_func; + + gint alignment; + gint size; + + GList *members; +}; + +struct _GIrNodeUnion +{ + GIrNode node; + + gboolean deprecated; + + GList *members; + GList *discriminators; + + gchar *gtype_name; + gchar *gtype_init; + + gchar *copy_func; + gchar *free_func; + + gint alignment; + gint size; + + gint discriminator_offset; + GIrNodeType *discriminator_type; +}; + + +GIrNode * _g_ir_node_new (GIrNodeTypeId type, + GIrModule *module); +void _g_ir_node_free (GIrNode *node); +guint32 _g_ir_node_get_size (GIrNode *node); +guint32 _g_ir_node_get_full_size (GIrNode *node); +void _g_ir_node_build_typelib (GIrNode *node, + GIrNode *parent, + GIrTypelibBuild *build, + guint32 *offset, + guint32 *offset2, + guint16 *count2); +int _g_ir_node_cmp (GIrNode *node, + GIrNode *other); +gboolean _g_ir_node_can_have_member (GIrNode *node); +void _g_ir_node_add_member (GIrNode *node, + GIrNodeFunction *member); +guint32 _g_ir_write_string (const gchar *str, + GHashTable *strings, + guchar *data, + guint32 *offset); + +const gchar * _g_ir_node_param_direction_string (GIrNodeParam * node); +const gchar * _g_ir_node_type_to_string (GIrNodeTypeId type); + +GIrNode *_g_ir_find_node (GIrTypelibBuild *build, + GIrModule *module, + const char *name); + +/* In giroffsets.c */ + +void _g_ir_node_compute_offsets (GIrTypelibBuild *build, + GIrNode *node); + + +G_END_DECLS diff --git a/girepository/girnode.c b/girepository/girnode.c new file mode 100644 index 000000000..2ebe54f85 --- /dev/null +++ b/girepository/girnode.c @@ -0,0 +1,2445 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Typelib creation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include "girnode-private.h" + +#include "gitypelib-internal.h" + +#include +#include +#include + +#ifdef _MSC_VER +#define strtoll _strtoi64 +#define strtoull _strtoui64 +#endif + +static gulong string_count = 0; +static gulong unique_string_count = 0; +static gulong string_size = 0; +static gulong unique_string_size = 0; +static gulong types_count = 0; +static gulong unique_types_count = 0; + +void +_g_irnode_init_stats (void) +{ + string_count = 0; + unique_string_count = 0; + string_size = 0; + unique_string_size = 0; + types_count = 0; + unique_types_count = 0; +} + +void +_g_irnode_dump_stats (void) +{ + g_message ("%lu strings (%lu before sharing), %lu bytes (%lu before sharing)", + unique_string_count, string_count, unique_string_size, string_size); + g_message ("%lu types (%lu before sharing)", unique_types_count, types_count); +} + +#define DO_ALIGNED_COPY(dest_addr, value, type) \ +do { \ + type tmp_var; \ + tmp_var = value; \ + memcpy(dest_addr, &tmp_var, sizeof(type)); \ +} while(0) + +#define ALIGN_VALUE(this, boundary) \ + (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1))) + + +const gchar * +_g_ir_node_type_to_string (GIrNodeTypeId type) +{ + switch (type) + { + case G_IR_NODE_FUNCTION: + return "function"; + case G_IR_NODE_CALLBACK: + return "callback"; + case G_IR_NODE_PARAM: + return "param"; + case G_IR_NODE_TYPE: + return "type"; + case G_IR_NODE_OBJECT: + return "object"; + case G_IR_NODE_INTERFACE: + return "interface"; + case G_IR_NODE_SIGNAL: + return "signal"; + case G_IR_NODE_PROPERTY: + return "property"; + case G_IR_NODE_VFUNC: + return "vfunc"; + case G_IR_NODE_FIELD: + return "field"; + case G_IR_NODE_ENUM: + return "enum"; + case G_IR_NODE_FLAGS: + return "flags"; + case G_IR_NODE_BOXED: + return "boxed"; + case G_IR_NODE_STRUCT: + return "struct"; + case G_IR_NODE_VALUE: + return "value"; + case G_IR_NODE_CONSTANT: + return "constant"; + case G_IR_NODE_XREF: + return "xref"; + case G_IR_NODE_UNION: + return "union"; + default: + return "unknown"; + } +} + +GIrNode * +_g_ir_node_new (GIrNodeTypeId type, + GIrModule *module) +{ + GIrNode *node = NULL; + + switch (type) + { + case G_IR_NODE_FUNCTION: + case G_IR_NODE_CALLBACK: + node = g_malloc0 (sizeof (GIrNodeFunction)); + break; + + case G_IR_NODE_PARAM: + node = g_malloc0 (sizeof (GIrNodeParam)); + break; + + case G_IR_NODE_TYPE: + node = g_malloc0 (sizeof (GIrNodeType)); + break; + + case G_IR_NODE_OBJECT: + case G_IR_NODE_INTERFACE: + node = g_malloc0 (sizeof (GIrNodeInterface)); + break; + + case G_IR_NODE_SIGNAL: + node = g_malloc0 (sizeof (GIrNodeSignal)); + break; + + case G_IR_NODE_PROPERTY: + node = g_malloc0 (sizeof (GIrNodeProperty)); + break; + + case G_IR_NODE_VFUNC: + node = g_malloc0 (sizeof (GIrNodeFunction)); + break; + + case G_IR_NODE_FIELD: + node = g_malloc0 (sizeof (GIrNodeField)); + break; + + case G_IR_NODE_ENUM: + case G_IR_NODE_FLAGS: + node = g_malloc0 (sizeof (GIrNodeEnum)); + break; + + case G_IR_NODE_BOXED: + node = g_malloc0 (sizeof (GIrNodeBoxed)); + break; + + case G_IR_NODE_STRUCT: + node = g_malloc0 (sizeof (GIrNodeStruct)); + break; + + case G_IR_NODE_VALUE: + node = g_malloc0 (sizeof (GIrNodeValue)); + break; + + case G_IR_NODE_CONSTANT: + node = g_malloc0 (sizeof (GIrNodeConstant)); + break; + + case G_IR_NODE_XREF: + node = g_malloc0 (sizeof (GIrNodeXRef)); + break; + + case G_IR_NODE_UNION: + node = g_malloc0 (sizeof (GIrNodeUnion)); + break; + + default: + g_error ("Unhandled node type %d\n", type); + break; + } + + node->type = type; + node->module = module; + node->offset = 0; + node->attributes = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_free); + + return node; +} + +void +_g_ir_node_free (GIrNode *node) +{ + GList *l; + + if (node == NULL) + return; + + switch (node->type) + { + case G_IR_NODE_FUNCTION: + case G_IR_NODE_CALLBACK: + { + GIrNodeFunction *function = (GIrNodeFunction *)node; + + g_free (node->name); + g_free (function->symbol); + g_free (function->property); + _g_ir_node_free ((GIrNode *)function->result); + for (l = function->parameters; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + g_list_free (function->parameters); + } + break; + + case G_IR_NODE_TYPE: + { + GIrNodeType *type = (GIrNodeType *)node; + + g_free (node->name); + _g_ir_node_free ((GIrNode *)type->parameter_type1); + _g_ir_node_free ((GIrNode *)type->parameter_type2); + + g_free (type->giinterface); + g_strfreev (type->errors); + + } + break; + + case G_IR_NODE_PARAM: + { + GIrNodeParam *param = (GIrNodeParam *)node; + + g_free (node->name); + _g_ir_node_free ((GIrNode *)param->type); + } + break; + + case G_IR_NODE_PROPERTY: + { + GIrNodeProperty *property = (GIrNodeProperty *)node; + + g_free (node->name); + g_free (property->setter); + g_free (property->getter); + _g_ir_node_free ((GIrNode *)property->type); + } + break; + + case G_IR_NODE_SIGNAL: + { + GIrNodeSignal *signal = (GIrNodeSignal *)node; + + g_free (node->name); + for (l = signal->parameters; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + g_list_free (signal->parameters); + _g_ir_node_free ((GIrNode *)signal->result); + } + break; + + case G_IR_NODE_VFUNC: + { + GIrNodeVFunc *vfunc = (GIrNodeVFunc *)node; + + g_free (node->name); + g_free (vfunc->invoker); + for (l = vfunc->parameters; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + g_list_free (vfunc->parameters); + _g_ir_node_free ((GIrNode *)vfunc->result); + } + break; + + case G_IR_NODE_FIELD: + { + GIrNodeField *field = (GIrNodeField *)node; + + g_free (node->name); + _g_ir_node_free ((GIrNode *)field->type); + _g_ir_node_free ((GIrNode *)field->callback); + } + break; + + case G_IR_NODE_OBJECT: + case G_IR_NODE_INTERFACE: + { + GIrNodeInterface *iface = (GIrNodeInterface *)node; + + g_free (node->name); + g_free (iface->gtype_name); + g_free (iface->gtype_init); + g_free (iface->ref_func); + g_free (iface->unref_func); + g_free (iface->set_value_func); + g_free (iface->get_value_func); + + + g_free (iface->glib_type_struct); + g_free (iface->parent); + + for (l = iface->interfaces; l; l = l->next) + g_free ((GIrNode *)l->data); + g_list_free (iface->interfaces); + + for (l = iface->members; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + g_list_free (iface->members); + + } + break; + + case G_IR_NODE_VALUE: + { + g_free (node->name); + } + break; + + case G_IR_NODE_ENUM: + case G_IR_NODE_FLAGS: + { + GIrNodeEnum *enum_ = (GIrNodeEnum *)node; + + g_free (node->name); + g_free (enum_->gtype_name); + g_free (enum_->gtype_init); + g_free (enum_->error_domain); + + for (l = enum_->values; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + g_list_free (enum_->values); + + for (l = enum_->methods; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + g_list_free (enum_->methods); + } + break; + + case G_IR_NODE_BOXED: + { + GIrNodeBoxed *boxed = (GIrNodeBoxed *)node; + + g_free (node->name); + g_free (boxed->gtype_name); + g_free (boxed->gtype_init); + + for (l = boxed->members; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + g_list_free (boxed->members); + } + break; + + case G_IR_NODE_STRUCT: + { + GIrNodeStruct *struct_ = (GIrNodeStruct *)node; + + g_free (node->name); + g_free (struct_->gtype_name); + g_free (struct_->gtype_init); + g_free (struct_->copy_func); + g_free (struct_->free_func); + + for (l = struct_->members; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + g_list_free (struct_->members); + } + break; + + case G_IR_NODE_CONSTANT: + { + GIrNodeConstant *constant = (GIrNodeConstant *)node; + + g_free (node->name); + g_free (constant->value); + _g_ir_node_free ((GIrNode *)constant->type); + } + break; + + case G_IR_NODE_XREF: + { + GIrNodeXRef *xref = (GIrNodeXRef *)node; + + g_free (node->name); + g_free (xref->namespace); + } + break; + + case G_IR_NODE_UNION: + { + GIrNodeUnion *union_ = (GIrNodeUnion *)node; + + g_free (node->name); + g_free (union_->gtype_name); + g_free (union_->gtype_init); + g_free (union_->copy_func); + g_free (union_->free_func); + + _g_ir_node_free ((GIrNode *)union_->discriminator_type); + for (l = union_->members; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + for (l = union_->discriminators; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + } + break; + + default: + g_error ("Unhandled node type %d\n", node->type); + break; + } + + g_hash_table_destroy (node->attributes); + + g_free (node); +} + +/* returns the fixed size of the blob */ +guint32 +_g_ir_node_get_size (GIrNode *node) +{ + GList *l; + gint size, n; + + switch (node->type) + { + case G_IR_NODE_CALLBACK: + size = sizeof (CallbackBlob); + break; + + case G_IR_NODE_FUNCTION: + size = sizeof (FunctionBlob); + break; + + case G_IR_NODE_PARAM: + /* See the comment in the G_IR_NODE_PARAM/ArgBlob writing below */ + size = sizeof (ArgBlob) - sizeof (SimpleTypeBlob); + break; + + case G_IR_NODE_TYPE: + size = sizeof (SimpleTypeBlob); + break; + + case G_IR_NODE_OBJECT: + { + GIrNodeInterface *iface = (GIrNodeInterface *)node; + + n = g_list_length (iface->interfaces); + size = sizeof (ObjectBlob) + 2 * (n + (n % 2)); + + for (l = iface->members; l; l = l->next) + size += _g_ir_node_get_size ((GIrNode *)l->data); + } + break; + + case G_IR_NODE_INTERFACE: + { + GIrNodeInterface *iface = (GIrNodeInterface *)node; + + n = g_list_length (iface->prerequisites); + size = sizeof (InterfaceBlob) + 2 * (n + (n % 2)); + + for (l = iface->members; l; l = l->next) + size += _g_ir_node_get_size ((GIrNode *)l->data); + } + break; + + case G_IR_NODE_ENUM: + case G_IR_NODE_FLAGS: + { + GIrNodeEnum *enum_ = (GIrNodeEnum *)node; + + size = sizeof (EnumBlob); + for (l = enum_->values; l; l = l->next) + size += _g_ir_node_get_size ((GIrNode *)l->data); + for (l = enum_->methods; l; l = l->next) + size += _g_ir_node_get_size ((GIrNode *)l->data); + } + break; + + case G_IR_NODE_VALUE: + size = sizeof (ValueBlob); + break; + + case G_IR_NODE_STRUCT: + { + GIrNodeStruct *struct_ = (GIrNodeStruct *)node; + + size = sizeof (StructBlob); + for (l = struct_->members; l; l = l->next) + size += _g_ir_node_get_size ((GIrNode *)l->data); + } + break; + + case G_IR_NODE_BOXED: + { + GIrNodeBoxed *boxed = (GIrNodeBoxed *)node; + + size = sizeof (StructBlob); + for (l = boxed->members; l; l = l->next) + size += _g_ir_node_get_size ((GIrNode *)l->data); + } + break; + + case G_IR_NODE_PROPERTY: + size = sizeof (PropertyBlob); + break; + + case G_IR_NODE_SIGNAL: + size = sizeof (SignalBlob); + break; + + case G_IR_NODE_VFUNC: + size = sizeof (VFuncBlob); + break; + + case G_IR_NODE_FIELD: + { + GIrNodeField *field = (GIrNodeField *)node; + + size = sizeof (FieldBlob); + if (field->callback) + size += _g_ir_node_get_size ((GIrNode *)field->callback); + } + break; + + case G_IR_NODE_CONSTANT: + size = sizeof (ConstantBlob); + break; + + case G_IR_NODE_XREF: + size = 0; + break; + + case G_IR_NODE_UNION: + { + GIrNodeUnion *union_ = (GIrNodeUnion *)node; + + size = sizeof (UnionBlob); + for (l = union_->members; l; l = l->next) + size += _g_ir_node_get_size ((GIrNode *)l->data); + for (l = union_->discriminators; l; l = l->next) + size += _g_ir_node_get_size ((GIrNode *)l->data); + } + break; + + default: + g_error ("Unhandled node type '%s'\n", + _g_ir_node_type_to_string (node->type)); + size = 0; + } + + g_debug ("node %p type '%s' size %d", node, + _g_ir_node_type_to_string (node->type), size); + + return size; +} + +static void +add_attribute_size (gpointer key, gpointer value, gpointer data) +{ + const gchar *key_str = key; + const gchar *value_str = value; + gint *size_p = data; + + *size_p += sizeof (AttributeBlob); + *size_p += ALIGN_VALUE (strlen (key_str) + 1, 4); + *size_p += ALIGN_VALUE (strlen (value_str) + 1, 4); +} + +/* returns the full size of the blob including variable-size parts (including attributes) */ +static guint32 +_g_ir_node_get_full_size_internal (GIrNode *parent, + GIrNode *node) +{ + GList *l; + gint size, n; + + if (node == NULL && parent != NULL) + g_error ("Caught NULL node, parent=%s", parent->name); + + g_debug ("node %p type '%s'", node, + _g_ir_node_type_to_string (node->type)); + + switch (node->type) + { + case G_IR_NODE_CALLBACK: + { + GIrNodeFunction *function = (GIrNodeFunction *)node; + size = sizeof (CallbackBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + for (l = function->parameters; l; l = l->next) + { + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + } + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)function->result); + } + break; + + case G_IR_NODE_FUNCTION: + { + GIrNodeFunction *function = (GIrNodeFunction *)node; + size = sizeof (FunctionBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += ALIGN_VALUE (strlen (function->symbol) + 1, 4); + for (l = function->parameters; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)function->result); + } + break; + + case G_IR_NODE_PARAM: + { + GIrNodeParam *param = (GIrNodeParam *)node; + + /* See the comment in the G_IR_NODE_PARAM/ArgBlob writing below */ + size = sizeof (ArgBlob) - sizeof (SimpleTypeBlob); + if (node->name) + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)param->type); + } + break; + + case G_IR_NODE_TYPE: + { + GIrNodeType *type = (GIrNodeType *)node; + size = sizeof (SimpleTypeBlob); + if (!GI_TYPE_TAG_IS_BASIC (type->tag)) + { + g_debug ("node %p type tag '%s'", node, + g_type_tag_to_string (type->tag)); + + switch (type->tag) + { + case GI_TYPE_TAG_ARRAY: + size = sizeof (ArrayTypeBlob); + if (type->parameter_type1) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)type->parameter_type1); + break; + case GI_TYPE_TAG_INTERFACE: + size += sizeof (InterfaceTypeBlob); + break; + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + size += sizeof (ParamTypeBlob); + if (type->parameter_type1) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)type->parameter_type1); + break; + case GI_TYPE_TAG_GHASH: + size += sizeof (ParamTypeBlob) * 2; + if (type->parameter_type1) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)type->parameter_type1); + if (type->parameter_type2) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)type->parameter_type2); + break; + case GI_TYPE_TAG_ERROR: + size += sizeof (ErrorTypeBlob); + break; + default: + g_error ("Unknown type tag %d\n", type->tag); + break; + } + } + } + break; + + case G_IR_NODE_OBJECT: + { + GIrNodeInterface *iface = (GIrNodeInterface *)node; + + n = g_list_length (iface->interfaces); + size = sizeof(ObjectBlob); + if (iface->parent) + size += ALIGN_VALUE (strlen (iface->parent) + 1, 4); + if (iface->glib_type_struct) + size += ALIGN_VALUE (strlen (iface->glib_type_struct) + 1, 4); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += ALIGN_VALUE (strlen (iface->gtype_name) + 1, 4); + if (iface->gtype_init) + size += ALIGN_VALUE (strlen (iface->gtype_init) + 1, 4); + if (iface->ref_func) + size += ALIGN_VALUE (strlen (iface->ref_func) + 1, 4); + if (iface->unref_func) + size += ALIGN_VALUE (strlen (iface->unref_func) + 1, 4); + if (iface->set_value_func) + size += ALIGN_VALUE (strlen (iface->set_value_func) + 1, 4); + if (iface->get_value_func) + size += ALIGN_VALUE (strlen (iface->get_value_func) + 1, 4); + size += 2 * (n + (n % 2)); + + for (l = iface->members; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + } + break; + + case G_IR_NODE_INTERFACE: + { + GIrNodeInterface *iface = (GIrNodeInterface *)node; + + n = g_list_length (iface->prerequisites); + size = sizeof (InterfaceBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += ALIGN_VALUE (strlen (iface->gtype_name) + 1, 4); + size += ALIGN_VALUE (strlen (iface->gtype_init) + 1, 4); + size += 2 * (n + (n % 2)); + + for (l = iface->members; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + } + break; + + case G_IR_NODE_ENUM: + case G_IR_NODE_FLAGS: + { + GIrNodeEnum *enum_ = (GIrNodeEnum *)node; + + size = sizeof (EnumBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + if (enum_->gtype_name) + { + size += ALIGN_VALUE (strlen (enum_->gtype_name) + 1, 4); + size += ALIGN_VALUE (strlen (enum_->gtype_init) + 1, 4); + } + if (enum_->error_domain) + size += ALIGN_VALUE (strlen (enum_->error_domain) + 1, 4); + + for (l = enum_->values; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + for (l = enum_->methods; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + } + break; + + case G_IR_NODE_VALUE: + { + size = sizeof (ValueBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + } + break; + + case G_IR_NODE_STRUCT: + { + GIrNodeStruct *struct_ = (GIrNodeStruct *)node; + + size = sizeof (StructBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + if (struct_->gtype_name) + size += ALIGN_VALUE (strlen (struct_->gtype_name) + 1, 4); + if (struct_->gtype_init) + size += ALIGN_VALUE (strlen (struct_->gtype_init) + 1, 4); + if (struct_->copy_func) + size += ALIGN_VALUE (strlen (struct_->copy_func) + 1, 4); + if (struct_->free_func) + size += ALIGN_VALUE (strlen (struct_->free_func) + 1, 4); + for (l = struct_->members; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + } + break; + + case G_IR_NODE_BOXED: + { + GIrNodeBoxed *boxed = (GIrNodeBoxed *)node; + + size = sizeof (StructBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + if (boxed->gtype_name) + { + size += ALIGN_VALUE (strlen (boxed->gtype_name) + 1, 4); + size += ALIGN_VALUE (strlen (boxed->gtype_init) + 1, 4); + } + for (l = boxed->members; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + } + break; + + case G_IR_NODE_PROPERTY: + { + GIrNodeProperty *prop = (GIrNodeProperty *)node; + + size = sizeof (PropertyBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)prop->type); + } + break; + + case G_IR_NODE_SIGNAL: + { + GIrNodeSignal *signal = (GIrNodeSignal *)node; + + size = sizeof (SignalBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + for (l = signal->parameters; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)signal->result); + } + break; + + case G_IR_NODE_VFUNC: + { + GIrNodeVFunc *vfunc = (GIrNodeVFunc *)node; + + size = sizeof (VFuncBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + for (l = vfunc->parameters; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)vfunc->result); + } + break; + + case G_IR_NODE_FIELD: + { + GIrNodeField *field = (GIrNodeField *)node; + + size = sizeof (FieldBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + if (field->callback) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)field->callback); + else + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)field->type); + } + break; + + case G_IR_NODE_CONSTANT: + { + GIrNodeConstant *constant = (GIrNodeConstant *)node; + + size = sizeof (ConstantBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + /* FIXME non-string values */ + size += ALIGN_VALUE (strlen (constant->value) + 1, 4); + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)constant->type); + } + break; + + case G_IR_NODE_XREF: + { + GIrNodeXRef *xref = (GIrNodeXRef *)node; + + size = 0; + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += ALIGN_VALUE (strlen (xref->namespace) + 1, 4); + } + break; + + case G_IR_NODE_UNION: + { + GIrNodeUnion *union_ = (GIrNodeUnion *)node; + + size = sizeof (UnionBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + if (union_->gtype_name) + size += ALIGN_VALUE (strlen (union_->gtype_name) + 1, 4); + if (union_->gtype_init) + size += ALIGN_VALUE (strlen (union_->gtype_init) + 1, 4); + if (union_->copy_func) + size += ALIGN_VALUE (strlen (union_->copy_func) + 1, 4); + if (union_->free_func) + size += ALIGN_VALUE (strlen (union_->free_func) + 1, 4); + for (l = union_->members; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + for (l = union_->discriminators; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + } + break; + + default: + g_error ("Unknown type tag %d\n", node->type); + size = 0; + } + + g_debug ("node %s%s%s%p type '%s' full size %d", + node->name ? "'" : "", + node->name ? node->name : "", + node->name ? "' " : "", + node, _g_ir_node_type_to_string (node->type), size); + + g_hash_table_foreach (node->attributes, add_attribute_size, &size); + + return size; +} + +guint32 +_g_ir_node_get_full_size (GIrNode *node) +{ + return _g_ir_node_get_full_size_internal (NULL, node); +} + +int +_g_ir_node_cmp (GIrNode *node, + GIrNode *other) +{ + if (node->type < other->type) + return -1; + else if (node->type > other->type) + return 1; + else + return strcmp (node->name, other->name); +} + +gboolean +_g_ir_node_can_have_member (GIrNode *node) +{ + switch (node->type) + { + case G_IR_NODE_OBJECT: + case G_IR_NODE_INTERFACE: + case G_IR_NODE_BOXED: + case G_IR_NODE_STRUCT: + case G_IR_NODE_UNION: + return TRUE; + /* list others individually rather than with default: so that compiler + * warns if new node types are added without adding them to the switch + */ + case G_IR_NODE_INVALID: + case G_IR_NODE_FUNCTION: + case G_IR_NODE_CALLBACK: + case G_IR_NODE_ENUM: + case G_IR_NODE_FLAGS: + case G_IR_NODE_CONSTANT: + case G_IR_NODE_INVALID_0: + case G_IR_NODE_PARAM: + case G_IR_NODE_TYPE: + case G_IR_NODE_PROPERTY: + case G_IR_NODE_SIGNAL: + case G_IR_NODE_VALUE: + case G_IR_NODE_VFUNC: + case G_IR_NODE_FIELD: + case G_IR_NODE_XREF: + return FALSE; + default: + g_assert_not_reached (); + }; + return FALSE; +} + +void +_g_ir_node_add_member (GIrNode *node, + GIrNodeFunction *member) +{ + g_return_if_fail (node != NULL); + g_return_if_fail (member != NULL); + + switch (node->type) + { + case G_IR_NODE_OBJECT: + case G_IR_NODE_INTERFACE: + { + GIrNodeInterface *iface = (GIrNodeInterface *)node; + iface->members = + g_list_insert_sorted (iface->members, member, + (GCompareFunc) _g_ir_node_cmp); + break; + } + case G_IR_NODE_BOXED: + { + GIrNodeBoxed *boxed = (GIrNodeBoxed *)node; + boxed->members = + g_list_insert_sorted (boxed->members, member, + (GCompareFunc) _g_ir_node_cmp); + break; + } + case G_IR_NODE_STRUCT: + { + GIrNodeStruct *struct_ = (GIrNodeStruct *)node; + struct_->members = + g_list_insert_sorted (struct_->members, member, + (GCompareFunc) _g_ir_node_cmp); + break; + } + case G_IR_NODE_UNION: + { + GIrNodeUnion *union_ = (GIrNodeUnion *)node; + union_->members = + g_list_insert_sorted (union_->members, member, + (GCompareFunc) _g_ir_node_cmp); + break; + } + default: + g_error ("Cannot add a member to unknown type tag type %d\n", + node->type); + break; + } +} + +const gchar * +_g_ir_node_param_direction_string (GIrNodeParam * node) +{ + if (node->out) + { + if (node->in) + return "in-out"; + else + return "out"; + } + return "in"; +} + +static gint64 +parse_int_value (const gchar *str) +{ + return g_ascii_strtoll (str, NULL, 0); +} + +static guint64 +parse_uint_value (const gchar *str) +{ + return g_ascii_strtoull (str, NULL, 0); +} + +static gdouble +parse_float_value (const gchar *str) +{ + return g_ascii_strtod (str, NULL); +} + +static gboolean +parse_boolean_value (const gchar *str) +{ + if (g_ascii_strcasecmp (str, "TRUE") == 0) + return TRUE; + + if (g_ascii_strcasecmp (str, "FALSE") == 0) + return FALSE; + + return parse_int_value (str) ? TRUE : FALSE; +} + +static GIrNode * +find_entry_node (GIrTypelibBuild *build, + const gchar *name, + guint16 *idx) + +{ + GIrModule *module = build->module; + GList *l; + gint i; + gchar **names; + gint n_names; + GIrNode *result = NULL; + + g_assert (name != NULL); + g_assert (strlen (name) > 0); + + names = g_strsplit (name, ".", 0); + n_names = g_strv_length (names); + if (n_names > 2) + g_error ("Too many name parts"); + + for (l = module->entries, i = 1; l; l = l->next, i++) + { + GIrNode *node = (GIrNode *)l->data; + + if (n_names > 1) + { + if (node->type != G_IR_NODE_XREF) + continue; + + if (((GIrNodeXRef *)node)->namespace == NULL || + strcmp (((GIrNodeXRef *)node)->namespace, names[0]) != 0) + continue; + } + + if (strcmp (node->name, names[n_names - 1]) == 0) + { + if (idx) + *idx = i; + + result = node; + goto out; + } + } + + if (n_names > 1) + { + GIrNode *node = _g_ir_node_new (G_IR_NODE_XREF, module); + + ((GIrNodeXRef *)node)->namespace = g_strdup (names[0]); + node->name = g_strdup (names[1]); + + module->entries = g_list_append (module->entries, node); + + if (idx) + *idx = g_list_length (module->entries); + + result = node; + + g_debug ("Creating XREF: %s %s", names[0], names[1]); + + goto out; + } + + + _g_ir_module_fatal (build, -1, "type reference '%s' not found", + name); + out: + + g_strfreev (names); + + return result; +} + +static guint16 +find_entry (GIrTypelibBuild *build, + const gchar *name) +{ + guint16 idx = 0; + + find_entry_node (build, name, &idx); + + return idx; +} + +static GIrModule * +find_namespace (GIrModule *module, + const char *name) +{ + GIrModule *target; + GList *l; + + if (strcmp (module->name, name) == 0) + return module; + + for (l = module->include_modules; l; l = l->next) + { + GIrModule *submodule = l->data; + + if (strcmp (submodule->name, name) == 0) + return submodule; + + target = find_namespace (submodule, name); + if (target) + return target; + } + return NULL; +} + +GIrNode * +_g_ir_find_node (GIrTypelibBuild *build, + GIrModule *src_module, + const char *name) +{ + GList *l; + GIrNode *return_node = NULL; + char **names = g_strsplit (name, ".", 0); + gint n_names = g_strv_length (names); + const char *target_name; + GIrModule *target_module; + + if (n_names == 1) + { + target_module = src_module; + target_name = name; + } + else + { + target_module = find_namespace (build->module, names[0]); + target_name = names[1]; + } + + /* find_namespace() may return NULL. */ + if (target_module == NULL) + goto done; + + for (l = target_module->entries; l; l = l->next) + { + GIrNode *node = (GIrNode *)l->data; + + if (strcmp (node->name, target_name) == 0) + { + return_node = node; + break; + } + } + +done: + g_strfreev (names); + + return return_node; +} + +static int +get_index_of_member_type (GIrNodeInterface *node, + GIrNodeTypeId type, + const char *name) +{ + guint index = -1; + GList *l; + + for (l = node->members; l; l = l->next) + { + GIrNode *member_node = l->data; + + if (member_node->type != type) + continue; + + index++; + + if (strcmp (member_node->name, name) == 0) + break; + } + + return index; +} + +static void +serialize_type (GIrTypelibBuild *build, + GIrNodeType *node, + GString *str) +{ + gint i; + + if (GI_TYPE_TAG_IS_BASIC (node->tag)) + { + g_string_append_printf (str, "%s%s", g_type_tag_to_string (node->tag), + node->is_pointer ? "*" : ""); + } + else if (node->tag == GI_TYPE_TAG_ARRAY) + { + if (node->array_type == GI_ARRAY_TYPE_C) + { + serialize_type (build, node->parameter_type1, str); + g_string_append (str, "["); + + if (node->has_length) + g_string_append_printf (str, "length=%d", node->length); + else if (node->has_size) + g_string_append_printf (str, "fixed-size=%d", node->size); + + if (node->zero_terminated) + g_string_append_printf (str, "%szero-terminated=1", + node->has_length ? "," : ""); + + g_string_append (str, "]"); + if (node->is_pointer) + g_string_append (str, "*"); + } + else if (node->array_type == GI_ARRAY_TYPE_BYTE_ARRAY) + { + /* We on purpose skip serializing parameter_type1, which should + always be void* + */ + g_string_append (str, "GByteArray"); + } + else + { + if (node->array_type == GI_ARRAY_TYPE_ARRAY) + g_string_append (str, "GArray"); + else + g_string_append (str, "GPtrArray"); + if (node->parameter_type1) + { + g_string_append (str, "<"); + serialize_type (build, node->parameter_type1, str); + g_string_append (str, ">"); + } + } + } + else if (node->tag == GI_TYPE_TAG_INTERFACE) + { + GIrNode *iface; + gchar *name; + + iface = find_entry_node (build, node->giinterface, NULL); + if (iface) + { + if (iface->type == G_IR_NODE_XREF) + g_string_append_printf (str, "%s.", ((GIrNodeXRef *)iface)->namespace); + name = iface->name; + } + else + { + g_warning ("Interface for type reference %s not found", node->giinterface); + name = node->giinterface; + } + + g_string_append_printf (str, "%s%s", name, + node->is_pointer ? "*" : ""); + } + else if (node->tag == GI_TYPE_TAG_GLIST) + { + g_string_append (str, "GList"); + if (node->parameter_type1) + { + g_string_append (str, "<"); + serialize_type (build, node->parameter_type1, str); + g_string_append (str, ">"); + } + } + else if (node->tag == GI_TYPE_TAG_GSLIST) + { + g_string_append (str, "GSList"); + if (node->parameter_type1) + { + g_string_append (str, "<"); + serialize_type (build, node->parameter_type1, str); + g_string_append (str, ">"); + } + } + else if (node->tag == GI_TYPE_TAG_GHASH) + { + g_string_append (str, "GHashTable"); + if (node->parameter_type1) + { + g_string_append (str, "<"); + serialize_type (build, node->parameter_type1, str); + g_string_append (str, ","); + serialize_type (build, node->parameter_type2, str); + g_string_append (str, ">"); + } + } + else if (node->tag == GI_TYPE_TAG_ERROR) + { + g_string_append (str, "GError"); + if (node->errors) + { + g_string_append (str, "<"); + for (i = 0; node->errors[i]; i++) + { + if (i > 0) + g_string_append (str, ","); + g_string_append (str, node->errors[i]); + } + g_string_append (str, ">"); + } + } +} + +static void +_g_ir_node_build_members (GList **members, + GIrNodeTypeId type, + guint16 *count, + GIrNode *parent, + GIrTypelibBuild *build, + guint32 *offset, + guint32 *offset2, + guint16 *count2) +{ + GList *l = *members; + + while (l) + { + GIrNode *member = (GIrNode *)l->data; + GList *next = l->next; + + if (member->type == type) + { + (*count)++; + _g_ir_node_build_typelib (member, parent, build, offset, offset2, count2); + *members = g_list_delete_link (*members, l); + } + l = next; + } +} + +static void +_g_ir_node_check_unhandled_members (GList **members, + GIrNodeTypeId container_type) +{ +#if 0 + if (*members) + { + GList *l; + + for (l = *members; l; l = l->next) + { + GIrNode *member = (GIrNode *)l->data; + g_printerr ("Unhandled '%s' member '%s' type '%s'\n", + _g_ir_node_type_to_string (container_type), + member->name, + _g_ir_node_type_to_string (member->type)); + } + + g_list_free (*members); + *members = NULL; + + g_error ("Unhandled members. Aborting."); + } +#else + g_list_free (*members); + *members = NULL; +#endif +} + +void +_g_ir_node_build_typelib (GIrNode *node, + GIrNode *parent, + GIrTypelibBuild *build, + guint32 *offset, + guint32 *offset2, + guint16 *count2) +{ + gboolean appended_stack; + GHashTable *strings = build->strings; + GHashTable *types = build->types; + guchar *data = build->data; + GList *l; + guint32 old_offset = *offset; + guint32 old_offset2 = *offset2; + + g_assert (node != NULL); + + g_debug ("build_typelib: %s%s(%s)", + node->name ? node->name : "", + node->name ? " " : "", + _g_ir_node_type_to_string (node->type)); + + if (build->stack) + appended_stack = node != (GIrNode*)build->stack->data; + else + appended_stack = TRUE; + if (appended_stack) + build->stack = g_list_prepend (build->stack, node); + + _g_ir_node_compute_offsets (build, node); + + /* We should only be building each node once. If we do a typelib expansion, we also + * reset the offset in girmodule.c. + */ + g_assert (node->offset == 0); + node->offset = *offset; + build->nodes_with_attributes = g_list_prepend (build->nodes_with_attributes, node); + + build->n_attributes += g_hash_table_size (node->attributes); + + switch (node->type) + { + case G_IR_NODE_TYPE: + { + GIrNodeType *type = (GIrNodeType *)node; + SimpleTypeBlob *blob = (SimpleTypeBlob *)&data[*offset]; + + *offset += sizeof (SimpleTypeBlob); + + if (GI_TYPE_TAG_IS_BASIC (type->tag)) + { + blob->flags.reserved = 0; + blob->flags.reserved2 = 0; + blob->flags.pointer = type->is_pointer; + blob->flags.reserved3 = 0; + blob->flags.tag = type->tag; + } + else + { + GString *str; + gchar *s; + gpointer value; + + str = g_string_new (0); + serialize_type (build, type, str); + s = g_string_free (str, FALSE); + + types_count += 1; + value = g_hash_table_lookup (types, s); + if (value) + { + blob->offset = GPOINTER_TO_UINT (value); + g_free (s); + } + else + { + unique_types_count += 1; + g_hash_table_insert (types, s, GUINT_TO_POINTER(*offset2)); + + blob->offset = *offset2; + switch (type->tag) + { + case GI_TYPE_TAG_ARRAY: + { + ArrayTypeBlob *array = (ArrayTypeBlob *)&data[*offset2]; + guint32 pos; + + array->pointer = type->is_pointer; + array->reserved = 0; + array->tag = type->tag; + array->zero_terminated = type->zero_terminated; + array->has_length = type->has_length; + array->has_size = type->has_size; + array->array_type = type->array_type; + array->reserved2 = 0; + if (array->has_length) + array->dimensions.length = type->length; + else if (array->has_size) + array->dimensions.size = type->size; + else + array->dimensions.length = -1; + + pos = *offset2 + G_STRUCT_OFFSET (ArrayTypeBlob, type); + *offset2 += sizeof (ArrayTypeBlob); + + _g_ir_node_build_typelib ((GIrNode *)type->parameter_type1, + node, build, &pos, offset2, NULL); + } + break; + + case GI_TYPE_TAG_INTERFACE: + { + InterfaceTypeBlob *iface = (InterfaceTypeBlob *)&data[*offset2]; + *offset2 += sizeof (InterfaceTypeBlob); + + iface->pointer = type->is_pointer; + iface->reserved = 0; + iface->tag = type->tag; + iface->reserved2 = 0; + iface->interface = find_entry (build, type->giinterface); + + } + break; + + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + { + ParamTypeBlob *param = (ParamTypeBlob *)&data[*offset2]; + guint32 pos; + + param->pointer = 1; + param->reserved = 0; + param->tag = type->tag; + param->reserved2 = 0; + param->n_types = 1; + + pos = *offset2 + G_STRUCT_OFFSET (ParamTypeBlob, type); + *offset2 += sizeof (ParamTypeBlob) + sizeof (SimpleTypeBlob); + + _g_ir_node_build_typelib ((GIrNode *)type->parameter_type1, + node, build, &pos, offset2, NULL); + } + break; + + case GI_TYPE_TAG_GHASH: + { + ParamTypeBlob *param = (ParamTypeBlob *)&data[*offset2]; + guint32 pos; + + param->pointer = 1; + param->reserved = 0; + param->tag = type->tag; + param->reserved2 = 0; + param->n_types = 2; + + pos = *offset2 + G_STRUCT_OFFSET (ParamTypeBlob, type); + *offset2 += sizeof (ParamTypeBlob) + sizeof (SimpleTypeBlob)*2; + + _g_ir_node_build_typelib ((GIrNode *)type->parameter_type1, + node, build, &pos, offset2, NULL); + _g_ir_node_build_typelib ((GIrNode *)type->parameter_type2, + node, build, &pos, offset2, NULL); + } + break; + + case GI_TYPE_TAG_ERROR: + { + ErrorTypeBlob *error_blob = (ErrorTypeBlob *)&data[*offset2]; + + error_blob->pointer = 1; + error_blob->reserved = 0; + error_blob->tag = type->tag; + error_blob->reserved2 = 0; + error_blob->n_domains = 0; + + *offset2 += sizeof (ErrorTypeBlob); + } + break; + + default: + g_error ("Unknown type tag %d\n", type->tag); + break; + } + } + } + } + break; + + case G_IR_NODE_FIELD: + { + GIrNodeField *field = (GIrNodeField *)node; + FieldBlob *blob; + + blob = (FieldBlob *)&data[*offset]; + + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->readable = field->readable; + blob->writable = field->writable; + blob->reserved = 0; + blob->bits = 0; + if (field->offset >= 0) + blob->struct_offset = field->offset; + else + blob->struct_offset = 0xFFFF; /* mark as unknown */ + + if (field->callback) + { + blob->has_embedded_type = TRUE; + blob->type.offset = GI_INFO_TYPE_CALLBACK; + *offset += sizeof (FieldBlob); + _g_ir_node_build_typelib ((GIrNode *)field->callback, + node, build, offset, offset2, NULL); + /* Fields with callbacks are bigger than normal, update count2 + * as an extra hint which represents the number of fields which are + * callbacks. This allows us to gain constant time performance in the + * repository for skipping over the fields section. + */ + if (count2) + (*count2)++; + } + else + { + blob->has_embedded_type = FALSE; + /* We handle the size member specially below, so subtract it */ + *offset += sizeof (FieldBlob) - sizeof (SimpleTypeBlob); + _g_ir_node_build_typelib ((GIrNode *)field->type, + node, build, offset, offset2, NULL); + } + } + break; + + case G_IR_NODE_PROPERTY: + { + GIrNodeProperty *prop = (GIrNodeProperty *)node; + PropertyBlob *blob = (PropertyBlob *)&data[*offset]; + /* We handle the size member specially below, so subtract it */ + *offset += sizeof (PropertyBlob) - sizeof (SimpleTypeBlob); + + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->deprecated = prop->deprecated; + blob->readable = prop->readable; + blob->writable = prop->writable; + blob->construct = prop->construct; + blob->construct_only = prop->construct_only; + blob->transfer_ownership = prop->transfer; + blob->transfer_container_ownership = prop->shallow_transfer; + blob->reserved = 0; + + if (prop->setter != NULL) + { + int index = get_index_of_member_type ((GIrNodeInterface*)parent, + G_IR_NODE_FUNCTION, + prop->setter); + if (index == -1) + { + g_error ("Unknown setter %s for property %s:%s", prop->setter, parent->name, node->name); + } + + blob->setter = (guint) index; + } + else + blob->setter = ACCESSOR_SENTINEL; + + if (prop->getter != NULL) + { + int index = get_index_of_member_type ((GIrNodeInterface*)parent, + G_IR_NODE_FUNCTION, + prop->getter); + if (index == -1) + { + g_error ("Unknown getter %s for property %s:%s", prop->getter, parent->name, node->name); + } + + blob->getter = (guint) index; + } + else + blob->getter = ACCESSOR_SENTINEL; + + _g_ir_node_build_typelib ((GIrNode *)prop->type, + node, build, offset, offset2, NULL); + } + break; + + case G_IR_NODE_FUNCTION: + { + FunctionBlob *blob = (FunctionBlob *)&data[*offset]; + SignatureBlob *blob2 = (SignatureBlob *)&data[*offset2]; + GIrNodeFunction *function = (GIrNodeFunction *)node; + guint32 signature; + gint n; + + signature = *offset2; + n = g_list_length (function->parameters); + + *offset += sizeof (FunctionBlob); + *offset2 += sizeof (SignatureBlob) + n * sizeof (ArgBlob); + + blob->blob_type = BLOB_TYPE_FUNCTION; + blob->deprecated = function->deprecated; + blob->is_static = !function->is_method; + blob->setter = FALSE; + blob->getter = FALSE; + blob->constructor = function->is_constructor; + blob->wraps_vfunc = function->wraps_vfunc; + blob->throws = function->throws; /* Deprecated. Also stored in SignatureBlob. */ + blob->index = 0; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->symbol = _g_ir_write_string (function->symbol, strings, data, offset2); + blob->signature = signature; + + if (function->is_setter || function->is_getter) + { + int index = get_index_of_member_type ((GIrNodeInterface*)parent, + G_IR_NODE_PROPERTY, + function->property); + if (index == -1) + { + g_error ("Unknown property %s:%s for accessor %s", parent->name, function->property, function->symbol); + } + + blob->setter = function->is_setter; + blob->getter = function->is_getter; + blob->index = (guint) index; + } + + /* function->result is special since it doesn't appear in the serialized format but + * we do want the attributes for it to appear + */ + build->nodes_with_attributes = g_list_prepend (build->nodes_with_attributes, function->result); + build->n_attributes += g_hash_table_size (((GIrNode *) function->result)->attributes); + g_assert (((GIrNode *) function->result)->offset == 0); + ((GIrNode *) function->result)->offset = signature; + + g_debug ("building function '%s'", function->symbol); + + _g_ir_node_build_typelib ((GIrNode *)function->result->type, + node, build, &signature, offset2, NULL); + + blob2->may_return_null = function->result->nullable; + blob2->caller_owns_return_value = function->result->transfer; + blob2->caller_owns_return_container = function->result->shallow_transfer; + blob2->skip_return = function->result->skip; + blob2->instance_transfer_ownership = function->instance_transfer_full; + blob2->reserved = 0; + blob2->n_arguments = n; + blob2->throws = function->throws; + + signature += 4; + + for (l = function->parameters; l; l = l->next) + { + GIrNode *param = (GIrNode *)l->data; + + _g_ir_node_build_typelib (param, node, build, &signature, offset2, NULL); + } + + } + break; + + case G_IR_NODE_CALLBACK: + { + CallbackBlob *blob = (CallbackBlob *)&data[*offset]; + SignatureBlob *blob2 = (SignatureBlob *)&data[*offset2]; + GIrNodeFunction *function = (GIrNodeFunction *)node; + guint32 signature; + gint n; + + signature = *offset2; + n = g_list_length (function->parameters); + + *offset += sizeof (CallbackBlob); + *offset2 += sizeof (SignatureBlob) + n * sizeof (ArgBlob); + + blob->blob_type = BLOB_TYPE_CALLBACK; + blob->deprecated = function->deprecated; + blob->reserved = 0; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->signature = signature; + + _g_ir_node_build_typelib ((GIrNode *)function->result->type, + node, build, &signature, offset2, NULL); + + blob2->may_return_null = function->result->nullable; + blob2->caller_owns_return_value = function->result->transfer; + blob2->caller_owns_return_container = function->result->shallow_transfer; + blob2->reserved = 0; + blob2->n_arguments = n; + blob2->throws = function->throws; + + signature += 4; + + for (l = function->parameters; l; l = l->next) + { + GIrNode *param = (GIrNode *)l->data; + + _g_ir_node_build_typelib (param, node, build, &signature, offset2, NULL); + } + } + break; + + case G_IR_NODE_SIGNAL: + { + SignalBlob *blob = (SignalBlob *)&data[*offset]; + SignatureBlob *blob2 = (SignatureBlob *)&data[*offset2]; + GIrNodeSignal *signal = (GIrNodeSignal *)node; + guint32 signature; + gint n; + + signature = *offset2; + n = g_list_length (signal->parameters); + + *offset += sizeof (SignalBlob); + *offset2 += sizeof (SignatureBlob) + n * sizeof (ArgBlob); + + blob->deprecated = signal->deprecated; + blob->run_first = signal->run_first; + blob->run_last = signal->run_last; + blob->run_cleanup = signal->run_cleanup; + blob->no_recurse = signal->no_recurse; + blob->detailed = signal->detailed; + blob->action = signal->action; + blob->no_hooks = signal->no_hooks; + blob->has_class_closure = 0; /* FIXME */ + blob->true_stops_emit = 0; /* FIXME */ + blob->reserved = 0; + blob->class_closure = 0; /* FIXME */ + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->signature = signature; + + /* signal->result is special since it doesn't appear in the serialized format but + * we do want the attributes for it to appear + */ + build->nodes_with_attributes = g_list_prepend (build->nodes_with_attributes, signal->result); + build->n_attributes += g_hash_table_size (((GIrNode *) signal->result)->attributes); + g_assert (((GIrNode *) signal->result)->offset == 0); + ((GIrNode *) signal->result)->offset = signature; + + _g_ir_node_build_typelib ((GIrNode *)signal->result->type, + node, build, &signature, offset2, NULL); + + blob2->may_return_null = signal->result->nullable; + blob2->caller_owns_return_value = signal->result->transfer; + blob2->caller_owns_return_container = signal->result->shallow_transfer; + blob2->instance_transfer_ownership = signal->instance_transfer_full; + blob2->reserved = 0; + blob2->n_arguments = n; + + signature += 4; + + for (l = signal->parameters; l; l = l->next) + { + GIrNode *param = (GIrNode *)l->data; + + _g_ir_node_build_typelib (param, node, build, &signature, offset2, NULL); + } + } + break; + + case G_IR_NODE_VFUNC: + { + VFuncBlob *blob = (VFuncBlob *)&data[*offset]; + SignatureBlob *blob2 = (SignatureBlob *)&data[*offset2]; + GIrNodeVFunc *vfunc = (GIrNodeVFunc *)node; + guint32 signature; + gint n; + + signature = *offset2; + n = g_list_length (vfunc->parameters); + + *offset += sizeof (VFuncBlob); + *offset2 += sizeof (SignatureBlob) + n * sizeof (ArgBlob); + + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->must_chain_up = 0; /* FIXME */ + blob->must_be_implemented = 0; /* FIXME */ + blob->must_not_be_implemented = 0; /* FIXME */ + blob->class_closure = 0; /* FIXME */ + blob->throws = vfunc->throws; /* Deprecated. Also stored in SignatureBlob. */ + blob->reserved = 0; + + if (vfunc->invoker) + { + int index = get_index_of_member_type ((GIrNodeInterface*)parent, G_IR_NODE_FUNCTION, vfunc->invoker); + if (index == -1) + { + g_error ("Unknown member function %s for vfunc %s", vfunc->invoker, node->name); + } + blob->invoker = (guint) index; + } + else + blob->invoker = 0x3ff; /* max of 10 bits */ + + blob->struct_offset = vfunc->offset; + blob->reserved2 = 0; + blob->signature = signature; + + _g_ir_node_build_typelib ((GIrNode *)vfunc->result->type, + node, build, &signature, offset2, NULL); + + blob2->may_return_null = vfunc->result->nullable; + blob2->caller_owns_return_value = vfunc->result->transfer; + blob2->caller_owns_return_container = vfunc->result->shallow_transfer; + blob2->instance_transfer_ownership = vfunc->instance_transfer_full; + blob2->reserved = 0; + blob2->n_arguments = n; + blob2->throws = vfunc->throws; + + signature += 4; + + for (l = vfunc->parameters; l; l = l->next) + { + GIrNode *param = (GIrNode *)l->data; + + _g_ir_node_build_typelib (param, node, build, &signature, offset2, NULL); + } + } + break; + + case G_IR_NODE_PARAM: + { + ArgBlob *blob = (ArgBlob *)&data[*offset]; + GIrNodeParam *param = (GIrNodeParam *)node; + + /* The offset for this one is smaller than the struct because + * we recursively build the simple type inline here below. + */ + *offset += sizeof (ArgBlob) - sizeof (SimpleTypeBlob); + + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->in = param->in; + blob->out = param->out; + blob->caller_allocates = param->caller_allocates; + blob->nullable = param->nullable; + blob->skip = param->skip; + blob->optional = param->optional; + blob->transfer_ownership = param->transfer; + blob->transfer_container_ownership = param->shallow_transfer; + blob->return_value = param->retval; + blob->scope = param->scope; + blob->reserved = 0; + blob->closure = param->closure; + blob->destroy = param->destroy; + + _g_ir_node_build_typelib ((GIrNode *)param->type, node, build, offset, offset2, NULL); + } + break; + + case G_IR_NODE_STRUCT: + { + StructBlob *blob = (StructBlob *)&data[*offset]; + GIrNodeStruct *struct_ = (GIrNodeStruct *)node; + GList *members; + + blob->blob_type = BLOB_TYPE_STRUCT; + blob->foreign = struct_->foreign; + blob->deprecated = struct_->deprecated; + blob->is_gtype_struct = struct_->is_gtype_struct; + blob->reserved = 0; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->alignment = struct_->alignment; + blob->size = struct_->size; + + if (struct_->gtype_name) + { + blob->unregistered = FALSE; + blob->gtype_name = _g_ir_write_string (struct_->gtype_name, strings, data, offset2); + blob->gtype_init = _g_ir_write_string (struct_->gtype_init, strings, data, offset2); + } + else + { + blob->unregistered = TRUE; + blob->gtype_name = 0; + blob->gtype_init = 0; + } + + if (struct_->copy_func) + blob->copy_func = _g_ir_write_string (struct_->copy_func, strings, data, offset2); + if (struct_->free_func) + blob->free_func = _g_ir_write_string (struct_->free_func, strings, data, offset2); + + blob->n_fields = 0; + blob->n_methods = 0; + + *offset += sizeof (StructBlob); + + members = g_list_copy (struct_->members); + + _g_ir_node_build_members (&members, G_IR_NODE_FIELD, &blob->n_fields, + node, build, offset, offset2, NULL); + + _g_ir_node_build_members (&members, G_IR_NODE_FUNCTION, &blob->n_methods, + node, build, offset, offset2, NULL); + + _g_ir_node_check_unhandled_members (&members, node->type); + + g_assert (members == NULL); + } + break; + + case G_IR_NODE_BOXED: + { + StructBlob *blob = (StructBlob *)&data[*offset]; + GIrNodeBoxed *boxed = (GIrNodeBoxed *)node; + GList *members; + + blob->blob_type = BLOB_TYPE_BOXED; + blob->deprecated = boxed->deprecated; + blob->unregistered = FALSE; + blob->reserved = 0; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->gtype_name = _g_ir_write_string (boxed->gtype_name, strings, data, offset2); + blob->gtype_init = _g_ir_write_string (boxed->gtype_init, strings, data, offset2); + blob->alignment = boxed->alignment; + blob->size = boxed->size; + + blob->n_fields = 0; + blob->n_methods = 0; + + *offset += sizeof (StructBlob); + + members = g_list_copy (boxed->members); + + _g_ir_node_build_members (&members, G_IR_NODE_FIELD, &blob->n_fields, + node, build, offset, offset2, NULL); + + _g_ir_node_build_members (&members, G_IR_NODE_FUNCTION, &blob->n_methods, + node, build, offset, offset2, NULL); + + _g_ir_node_check_unhandled_members (&members, node->type); + + g_assert (members == NULL); + } + break; + + case G_IR_NODE_UNION: + { + UnionBlob *blob = (UnionBlob *)&data[*offset]; + GIrNodeUnion *union_ = (GIrNodeUnion *)node; + GList *members; + + blob->blob_type = BLOB_TYPE_UNION; + blob->deprecated = union_->deprecated; + blob->reserved = 0; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->alignment = union_->alignment; + blob->size = union_->size; + if (union_->gtype_name) + { + blob->unregistered = FALSE; + blob->gtype_name = _g_ir_write_string (union_->gtype_name, strings, data, offset2); + blob->gtype_init = _g_ir_write_string (union_->gtype_init, strings, data, offset2); + } + else + { + blob->unregistered = TRUE; + blob->gtype_name = 0; + blob->gtype_init = 0; + } + + blob->n_fields = 0; + blob->n_functions = 0; + + blob->discriminator_offset = union_->discriminator_offset; + + if (union_->copy_func) + blob->copy_func = _g_ir_write_string (union_->copy_func, strings, data, offset2); + if (union_->free_func) + blob->free_func = _g_ir_write_string (union_->free_func, strings, data, offset2); + + /* We don't support Union discriminators right now. */ + /* + if (union_->discriminator_type) + { + *offset += 28; + blob->discriminated = TRUE; + _g_ir_node_build_typelib ((GIrNode *)union_->discriminator_type, + build, offset, offset2, NULL); + } + else + { + */ + *offset += sizeof (UnionBlob); + blob->discriminated = FALSE; + blob->discriminator_type.offset = 0; + + members = g_list_copy (union_->members); + + _g_ir_node_build_members (&members, G_IR_NODE_FIELD, &blob->n_fields, + node, build, offset, offset2, NULL); + + _g_ir_node_build_members (&members, G_IR_NODE_FUNCTION, &blob->n_functions, + node, build, offset, offset2, NULL); + + _g_ir_node_check_unhandled_members (&members, node->type); + + g_assert (members == NULL); + + if (union_->discriminator_type) + { + for (l = union_->discriminators; l; l = l->next) + { + GIrNode *member = (GIrNode *)l->data; + + _g_ir_node_build_typelib (member, node, build, offset, offset2, NULL); + } + } + } + break; + + case G_IR_NODE_ENUM: + case G_IR_NODE_FLAGS: + { + EnumBlob *blob = (EnumBlob *)&data[*offset]; + GIrNodeEnum *enum_ = (GIrNodeEnum *)node; + + *offset += sizeof (EnumBlob); + + if (node->type == G_IR_NODE_ENUM) + blob->blob_type = BLOB_TYPE_ENUM; + else + blob->blob_type = BLOB_TYPE_FLAGS; + + blob->deprecated = enum_->deprecated; + blob->reserved = 0; + blob->storage_type = enum_->storage_type; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + if (enum_->gtype_name) + { + blob->unregistered = FALSE; + blob->gtype_name = _g_ir_write_string (enum_->gtype_name, strings, data, offset2); + blob->gtype_init = _g_ir_write_string (enum_->gtype_init, strings, data, offset2); + } + else + { + blob->unregistered = TRUE; + blob->gtype_name = 0; + blob->gtype_init = 0; + } + if (enum_->error_domain) + blob->error_domain = _g_ir_write_string (enum_->error_domain, strings, data, offset2); + else + blob->error_domain = 0; + + blob->n_values = 0; + blob->n_methods = 0; + + for (l = enum_->values; l; l = l->next) + { + GIrNode *value = (GIrNode *)l->data; + + blob->n_values++; + _g_ir_node_build_typelib (value, node, build, offset, offset2, NULL); + } + + for (l = enum_->methods; l; l = l->next) + { + GIrNode *method = (GIrNode *)l->data; + + blob->n_methods++; + _g_ir_node_build_typelib (method, node, build, offset, offset2, NULL); + } + } + break; + + case G_IR_NODE_OBJECT: + { + ObjectBlob *blob = (ObjectBlob *)&data[*offset]; + GIrNodeInterface *object = (GIrNodeInterface *)node; + GList *members; + + blob->blob_type = BLOB_TYPE_OBJECT; + blob->abstract = object->abstract; + blob->fundamental = object->fundamental; + blob->final_ = object->final_; + blob->deprecated = object->deprecated; + blob->reserved = 0; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->gtype_name = _g_ir_write_string (object->gtype_name, strings, data, offset2); + blob->gtype_init = _g_ir_write_string (object->gtype_init, strings, data, offset2); + if (object->ref_func) + blob->ref_func = _g_ir_write_string (object->ref_func, strings, data, offset2); + if (object->unref_func) + blob->unref_func = _g_ir_write_string (object->unref_func, strings, data, offset2); + if (object->set_value_func) + blob->set_value_func = _g_ir_write_string (object->set_value_func, strings, data, offset2); + if (object->get_value_func) + blob->get_value_func = _g_ir_write_string (object->get_value_func, strings, data, offset2); + if (object->parent) + blob->parent = find_entry (build, object->parent); + else + blob->parent = 0; + if (object->glib_type_struct) + blob->gtype_struct = find_entry (build, object->glib_type_struct); + else + blob->gtype_struct = 0; + + blob->n_interfaces = 0; + blob->n_fields = 0; + blob->n_properties = 0; + blob->n_methods = 0; + blob->n_signals = 0; + blob->n_vfuncs = 0; + blob->n_constants = 0; + blob->n_field_callbacks = 0; + + *offset += sizeof(ObjectBlob); + for (l = object->interfaces; l; l = l->next) + { + blob->n_interfaces++; + *(guint16*)&data[*offset] = find_entry (build, (gchar *)l->data); + *offset += 2; + } + + members = g_list_copy (object->members); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_FIELD, &blob->n_fields, + node, build, offset, offset2, &blob->n_field_callbacks); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_PROPERTY, &blob->n_properties, + node, build, offset, offset2, NULL); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_FUNCTION, &blob->n_methods, + node, build, offset, offset2, NULL); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_SIGNAL, &blob->n_signals, + node, build, offset, offset2, NULL); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_VFUNC, &blob->n_vfuncs, + node, build, offset, offset2, NULL); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_CONSTANT, &blob->n_constants, + node, build, offset, offset2, NULL); + + _g_ir_node_check_unhandled_members (&members, node->type); + + g_assert (members == NULL); + } + break; + + case G_IR_NODE_INTERFACE: + { + InterfaceBlob *blob = (InterfaceBlob *)&data[*offset]; + GIrNodeInterface *iface = (GIrNodeInterface *)node; + GList *members; + + blob->blob_type = BLOB_TYPE_INTERFACE; + blob->deprecated = iface->deprecated; + blob->reserved = 0; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->gtype_name = _g_ir_write_string (iface->gtype_name, strings, data, offset2); + blob->gtype_init = _g_ir_write_string (iface->gtype_init, strings, data, offset2); + if (iface->glib_type_struct) + blob->gtype_struct = find_entry (build, iface->glib_type_struct); + else + blob->gtype_struct = 0; + blob->n_prerequisites = 0; + blob->n_properties = 0; + blob->n_methods = 0; + blob->n_signals = 0; + blob->n_vfuncs = 0; + blob->n_constants = 0; + + *offset += sizeof (InterfaceBlob); + for (l = iface->prerequisites; l; l = l->next) + { + blob->n_prerequisites++; + *(guint16*)&data[*offset] = find_entry (build, (gchar *)l->data); + *offset += 2; + } + + members = g_list_copy (iface->members); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_PROPERTY, &blob->n_properties, + node, build, offset, offset2, NULL); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_FUNCTION, &blob->n_methods, + node, build, offset, offset2, NULL); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_SIGNAL, &blob->n_signals, + node, build, offset, offset2, NULL); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_VFUNC, &blob->n_vfuncs, + node, build, offset, offset2, NULL); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_CONSTANT, &blob->n_constants, + node, build, offset, offset2, NULL); + + _g_ir_node_check_unhandled_members (&members, node->type); + + g_assert (members == NULL); + } + break; + + + case G_IR_NODE_VALUE: + { + GIrNodeValue *value = (GIrNodeValue *)node; + ValueBlob *blob = (ValueBlob *)&data[*offset]; + *offset += sizeof (ValueBlob); + + blob->deprecated = value->deprecated; + blob->reserved = 0; + blob->unsigned_value = value->value >= 0 ? 1 : 0; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->value = (gint32)value->value; + } + break; + + case G_IR_NODE_CONSTANT: + { + GIrNodeConstant *constant = (GIrNodeConstant *)node; + ConstantBlob *blob = (ConstantBlob *)&data[*offset]; + guint32 pos; + + pos = *offset + G_STRUCT_OFFSET (ConstantBlob, type); + *offset += sizeof (ConstantBlob); + + blob->blob_type = BLOB_TYPE_CONSTANT; + blob->deprecated = constant->deprecated; + blob->reserved = 0; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + + blob->offset = *offset2; + switch (constant->type->tag) + { + case GI_TYPE_TAG_BOOLEAN: + blob->size = 4; + *(gboolean*)&data[blob->offset] = parse_boolean_value (constant->value); + break; + case GI_TYPE_TAG_INT8: + blob->size = 1; + *(gint8*)&data[blob->offset] = (gint8) parse_int_value (constant->value); + break; + case GI_TYPE_TAG_UINT8: + blob->size = 1; + *(guint8*)&data[blob->offset] = (guint8) parse_uint_value (constant->value); + break; + case GI_TYPE_TAG_INT16: + blob->size = 2; + *(gint16*)&data[blob->offset] = (gint16) parse_int_value (constant->value); + break; + case GI_TYPE_TAG_UINT16: + blob->size = 2; + *(guint16*)&data[blob->offset] = (guint16) parse_uint_value (constant->value); + break; + case GI_TYPE_TAG_INT32: + blob->size = 4; + *(gint32*)&data[blob->offset] = (gint32) parse_int_value (constant->value); + break; + case GI_TYPE_TAG_UINT32: + blob->size = 4; + *(guint32*)&data[blob->offset] = (guint32) parse_uint_value (constant->value); + break; + case GI_TYPE_TAG_INT64: + blob->size = 8; + DO_ALIGNED_COPY(&data[blob->offset], parse_int_value (constant->value), gint64); + break; + case GI_TYPE_TAG_UINT64: + blob->size = 8; + DO_ALIGNED_COPY(&data[blob->offset], parse_uint_value (constant->value), guint64); + break; + case GI_TYPE_TAG_FLOAT: + blob->size = sizeof (gfloat); + DO_ALIGNED_COPY(&data[blob->offset], parse_float_value (constant->value), gfloat); + break; + case GI_TYPE_TAG_DOUBLE: + blob->size = sizeof (gdouble); + DO_ALIGNED_COPY(&data[blob->offset], parse_float_value (constant->value), gdouble); + break; + case GI_TYPE_TAG_UTF8: + case GI_TYPE_TAG_FILENAME: + blob->size = strlen (constant->value) + 1; + memcpy (&data[blob->offset], constant->value, blob->size); + break; + default: + break; + } + *offset2 += ALIGN_VALUE (blob->size, 4); + + _g_ir_node_build_typelib ((GIrNode *)constant->type, node, build, &pos, offset2, NULL); + } + break; + default: + g_assert_not_reached (); + } + + g_debug ("node %s%s%s%p type '%s', offset %d -> %d, offset2 %d -> %d", + node->name ? "'" : "", + node->name ? node->name : "", + node->name ? "' " : "", + node, _g_ir_node_type_to_string (node->type), + old_offset, *offset, old_offset2, *offset2); + + if (*offset2 - old_offset2 + *offset - old_offset > _g_ir_node_get_full_size (node)) + g_error ("exceeding space reservation; offset: %d (prev %d) offset2: %d (prev %d) nodesize: %d", + *offset, old_offset, *offset2, old_offset2, _g_ir_node_get_full_size (node)); + + if (appended_stack) + build->stack = g_list_delete_link (build->stack, build->stack); +} + +/* if str is already in the pool, return previous location, otherwise write str + * to the typelib at offset, put it in the pool and update offset. If the + * typelib is not large enough to hold the string, reallocate it. + */ +guint32 +_g_ir_write_string (const gchar *str, + GHashTable *strings, + guchar *data, + guint32 *offset) +{ + gpointer value; + guint32 start; + + string_count += 1; + string_size += strlen (str); + + value = g_hash_table_lookup (strings, str); + + if (value) + return GPOINTER_TO_UINT (value); + + unique_string_count += 1; + unique_string_size += strlen (str); + + g_hash_table_insert (strings, (gpointer)str, GUINT_TO_POINTER (*offset)); + + start = *offset; + *offset = ALIGN_VALUE (start + strlen (str) + 1, 4); + + strcpy ((gchar*)&data[start], str); + + return start; +} + diff --git a/girepository/giroffsets.c b/girepository/giroffsets.c new file mode 100644 index 000000000..b562566ee --- /dev/null +++ b/girepository/giroffsets.c @@ -0,0 +1,591 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Compute structure offsets + * + * Copyright (C) 2008 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "girffi.h" + +#include "girnode-private.h" + +#include + +/* The C standard specifies that an enumeration can be any char or any signed + * or unsigned integer type capable of representing all the values of the + * enumeration. We use test enumerations to figure out what choices the + * compiler makes. (Ignoring > 32 bit enumerations) + */ + +typedef enum { + ENUM_1 = 1 /* compiler could use int8, uint8, int16, uint16, int32, uint32 */ +} Enum1; + +typedef enum { + ENUM_2 = 128 /* compiler could use uint8, int16, uint16, int32, uint32 */ +} Enum2; + +typedef enum { + ENUM_3 = 257 /* compiler could use int16, uint16, int32, uint32 */ +} Enum3; + +typedef enum { + ENUM_4 = G_MAXSHORT + 1 /* compiler could use uint16, int32, uint32 */ +} Enum4; + +typedef enum { + ENUM_5 = G_MAXUSHORT + 1 /* compiler could use int32, uint32 */ +} Enum5; + +typedef enum { + ENUM_6 = ((guint)G_MAXINT) + 1 /* compiler could use uint32 */ +} Enum6; + +typedef enum { + ENUM_7 = -1 /* compiler could use int8, int16, int32 */ +} Enum7; + +typedef enum { + ENUM_8 = -129 /* compiler could use int16, int32 */ +} Enum8; + +typedef enum { + ENUM_9 = G_MINSHORT - 1 /* compiler could use int32 */ +} Enum9; + +static void +compute_enum_storage_type (GIrNodeEnum *enum_node) +{ + GList *l; + gint64 max_value = 0; + gint64 min_value = 0; + int width; + gboolean signed_type; + + if (enum_node->storage_type != GI_TYPE_TAG_VOID) /* already done */ + return; + + for (l = enum_node->values; l; l = l->next) + { + GIrNodeValue *value = l->data; + if (value->value > max_value) + max_value = value->value; + if (value->value < min_value) + min_value = value->value; + } + + if (min_value < 0) + { + signed_type = TRUE; + + if (min_value > -128 && max_value <= 127) + width = sizeof(Enum7); + else if (min_value >= G_MINSHORT && max_value <= G_MAXSHORT) + width = sizeof(Enum8); + else + width = sizeof(Enum9); + } + else + { + if (max_value <= 127) + { + width = sizeof (Enum1); + signed_type = (gint64)(Enum1)(-1) < 0; + } + else if (max_value <= 255) + { + width = sizeof (Enum2); + signed_type = (gint64)(Enum2)(-1) < 0; + } + else if (max_value <= G_MAXSHORT) + { + width = sizeof (Enum3); + signed_type = (gint64)(Enum3)(-1) < 0; + } + else if (max_value <= G_MAXUSHORT) + { + width = sizeof (Enum4); + signed_type = (gint64)(Enum4)(-1) < 0; + } + else if (max_value <= G_MAXINT) + { + width = sizeof (Enum5); + signed_type = (gint64)(Enum5)(-1) < 0; + } + else + { + width = sizeof (Enum6); + signed_type = (gint64)(Enum6)(-1) < 0; + } + } + + if (width == 1) + enum_node->storage_type = signed_type ? GI_TYPE_TAG_INT8 : GI_TYPE_TAG_UINT8; + else if (width == 2) + enum_node->storage_type = signed_type ? GI_TYPE_TAG_INT16 : GI_TYPE_TAG_UINT16; + else if (width == 4) + enum_node->storage_type = signed_type ? GI_TYPE_TAG_INT32 : GI_TYPE_TAG_UINT32; + else if (width == 8) + enum_node->storage_type = signed_type ? GI_TYPE_TAG_INT64 : GI_TYPE_TAG_UINT64; + else + g_error ("Unexpected enum width %d", width); +} + +static gboolean +get_enum_size_alignment (GIrNodeEnum *enum_node, + gint *size, + gint *alignment) +{ + ffi_type *type_ffi; + + compute_enum_storage_type (enum_node); + + switch (enum_node->storage_type) + { + case GI_TYPE_TAG_INT8: + case GI_TYPE_TAG_UINT8: + type_ffi = &ffi_type_uint8; + break; + case GI_TYPE_TAG_INT16: + case GI_TYPE_TAG_UINT16: + type_ffi = &ffi_type_uint16; + break; + case GI_TYPE_TAG_INT32: + case GI_TYPE_TAG_UINT32: + type_ffi = &ffi_type_uint32; + break; + case GI_TYPE_TAG_INT64: + case GI_TYPE_TAG_UINT64: + type_ffi = &ffi_type_uint64; + break; + default: + g_error ("Unexpected enum storage type %s", + g_type_tag_to_string (enum_node->storage_type)); + } + + *size = type_ffi->size; + *alignment = type_ffi->alignment; + + return TRUE; +} + +static gboolean +get_interface_size_alignment (GIrTypelibBuild *build, + GIrNodeType *type, + gint *size, + gint *alignment, + const char *who) +{ + GIrNode *iface; + + iface = _g_ir_find_node (build, ((GIrNode*)type)->module, type->giinterface); + if (!iface) + { + _g_ir_module_fatal (build, 0, "Can't resolve type '%s' for %s", type->giinterface, who); + *size = -1; + *alignment = -1; + return FALSE; + } + + _g_ir_node_compute_offsets (build, iface); + + switch (iface->type) + { + case G_IR_NODE_BOXED: + { + GIrNodeBoxed *boxed = (GIrNodeBoxed *)iface; + *size = boxed->size; + *alignment = boxed->alignment; + break; + } + case G_IR_NODE_STRUCT: + { + GIrNodeStruct *struct_ = (GIrNodeStruct *)iface; + *size = struct_->size; + *alignment = struct_->alignment; + break; + } + case G_IR_NODE_OBJECT: + case G_IR_NODE_INTERFACE: + { + GIrNodeInterface *interface = (GIrNodeInterface *)iface; + *size = interface->size; + *alignment = interface->alignment; + break; + } + case G_IR_NODE_UNION: + { + GIrNodeUnion *union_ = (GIrNodeUnion *)iface; + *size = union_->size; + *alignment = union_->alignment; + break; + } + case G_IR_NODE_ENUM: + case G_IR_NODE_FLAGS: + { + return get_enum_size_alignment ((GIrNodeEnum *)iface, + size, alignment); + } + case G_IR_NODE_CALLBACK: + { + *size = ffi_type_pointer.size; + *alignment = ffi_type_pointer.alignment; + break; + } + default: + { + g_warning ("%s has is not a pointer and is of type %s", + who, + _g_ir_node_type_to_string (iface->type)); + *size = -1; + *alignment = -1; + break; + } + } + + return *alignment > 0; +} + +static gboolean +get_type_size_alignment (GIrTypelibBuild *build, + GIrNodeType *type, + gint *size, + gint *alignment, + const char *who) +{ + ffi_type *type_ffi; + + if (type->is_pointer) + { + type_ffi = &ffi_type_pointer; + } + else if (type->tag == GI_TYPE_TAG_ARRAY) + { + gint elt_size, elt_alignment; + + if (!type->has_size + || !get_type_size_alignment(build, type->parameter_type1, + &elt_size, &elt_alignment, who)) + { + *size = -1; + *alignment = -1; + return FALSE; + } + + *size = type->size * elt_size; + *alignment = elt_alignment; + + return TRUE; + } + else + { + if (type->tag == GI_TYPE_TAG_INTERFACE) + { + return get_interface_size_alignment (build, type, size, alignment, who); + } + else + { + type_ffi = gi_type_tag_get_ffi_type (type->tag, type->is_pointer); + + if (type_ffi == &ffi_type_void) + { + g_warning ("%s has void type", who); + *size = -1; + *alignment = -1; + return FALSE; + } + else if (type_ffi == &ffi_type_pointer) + { + g_warning ("%s has is not a pointer and is of type %s", + who, + g_type_tag_to_string (type->tag)); + *size = -1; + *alignment = -1; + return FALSE; + } + } + } + + g_assert (type_ffi); + *size = type_ffi->size; + *alignment = type_ffi->alignment; + + return TRUE; +} + +static gboolean +get_field_size_alignment (GIrTypelibBuild *build, + GIrNodeField *field, + GIrNode *parent_node, + gint *size, + gint *alignment) +{ + GIrModule *module = build->module; + gchar *who; + gboolean success; + + who = g_strdup_printf ("field %s.%s.%s", module->name, parent_node->name, ((GIrNode *)field)->name); + + if (field->callback) + { + *size = ffi_type_pointer.size; + *alignment = ffi_type_pointer.alignment; + success = TRUE; + } + else + success = get_type_size_alignment (build, field->type, size, alignment, who); + g_free (who); + + return success; +} + +#define GI_ALIGN(n, align) (((n) + (align) - 1) & ~((align) - 1)) + +static gboolean +compute_struct_field_offsets (GIrTypelibBuild *build, + GIrNode *node, + GList *members, + gint *size_out, + gint *alignment_out) +{ + int size = 0; + int alignment = 1; + GList *l; + gboolean have_error = FALSE; + + *alignment_out = -2; /* mark to detect recursion */ + + for (l = members; l; l = l->next) + { + GIrNode *member = (GIrNode *)l->data; + + if (member->type == G_IR_NODE_FIELD) + { + GIrNodeField *field = (GIrNodeField *)member; + + if (!have_error) + { + int member_size; + int member_alignment; + + if (get_field_size_alignment (build, field, node, + &member_size, &member_alignment)) + { + size = GI_ALIGN (size, member_alignment); + alignment = MAX (alignment, member_alignment); + field->offset = size; + size += member_size; + } + else + have_error = TRUE; + } + + if (have_error) + field->offset = -1; + } + else if (member->type == G_IR_NODE_CALLBACK) + { + size = GI_ALIGN (size, ffi_type_pointer.alignment); + alignment = MAX (alignment, ffi_type_pointer.alignment); + size += ffi_type_pointer.size; + } + } + + /* Structs are tail-padded out to a multiple of their alignment */ + size = GI_ALIGN (size, alignment); + + if (!have_error) + { + *size_out = size; + *alignment_out = alignment; + } + else + { + *size_out = -1; + *alignment_out = -1; + } + + return !have_error; +} + +static gboolean +compute_union_field_offsets (GIrTypelibBuild *build, + GIrNode *node, + GList *members, + gint *size_out, + gint *alignment_out) +{ + int size = 0; + int alignment = 1; + GList *l; + gboolean have_error = FALSE; + + *alignment_out = -2; /* mark to detect recursion */ + + for (l = members; l; l = l->next) + { + GIrNode *member = (GIrNode *)l->data; + + if (member->type == G_IR_NODE_FIELD) + { + GIrNodeField *field = (GIrNodeField *)member; + + if (!have_error) + { + int member_size; + int member_alignment; + + if (get_field_size_alignment (build,field, node, + &member_size, &member_alignment)) + { + size = MAX (size, member_size); + alignment = MAX (alignment, member_alignment); + } + else + have_error = TRUE; + } + } + } + + /* Unions are tail-padded out to a multiple of their alignment */ + size = GI_ALIGN (size, alignment); + + if (!have_error) + { + *size_out = size; + *alignment_out = alignment; + } + else + { + *size_out = -1; + *alignment_out = -1; + } + + return !have_error; +} + +static gboolean +check_needs_computation (GIrTypelibBuild *build, + GIrNode *node, + gint alignment) +{ + GIrModule *module = build->module; + /* + * 0: Not yet computed + * >0: Previously succeeded + * -1: Previously failed + * -2: In progress + */ + if (alignment == -2) + { + g_warning ("Recursion encountered when computing the size of %s.%s", + module->name, node->name); + } + + return alignment == 0; +} + +/* + * _g_ir_node_compute_offsets: + * @build: Current typelib build + * @node: a #GIrNode + * + * If a node is a a structure or union, makes sure that the field + * offsets have been computed, and also computes the overall size and + * alignment for the type. + */ +void +_g_ir_node_compute_offsets (GIrTypelibBuild *build, + GIrNode *node) +{ + gboolean appended_stack; + + if (build->stack) + appended_stack = node != (GIrNode*)build->stack->data; + else + appended_stack = TRUE; + if (appended_stack) + build->stack = g_list_prepend (build->stack, node); + + switch (node->type) + { + case G_IR_NODE_BOXED: + { + GIrNodeBoxed *boxed = (GIrNodeBoxed *)node; + + if (!check_needs_computation (build, node, boxed->alignment)) + return; + + compute_struct_field_offsets (build, node, boxed->members, + &boxed->size, &boxed->alignment); + break; + } + case G_IR_NODE_STRUCT: + { + GIrNodeStruct *struct_ = (GIrNodeStruct *)node; + + if (!check_needs_computation (build, node, struct_->alignment)) + return; + + compute_struct_field_offsets (build, node, struct_->members, + &struct_->size, &struct_->alignment); + break; + } + case G_IR_NODE_OBJECT: + case G_IR_NODE_INTERFACE: + { + GIrNodeInterface *iface = (GIrNodeInterface *)node; + + if (!check_needs_computation (build, node, iface->alignment)) + return; + + compute_struct_field_offsets (build, node, iface->members, + &iface->size, &iface->alignment); + break; + } + case G_IR_NODE_UNION: + { + GIrNodeUnion *union_ = (GIrNodeUnion *)node; + + if (!check_needs_computation (build, node, union_->alignment)) + return; + + compute_union_field_offsets (build, (GIrNode*)union_, union_->members, + &union_->size, &union_->alignment); + break; + } + case G_IR_NODE_ENUM: + case G_IR_NODE_FLAGS: + { + GIrNodeEnum *enum_ = (GIrNodeEnum *)node; + + if (enum_->storage_type != GI_TYPE_TAG_VOID) /* already done */ + return; + + compute_enum_storage_type (enum_); + + break; + } + default: + break; + } + + if (appended_stack) + build->stack = g_list_delete_link (build->stack, build->stack); +} diff --git a/girepository/girparser-private.h b/girepository/girparser-private.h new file mode 100644 index 000000000..a7e704fd4 --- /dev/null +++ b/girepository/girparser-private.h @@ -0,0 +1,49 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: A parser for the XML GIR format + * + * Copyright (C) 2005 Matthias Clasen + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#include "girmodule-private.h" + +typedef struct _GIrParser GIrParser; + +GIrParser *_g_ir_parser_new (void); +void _g_ir_parser_free (GIrParser *parser); +void _g_ir_parser_set_includes (GIrParser *parser, + const gchar *const *includes); + +GIrModule *_g_ir_parser_parse_string (GIrParser *parser, + const gchar *namespace, + const gchar *filename, + const gchar *buffer, + gssize length, + GError **error); +GIrModule *_g_ir_parser_parse_file (GIrParser *parser, + const gchar *filename, + GError **error); + +G_END_DECLS diff --git a/girepository/girparser.c b/girepository/girparser.c new file mode 100644 index 000000000..57cd26bf4 --- /dev/null +++ b/girepository/girparser.c @@ -0,0 +1,3815 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: A parser for the XML GIR format + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008 Philip Van Hoof + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include "girparser-private.h" + +#include "girnode-private.h" +#include "gitypelib-internal.h" + +#include +#include +#include + +/* This is a "major" version in the sense that it's only bumped + * for incompatible changes. + */ +#define SUPPORTED_GIR_VERSION "1.2" + +#ifdef G_OS_WIN32 + +#include + +#ifdef GIR_DIR +#undef GIR_DIR +#endif + +/* GIR_DIR is used only in code called just once, + * so no problem leaking this + */ +#define GIR_DIR \ + g_build_filename (g_win32_get_package_installation_directory_of_module(NULL), \ + "share", \ + GIR_SUFFIX, \ + NULL) +#endif + +struct _GIrParser +{ + gchar **includes; + gchar **gi_gir_path; + GList *parsed_modules; /* All previously parsed modules */ +}; + +typedef enum +{ + STATE_NONE = 0, + STATE_START, + STATE_END, + STATE_REPOSITORY, + STATE_INCLUDE, + STATE_C_INCLUDE, /* 5 */ + STATE_PACKAGE, + STATE_NAMESPACE, + STATE_ENUM, + STATE_BITFIELD, + STATE_FUNCTION, /* 10 */ + STATE_FUNCTION_RETURN, + STATE_FUNCTION_PARAMETERS, + STATE_FUNCTION_PARAMETER, + STATE_CLASS, + STATE_CLASS_FIELD, /* 15 */ + STATE_CLASS_PROPERTY, + STATE_INTERFACE, + STATE_INTERFACE_PROPERTY, + STATE_INTERFACE_FIELD, + STATE_IMPLEMENTS, /* 20 */ + STATE_PREREQUISITE, + STATE_BOXED, + STATE_BOXED_FIELD, + STATE_STRUCT, + STATE_STRUCT_FIELD, /* 25 */ + STATE_UNION, + STATE_UNION_FIELD, + STATE_NAMESPACE_CONSTANT, + STATE_CLASS_CONSTANT, + STATE_INTERFACE_CONSTANT, /* 30 */ + STATE_ALIAS, + STATE_TYPE, + STATE_ATTRIBUTE, + STATE_PASSTHROUGH +} ParseState; + +typedef struct _ParseContext ParseContext; +struct _ParseContext +{ + GIrParser *parser; + + ParseState state; + int unknown_depth; + ParseState prev_state; + + GList *modules; + GList *include_modules; + GList *dependencies; + GHashTable *aliases; + GHashTable *disguised_structures; + GHashTable *pointer_structures; + + const char *file_path; + const char *namespace; + const char *c_prefix; + GIrModule *current_module; + GSList *node_stack; + char *current_alias; + GIrNode *current_typed; + GList *type_stack; + GList *type_parameters; + int type_depth; + ParseState in_embedded_state; +}; +#define CURRENT_NODE(ctx) ((GIrNode *)((ctx)->node_stack->data)) + +static void start_element_handler (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error); +static void end_element_handler (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error); +static void text_handler (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error); +static void cleanup (GMarkupParseContext *context, + GError *error, + gpointer user_data); +static void state_switch (ParseContext *ctx, ParseState newstate); + + +static GMarkupParser markup_parser = +{ + start_element_handler, + end_element_handler, + text_handler, + NULL, + cleanup +}; + +static gboolean +start_alias (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error); +static gboolean +start_type (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error); + +static const gchar *find_attribute (const gchar *name, + const gchar **attribute_names, + const gchar **attribute_values); + + +GIrParser * +_g_ir_parser_new (void) +{ + GIrParser *parser = g_slice_new0 (GIrParser); + const char *gi_gir_path = g_getenv ("GI_GIR_PATH"); + + if (gi_gir_path != NULL) + parser->gi_gir_path = g_strsplit (gi_gir_path, G_SEARCHPATH_SEPARATOR_S, 0); + + return parser; +} + +void +_g_ir_parser_free (GIrParser *parser) +{ + GList *l; + + g_strfreev (parser->includes); + g_strfreev (parser->gi_gir_path); + + for (l = parser->parsed_modules; l; l = l->next) + _g_ir_module_free (l->data); + + g_slice_free (GIrParser, parser); +} + +void +_g_ir_parser_set_includes (GIrParser *parser, + const gchar *const *includes) +{ + g_strfreev (parser->includes); + + parser->includes = g_strdupv ((char **)includes); +} + +static void +firstpass_start_element_handler (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + ParseContext *ctx = user_data; + + if (strcmp (element_name, "alias") == 0) + { + start_alias (context, element_name, attribute_names, attribute_values, + ctx, error); + } + else if (ctx->state == STATE_ALIAS && strcmp (element_name, "type") == 0) + { + start_type (context, element_name, attribute_names, attribute_values, + ctx, error); + } + else if (strcmp (element_name, "record") == 0) + { + const gchar *name; + const gchar *disguised; + const gchar *pointer; + + name = find_attribute ("name", attribute_names, attribute_values); + disguised = find_attribute ("disguised", attribute_names, attribute_values); + pointer = find_attribute ("pointer", attribute_names, attribute_values); + + if (g_strcmp0 (pointer, "1") == 0) + { + char *key; + + key = g_strdup_printf ("%s.%s", ctx->namespace, name); + g_hash_table_replace (ctx->pointer_structures, key, GINT_TO_POINTER (1)); + } + else if (g_strcmp0 (disguised, "1") == 0) + { + char *key; + + key = g_strdup_printf ("%s.%s", ctx->namespace, name); + g_hash_table_replace (ctx->disguised_structures, key, GINT_TO_POINTER (1)); + } + } +} + +static void +firstpass_end_element_handler (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + ParseContext *ctx = user_data; + if (strcmp (element_name, "alias") == 0) + { + state_switch (ctx, STATE_NAMESPACE); + g_free (ctx->current_alias); + ctx->current_alias = NULL; + } + else if (strcmp (element_name, "type") == 0 && ctx->state == STATE_TYPE) + state_switch (ctx, ctx->prev_state); +} + +static GMarkupParser firstpass_parser = +{ + firstpass_start_element_handler, + firstpass_end_element_handler, + NULL, + NULL, + NULL, +}; + +static char * +locate_gir (GIrParser *parser, + const char *girname) +{ + const gchar *const *datadirs; + const gchar *const *dir; + char *path = NULL; + + g_debug ("Looking for %s", girname); + datadirs = g_get_system_data_dirs (); + + if (parser->includes != NULL) + { + for (dir = (const gchar *const *)parser->includes; *dir; dir++) + { + path = g_build_filename (*dir, girname, NULL); + g_debug ("Trying %s from includes", path); + if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) + return g_steal_pointer (&path); + g_clear_pointer (&path, g_free); + } + } + + if (parser->gi_gir_path != NULL) + { + for (dir = (const gchar *const *) parser->gi_gir_path; *dir; dir++) + { + if (**dir == '\0') + continue; + + path = g_build_filename (*dir, girname, NULL); + g_debug ("Trying %s from GI_GIR_PATH", path); + if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) + return g_steal_pointer (&path); + g_clear_pointer (&path, g_free); + } + } + + path = g_build_filename (g_get_user_data_dir (), GIR_SUFFIX, girname, NULL); + g_debug ("Trying %s from user data dir", path); + if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) + return g_steal_pointer (&path); + g_clear_pointer (&path, g_free); + + for (dir = datadirs; *dir; dir++) + { + path = g_build_filename (*dir, GIR_SUFFIX, girname, NULL); + g_debug ("Trying %s from system data dirs", path); + if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) + return g_steal_pointer (&path); + g_clear_pointer (&path, g_free); + } + + path = g_build_filename (GIR_DIR, girname, NULL); + g_debug ("Trying %s from GIR_DIR", path); + if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) + return g_steal_pointer (&path); + g_clear_pointer (&path, g_free); + + path = g_build_filename (GOBJECT_INTROSPECTION_DATADIR, GIR_SUFFIX, girname, NULL); + g_debug ("Trying %s from DATADIR", path); + if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) + return g_steal_pointer (&path); + g_clear_pointer (&path, g_free); + +#ifdef G_OS_UNIX + path = g_build_filename ("/usr/share", GIR_SUFFIX, girname, NULL); + g_debug ("Trying %s", path); + if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) + return g_steal_pointer (&path); + g_clear_pointer (&path, g_free); +#endif + + g_debug ("Did not find %s", girname); + return NULL; +} + +#define MISSING_ATTRIBUTE(context,error,element,attribute) \ + do { \ + int line_number, char_number; \ + g_markup_parse_context_get_position (context, &line_number, &char_number); \ + g_set_error (error, \ + G_MARKUP_ERROR, \ + G_MARKUP_ERROR_INVALID_CONTENT, \ + "Line %d, character %d: The attribute '%s' on the element '%s' must be specified", \ + line_number, char_number, attribute, element); \ + } while (0) + +static const gchar * +find_attribute (const gchar *name, + const gchar **attribute_names, + const gchar **attribute_values) +{ + gint i; + + for (i = 0; attribute_names[i] != NULL; i++) + if (strcmp (attribute_names[i], name) == 0) + return attribute_values[i]; + + return 0; +} + +static void +state_switch (ParseContext *ctx, ParseState newstate) +{ + g_assert (ctx->state != newstate); + ctx->prev_state = ctx->state; + ctx->state = newstate; + + if (ctx->state == STATE_PASSTHROUGH) + ctx->unknown_depth = 1; +} + +static GIrNode * +pop_node (ParseContext *ctx) +{ + GSList *top; + GIrNode *node; + g_assert (ctx->node_stack != 0); + + top = ctx->node_stack; + node = top->data; + + g_debug ("popping node %d %s", node->type, node->name); + ctx->node_stack = top->next; + g_slist_free_1 (top); + return node; +} + +static void +push_node (ParseContext *ctx, GIrNode *node) +{ + g_assert (node != NULL); + g_debug ("pushing node %d %s", node->type, node->name); + ctx->node_stack = g_slist_prepend (ctx->node_stack, node); +} + +static GIrNodeType * parse_type_internal (GIrModule *module, + const gchar *str, gchar **next, gboolean in_glib, + gboolean in_gobject); + +typedef struct { + const gchar *str; + guint size; + guint is_signed : 1; +} IntegerAliasInfo; + +static IntegerAliasInfo integer_aliases[] = { + { "gchar", SIZEOF_CHAR, 1 }, + { "guchar", SIZEOF_CHAR, 0 }, + { "gshort", SIZEOF_SHORT, 1 }, + { "gushort", SIZEOF_SHORT, 0 }, + { "gint", SIZEOF_INT, 1 }, + { "guint", SIZEOF_INT, 0 }, + { "glong", SIZEOF_LONG, 1 }, + { "gulong", SIZEOF_LONG, 0 }, + { "gssize", GLIB_SIZEOF_SIZE_T, 1 }, + { "gsize", GLIB_SIZEOF_SIZE_T, 0 }, + { "gintptr", GLIB_SIZEOF_SIZE_T, 1 }, + { "guintptr", GLIB_SIZEOF_SIZE_T, 0 }, +}; + +typedef struct { + const gchar *str; + gint tag; + gboolean pointer; +} BasicTypeInfo; + +#define BASIC_TYPE_FIXED_OFFSET 3 + +static BasicTypeInfo basic_types[] = { + { "none", GI_TYPE_TAG_VOID, 0 }, + { "gpointer", GI_TYPE_TAG_VOID, 1 }, + + { "gboolean", GI_TYPE_TAG_BOOLEAN, 0 }, + { "gint8", GI_TYPE_TAG_INT8, 0 }, /* Start of BASIC_TYPE_FIXED_OFFSET */ + { "guint8", GI_TYPE_TAG_UINT8, 0 }, + { "gint16", GI_TYPE_TAG_INT16, 0 }, + { "guint16", GI_TYPE_TAG_UINT16, 0 }, + { "gint32", GI_TYPE_TAG_INT32, 0 }, + { "guint32", GI_TYPE_TAG_UINT32, 0 }, + { "gint64", GI_TYPE_TAG_INT64, 0 }, + { "guint64", GI_TYPE_TAG_UINT64, 0 }, + { "gfloat", GI_TYPE_TAG_FLOAT, 0 }, + { "gdouble", GI_TYPE_TAG_DOUBLE, 0 }, + { "GType", GI_TYPE_TAG_GTYPE, 0 }, + { "utf8", GI_TYPE_TAG_UTF8, 1 }, + { "filename", GI_TYPE_TAG_FILENAME,1 }, + { "gunichar", GI_TYPE_TAG_UNICHAR, 0 }, +}; + +static const BasicTypeInfo * +parse_basic (const char *str) +{ + guint i; + guint n_basic = G_N_ELEMENTS (basic_types); + + for (i = 0; i < n_basic; i++) + { + if (strcmp (str, basic_types[i].str) == 0) + return &(basic_types[i]); + } + for (i = 0; i < G_N_ELEMENTS (integer_aliases); i++) + { + if (strcmp (str, integer_aliases[i].str) == 0) + { + switch (integer_aliases[i].size) + { + case sizeof(guint8): + if (integer_aliases[i].is_signed) + return &basic_types[BASIC_TYPE_FIXED_OFFSET]; + else + return &basic_types[BASIC_TYPE_FIXED_OFFSET+1]; + break; + case sizeof(guint16): + if (integer_aliases[i].is_signed) + return &basic_types[BASIC_TYPE_FIXED_OFFSET+2]; + else + return &basic_types[BASIC_TYPE_FIXED_OFFSET+3]; + break; + case sizeof(guint32): + if (integer_aliases[i].is_signed) + return &basic_types[BASIC_TYPE_FIXED_OFFSET+4]; + else + return &basic_types[BASIC_TYPE_FIXED_OFFSET+5]; + break; + case sizeof(guint64): + if (integer_aliases[i].is_signed) + return &basic_types[BASIC_TYPE_FIXED_OFFSET+6]; + else + return &basic_types[BASIC_TYPE_FIXED_OFFSET+7]; + break; + default: + g_assert_not_reached (); + } + } + } + return NULL; +} + +static GIrNodeType * +parse_type_internal (GIrModule *module, + const gchar *str, + char **next, + gboolean in_glib, + gboolean in_gobject) +{ + const BasicTypeInfo *basic; + GIrNodeType *type; + char *temporary_type = NULL; + + type = (GIrNodeType *)_g_ir_node_new (G_IR_NODE_TYPE, module); + + type->unparsed = g_strdup (str); + + /* See comment below on GLib.List handling */ + if (in_gobject && strcmp (str, "Type") == 0) + { + temporary_type = g_strdup ("GLib.Type"); + str = temporary_type; + } + + basic = parse_basic (str); + if (basic != NULL) + { + type->is_basic = TRUE; + type->tag = basic->tag; + type->is_pointer = basic->pointer; + + str += strlen(basic->str); + } + else if (in_glib) + { + /* If we're inside GLib, handle "List" etc. by prefixing with + * "GLib." so the parsing code below doesn't have to get more + * special. + */ + if (g_str_has_prefix (str, "List<") || + strcmp (str, "List") == 0) + { + temporary_type = g_strdup_printf ("GLib.List%s", str + 4); + str = temporary_type; + } + else if (g_str_has_prefix (str, "SList<") || + strcmp (str, "SList") == 0) + { + temporary_type = g_strdup_printf ("GLib.SList%s", str + 5); + str = temporary_type; + } + else if (g_str_has_prefix (str, "HashTable<") || + strcmp (str, "HashTable") == 0) + { + temporary_type = g_strdup_printf ("GLib.HashTable%s", str + 9); + str = temporary_type; + } + else if (g_str_has_prefix (str, "Error<") || + strcmp (str, "Error") == 0) + { + temporary_type = g_strdup_printf ("GLib.Error%s", str + 5); + str = temporary_type; + } + } + + if (basic != NULL) + /* found a basic type */; + else if (g_str_has_prefix (str, "GLib.List") || + g_str_has_prefix (str, "GLib.SList")) + { + str += strlen ("GLib."); + if (g_str_has_prefix (str, "List")) + { + type->tag = GI_TYPE_TAG_GLIST; + type->is_glist = TRUE; + type->is_pointer = TRUE; + str += strlen ("List"); + } + else + { + type->tag = GI_TYPE_TAG_GSLIST; + type->is_gslist = TRUE; + type->is_pointer = TRUE; + str += strlen ("SList"); + } + } + else if (g_str_has_prefix (str, "GLib.HashTable")) + { + str += strlen ("GLib."); + + type->tag = GI_TYPE_TAG_GHASH; + type->is_ghashtable = TRUE; + type->is_pointer = TRUE; + str += strlen ("HashTable"); + } + else if (g_str_has_prefix (str, "GLib.Error")) + { + str += strlen ("GLib."); + + type->tag = GI_TYPE_TAG_ERROR; + type->is_error = TRUE; + type->is_pointer = TRUE; + str += strlen ("Error"); + + if (*str == '<') + { + char *tmp, *end; + (str)++; + + end = strchr (str, '>'); + tmp = g_strndup (str, end - str); + type->errors = g_strsplit (tmp, ",", 0); + g_free (tmp); + + str = end; + } + } + else + { + const char *start; + type->tag = GI_TYPE_TAG_INTERFACE; + type->is_interface = TRUE; + start = str; + + /* must be an interface type */ + while (g_ascii_isalnum (*str) || + *str == '.' || + *str == '-' || + *str == '_' || + *str == ':') + (str)++; + + type->giinterface = g_strndup (start, str - start); + } + + if (next) + *next = (char*)str; + g_assert (type->tag >= 0 && type->tag < GI_TYPE_TAG_N_TYPES); + g_free (temporary_type); + return type; + +/* error: */ + _g_ir_node_free ((GIrNode *)type); + g_free (temporary_type); + return NULL; +} + +static const char * +resolve_aliases (ParseContext *ctx, const gchar *type) +{ + gpointer orig; + gpointer value; + GSList *seen_values = NULL; + const gchar *lookup; + gchar *prefixed; + + if (strchr (type, '.') == NULL) + { + prefixed = g_strdup_printf ("%s.%s", ctx->namespace, type); + lookup = prefixed; + } + else + { + lookup = type; + prefixed = NULL; + } + + seen_values = g_slist_prepend (seen_values, (char*)lookup); + while (g_hash_table_lookup_extended (ctx->current_module->aliases, lookup, &orig, &value)) + { + g_debug ("Resolved: %s => %s\n", lookup, (char*)value); + lookup = value; + if (g_slist_find_custom (seen_values, lookup, + (GCompareFunc)strcmp) != NULL) + break; + seen_values = g_slist_prepend (seen_values, (gchar*)lookup); + } + g_slist_free (seen_values); + + if (lookup == prefixed) + lookup = type; + + g_free (prefixed); + + return lookup; +} + +static void +is_pointer_or_disguised_structure (ParseContext *ctx, + const gchar *type, + gboolean *is_pointer, + gboolean *is_disguised) +{ + const gchar *lookup; + gchar *prefixed; + + if (strchr (type, '.') == NULL) + { + prefixed = g_strdup_printf ("%s.%s", ctx->namespace, type); + lookup = prefixed; + } + else + { + lookup = type; + prefixed = NULL; + } + + if (is_pointer != NULL) + *is_pointer = g_hash_table_lookup (ctx->current_module->pointer_structures, lookup) != NULL; + if (is_disguised != NULL) + *is_disguised = g_hash_table_lookup (ctx->current_module->disguised_structures, lookup) != NULL; + + g_free (prefixed); +} + +static GIrNodeType * +parse_type (ParseContext *ctx, const gchar *type) +{ + GIrNodeType *node; + const BasicTypeInfo *basic; + gboolean in_glib, in_gobject; + + in_glib = strcmp (ctx->namespace, "GLib") == 0; + in_gobject = strcmp (ctx->namespace, "GObject") == 0; + + /* Do not search aliases for basic types */ + basic = parse_basic (type); + if (basic == NULL) + type = resolve_aliases (ctx, type); + + node = parse_type_internal (ctx->current_module, type, NULL, in_glib, in_gobject); + if (node) + g_debug ("Parsed type: %s => %d", type, node->tag); + else + g_critical ("Failed to parse type: '%s'", type); + + return node; +} + +static gboolean +introspectable_prelude (GMarkupParseContext *context, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + ParseState new_state) +{ + const gchar *introspectable_arg; + const gchar *shadowed_by; + gboolean introspectable; + + g_assert (ctx->state != STATE_PASSTHROUGH); + + introspectable_arg = find_attribute ("introspectable", attribute_names, attribute_values); + shadowed_by = find_attribute ("shadowed-by", attribute_names, attribute_values); + + introspectable = !(introspectable_arg && atoi (introspectable_arg) == 0) && shadowed_by == NULL; + + if (introspectable) + state_switch (ctx, new_state); + else + state_switch (ctx, STATE_PASSTHROUGH); + + return introspectable; +} + +static gboolean +start_glib_boxed (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *typename; + const gchar *typeinit; + const gchar *deprecated; + GIrNodeBoxed *boxed; + + if (!(strcmp (element_name, "glib:boxed") == 0 && + ctx->state == STATE_NAMESPACE)) + return FALSE; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_BOXED)) + return TRUE; + + name = find_attribute ("glib:name", attribute_names, attribute_values); + typename = find_attribute ("glib:type-name", attribute_names, attribute_values); + typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "glib:name"); + return FALSE; + } + else if (typename == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "glib:type-name"); + return FALSE; + } + else if (typeinit == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "glib:get-type"); + return FALSE; + } + + boxed = (GIrNodeBoxed *) _g_ir_node_new (G_IR_NODE_BOXED, + ctx->current_module); + + ((GIrNode *)boxed)->name = g_strdup (name); + boxed->gtype_name = g_strdup (typename); + boxed->gtype_init = g_strdup (typeinit); + if (deprecated) + boxed->deprecated = TRUE; + else + boxed->deprecated = FALSE; + + push_node (ctx, (GIrNode *)boxed); + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, boxed); + + return TRUE; +} + +static gboolean +start_function (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *shadows; + const gchar *symbol; + const gchar *deprecated; + const gchar *throws; + const gchar *set_property; + const gchar *get_property; + GIrNodeFunction *function; + gboolean found = FALSE; + ParseState in_embedded_state = STATE_NONE; + + switch (ctx->state) + { + case STATE_NAMESPACE: + found = (strcmp (element_name, "function") == 0 || + strcmp (element_name, "callback") == 0); + break; + case STATE_CLASS: + case STATE_BOXED: + case STATE_STRUCT: + case STATE_UNION: + found = strcmp (element_name, "constructor") == 0; + /* fallthrough */ + G_GNUC_FALLTHROUGH; + case STATE_INTERFACE: + found = (found || + strcmp (element_name, "function") == 0 || + strcmp (element_name, "method") == 0 || + strcmp (element_name, "callback") == 0); + break; + case STATE_ENUM: + found = strcmp (element_name, "function") == 0; + break; + case STATE_CLASS_FIELD: + case STATE_STRUCT_FIELD: + found = (found || strcmp (element_name, "callback") == 0); + in_embedded_state = ctx->state; + break; + default: + break; + } + + if (!found) + return FALSE; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_FUNCTION)) + return TRUE; + + ctx->in_embedded_state = in_embedded_state; + + name = find_attribute ("name", attribute_names, attribute_values); + shadows = find_attribute ("shadows", attribute_names, attribute_values); + symbol = find_attribute ("c:identifier", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + throws = find_attribute ("throws", attribute_names, attribute_values); + set_property = find_attribute ("glib:set-property", attribute_names, attribute_values); + get_property = find_attribute ("glib:get-property", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + else if (strcmp (element_name, "callback") != 0 && symbol == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "c:identifier"); + return FALSE; + } + + if (shadows) + name = shadows; + + function = (GIrNodeFunction *) _g_ir_node_new (G_IR_NODE_FUNCTION, + ctx->current_module); + + ((GIrNode *)function)->name = g_strdup (name); + function->symbol = g_strdup (symbol); + function->parameters = NULL; + if (deprecated) + function->deprecated = TRUE; + else + function->deprecated = FALSE; + + if (strcmp (element_name, "method") == 0 || + strcmp (element_name, "constructor") == 0) + { + function->is_method = TRUE; + + if (strcmp (element_name, "constructor") == 0) + function->is_constructor = TRUE; + else + function->is_constructor = FALSE; + + if (set_property != NULL) + { + function->is_setter = TRUE; + function->is_getter = FALSE; + function->property = g_strdup (set_property); + } + else if (get_property != NULL) + { + function->is_setter = FALSE; + function->is_getter = TRUE; + function->property = g_strdup (get_property); + } + else + { + function->is_setter = FALSE; + function->is_getter = FALSE; + function->property = NULL; + } + } + else + { + function->is_method = FALSE; + function->is_setter = FALSE; + function->is_getter = FALSE; + function->is_constructor = FALSE; + if (strcmp (element_name, "callback") == 0) + ((GIrNode *)function)->type = G_IR_NODE_CALLBACK; + } + + if (throws && strcmp (throws, "1") == 0) + function->throws = TRUE; + else + function->throws = FALSE; + + if (ctx->node_stack == NULL) + { + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, function); + } + else if (ctx->current_typed) + { + GIrNodeField *field; + + field = (GIrNodeField *)ctx->current_typed; + field->callback = function; + } + else + switch (CURRENT_NODE (ctx)->type) + { + case G_IR_NODE_INTERFACE: + case G_IR_NODE_OBJECT: + { + GIrNodeInterface *iface; + + iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + iface->members = g_list_append (iface->members, function); + } + break; + case G_IR_NODE_BOXED: + { + GIrNodeBoxed *boxed; + + boxed = (GIrNodeBoxed *)CURRENT_NODE (ctx); + boxed->members = g_list_append (boxed->members, function); + } + break; + case G_IR_NODE_STRUCT: + { + GIrNodeStruct *struct_; + + struct_ = (GIrNodeStruct *)CURRENT_NODE (ctx); + struct_->members = g_list_append (struct_->members, function); } + break; + case G_IR_NODE_UNION: + { + GIrNodeUnion *union_; + + union_ = (GIrNodeUnion *)CURRENT_NODE (ctx); + union_->members = g_list_append (union_->members, function); + } + break; + case G_IR_NODE_ENUM: + case G_IR_NODE_FLAGS: + { + GIrNodeEnum *enum_; + + enum_ = (GIrNodeEnum *)CURRENT_NODE (ctx); + enum_->methods = g_list_append (enum_->methods, function); + } + break; + default: + g_assert_not_reached (); + } + + push_node(ctx, (GIrNode *)function); + + return TRUE; +} + +static void +parse_property_transfer (GIrNodeProperty *property, + const gchar *transfer, + ParseContext *ctx) +{ + if (transfer == NULL) + { +#if 0 + GIrNodeInterface *iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + + g_debug ("required attribute 'transfer-ownership' is missing from " + "property '%s' in type '%s.%s'. Assuming 'none'\n", + property->node.name, ctx->namespace, iface->node.name); +#endif + transfer = "none"; + } + if (strcmp (transfer, "none") == 0) + { + property->transfer = FALSE; + property->shallow_transfer = FALSE; + } + else if (strcmp (transfer, "container") == 0) + { + property->transfer = FALSE; + property->shallow_transfer = TRUE; + } + else if (strcmp (transfer, "full") == 0) + { + property->transfer = TRUE; + property->shallow_transfer = FALSE; + } + else + { + GIrNodeInterface *iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + + g_warning ("Unknown transfer-ownership value: '%s' for property '%s' in " + "type '%s.%s'", transfer, property->node.name, ctx->namespace, + iface->node.name); + } +} + +static gboolean +parse_param_transfer (GIrNodeParam *param, const gchar *transfer, const gchar *name, + GError **error) +{ + if (transfer == NULL) + { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "required attribute 'transfer-ownership' missing"); + return FALSE; + } + else if (strcmp (transfer, "none") == 0) + { + param->transfer = FALSE; + param->shallow_transfer = FALSE; + } + else if (strcmp (transfer, "container") == 0) + { + param->transfer = FALSE; + param->shallow_transfer = TRUE; + } + else if (strcmp (transfer, "full") == 0) + { + param->transfer = TRUE; + param->shallow_transfer = FALSE; + } + else + { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "invalid value for 'transfer-ownership': %s", transfer); + return FALSE; + } + return TRUE; +} + +static gboolean +start_instance_parameter (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *transfer; + gboolean transfer_full; + + if (!(strcmp (element_name, "instance-parameter") == 0 && + ctx->state == STATE_FUNCTION_PARAMETERS)) + return FALSE; + + transfer = find_attribute ("transfer-ownership", attribute_names, attribute_values); + + state_switch (ctx, STATE_PASSTHROUGH); + + if (g_strcmp0 (transfer, "full") == 0) + transfer_full = TRUE; + else if (g_strcmp0 (transfer, "none") == 0) + transfer_full = FALSE; + else + { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "invalid value for 'transfer-ownership' for instance parameter: %s", transfer); + return FALSE; + } + + switch (CURRENT_NODE (ctx)->type) + { + case G_IR_NODE_FUNCTION: + case G_IR_NODE_CALLBACK: + { + GIrNodeFunction *func; + + func = (GIrNodeFunction *)CURRENT_NODE (ctx); + func->instance_transfer_full = transfer_full; + } + break; + case G_IR_NODE_SIGNAL: + { + GIrNodeSignal *signal; + + signal = (GIrNodeSignal *)CURRENT_NODE (ctx); + signal->instance_transfer_full = transfer_full; + } + break; + case G_IR_NODE_VFUNC: + { + GIrNodeVFunc *vfunc; + + vfunc = (GIrNodeVFunc *)CURRENT_NODE (ctx); + vfunc->instance_transfer_full = transfer_full; + } + break; + default: + g_assert_not_reached (); + } + + return TRUE; +} + +static gboolean +start_parameter (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *direction; + const gchar *retval; + const gchar *optional; + const gchar *caller_allocates; + const gchar *allow_none; + const gchar *transfer; + const gchar *scope; + const gchar *closure; + const gchar *destroy; + const gchar *skip; + const gchar *nullable; + GIrNodeParam *param; + + if (!(strcmp (element_name, "parameter") == 0 && + ctx->state == STATE_FUNCTION_PARAMETERS)) + return FALSE; + + name = find_attribute ("name", attribute_names, attribute_values); + direction = find_attribute ("direction", attribute_names, attribute_values); + retval = find_attribute ("retval", attribute_names, attribute_values); + optional = find_attribute ("optional", attribute_names, attribute_values); + allow_none = find_attribute ("allow-none", attribute_names, attribute_values); + caller_allocates = find_attribute ("caller-allocates", attribute_names, attribute_values); + transfer = find_attribute ("transfer-ownership", attribute_names, attribute_values); + scope = find_attribute ("scope", attribute_names, attribute_values); + closure = find_attribute ("closure", attribute_names, attribute_values); + destroy = find_attribute ("destroy", attribute_names, attribute_values); + skip = find_attribute ("skip", attribute_names, attribute_values); + nullable = find_attribute ("nullable", attribute_names, attribute_values); + + if (name == NULL) + name = "unknown"; + + param = (GIrNodeParam *)_g_ir_node_new (G_IR_NODE_PARAM, + ctx->current_module); + + ctx->current_typed = (GIrNode*) param; + ctx->current_typed->name = g_strdup (name); + + state_switch (ctx, STATE_FUNCTION_PARAMETER); + + if (direction && strcmp (direction, "out") == 0) + { + param->in = FALSE; + param->out = TRUE; + if (caller_allocates == NULL) + param->caller_allocates = FALSE; + else + param->caller_allocates = strcmp (caller_allocates, "1") == 0; + } + else if (direction && strcmp (direction, "inout") == 0) + { + param->in = TRUE; + param->out = TRUE; + param->caller_allocates = FALSE; + } + else + { + param->in = TRUE; + param->out = FALSE; + param->caller_allocates = FALSE; + } + + if (retval && strcmp (retval, "1") == 0) + param->retval = TRUE; + else + param->retval = FALSE; + + if (optional && strcmp (optional, "1") == 0) + param->optional = TRUE; + else + param->optional = FALSE; + + if (nullable && strcmp (nullable, "1") == 0) + param->nullable = TRUE; + else + param->nullable = FALSE; + + if (allow_none && strcmp (allow_none, "1") == 0) + { + if (param->out) + param->optional = TRUE; + else + param->nullable = TRUE; + } + + if (skip && strcmp (skip, "1") == 0) + param->skip = TRUE; + else + param->skip = FALSE; + + if (!parse_param_transfer (param, transfer, name, error)) + return FALSE; + + if (scope && strcmp (scope, "call") == 0) + param->scope = GI_SCOPE_TYPE_CALL; + else if (scope && strcmp (scope, "async") == 0) + param->scope = GI_SCOPE_TYPE_ASYNC; + else if (scope && strcmp (scope, "notified") == 0) + param->scope = GI_SCOPE_TYPE_NOTIFIED; + else if (scope && strcmp (scope, "forever") == 0) + param->scope = GI_SCOPE_TYPE_FOREVER; + else + param->scope = GI_SCOPE_TYPE_INVALID; + + param->closure = closure ? atoi (closure) : -1; + param->destroy = destroy ? atoi (destroy) : -1; + + ((GIrNode *)param)->name = g_strdup (name); + + switch (CURRENT_NODE (ctx)->type) + { + case G_IR_NODE_FUNCTION: + case G_IR_NODE_CALLBACK: + { + GIrNodeFunction *func; + + func = (GIrNodeFunction *)CURRENT_NODE (ctx); + func->parameters = g_list_append (func->parameters, param); + } + break; + case G_IR_NODE_SIGNAL: + { + GIrNodeSignal *signal; + + signal = (GIrNodeSignal *)CURRENT_NODE (ctx); + signal->parameters = g_list_append (signal->parameters, param); + } + break; + case G_IR_NODE_VFUNC: + { + GIrNodeVFunc *vfunc; + + vfunc = (GIrNodeVFunc *)CURRENT_NODE (ctx); + vfunc->parameters = g_list_append (vfunc->parameters, param); + } + break; + default: + g_assert_not_reached (); + } + + return TRUE; +} + +static gboolean +start_field (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *readable; + const gchar *writable; + const gchar *bits; + const gchar *branch; + GIrNodeField *field; + ParseState target_state; + gboolean introspectable; + + switch (ctx->state) + { + case STATE_CLASS: + target_state = STATE_CLASS_FIELD; + break; + case STATE_BOXED: + target_state = STATE_BOXED_FIELD; + break; + case STATE_STRUCT: + target_state = STATE_STRUCT_FIELD; + break; + case STATE_UNION: + target_state = STATE_UNION_FIELD; + break; + case STATE_INTERFACE: + target_state = STATE_INTERFACE_FIELD; + break; + default: + return FALSE; + } + + if (strcmp (element_name, "field") != 0) + return FALSE; + + g_assert (ctx->state != STATE_PASSTHROUGH); + + /* We handle introspectability specially here; we replace with just gpointer + * for the type. + */ + introspectable = introspectable_prelude (context, attribute_names, attribute_values, ctx, target_state); + + name = find_attribute ("name", attribute_names, attribute_values); + readable = find_attribute ("readable", attribute_names, attribute_values); + writable = find_attribute ("writable", attribute_names, attribute_values); + bits = find_attribute ("bits", attribute_names, attribute_values); + branch = find_attribute ("branch", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + field = (GIrNodeField *)_g_ir_node_new (G_IR_NODE_FIELD, + ctx->current_module); + if (introspectable) + { + ctx->current_typed = (GIrNode*) field; + } + else + { + field->type = parse_type (ctx, "gpointer"); + } + + ((GIrNode *)field)->name = g_strdup (name); + /* Fields are assumed to be read-only. + * (see also girwriter.py and generate.c) + */ + field->readable = readable == NULL || strcmp (readable, "0") == 0; + field->writable = writable != NULL && strcmp (writable, "1") == 0; + + if (bits) + field->bits = atoi (bits); + else + field->bits = 0; + + switch (CURRENT_NODE (ctx)->type) + { + case G_IR_NODE_OBJECT: + { + GIrNodeInterface *iface; + + iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + iface->members = g_list_append (iface->members, field); + } + break; + case G_IR_NODE_INTERFACE: + { + GIrNodeInterface *iface; + + iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + iface->members = g_list_append (iface->members, field); + } + break; + case G_IR_NODE_BOXED: + { + GIrNodeBoxed *boxed; + + boxed = (GIrNodeBoxed *)CURRENT_NODE (ctx); + boxed->members = g_list_append (boxed->members, field); + } + break; + case G_IR_NODE_STRUCT: + { + GIrNodeStruct *struct_; + + struct_ = (GIrNodeStruct *)CURRENT_NODE (ctx); + struct_->members = g_list_append (struct_->members, field); + } + break; + case G_IR_NODE_UNION: + { + GIrNodeUnion *union_; + + union_ = (GIrNodeUnion *)CURRENT_NODE (ctx); + union_->members = g_list_append (union_->members, field); + if (branch) + { + GIrNodeConstant *constant; + + constant = (GIrNodeConstant *) _g_ir_node_new (G_IR_NODE_CONSTANT, + ctx->current_module); + ((GIrNode *)constant)->name = g_strdup (name); + constant->value = g_strdup (branch); + constant->type = union_->discriminator_type; + constant->deprecated = FALSE; + + union_->discriminators = g_list_append (union_->discriminators, constant); + } + } + break; + default: + g_assert_not_reached (); + } + + return TRUE; +} + +static gboolean +start_alias (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + + name = find_attribute ("name", attribute_names, attribute_values); + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + ctx->current_alias = g_strdup (name); + state_switch (ctx, STATE_ALIAS); + + return TRUE; +} + +static gboolean +start_enum (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *typename; + const gchar *typeinit; + const gchar *deprecated; + const gchar *error_domain; + GIrNodeEnum *enum_; + + if (!((strcmp (element_name, "enumeration") == 0 && ctx->state == STATE_NAMESPACE) || + (strcmp (element_name, "bitfield") == 0 && ctx->state == STATE_NAMESPACE))) + return FALSE; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_ENUM)) + return TRUE; + + name = find_attribute ("name", attribute_names, attribute_values); + typename = find_attribute ("glib:type-name", attribute_names, attribute_values); + typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values); + error_domain = find_attribute ("glib:error-domain", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + if (strcmp (element_name, "enumeration") == 0) + enum_ = (GIrNodeEnum *) _g_ir_node_new (G_IR_NODE_ENUM, + ctx->current_module); + else + enum_ = (GIrNodeEnum *) _g_ir_node_new (G_IR_NODE_FLAGS, + ctx->current_module); + ((GIrNode *)enum_)->name = g_strdup (name); + enum_->gtype_name = g_strdup (typename); + enum_->gtype_init = g_strdup (typeinit); + enum_->error_domain = g_strdup (error_domain); + + if (deprecated) + enum_->deprecated = TRUE; + else + enum_->deprecated = FALSE; + + push_node (ctx, (GIrNode *) enum_); + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, enum_); + + return TRUE; +} + +static gboolean +start_property (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + ParseState target_state; + const gchar *name; + const gchar *readable; + const gchar *writable; + const gchar *construct; + const gchar *construct_only; + const gchar *transfer; + const gchar *setter; + const gchar *getter; + GIrNodeProperty *property; + GIrNodeInterface *iface; + + if (!(strcmp (element_name, "property") == 0 && + (ctx->state == STATE_CLASS || + ctx->state == STATE_INTERFACE))) + return FALSE; + + if (ctx->state == STATE_CLASS) + target_state = STATE_CLASS_PROPERTY; + else if (ctx->state == STATE_INTERFACE) + target_state = STATE_INTERFACE_PROPERTY; + else + g_assert_not_reached (); + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, target_state)) + return TRUE; + + + name = find_attribute ("name", attribute_names, attribute_values); + readable = find_attribute ("readable", attribute_names, attribute_values); + writable = find_attribute ("writable", attribute_names, attribute_values); + construct = find_attribute ("construct", attribute_names, attribute_values); + construct_only = find_attribute ("construct-only", attribute_names, attribute_values); + transfer = find_attribute ("transfer-ownership", attribute_names, attribute_values); + setter = find_attribute ("setter", attribute_names, attribute_values); + getter = find_attribute ("getter", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + property = (GIrNodeProperty *) _g_ir_node_new (G_IR_NODE_PROPERTY, + ctx->current_module); + ctx->current_typed = (GIrNode*) property; + + ((GIrNode *)property)->name = g_strdup (name); + + /* Assume properties are readable */ + if (readable == NULL || strcmp (readable, "1") == 0) + property->readable = TRUE; + else + property->readable = FALSE; + if (writable && strcmp (writable, "1") == 0) + property->writable = TRUE; + else + property->writable = FALSE; + if (construct && strcmp (construct, "1") == 0) + property->construct = TRUE; + else + property->construct = FALSE; + if (construct_only && strcmp (construct_only, "1") == 0) + property->construct_only = TRUE; + else + property->construct_only = FALSE; + + property->setter = g_strdup (setter); + property->getter = g_strdup (getter); + + parse_property_transfer (property, transfer, ctx); + + iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + iface->members = g_list_append (iface->members, property); + + return TRUE; +} + +static gint64 +parse_value (const gchar *str) +{ + gchar *shift_op; + + /* FIXME just a quick hack */ + shift_op = strstr (str, "<<"); + + if (shift_op) + { + gint64 base, shift; + + base = g_ascii_strtoll (str, NULL, 10); + shift = g_ascii_strtoll (shift_op + 3, NULL, 10); + + return base << shift; + } + else + return g_ascii_strtoll (str, NULL, 10); + + return 0; +} + +static gboolean +start_member (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *value; + const gchar *deprecated; + const gchar *c_identifier; + GIrNodeEnum *enum_; + GIrNodeValue *value_; + + if (!(strcmp (element_name, "member") == 0 && + ctx->state == STATE_ENUM)) + return FALSE; + + name = find_attribute ("name", attribute_names, attribute_values); + value = find_attribute ("value", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + c_identifier = find_attribute ("c:identifier", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + value_ = (GIrNodeValue *) _g_ir_node_new (G_IR_NODE_VALUE, + ctx->current_module); + + ((GIrNode *)value_)->name = g_strdup (name); + + value_->value = parse_value (value); + + if (deprecated) + value_->deprecated = TRUE; + else + value_->deprecated = FALSE; + + g_hash_table_insert (((GIrNode *)value_)->attributes, + g_strdup ("c:identifier"), + g_strdup (c_identifier)); + + enum_ = (GIrNodeEnum *)CURRENT_NODE (ctx); + enum_->values = g_list_append (enum_->values, value_); + + return TRUE; +} + +static gboolean +start_constant (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + ParseState prev_state; + ParseState target_state; + const gchar *name; + const gchar *value; + const gchar *deprecated; + GIrNodeConstant *constant; + + if (!(strcmp (element_name, "constant") == 0 && + (ctx->state == STATE_NAMESPACE || + ctx->state == STATE_CLASS || + ctx->state == STATE_INTERFACE))) + return FALSE; + + switch (ctx->state) + { + case STATE_NAMESPACE: + target_state = STATE_NAMESPACE_CONSTANT; + break; + case STATE_CLASS: + target_state = STATE_CLASS_CONSTANT; + break; + case STATE_INTERFACE: + target_state = STATE_INTERFACE_CONSTANT; + break; + default: + g_assert_not_reached (); + } + + prev_state = ctx->state; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, target_state)) + return TRUE; + + name = find_attribute ("name", attribute_names, attribute_values); + value = find_attribute ("value", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + else if (value == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "value"); + return FALSE; + } + + constant = (GIrNodeConstant *) _g_ir_node_new (G_IR_NODE_CONSTANT, + ctx->current_module); + + ((GIrNode *)constant)->name = g_strdup (name); + constant->value = g_strdup (value); + + ctx->current_typed = (GIrNode*) constant; + + if (deprecated) + constant->deprecated = TRUE; + else + constant->deprecated = FALSE; + + if (prev_state == STATE_NAMESPACE) + { + push_node (ctx, (GIrNode *) constant); + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, constant); + } + else + { + GIrNodeInterface *iface; + + iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + iface->members = g_list_append (iface->members, constant); + } + + return TRUE; +} + +static gboolean +start_interface (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *typename; + const gchar *typeinit; + const gchar *deprecated; + const gchar *glib_type_struct; + GIrNodeInterface *iface; + + if (!(strcmp (element_name, "interface") == 0 && + ctx->state == STATE_NAMESPACE)) + return FALSE; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_INTERFACE)) + return TRUE; + + name = find_attribute ("name", attribute_names, attribute_values); + typename = find_attribute ("glib:type-name", attribute_names, attribute_values); + typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values); + glib_type_struct = find_attribute ("glib:type-struct", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + else if (typename == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "glib:type-name"); + return FALSE; + } + else if (typeinit == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "glib:get-type"); + return FALSE; + } + + iface = (GIrNodeInterface *) _g_ir_node_new (G_IR_NODE_INTERFACE, + ctx->current_module); + ((GIrNode *)iface)->name = g_strdup (name); + iface->gtype_name = g_strdup (typename); + iface->gtype_init = g_strdup (typeinit); + iface->glib_type_struct = g_strdup (glib_type_struct); + if (deprecated) + iface->deprecated = TRUE; + else + iface->deprecated = FALSE; + + push_node (ctx, (GIrNode *) iface); + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, iface); + + return TRUE; +} + +static gboolean +start_class (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *parent; + const gchar *glib_type_struct; + const gchar *typename; + const gchar *typeinit; + const gchar *deprecated; + const gchar *abstract; + const gchar *fundamental; + const gchar *final; + const gchar *ref_func; + const gchar *unref_func; + const gchar *set_value_func; + const gchar *get_value_func; + GIrNodeInterface *iface; + + if (!(strcmp (element_name, "class") == 0 && + ctx->state == STATE_NAMESPACE)) + return FALSE; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_CLASS)) + return TRUE; + + name = find_attribute ("name", attribute_names, attribute_values); + parent = find_attribute ("parent", attribute_names, attribute_values); + glib_type_struct = find_attribute ("glib:type-struct", attribute_names, attribute_values); + typename = find_attribute ("glib:type-name", attribute_names, attribute_values); + typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + abstract = find_attribute ("abstract", attribute_names, attribute_values); + final = find_attribute ("final", attribute_names, attribute_values); + fundamental = find_attribute ("glib:fundamental", attribute_names, attribute_values); + ref_func = find_attribute ("glib:ref-func", attribute_names, attribute_values); + unref_func = find_attribute ("glib:unref-func", attribute_names, attribute_values); + set_value_func = find_attribute ("glib:set-value-func", attribute_names, attribute_values); + get_value_func = find_attribute ("glib:get-value-func", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + else if (typename == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "glib:type-name"); + return FALSE; + } + else if (typeinit == NULL && strcmp (typename, "GObject")) + { + MISSING_ATTRIBUTE (context, error, element_name, "glib:get-type"); + return FALSE; + } + + iface = (GIrNodeInterface *) _g_ir_node_new (G_IR_NODE_OBJECT, + ctx->current_module); + ((GIrNode *)iface)->name = g_strdup (name); + iface->gtype_name = g_strdup (typename); + iface->gtype_init = g_strdup (typeinit); + iface->parent = g_strdup (parent); + iface->glib_type_struct = g_strdup (glib_type_struct); + if (deprecated) + iface->deprecated = TRUE; + else + iface->deprecated = FALSE; + + iface->abstract = abstract && strcmp (abstract, "1") == 0; + iface->final_ = final && strcmp (final, "1") == 0; + + if (fundamental) + iface->fundamental = TRUE; + if (ref_func) + iface->ref_func = g_strdup (ref_func); + if (unref_func) + iface->unref_func = g_strdup (unref_func); + if (set_value_func) + iface->set_value_func = g_strdup (set_value_func); + if (get_value_func) + iface->get_value_func = g_strdup (get_value_func); + + push_node (ctx, (GIrNode *) iface); + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, iface); + + return TRUE; +} + +static gboolean +start_type (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *ctype; + gboolean in_alias = FALSE; + gboolean is_array; + gboolean is_varargs; + GIrNodeType *typenode; + + is_array = strcmp (element_name, "array") == 0; + is_varargs = strcmp (element_name, "varargs") == 0; + + if (!(is_array || is_varargs || (strcmp (element_name, "type") == 0))) + return FALSE; + + if (ctx->state == STATE_TYPE) + { + ctx->type_depth++; + ctx->type_stack = g_list_prepend (ctx->type_stack, ctx->type_parameters); + ctx->type_parameters = NULL; + } + else if (ctx->state == STATE_FUNCTION_PARAMETER || + ctx->state == STATE_FUNCTION_RETURN || + ctx->state == STATE_STRUCT_FIELD || + ctx->state == STATE_UNION_FIELD || + ctx->state == STATE_CLASS_PROPERTY || + ctx->state == STATE_CLASS_FIELD || + ctx->state == STATE_INTERFACE_FIELD || + ctx->state == STATE_INTERFACE_PROPERTY || + ctx->state == STATE_BOXED_FIELD || + ctx->state == STATE_NAMESPACE_CONSTANT || + ctx->state == STATE_CLASS_CONSTANT || + ctx->state == STATE_INTERFACE_CONSTANT || + ctx->state == STATE_ALIAS + ) + { + if (ctx->state == STATE_ALIAS) + in_alias = TRUE; + state_switch (ctx, STATE_TYPE); + ctx->type_depth = 1; + ctx->type_stack = NULL; + ctx->type_parameters = NULL; + } + + name = find_attribute ("name", attribute_names, attribute_values); + + if (in_alias && ctx->current_alias) + { + char *key; + char *value; + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + key = g_strdup_printf ("%s.%s", ctx->namespace, ctx->current_alias); + if (!strchr (name, '.')) + { + const BasicTypeInfo *basic = parse_basic (name); + if (!basic) + { + /* For non-basic types, re-qualify the interface */ + value = g_strdup_printf ("%s.%s", ctx->namespace, name); + } + else + { + value = g_strdup (name); + } + } + else + value = g_strdup (name); + + g_hash_table_replace (ctx->aliases, key, value); + + return TRUE; + } + else if (!ctx->current_module || in_alias) + return TRUE; + + if (!ctx->current_typed) + { + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "The element is invalid here"); + return FALSE; + } + + if (is_varargs) + return TRUE; + + if (is_array) + { + const char *zero; + const char *len; + const char *size; + + typenode = (GIrNodeType *)_g_ir_node_new (G_IR_NODE_TYPE, + ctx->current_module); + + typenode->tag = GI_TYPE_TAG_ARRAY; + typenode->is_pointer = TRUE; + typenode->is_array = TRUE; + + if (name && strcmp (name, "GLib.Array") == 0) { + typenode->array_type = GI_ARRAY_TYPE_ARRAY; + } else if (name && strcmp (name, "GLib.ByteArray") == 0) { + typenode->array_type = GI_ARRAY_TYPE_BYTE_ARRAY; + } else if (name && strcmp (name, "GLib.PtrArray") == 0) { + typenode->array_type = GI_ARRAY_TYPE_PTR_ARRAY; + } else { + typenode->array_type = GI_ARRAY_TYPE_C; + } + + if (typenode->array_type == GI_ARRAY_TYPE_C) { + zero = find_attribute ("zero-terminated", attribute_names, attribute_values); + len = find_attribute ("length", attribute_names, attribute_values); + size = find_attribute ("fixed-size", attribute_names, attribute_values); + + typenode->has_length = len != NULL; + typenode->length = typenode->has_length ? atoi (len) : -1; + + typenode->has_size = size != NULL; + typenode->size = typenode->has_size ? atoi (size) : -1; + + if (zero) + typenode->zero_terminated = strcmp(zero, "1") == 0; + else + /* If neither zero-terminated nor length nor fixed-size is given, assume zero-terminated. */ + typenode->zero_terminated = !(typenode->has_length || typenode->has_size); + + if (typenode->has_size && ctx->current_typed->type == G_IR_NODE_FIELD) + typenode->is_pointer = FALSE; + } else { + typenode->zero_terminated = FALSE; + typenode->has_length = FALSE; + typenode->length = -1; + typenode->has_size = FALSE; + typenode->size = -1; + } + } + else + { + int pointer_depth; + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + pointer_depth = 0; + ctype = find_attribute ("c:type", attribute_names, attribute_values); + if (ctype != NULL) + { + const char *cp = ctype + strlen(ctype) - 1; + while (cp > ctype && *cp-- == '*') + pointer_depth++; + + if (g_str_has_prefix (ctype, "gpointer") + || g_str_has_prefix (ctype, "gconstpointer")) + pointer_depth++; + } + + if (ctx->current_typed->type == G_IR_NODE_PARAM && + ((GIrNodeParam *)ctx->current_typed)->out && + pointer_depth > 0) + pointer_depth--; + + typenode = parse_type (ctx, name); + + /* A "pointer" structure is one where the c:type is a typedef that + * to a pointer to a structure; we used to call them "disguised" + * structures as well. + */ + if (typenode->tag == GI_TYPE_TAG_INTERFACE) + { + gboolean is_pointer = FALSE; + gboolean is_disguised = FALSE; + + is_pointer_or_disguised_structure (ctx, typenode->giinterface, + &is_pointer, + &is_disguised); + + if (is_pointer || is_disguised) + pointer_depth++; + } + + if (pointer_depth > 0) + typenode->is_pointer = TRUE; + } + + ctx->type_parameters = g_list_append (ctx->type_parameters, typenode); + + return TRUE; +} + +static void +end_type_top (ParseContext *ctx) +{ + GIrNodeType *typenode; + + if (!ctx->type_parameters) + goto out; + + typenode = (GIrNodeType*)ctx->type_parameters->data; + + /* Default to pointer for unspecified containers */ + if (typenode->tag == GI_TYPE_TAG_ARRAY || + typenode->tag == GI_TYPE_TAG_GLIST || + typenode->tag == GI_TYPE_TAG_GSLIST) + { + if (typenode->parameter_type1 == NULL) + typenode->parameter_type1 = parse_type (ctx, "gpointer"); + } + else if (typenode->tag == GI_TYPE_TAG_GHASH) + { + if (typenode->parameter_type1 == NULL) + { + typenode->parameter_type1 = parse_type (ctx, "gpointer"); + typenode->parameter_type2 = parse_type (ctx, "gpointer"); + } + } + + switch (ctx->current_typed->type) + { + case G_IR_NODE_PARAM: + { + GIrNodeParam *param = (GIrNodeParam *)ctx->current_typed; + param->type = typenode; + } + break; + case G_IR_NODE_FIELD: + { + GIrNodeField *field = (GIrNodeField *)ctx->current_typed; + field->type = typenode; + } + break; + case G_IR_NODE_PROPERTY: + { + GIrNodeProperty *property = (GIrNodeProperty *) ctx->current_typed; + property->type = typenode; + } + break; + case G_IR_NODE_CONSTANT: + { + GIrNodeConstant *constant = (GIrNodeConstant *)ctx->current_typed; + constant->type = typenode; + } + break; + default: + g_printerr("current node is %d\n", CURRENT_NODE (ctx)->type); + g_assert_not_reached (); + } + g_list_free (ctx->type_parameters); + + out: + ctx->type_depth = 0; + ctx->type_parameters = NULL; + ctx->current_typed = NULL; +} + +static void +end_type_recurse (ParseContext *ctx) +{ + GIrNodeType *parent; + GIrNodeType *param = NULL; + + parent = (GIrNodeType *) ((GList*)ctx->type_stack->data)->data; + if (ctx->type_parameters) + param = (GIrNodeType *) ctx->type_parameters->data; + + if (parent->tag == GI_TYPE_TAG_ARRAY || + parent->tag == GI_TYPE_TAG_GLIST || + parent->tag == GI_TYPE_TAG_GSLIST) + { + g_assert (param != NULL); + + if (parent->parameter_type1 == NULL) + parent->parameter_type1 = param; + else + g_assert_not_reached (); + } + else if (parent->tag == GI_TYPE_TAG_GHASH) + { + g_assert (param != NULL); + + if (parent->parameter_type1 == NULL) + parent->parameter_type1 = param; + else if (parent->parameter_type2 == NULL) + parent->parameter_type2 = param; + else + g_assert_not_reached (); + } + g_list_free (ctx->type_parameters); + ctx->type_parameters = (GList *)ctx->type_stack->data; + ctx->type_stack = g_list_delete_link (ctx->type_stack, ctx->type_stack); +} + +static void +end_type (ParseContext *ctx) +{ + if (ctx->type_depth == 1) + { + end_type_top (ctx); + state_switch (ctx, ctx->prev_state); + } + else + { + end_type_recurse (ctx); + ctx->type_depth--; + } +} + +static gboolean +start_attribute (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *value; + GIrNode *curnode; + + if (strcmp (element_name, "attribute") != 0 || ctx->node_stack == NULL) + return FALSE; + + name = find_attribute ("name", attribute_names, attribute_values); + value = find_attribute ("value", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + if (value == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "value"); + return FALSE; + } + + state_switch (ctx, STATE_ATTRIBUTE); + + curnode = CURRENT_NODE (ctx); + + if (ctx->current_typed && ctx->current_typed->type == G_IR_NODE_PARAM) + { + g_hash_table_insert (ctx->current_typed->attributes, g_strdup (name), g_strdup (value)); + } + else + { + g_hash_table_insert (curnode->attributes, g_strdup (name), g_strdup (value)); + } + + return TRUE; +} + +static gboolean +start_return_value (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + GIrNodeParam *param; + const gchar *transfer; + const gchar *skip; + const gchar *nullable; + + if (!(strcmp (element_name, "return-value") == 0 && + ctx->state == STATE_FUNCTION)) + return FALSE; + + param = (GIrNodeParam *)_g_ir_node_new (G_IR_NODE_PARAM, + ctx->current_module); + param->in = FALSE; + param->out = FALSE; + param->retval = TRUE; + + ctx->current_typed = (GIrNode*) param; + + state_switch (ctx, STATE_FUNCTION_RETURN); + + skip = find_attribute ("skip", attribute_names, attribute_values); + if (skip && strcmp (skip, "1") == 0) + param->skip = TRUE; + else + param->skip = FALSE; + + transfer = find_attribute ("transfer-ownership", attribute_names, attribute_values); + if (!parse_param_transfer (param, transfer, NULL, error)) + return FALSE; + + nullable = find_attribute ("nullable", attribute_names, attribute_values); + if (nullable && g_str_equal (nullable, "1")) + param->nullable = TRUE; + + switch (CURRENT_NODE (ctx)->type) + { + case G_IR_NODE_FUNCTION: + case G_IR_NODE_CALLBACK: + { + GIrNodeFunction *func = (GIrNodeFunction *)CURRENT_NODE (ctx); + func->result = param; + } + break; + case G_IR_NODE_SIGNAL: + { + GIrNodeSignal *signal = (GIrNodeSignal *)CURRENT_NODE (ctx); + signal->result = param; + } + break; + case G_IR_NODE_VFUNC: + { + GIrNodeVFunc *vfunc = (GIrNodeVFunc *)CURRENT_NODE (ctx); + vfunc->result = param; + } + break; + default: + g_assert_not_reached (); + } + + return TRUE; +} + +static gboolean +start_implements (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + GIrNodeInterface *iface; + const char *name; + + if (strcmp (element_name, "implements") != 0 || + !(ctx->state == STATE_CLASS)) + return FALSE; + + state_switch (ctx, STATE_IMPLEMENTS); + + name = find_attribute ("name", attribute_names, attribute_values); + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + iface->interfaces = g_list_append (iface->interfaces, g_strdup (name)); + + return TRUE; +} + +static gboolean +start_glib_signal (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *when; + const gchar *no_recurse; + const gchar *detailed; + const gchar *action; + const gchar *no_hooks; + const gchar *has_class_closure; + GIrNodeInterface *iface; + GIrNodeSignal *signal; + + if (!(strcmp (element_name, "glib:signal") == 0 && + (ctx->state == STATE_CLASS || + ctx->state == STATE_INTERFACE))) + return FALSE; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_FUNCTION)) + return TRUE; + + name = find_attribute ("name", attribute_names, attribute_values); + when = find_attribute ("when", attribute_names, attribute_values); + no_recurse = find_attribute ("no-recurse", attribute_names, attribute_values); + detailed = find_attribute ("detailed", attribute_names, attribute_values); + action = find_attribute ("action", attribute_names, attribute_values); + no_hooks = find_attribute ("no-hooks", attribute_names, attribute_values); + has_class_closure = find_attribute ("has-class-closure", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + signal = (GIrNodeSignal *)_g_ir_node_new (G_IR_NODE_SIGNAL, + ctx->current_module); + + ((GIrNode *)signal)->name = g_strdup (name); + + signal->run_first = FALSE; + signal->run_last = FALSE; + signal->run_cleanup = FALSE; + if (when == NULL || g_ascii_strcasecmp (when, "LAST") == 0) + signal->run_last = TRUE; + else if (g_ascii_strcasecmp (when, "FIRST") == 0) + signal->run_first = TRUE; + else + signal->run_cleanup = TRUE; + + if (no_recurse && strcmp (no_recurse, "1") == 0) + signal->no_recurse = TRUE; + else + signal->no_recurse = FALSE; + if (detailed && strcmp (detailed, "1") == 0) + signal->detailed = TRUE; + else + signal->detailed = FALSE; + if (action && strcmp (action, "1") == 0) + signal->action = TRUE; + else + signal->action = FALSE; + if (no_hooks && strcmp (no_hooks, "1") == 0) + signal->no_hooks = TRUE; + else + signal->no_hooks = FALSE; + if (has_class_closure && strcmp (has_class_closure, "1") == 0) + signal->has_class_closure = TRUE; + else + signal->has_class_closure = FALSE; + + iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + iface->members = g_list_append (iface->members, signal); + + push_node (ctx, (GIrNode *)signal); + + return TRUE; +} + +static gboolean +start_vfunc (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *must_chain_up; + const gchar *override; + const gchar *is_class_closure; + const gchar *offset; + const gchar *invoker; + const gchar *throws; + GIrNodeInterface *iface; + GIrNodeVFunc *vfunc; + + if (!(strcmp (element_name, "virtual-method") == 0 && + (ctx->state == STATE_CLASS || + ctx->state == STATE_INTERFACE))) + return FALSE; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_FUNCTION)) + return TRUE; + + name = find_attribute ("name", attribute_names, attribute_values); + must_chain_up = find_attribute ("must-chain-up", attribute_names, attribute_values); + override = find_attribute ("override", attribute_names, attribute_values); + is_class_closure = find_attribute ("is-class-closure", attribute_names, attribute_values); + offset = find_attribute ("offset", attribute_names, attribute_values); + invoker = find_attribute ("invoker", attribute_names, attribute_values); + throws = find_attribute ("throws", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + vfunc = (GIrNodeVFunc *)_g_ir_node_new (G_IR_NODE_VFUNC, + ctx->current_module); + + ((GIrNode *)vfunc)->name = g_strdup (name); + + if (must_chain_up && strcmp (must_chain_up, "1") == 0) + vfunc->must_chain_up = TRUE; + else + vfunc->must_chain_up = FALSE; + + if (override && strcmp (override, "always") == 0) + { + vfunc->must_be_implemented = TRUE; + vfunc->must_not_be_implemented = FALSE; + } + else if (override && strcmp (override, "never") == 0) + { + vfunc->must_be_implemented = FALSE; + vfunc->must_not_be_implemented = TRUE; + } + else + { + vfunc->must_be_implemented = FALSE; + vfunc->must_not_be_implemented = FALSE; + } + + if (is_class_closure && strcmp (is_class_closure, "1") == 0) + vfunc->is_class_closure = TRUE; + else + vfunc->is_class_closure = FALSE; + + if (throws && strcmp (throws, "1") == 0) + vfunc->throws = TRUE; + else + vfunc->throws = FALSE; + + if (offset) + vfunc->offset = atoi (offset); + else + vfunc->offset = 0xFFFF; + + vfunc->invoker = g_strdup (invoker); + + iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + iface->members = g_list_append (iface->members, vfunc); + + push_node (ctx, (GIrNode *)vfunc); + + return TRUE; +} + +static gboolean +start_struct (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *deprecated; + const gchar *disguised; + const gchar *opaque; + const gchar *pointer; + const gchar *gtype_name; + const gchar *gtype_init; + const gchar *gtype_struct; + const gchar *foreign; + const gchar *copy_func; + const gchar *free_func; + GIrNodeStruct *struct_; + + if (!(strcmp (element_name, "record") == 0 && + (ctx->state == STATE_NAMESPACE || + ctx->state == STATE_UNION || + ctx->state == STATE_STRUCT || + ctx->state == STATE_CLASS))) + return FALSE; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_STRUCT)) + return TRUE; + + name = find_attribute ("name", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + disguised = find_attribute ("disguised", attribute_names, attribute_values); + pointer = find_attribute ("pointer", attribute_names, attribute_values); + opaque = find_attribute ("opaque", attribute_names, attribute_values); + gtype_name = find_attribute ("glib:type-name", attribute_names, attribute_values); + gtype_init = find_attribute ("glib:get-type", attribute_names, attribute_values); + gtype_struct = find_attribute ("glib:is-gtype-struct-for", attribute_names, attribute_values); + foreign = find_attribute ("foreign", attribute_names, attribute_values); + copy_func = find_attribute ("copy-function", attribute_names, attribute_values); + free_func = find_attribute ("free-function", attribute_names, attribute_values); + + if (name == NULL && ctx->node_stack == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + if ((gtype_name == NULL && gtype_init != NULL)) + { + MISSING_ATTRIBUTE (context, error, element_name, "glib:type-name"); + return FALSE; + } + if ((gtype_name != NULL && gtype_init == NULL)) + { + MISSING_ATTRIBUTE (context, error, element_name, "glib:get-type"); + return FALSE; + } + + struct_ = (GIrNodeStruct *) _g_ir_node_new (G_IR_NODE_STRUCT, + ctx->current_module); + + ((GIrNode *)struct_)->name = g_strdup (name ? name : ""); + if (deprecated) + struct_->deprecated = TRUE; + else + struct_->deprecated = FALSE; + + if (g_strcmp0 (disguised, "1") == 0) + struct_->disguised = TRUE; + + if (g_strcmp0 (pointer, "1") == 0) + struct_->pointer = TRUE; + + if (g_strcmp0 (opaque, "1") == 0) + struct_->opaque = TRUE; + + struct_->is_gtype_struct = gtype_struct != NULL; + + struct_->gtype_name = g_strdup (gtype_name); + struct_->gtype_init = g_strdup (gtype_init); + + struct_->foreign = (g_strcmp0 (foreign, "1") == 0); + + struct_->copy_func = g_strdup (copy_func); + struct_->free_func = g_strdup (free_func); + + if (ctx->node_stack == NULL) + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, struct_); + push_node (ctx, (GIrNode *)struct_); + return TRUE; +} + +static gboolean +start_union (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *deprecated; + const gchar *typename; + const gchar *typeinit; + const gchar *copy_func; + const gchar *free_func; + GIrNodeUnion *union_; + + if (!(strcmp (element_name, "union") == 0 && + (ctx->state == STATE_NAMESPACE || + ctx->state == STATE_UNION || + ctx->state == STATE_STRUCT || + ctx->state == STATE_CLASS))) + return FALSE; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_UNION)) + return TRUE; + + name = find_attribute ("name", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + typename = find_attribute ("glib:type-name", attribute_names, attribute_values); + typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values); + copy_func = find_attribute ("copy-function", attribute_names, attribute_values); + free_func = find_attribute ("free-function", attribute_names, attribute_values); + + if (name == NULL && ctx->node_stack == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + union_ = (GIrNodeUnion *) _g_ir_node_new (G_IR_NODE_UNION, + ctx->current_module); + + ((GIrNode *)union_)->name = g_strdup (name ? name : ""); + union_->gtype_name = g_strdup (typename); + union_->gtype_init = g_strdup (typeinit); + union_->copy_func = g_strdup (copy_func); + union_->free_func = g_strdup (free_func); + if (deprecated) + union_->deprecated = TRUE; + else + union_->deprecated = FALSE; + + if (ctx->node_stack == NULL) + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, union_); + push_node (ctx, (GIrNode *)union_); + return TRUE; +} + +static gboolean +start_discriminator (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *type; + const gchar *offset; + if (!(strcmp (element_name, "discriminator") == 0 && + ctx->state == STATE_UNION)) + return FALSE; + + type = find_attribute ("type", attribute_names, attribute_values); + offset = find_attribute ("offset", attribute_names, attribute_values); + if (type == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "type"); + return FALSE; + } + else if (offset == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "offset"); + return FALSE; + } + + ((GIrNodeUnion *)CURRENT_NODE (ctx))->discriminator_type + = parse_type (ctx, type); + ((GIrNodeUnion *)CURRENT_NODE (ctx))->discriminator_offset + = atoi (offset); + + return TRUE; +} + +static gboolean +parse_include (GMarkupParseContext *context, + ParseContext *ctx, + const char *name, + const char *version) +{ + GError *error = NULL; + gchar *buffer; + gsize length; + gchar *girpath, *girname; + GIrModule *module; + GList *l; + + for (l = ctx->parser->parsed_modules; l; l = l->next) + { + GIrModule *m = l->data; + + if (strcmp (m->name, name) == 0) + { + if (strcmp (m->version, version) == 0) + { + ctx->include_modules = g_list_prepend (ctx->include_modules, m); + + return TRUE; + } + else + { + g_printerr ("Module '%s' imported with conflicting versions '%s' and '%s'\n", + name, m->version, version); + return FALSE; + } + } + } + + girname = g_strdup_printf ("%s-%s.gir", name, version); + girpath = locate_gir (ctx->parser, girname); + + if (girpath == NULL) + { + g_printerr ("Could not find GIR file '%s'; check XDG_DATA_DIRS or use --includedir\n", + girname); + g_free (girname); + return FALSE; + } + g_free (girname); + + g_debug ("Parsing include %s\n", girpath); + + if (!g_file_get_contents (girpath, &buffer, &length, &error)) + { + g_printerr ("%s: %s\n", girpath, error->message); + g_clear_error (&error); + g_free (girpath); + return FALSE; + } + + module = _g_ir_parser_parse_string (ctx->parser, name, girpath, buffer, length, &error); + g_free (buffer); + if (error != NULL) + { + int line_number, char_number; + g_markup_parse_context_get_position (context, &line_number, &char_number); + g_printerr ("%s:%d:%d: error: %s\n", girpath, line_number, char_number, error->message); + g_clear_error (&error); + g_free (girpath); + return FALSE; + } + g_free (girpath); + + ctx->include_modules = g_list_append (ctx->include_modules, + module); + + return TRUE; +} + +extern GLogLevelFlags logged_levels; + +static void +start_element_handler (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + ParseContext *ctx = user_data; + + if (logged_levels & G_LOG_LEVEL_DEBUG) + { + GString *tags = g_string_new (""); + int i; + for (i = 0; attribute_names[i]; i++) + g_string_append_printf (tags, "%s=\"%s\" ", + attribute_names[i], + attribute_values[i]); + + if (i) + { + g_string_insert_c (tags, 0, ' '); + g_string_truncate (tags, tags->len - 1); + } + g_debug ("<%s%s>", element_name, tags->str); + g_string_free (tags, TRUE); + } + + if (ctx->state == STATE_PASSTHROUGH) + { + ctx->unknown_depth += 1; + return; + } + + switch (element_name[0]) + { + case 'a': + if (ctx->state == STATE_NAMESPACE && strcmp (element_name, "alias") == 0) + { + state_switch (ctx, STATE_ALIAS); + goto out; + } + if (start_type (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_attribute (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + case 'b': + if (start_enum (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + case 'c': + if (start_function (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_constant (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_class (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 'd': + if (start_discriminator (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + if (strcmp ("doc", element_name) == 0 || strcmp ("doc-deprecated", element_name) == 0 || + strcmp ("doc-stability", element_name) == 0 || strcmp ("doc-version", element_name) == 0 || + strcmp ("docsection", element_name) == 0) + { + state_switch (ctx, STATE_PASSTHROUGH); + goto out; + } + break; + + case 'e': + if (start_enum (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 'f': + if (strcmp ("function-macro", element_name) == 0) + { + state_switch (ctx, STATE_PASSTHROUGH); + goto out; + } + else if (start_function (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_field (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 'g': + if (start_glib_boxed (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_glib_signal (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 'i': + if (strcmp (element_name, "include") == 0 && + ctx->state == STATE_REPOSITORY) + { + const gchar *name; + const gchar *version; + + name = find_attribute ("name", attribute_names, attribute_values); + version = find_attribute ("version", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + break; + } + if (version == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "version"); + break; + } + + if (!parse_include (context, ctx, name, version)) + { + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Failed to parse included gir %s-%s", + name, + version); + return; + } + + ctx->dependencies = g_list_prepend (ctx->dependencies, + g_strdup_printf ("%s-%s", name, version)); + + + state_switch (ctx, STATE_INCLUDE); + goto out; + } + if (start_interface (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_implements (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_instance_parameter (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (strcmp (element_name, "c:include") == 0) + { + state_switch (ctx, STATE_C_INCLUDE); + goto out; + } + break; + + case 'm': + if (start_function (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_member (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 'n': + if (strcmp (element_name, "namespace") == 0 && ctx->state == STATE_REPOSITORY) + { + const gchar *name, *version, *shared_library, *cprefix; + + if (ctx->current_module != NULL) + { + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Only one element is currently allowed per "); + goto out; + } + + name = find_attribute ("name", attribute_names, attribute_values); + version = find_attribute ("version", attribute_names, attribute_values); + shared_library = find_attribute ("shared-library", attribute_names, attribute_values); + cprefix = find_attribute ("c:identifier-prefixes", attribute_names, attribute_values); + /* Backwards compatibility; vala currently still generates this */ + if (cprefix == NULL) + cprefix = find_attribute ("c:prefix", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (context, error, element_name, "name"); + else if (version == NULL) + MISSING_ATTRIBUTE (context, error, element_name, "version"); + else + { + GList *l; + + if (strcmp (name, ctx->namespace) != 0) + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + " name element '%s' doesn't match file name '%s'", + name, ctx->namespace); + + ctx->current_module = _g_ir_module_new (name, version, shared_library, cprefix); + + ctx->current_module->aliases = ctx->aliases; + ctx->aliases = NULL; + ctx->current_module->disguised_structures = ctx->disguised_structures; + ctx->current_module->pointer_structures = ctx->pointer_structures; + ctx->disguised_structures = NULL; + ctx->pointer_structures = NULL; + + for (l = ctx->include_modules; l; l = l->next) + _g_ir_module_add_include_module (ctx->current_module, l->data); + + g_list_free (ctx->include_modules); + ctx->include_modules = NULL; + + ctx->modules = g_list_append (ctx->modules, ctx->current_module); + ctx->current_module->dependencies = ctx->dependencies; + + state_switch (ctx, STATE_NAMESPACE); + goto out; + } + } + break; + + case 'p': + if (start_property (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (strcmp (element_name, "parameters") == 0 && + ctx->state == STATE_FUNCTION) + { + state_switch (ctx, STATE_FUNCTION_PARAMETERS); + + goto out; + } + else if (start_parameter (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (strcmp (element_name, "prerequisite") == 0 && + ctx->state == STATE_INTERFACE) + { + const gchar *name; + + name = find_attribute ("name", attribute_names, attribute_values); + + state_switch (ctx, STATE_PREREQUISITE); + + if (name == NULL) + MISSING_ATTRIBUTE (context, error, element_name, "name"); + else + { + GIrNodeInterface *iface; + + iface = (GIrNodeInterface *)CURRENT_NODE(ctx); + iface->prerequisites = g_list_append (iface->prerequisites, g_strdup (name)); + } + goto out; + } + else if (strcmp (element_name, "package") == 0 && + ctx->state == STATE_REPOSITORY) + { + state_switch (ctx, STATE_PACKAGE); + goto out; + } + break; + + case 'r': + if (strcmp (element_name, "repository") == 0 && ctx->state == STATE_START) + { + const gchar *version; + + version = find_attribute ("version", attribute_names, attribute_values); + + if (version == NULL) + MISSING_ATTRIBUTE (context, error, element_name, "version"); + else if (strcmp (version, SUPPORTED_GIR_VERSION) != 0) + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Unsupported version '%s'", + version); + else + state_switch (ctx, STATE_REPOSITORY); + + goto out; + } + else if (start_return_value (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_struct (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 's': + if (strcmp ("source-position", element_name) == 0) + { + state_switch (ctx, STATE_PASSTHROUGH); + goto out; + } + break; + case 'u': + if (start_union (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 't': + if (start_type (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 'v': + if (start_vfunc (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + if (start_type (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + default: + break; + } + + if (*error == NULL && ctx->state != STATE_PASSTHROUGH) + { + gint line_number, char_number; + g_markup_parse_context_get_position (context, &line_number, &char_number); + if (!g_str_has_prefix (element_name, "c:")) + g_printerr ("%s:%d:%d: warning: element %s from state %d is unknown, ignoring\n", + ctx->file_path, line_number, char_number, element_name, + ctx->state); + state_switch (ctx, STATE_PASSTHROUGH); + } + + out: + if (*error) + { + gint line_number, char_number; + g_markup_parse_context_get_position (context, &line_number, &char_number); + + g_printerr ("%s:%d:%d: error: %s\n", ctx->file_path, line_number, char_number, (*error)->message); + } +} + +static gboolean +require_one_of_end_elements (GMarkupParseContext *context, + ParseContext *ctx, + const char *actual_name, + GError **error, + ...) +{ + va_list args; + int line_number, char_number; + const char *expected; + gboolean matched = FALSE; + + va_start (args, error); + + while ((expected = va_arg (args, const char*)) != NULL) + { + if (strcmp (expected, actual_name) == 0) + { + matched = TRUE; + break; + } + } + + va_end (args); + + if (matched) + return TRUE; + + g_markup_parse_context_get_position (context, &line_number, &char_number); + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Unexpected end tag '%s' on line %d char %d; current state=%d (prev=%d)", + actual_name, + line_number, char_number, ctx->state, ctx->prev_state); + return FALSE; +} + +static gboolean +state_switch_end_struct_or_union (GMarkupParseContext *context, + ParseContext *ctx, + const gchar *element_name, + GError **error) +{ + pop_node (ctx); + if (ctx->node_stack == NULL) + { + state_switch (ctx, STATE_NAMESPACE); + } + else + { + if (CURRENT_NODE (ctx)->type == G_IR_NODE_STRUCT) + state_switch (ctx, STATE_STRUCT); + else if (CURRENT_NODE (ctx)->type == G_IR_NODE_UNION) + state_switch (ctx, STATE_UNION); + else if (CURRENT_NODE (ctx)->type == G_IR_NODE_OBJECT) + state_switch (ctx, STATE_CLASS); + else + { + int line_number, char_number; + g_markup_parse_context_get_position (context, &line_number, &char_number); + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Unexpected end tag '%s' on line %d char %d", + element_name, + line_number, char_number); + return FALSE; + } + } + return TRUE; +} + +static gboolean +require_end_element (GMarkupParseContext *context, + ParseContext *ctx, + const char *expected_name, + const char *actual_name, + GError **error) +{ + return require_one_of_end_elements (context, ctx, actual_name, error, expected_name, NULL); +} + +static void +end_element_handler (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + ParseContext *ctx = user_data; + + g_debug ("", element_name); + + switch (ctx->state) + { + case STATE_START: + case STATE_END: + /* no need to GError here, GMarkup already catches this */ + break; + + case STATE_REPOSITORY: + state_switch (ctx, STATE_END); + break; + + case STATE_INCLUDE: + if (require_end_element (context, ctx, "include", element_name, error)) + { + state_switch (ctx, STATE_REPOSITORY); + } + break; + + case STATE_C_INCLUDE: + if (require_end_element (context, ctx, "c:include", element_name, error)) + { + state_switch (ctx, STATE_REPOSITORY); + } + break; + + case STATE_PACKAGE: + if (require_end_element (context, ctx, "package", element_name, error)) + { + state_switch (ctx, STATE_REPOSITORY); + } + break; + + case STATE_NAMESPACE: + if (require_end_element (context, ctx, "namespace", element_name, error)) + { + ctx->current_module = NULL; + state_switch (ctx, STATE_REPOSITORY); + } + break; + + case STATE_ALIAS: + if (require_end_element (context, ctx, "alias", element_name, error)) + { + g_free (ctx->current_alias); + ctx->current_alias = NULL; + state_switch (ctx, STATE_NAMESPACE); + } + break; + + case STATE_FUNCTION_RETURN: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "return-value", element_name, error)) + { + state_switch (ctx, STATE_FUNCTION); + } + break; + + case STATE_FUNCTION_PARAMETERS: + if (require_end_element (context, ctx, "parameters", element_name, error)) + { + state_switch (ctx, STATE_FUNCTION); + } + break; + + case STATE_FUNCTION_PARAMETER: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "parameter", element_name, error)) + { + state_switch (ctx, STATE_FUNCTION_PARAMETERS); + } + break; + + case STATE_FUNCTION: + { + pop_node (ctx); + if (ctx->node_stack == NULL) + { + state_switch (ctx, STATE_NAMESPACE); + } + else + { + g_debug("case STATE_FUNCTION %d", CURRENT_NODE (ctx)->type); + if (ctx->in_embedded_state != STATE_NONE) + { + state_switch (ctx, ctx->in_embedded_state); + ctx->in_embedded_state = STATE_NONE; + } + else if (CURRENT_NODE (ctx)->type == G_IR_NODE_INTERFACE) + state_switch (ctx, STATE_INTERFACE); + else if (CURRENT_NODE (ctx)->type == G_IR_NODE_OBJECT) + state_switch (ctx, STATE_CLASS); + else if (CURRENT_NODE (ctx)->type == G_IR_NODE_BOXED) + state_switch (ctx, STATE_BOXED); + else if (CURRENT_NODE (ctx)->type == G_IR_NODE_STRUCT) + state_switch (ctx, STATE_STRUCT); + else if (CURRENT_NODE (ctx)->type == G_IR_NODE_UNION) + state_switch (ctx, STATE_UNION); + else if (CURRENT_NODE (ctx)->type == G_IR_NODE_ENUM || + CURRENT_NODE (ctx)->type == G_IR_NODE_FLAGS) + state_switch (ctx, STATE_ENUM); + else + { + int line_number, char_number; + g_markup_parse_context_get_position (context, &line_number, &char_number); + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Unexpected end tag '%s' on line %d char %d", + element_name, + line_number, char_number); + } + } + } + break; + + case STATE_CLASS_FIELD: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "field", element_name, error)) + { + state_switch (ctx, STATE_CLASS); + } + break; + + case STATE_CLASS_PROPERTY: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "property", element_name, error)) + { + state_switch (ctx, STATE_CLASS); + } + break; + + case STATE_CLASS: + if (require_end_element (context, ctx, "class", element_name, error)) + { + pop_node (ctx); + state_switch (ctx, STATE_NAMESPACE); + } + break; + + case STATE_INTERFACE_PROPERTY: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "property", element_name, error)) + { + state_switch (ctx, STATE_INTERFACE); + } + break; + + case STATE_INTERFACE_FIELD: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "field", element_name, error)) + { + state_switch (ctx, STATE_INTERFACE); + } + break; + + case STATE_INTERFACE: + if (require_end_element (context, ctx, "interface", element_name, error)) + { + pop_node (ctx); + state_switch (ctx, STATE_NAMESPACE); + } + break; + + case STATE_ENUM: + if (strcmp ("member", element_name) == 0) + break; + else if (strcmp ("function", element_name) == 0) + break; + else if (require_one_of_end_elements (context, ctx, + element_name, error, "enumeration", + "bitfield", NULL)) + { + pop_node (ctx); + state_switch (ctx, STATE_NAMESPACE); + } + break; + + case STATE_BOXED: + if (require_end_element (context, ctx, "glib:boxed", element_name, error)) + { + pop_node (ctx); + state_switch (ctx, STATE_NAMESPACE); + } + break; + + case STATE_BOXED_FIELD: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "field", element_name, error)) + { + state_switch (ctx, STATE_BOXED); + } + break; + + case STATE_STRUCT_FIELD: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "field", element_name, error)) + { + state_switch (ctx, STATE_STRUCT); + } + break; + + case STATE_STRUCT: + if (require_end_element (context, ctx, "record", element_name, error)) + { + state_switch_end_struct_or_union (context, ctx, element_name, error); + } + break; + + case STATE_UNION_FIELD: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "field", element_name, error)) + { + state_switch (ctx, STATE_UNION); + } + break; + + case STATE_UNION: + if (require_end_element (context, ctx, "union", element_name, error)) + { + state_switch_end_struct_or_union (context, ctx, element_name, error); + } + break; + case STATE_IMPLEMENTS: + if (strcmp ("interface", element_name) == 0) + break; + if (require_end_element (context, ctx, "implements", element_name, error)) + state_switch (ctx, STATE_CLASS); + break; + case STATE_PREREQUISITE: + if (require_end_element (context, ctx, "prerequisite", element_name, error)) + state_switch (ctx, STATE_INTERFACE); + break; + case STATE_NAMESPACE_CONSTANT: + case STATE_CLASS_CONSTANT: + case STATE_INTERFACE_CONSTANT: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "constant", element_name, error)) + { + switch (ctx->state) + { + case STATE_NAMESPACE_CONSTANT: + pop_node (ctx); + state_switch (ctx, STATE_NAMESPACE); + break; + case STATE_CLASS_CONSTANT: + state_switch (ctx, STATE_CLASS); + break; + case STATE_INTERFACE_CONSTANT: + state_switch (ctx, STATE_INTERFACE); + break; + default: + g_assert_not_reached (); + break; + } + } + break; + case STATE_TYPE: + if ((strcmp ("type", element_name) == 0) || (strcmp ("array", element_name) == 0) || + (strcmp ("varargs", element_name) == 0)) + { + end_type (ctx); + } + break; + case STATE_ATTRIBUTE: + if (strcmp ("attribute", element_name) == 0) + { + state_switch (ctx, ctx->prev_state); + } + break; + + case STATE_PASSTHROUGH: + ctx->unknown_depth -= 1; + g_assert (ctx->unknown_depth >= 0); + if (ctx->unknown_depth == 0) + state_switch (ctx, ctx->prev_state); + break; + default: + g_error ("Unhandled state %d in end_element_handler\n", ctx->state); + } +} + +static void +text_handler (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + /* FIXME warn about non-whitespace text */ +} + +static void +cleanup (GMarkupParseContext *context, + GError *error, + gpointer user_data) +{ + ParseContext *ctx = user_data; + GList *m; + + for (m = ctx->modules; m; m = m->next) + _g_ir_module_free (m->data); + g_list_free (ctx->modules); + ctx->modules = NULL; + + ctx->current_module = NULL; +} + +/** + * _g_ir_parser_parse_string: + * @parser: a #GIrParser + * @namespace: the namespace of the string + * @filename: (allow-none): Path to parsed file, or %NULL + * @buffer: the data containing the XML + * @length: length of the data + * @error: return location for a #GError, or %NULL + * + * Parse a string that holds a complete GIR XML file, and return a list of a + * a #GirModule for each <namespace/> element within the file. + * + * Returns: (transfer none): a new #GirModule + */ +GIrModule * +_g_ir_parser_parse_string (GIrParser *parser, + const gchar *namespace, + const gchar *filename, + const gchar *buffer, + gssize length, + GError **error) +{ + ParseContext ctx = { 0 }; + GMarkupParseContext *context; + + ctx.parser = parser; + ctx.state = STATE_START; + ctx.file_path = filename; + ctx.namespace = namespace; + ctx.include_modules = NULL; + ctx.aliases = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + ctx.disguised_structures = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + ctx.pointer_structures = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + ctx.type_depth = 0; + ctx.dependencies = NULL; + ctx.current_module = NULL; + + context = g_markup_parse_context_new (&firstpass_parser, 0, &ctx, NULL); + + if (!g_markup_parse_context_parse (context, buffer, length, error)) + goto out; + + if (!g_markup_parse_context_end_parse (context, error)) + goto out; + + g_markup_parse_context_free (context); + + ctx.state = STATE_START; + context = g_markup_parse_context_new (&markup_parser, 0, &ctx, NULL); + if (!g_markup_parse_context_parse (context, buffer, length, error)) + goto out; + + if (!g_markup_parse_context_end_parse (context, error)) + goto out; + + parser->parsed_modules = g_list_concat (g_list_copy (ctx.modules), + parser->parsed_modules); + + out: + + if (ctx.modules == NULL) + { + /* An error occurred before we created a module, so we haven't + * transferred ownership of these hash tables to the module. + */ + g_clear_pointer (&ctx.aliases, g_hash_table_unref); + g_clear_pointer (&ctx.disguised_structures, g_hash_table_unref); + g_clear_pointer (&ctx.pointer_structures, g_hash_table_unref); + g_list_free (ctx.include_modules); + } + + g_markup_parse_context_free (context); + + if (ctx.modules) + return ctx.modules->data; + + if (error && *error == NULL) + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Expected namespace element in the gir file"); + return NULL; +} + +/** + * _g_ir_parser_parse_file: + * @parser: a #GIrParser + * @filename: filename to parse + * @error: return location for a #GError, or %NULL + * + * Parse GIR XML file, and return a list of a a #GirModule for each + * <namespace/> element within the file. + * + * Returns: (transfer container): a newly allocated list of #GIrModule. The modules themselves + * are owned by the #GIrParser and will be freed along with the parser. + */ +GIrModule * +_g_ir_parser_parse_file (GIrParser *parser, + const gchar *filename, + GError **error) +{ + gchar *buffer; + gsize length; + GIrModule *module; + char *dash; + char *namespace; + + if (!g_str_has_suffix (filename, ".gir")) + { + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Expected filename to end with '.gir'"); + return NULL; + } + + g_debug ("[parsing] filename %s", filename); + + namespace = g_path_get_basename (filename); + namespace[strlen(namespace)-4] = '\0'; + + /* Remove version */ + dash = strstr (namespace, "-"); + if (dash != NULL) + *dash = '\0'; + + if (!g_file_get_contents (filename, &buffer, &length, error)) + { + g_free (namespace); + + return NULL; + } + + module = _g_ir_parser_parse_string (parser, namespace, filename, buffer, length, error); + + g_free (namespace); + + g_free (buffer); + + return module; +} + + diff --git a/girepository/girwriter-private.h b/girepository/girwriter-private.h new file mode 100644 index 000000000..ec1fafdc7 --- /dev/null +++ b/girepository/girwriter-private.h @@ -0,0 +1,35 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: IDL writer + * + * Copyright (C) 2007 Johan Dahlin + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +void g_ir_writer_write (const char *filename, + const char *ns, + gboolean needs_prefix, + gboolean show_all); + +G_END_DECLS diff --git a/girepository/girwriter.c b/girepository/girwriter.c new file mode 100644 index 000000000..85dcc25a0 --- /dev/null +++ b/girepository/girwriter.c @@ -0,0 +1,1466 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: IDL generator + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include "girwriter-private.h" + +#include "girepository.h" +#include "gitypelib-internal.h" + +#include +#include +#include + +#include +#include +#include + +typedef struct { + FILE *file; + GSList *stack; + gboolean show_all; +} Xml; + +typedef struct { + char *name; + guint has_children : 1; +} XmlElement; + +static XmlElement * +xml_element_new (const char *name) +{ + XmlElement *elem; + + elem = g_slice_new (XmlElement); + elem->name = g_strdup (name); + elem->has_children = FALSE; + return elem; +} + +static void +xml_element_free (XmlElement *elem) +{ + g_free (elem->name); + g_slice_free (XmlElement, elem); +} + +static void +xml_printf (Xml *xml, const char *fmt, ...) G_GNUC_PRINTF (2, 3); + +static void +xml_printf (Xml *xml, const char *fmt, ...) +{ + va_list ap; + char *s; + + va_start (ap, fmt); + s = g_markup_vprintf_escaped (fmt, ap); + fputs (s, xml->file); + g_free (s); + va_end (ap); +} + +static void +xml_start_element (Xml *xml, const char *element_name) +{ + XmlElement *parent = NULL; + + if (xml->stack) + { + parent = xml->stack->data; + + if (!parent->has_children) + xml_printf (xml, ">\n"); + + parent->has_children = TRUE; + } + + xml_printf (xml, "%*s<%s", g_slist_length(xml->stack)*2, "", element_name); + + xml->stack = g_slist_prepend (xml->stack, xml_element_new (element_name)); +} + +static void +xml_end_element (Xml *xml, const char *name) +{ + XmlElement *elem; + + g_assert (xml->stack != NULL); + + elem = xml->stack->data; + xml->stack = g_slist_delete_link (xml->stack, xml->stack); + + if (name != NULL) + g_assert_cmpstr (name, ==, elem->name); + + if (elem->has_children) + xml_printf (xml, "%*s\n", g_slist_length (xml->stack)*2, "", elem->name); + else + xml_printf (xml, "/>\n"); + + xml_element_free (elem); +} + +static void +xml_end_element_unchecked (Xml *xml) +{ + xml_end_element (xml, NULL); +} + +static Xml * +xml_open (FILE *file) +{ + Xml *xml; + + xml = g_slice_new (Xml); + xml->file = file; + xml->stack = NULL; + + return xml; +} + +static void +xml_close (Xml *xml) +{ + g_assert (xml->stack == NULL); + if (xml->file != NULL) + { + fflush (xml->file); + if (xml->file != stdout) + fclose (xml->file); + xml->file = NULL; + } +} + +static void +xml_free (Xml *xml) +{ + xml_close (xml); + g_slice_free (Xml, xml); +} + + +static void +check_unresolved (GIBaseInfo *info) +{ + if (g_base_info_get_type (info) != GI_INFO_TYPE_UNRESOLVED) + return; + + g_critical ("Found unresolved type '%s' '%s'\n", + g_base_info_get_name (info), g_base_info_get_namespace (info)); +} + +static void +write_type_name (const gchar *ns, + GIBaseInfo *info, + Xml *file) +{ + if (strcmp (ns, g_base_info_get_namespace (info)) != 0) + xml_printf (file, "%s.", g_base_info_get_namespace (info)); + + xml_printf (file, "%s", g_base_info_get_name (info)); +} + +static void +write_type_name_attribute (const gchar *ns, + GIBaseInfo *info, + const char *attr_name, + Xml *file) +{ + xml_printf (file, " %s=\"", attr_name); + write_type_name (ns, info, file); + xml_printf (file, "\""); +} + + static void +write_ownership_transfer (GITransfer transfer, + Xml *file) +{ + switch (transfer) + { + case GI_TRANSFER_NOTHING: + xml_printf (file, " transfer-ownership=\"none\""); + break; + case GI_TRANSFER_CONTAINER: + xml_printf (file, " transfer-ownership=\"container\""); + break; + case GI_TRANSFER_EVERYTHING: + xml_printf (file, " transfer-ownership=\"full\""); + break; + default: + g_assert_not_reached (); + } +} + +static void +write_type_info (const gchar *ns, + GITypeInfo *info, + Xml *file) +{ + gint tag; + GITypeInfo *type; + gboolean is_pointer; + + check_unresolved ((GIBaseInfo*)info); + + tag = g_type_info_get_tag (info); + is_pointer = g_type_info_is_pointer (info); + + if (tag == GI_TYPE_TAG_VOID) + { + xml_start_element (file, "type"); + + xml_printf (file, " name=\"%s\"", is_pointer ? "any" : "none"); + + xml_end_element (file, "type"); + } + else if (GI_TYPE_TAG_IS_BASIC (tag)) + { + xml_start_element (file, "type"); + xml_printf (file, " name=\"%s\"", g_type_tag_to_string (tag)); + xml_end_element (file, "type"); + } + else if (tag == GI_TYPE_TAG_ARRAY) + { + gint length, size; + const char *name = NULL; + + xml_start_element (file, "array"); + + switch (g_type_info_get_array_type (info)) { + case GI_ARRAY_TYPE_C: + break; + case GI_ARRAY_TYPE_ARRAY: + name = "GLib.Array"; + break; + case GI_ARRAY_TYPE_PTR_ARRAY: + name = "GLib.PtrArray"; + break; + case GI_ARRAY_TYPE_BYTE_ARRAY: + name = "GLib.ByteArray"; + break; + default: + break; + } + + if (name) + xml_printf (file, " name=\"%s\"", name); + + type = g_type_info_get_param_type (info, 0); + + length = g_type_info_get_array_length (info); + if (length >= 0) + xml_printf (file, " length=\"%d\"", length); + + size = g_type_info_get_array_fixed_size (info); + if (size >= 0) + xml_printf (file, " fixed-size=\"%d\"", size); + + if (g_type_info_is_zero_terminated (info)) + xml_printf (file, " zero-terminated=\"1\""); + + write_type_info (ns, type, file); + + g_base_info_unref ((GIBaseInfo *)type); + + xml_end_element (file, "array"); + } + else if (tag == GI_TYPE_TAG_INTERFACE) + { + GIBaseInfo *iface = g_type_info_get_interface (info); + xml_start_element (file, "type"); + write_type_name_attribute (ns, iface, "name", file); + xml_end_element (file, "type"); + g_base_info_unref (iface); + } + else if (tag == GI_TYPE_TAG_GLIST) + { + xml_start_element (file, "type"); + xml_printf (file, " name=\"GLib.List\""); + type = g_type_info_get_param_type (info, 0); + if (type) + { + write_type_info (ns, type, file); + g_base_info_unref ((GIBaseInfo *)type); + } + xml_end_element (file, "type"); + } + else if (tag == GI_TYPE_TAG_GSLIST) + { + xml_start_element (file, "type"); + xml_printf (file, " name=\"GLib.SList\""); + type = g_type_info_get_param_type (info, 0); + if (type) + { + write_type_info (ns, type, file); + g_base_info_unref ((GIBaseInfo *)type); + } + xml_end_element (file, "type"); + } + else if (tag == GI_TYPE_TAG_GHASH) + { + xml_start_element (file, "type"); + xml_printf (file, " name=\"GLib.HashTable\""); + type = g_type_info_get_param_type (info, 0); + if (type) + { + write_type_info (ns, type, file); + g_base_info_unref ((GIBaseInfo *)type); + type = g_type_info_get_param_type (info, 1); + write_type_info (ns, type, file); + g_base_info_unref ((GIBaseInfo *)type); + } + xml_end_element (file, "type"); + } + else if (tag == GI_TYPE_TAG_ERROR) + { + xml_start_element (file, "type"); + xml_printf (file, " name=\"GLib.Error\""); + xml_end_element (file, "type"); + } + else + { + g_printerr ("Unhandled type tag %d\n", tag); + g_assert_not_reached (); + } +} + +static void +write_attributes (Xml *file, + GIBaseInfo *info) +{ + GIAttributeIter iter = { 0, }; + char *name, *value; + + while (g_base_info_iterate_attributes (info, &iter, &name, &value)) + { + xml_start_element (file, "attribute"); + xml_printf (file, " name=\"%s\" value=\"%s\"", name, value); + xml_end_element (file, "attribute"); + } +} + +static void +write_return_value_attributes (Xml *file, + GICallableInfo *info) +{ + GIAttributeIter iter = { 0, }; + char *name, *value; + + while (g_callable_info_iterate_return_attributes (info, &iter, &name, &value)) + { + xml_start_element (file, "attribute"); + xml_printf (file, " name=\"%s\" value=\"%s\"", name, value); + xml_end_element (file, "attribute"); + } +} + +static void +write_constant_value (const gchar *ns, + GITypeInfo *info, + GIArgument *argument, + Xml *file); + +static void +write_callback_info (const gchar *ns, + GICallbackInfo *info, + Xml *file); + +static void +write_field_info (const gchar *ns, + GIFieldInfo *info, + GIConstantInfo *branch, + Xml *file) +{ + const gchar *name; + GIFieldInfoFlags flags; + gint size; + gint offset; + GITypeInfo *type; + GIBaseInfo *interface; + GIArgument value; + + name = g_base_info_get_name ((GIBaseInfo *)info); + flags = g_field_info_get_flags (info); + size = g_field_info_get_size (info); + offset = g_field_info_get_offset (info); + + xml_start_element (file, "field"); + xml_printf (file, " name=\"%s\"", name); + + /* Fields are assumed to be read-only + * (see also girwriter.py and girparser.c) + */ + if (!(flags & GI_FIELD_IS_READABLE)) + xml_printf (file, " readable=\"0\""); + if (flags & GI_FIELD_IS_WRITABLE) + xml_printf (file, " writable=\"1\""); + + if (size) + xml_printf (file, " bits=\"%d\"", size); + + write_attributes (file, (GIBaseInfo*) info); + + type = g_field_info_get_type (info); + + if (branch) + { + xml_printf (file, " branch=\""); + g_base_info_unref ((GIBaseInfo *)type); + type = g_constant_info_get_type (branch); + g_constant_info_get_value (branch, &value); + write_constant_value (ns, type, &value, file); + xml_printf (file, "\""); + } + + if (file->show_all) + { + if (offset >= 0) + xml_printf (file, "offset=\"%d\"", offset); + } + + interface = g_type_info_get_interface (type); + if (interface && g_base_info_get_type(interface) == GI_INFO_TYPE_CALLBACK) + write_callback_info (ns, (GICallbackInfo *)interface, file); + else + write_type_info (ns, type, file); + + if (interface) + g_base_info_unref (interface); + + g_base_info_unref ((GIBaseInfo *)type); + + xml_end_element (file, "field"); +} + +static void +write_callable_info (const gchar *ns, + GICallableInfo *info, + Xml *file) +{ + GITypeInfo *type; + gint i; + + if (g_callable_info_can_throw_gerror (info)) + xml_printf (file, " throws=\"1\""); + + write_attributes (file, (GIBaseInfo*) info); + + type = g_callable_info_get_return_type (info); + + xml_start_element (file, "return-value"); + + write_ownership_transfer (g_callable_info_get_caller_owns (info), file); + + if (g_callable_info_may_return_null (info)) + xml_printf (file, " allow-none=\"1\""); + + if (g_callable_info_skip_return (info)) + xml_printf (file, " skip=\"1\""); + + write_return_value_attributes (file, info); + + write_type_info (ns, type, file); + + xml_end_element (file, "return-value"); + + if (g_callable_info_get_n_args (info) <= 0) + return; + + xml_start_element (file, "parameters"); + for (i = 0; i < g_callable_info_get_n_args (info); i++) + { + GIArgInfo *arg = g_callable_info_get_arg (info, i); + + xml_start_element (file, "parameter"); + xml_printf (file, " name=\"%s\"", + g_base_info_get_name ((GIBaseInfo *) arg)); + + write_ownership_transfer (g_arg_info_get_ownership_transfer (arg), file); + + switch (g_arg_info_get_direction (arg)) + { + case GI_DIRECTION_IN: + break; + case GI_DIRECTION_OUT: + xml_printf (file, " direction=\"out\" caller-allocates=\"%s\"", + g_arg_info_is_caller_allocates (arg) ? "1" : "0"); + break; + case GI_DIRECTION_INOUT: + xml_printf (file, " direction=\"inout\""); + break; + default: + g_assert_not_reached (); + } + + if (g_arg_info_may_be_null (arg)) + xml_printf (file, " allow-none=\"1\""); + + if (g_arg_info_is_return_value (arg)) + xml_printf (file, " retval=\"1\""); + + if (g_arg_info_is_optional (arg)) + xml_printf (file, " optional=\"1\""); + + switch (g_arg_info_get_scope (arg)) + { + case GI_SCOPE_TYPE_INVALID: + break; + case GI_SCOPE_TYPE_CALL: + xml_printf (file, " scope=\"call\""); + break; + case GI_SCOPE_TYPE_ASYNC: + xml_printf (file, " scope=\"async\""); + break; + case GI_SCOPE_TYPE_NOTIFIED: + xml_printf (file, " scope=\"notified\""); + break; + case GI_SCOPE_TYPE_FOREVER: + xml_printf (file, " scope=\"forever\""); + break; + default: + g_assert_not_reached (); + } + + if (g_arg_info_get_closure (arg) >= 0) + xml_printf (file, " closure=\"%d\"", g_arg_info_get_closure (arg)); + + if (g_arg_info_get_destroy (arg) >= 0) + xml_printf (file, " destroy=\"%d\"", g_arg_info_get_destroy (arg)); + + if (g_arg_info_is_skip (arg)) + xml_printf (file, " skip=\"1\""); + + write_attributes (file, (GIBaseInfo*) arg); + + type = g_arg_info_get_type (arg); + write_type_info (ns, type, file); + + xml_end_element (file, "parameter"); + + g_base_info_unref ((GIBaseInfo *)arg); + } + + xml_end_element (file, "parameters"); + g_base_info_unref ((GIBaseInfo *)type); +} + +static void +write_function_info (const gchar *ns, + GIFunctionInfo *info, + Xml *file) +{ + GIFunctionInfoFlags flags; + const gchar *tag; + const gchar *name; + const gchar *symbol; + gboolean deprecated; + + flags = g_function_info_get_flags (info); + name = g_base_info_get_name ((GIBaseInfo *)info); + symbol = g_function_info_get_symbol (info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + if (flags & GI_FUNCTION_IS_CONSTRUCTOR) + tag = "constructor"; + else if (flags & GI_FUNCTION_IS_METHOD) + tag = "method"; + else + tag = "function"; + + xml_start_element (file, tag); + xml_printf (file, " name=\"%s\" c:identifier=\"%s\"", + name, symbol); + + if ((flags & GI_FUNCTION_IS_SETTER) || (flags & GI_FUNCTION_IS_GETTER)) + { + GIPropertyInfo *property = g_function_info_get_property (info); + + if (property != NULL) + { + const char *property_name = g_base_info_get_name ((GIBaseInfo *)property); + + if (flags & GI_FUNCTION_IS_SETTER) + xml_printf (file, " glib:set-property=\"%s\"", property_name); + else if (flags & GI_FUNCTION_IS_GETTER) + xml_printf (file, " glib:get-property=\"%s\"", property_name); + + g_base_info_unref (property); + } + } + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + write_callable_info (ns, (GICallableInfo*)info, file); + xml_end_element (file, tag); +} + +static void +write_callback_info (const gchar *ns, + GICallbackInfo *info, + Xml *file) +{ + const gchar *name; + gboolean deprecated; + + name = g_base_info_get_name ((GIBaseInfo *)info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + xml_start_element (file, "callback"); + xml_printf (file, " name=\"%s\"", name); + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + write_callable_info (ns, (GICallableInfo*)info, file); + xml_end_element (file, "callback"); +} + +static void +write_struct_info (const gchar *ns, + GIStructInfo *info, + Xml *file) +{ + const gchar *name; + const gchar *type_name; + const gchar *type_init; + const gchar *func; + gboolean deprecated; + gboolean is_gtype_struct; + gboolean foreign; + gint i; + gint size; + int n_elts; + + name = g_base_info_get_name ((GIBaseInfo *)info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info); + type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info); + + if (g_base_info_get_type ((GIBaseInfo *)info) == GI_INFO_TYPE_BOXED) + { + xml_start_element (file, "glib:boxed"); + xml_printf (file, " glib:name=\"%s\"", name); + } + else + { + xml_start_element (file, "record"); + xml_printf (file, " name=\"%s\"", name); + } + + if (type_name != NULL) + xml_printf (file, " glib:type-name=\"%s\" glib:get-type=\"%s\"", type_name, type_init); + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + is_gtype_struct = g_struct_info_is_gtype_struct (info); + if (is_gtype_struct) + xml_printf (file, " glib:is-gtype-struct=\"1\""); + + func = g_struct_info_get_copy_function (info); + if (func) + xml_printf (file, " copy-function=\"%s\"", func); + + func = g_struct_info_get_free_function (info); + if (func) + xml_printf (file, " free-function=\"%s\"", func); + + write_attributes (file, (GIBaseInfo*) info); + + size = g_struct_info_get_size (info); + if (file->show_all && size >= 0) + xml_printf (file, " size=\"%d\"", size); + + foreign = g_struct_info_is_foreign (info); + if (foreign) + xml_printf (file, " foreign=\"1\""); + + n_elts = g_struct_info_get_n_fields (info) + g_struct_info_get_n_methods (info); + if (n_elts > 0) + { + for (i = 0; i < g_struct_info_get_n_fields (info); i++) + { + GIFieldInfo *field = g_struct_info_get_field (info, i); + write_field_info (ns, field, NULL, file); + g_base_info_unref ((GIBaseInfo *)field); + } + + for (i = 0; i < g_struct_info_get_n_methods (info); i++) + { + GIFunctionInfo *function = g_struct_info_get_method (info, i); + write_function_info (ns, function, file); + g_base_info_unref ((GIBaseInfo *)function); + } + + } + + xml_end_element_unchecked (file); +} + +static void +write_value_info (const gchar *ns, + GIValueInfo *info, + Xml *file) +{ + const gchar *name; + gint64 value; + gchar *value_str; + gboolean deprecated; + + name = g_base_info_get_name ((GIBaseInfo *)info); + value = g_value_info_get_value (info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + xml_start_element (file, "member"); + value_str = g_strdup_printf ("%" G_GINT64_FORMAT, value); + xml_printf (file, " name=\"%s\" value=\"%s\"", name, value_str); + g_free (value_str); + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + write_attributes (file, (GIBaseInfo*) info); + + xml_end_element (file, "member"); +} + +static void +write_constant_value (const gchar *ns, + GITypeInfo *type, + GIArgument *value, + Xml *file) +{ + switch (g_type_info_get_tag (type)) + { + case GI_TYPE_TAG_BOOLEAN: + xml_printf (file, "%d", value->v_boolean); + break; + case GI_TYPE_TAG_INT8: + xml_printf (file, "%d", value->v_int8); + break; + case GI_TYPE_TAG_UINT8: + xml_printf (file, "%d", value->v_uint8); + break; + case GI_TYPE_TAG_INT16: + xml_printf (file, "%" G_GINT16_FORMAT, value->v_int16); + break; + case GI_TYPE_TAG_UINT16: + xml_printf (file, "%" G_GUINT16_FORMAT, value->v_uint16); + break; + case GI_TYPE_TAG_INT32: + xml_printf (file, "%" G_GINT32_FORMAT, value->v_int32); + break; + case GI_TYPE_TAG_UINT32: + xml_printf (file, "%" G_GUINT32_FORMAT, value->v_uint32); + break; + case GI_TYPE_TAG_INT64: + xml_printf (file, "%" G_GINT64_FORMAT, value->v_int64); + break; + case GI_TYPE_TAG_UINT64: + xml_printf (file, "%" G_GUINT64_FORMAT, value->v_uint64); + break; + case GI_TYPE_TAG_FLOAT: + xml_printf (file, "%f", (double)value->v_float); + break; + case GI_TYPE_TAG_DOUBLE: + xml_printf (file, "%f", value->v_double); + break; + case GI_TYPE_TAG_UTF8: + case GI_TYPE_TAG_FILENAME: + xml_printf (file, "%s", value->v_string); + break; + default: + g_assert_not_reached (); + } +} + +static void +write_constant_info (const gchar *ns, + GIConstantInfo *info, + Xml *file) +{ + GITypeInfo *type; + const gchar *name; + GIArgument value; + + name = g_base_info_get_name ((GIBaseInfo *)info); + + xml_start_element (file, "constant"); + xml_printf (file, " name=\"%s\"", name); + + type = g_constant_info_get_type (info); + xml_printf (file, " value=\""); + + g_constant_info_get_value (info, &value); + write_constant_value (ns, type, &value, file); + xml_printf (file, "\""); + + write_type_info (ns, type, file); + + write_attributes (file, (GIBaseInfo*) info); + + xml_end_element (file, "constant"); + + g_base_info_unref ((GIBaseInfo *)type); +} + + +static void +write_enum_info (const gchar *ns, + GIEnumInfo *info, + Xml *file) +{ + const gchar *name; + const gchar *type_name; + const gchar *type_init; + const gchar *error_domain; + gboolean deprecated; + gint i; + + name = g_base_info_get_name ((GIBaseInfo *)info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info); + type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info); + error_domain = g_enum_info_get_error_domain (info); + + if (g_base_info_get_type ((GIBaseInfo *)info) == GI_INFO_TYPE_ENUM) + xml_start_element (file, "enumeration"); + else + xml_start_element (file, "bitfield"); + xml_printf (file, " name=\"%s\"", name); + + if (type_init) + xml_printf (file, " glib:type-name=\"%s\" glib:get-type=\"%s\"", type_name, type_init); + if (error_domain) + xml_printf (file, " glib:error-domain=\"%s\"", error_domain); + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + write_attributes (file, (GIBaseInfo*) info); + + for (i = 0; i < g_enum_info_get_n_values (info); i++) + { + GIValueInfo *value = g_enum_info_get_value (info, i); + write_value_info (ns, value, file); + g_base_info_unref ((GIBaseInfo *)value); + } + + xml_end_element_unchecked (file); +} + +static void +write_signal_info (const gchar *ns, + GISignalInfo *info, + Xml *file) +{ + GSignalFlags flags; + const gchar *name; + gboolean deprecated; + + name = g_base_info_get_name ((GIBaseInfo *)info); + flags = g_signal_info_get_flags (info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + xml_start_element (file, "glib:signal"); + xml_printf (file, " name=\"%s\"", name); + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + if (flags & G_SIGNAL_RUN_FIRST) + xml_printf (file, " when=\"FIRST\""); + else if (flags & G_SIGNAL_RUN_LAST) + xml_printf (file, " when=\"LAST\""); + else if (flags & G_SIGNAL_RUN_CLEANUP) + xml_printf (file, " when=\"CLEANUP\""); + + if (flags & G_SIGNAL_NO_RECURSE) + xml_printf (file, " no-recurse=\"1\""); + + if (flags & G_SIGNAL_DETAILED) + xml_printf (file, " detailed=\"1\""); + + if (flags & G_SIGNAL_ACTION) + xml_printf (file, " action=\"1\""); + + if (flags & G_SIGNAL_NO_HOOKS) + xml_printf (file, " no-hooks=\"1\""); + + write_callable_info (ns, (GICallableInfo*)info, file); + + xml_end_element (file, "glib:signal"); +} + +static void +write_vfunc_info (const gchar *ns, + GIVFuncInfo *info, + Xml *file) +{ + GIVFuncInfoFlags flags; + const gchar *name; + GIFunctionInfo *invoker; + gboolean deprecated; + gint offset; + + name = g_base_info_get_name ((GIBaseInfo *)info); + flags = g_vfunc_info_get_flags (info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + offset = g_vfunc_info_get_offset (info); + invoker = g_vfunc_info_get_invoker (info); + + xml_start_element (file, "virtual-method"); + xml_printf (file, " name=\"%s\"", name); + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + if (flags & GI_VFUNC_MUST_CHAIN_UP) + xml_printf (file, " must-chain-up=\"1\""); + + if (flags & GI_VFUNC_MUST_OVERRIDE) + xml_printf (file, " override=\"always\""); + else if (flags & GI_VFUNC_MUST_NOT_OVERRIDE) + xml_printf (file, " override=\"never\""); + + xml_printf (file, " offset=\"%d\"", offset); + + if (invoker) + { + xml_printf (file, " invoker=\"%s\"", g_base_info_get_name ((GIBaseInfo*)invoker)); + g_base_info_unref ((GIBaseInfo *)invoker); + } + + write_callable_info (ns, (GICallableInfo*)info, file); + + xml_end_element (file, "virtual-method"); +} + +static void +write_property_info (const gchar *ns, + GIPropertyInfo *info, + Xml *file) +{ + GParamFlags flags; + const gchar *name; + gboolean deprecated; + GITypeInfo *type; + + name = g_base_info_get_name ((GIBaseInfo *)info); + flags = g_property_info_get_flags (info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + xml_start_element (file, "property"); + xml_printf (file, " name=\"%s\"", name); + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + /* Properties are assumed to be read-only (see also girwriter.py) */ + if (!(flags & G_PARAM_READABLE)) + xml_printf (file, " readable=\"0\""); + if (flags & G_PARAM_WRITABLE) + xml_printf (file, " writable=\"1\""); + + if (flags & G_PARAM_CONSTRUCT) + xml_printf (file, " construct=\"1\""); + + if (flags & G_PARAM_CONSTRUCT_ONLY) + xml_printf (file, " construct-only=\"1\""); + + if (flags & G_PARAM_READABLE) + { + GIFunctionInfo *getter = g_property_info_get_getter (info); + + if (getter != NULL) + { + xml_printf (file, " getter=\"%s\"", g_base_info_get_name ((GIBaseInfo *) getter)); + g_base_info_unref ((GIBaseInfo *) getter); + } + } + + if (flags & G_PARAM_WRITABLE) + { + GIFunctionInfo *setter = g_property_info_get_setter (info); + + if (setter != NULL) + { + xml_printf (file, " setter=\"%s\"", g_base_info_get_name ((GIBaseInfo *) setter)); + g_base_info_unref ((GIBaseInfo *) setter); + } + } + + write_ownership_transfer (g_property_info_get_ownership_transfer (info), file); + + write_attributes (file, (GIBaseInfo*) info); + + type = g_property_info_get_type (info); + + write_type_info (ns, type, file); + + xml_end_element (file, "property"); +} + +static void +write_object_info (const gchar *ns, + GIObjectInfo *info, + Xml *file) +{ + const gchar *name; + const gchar *type_name; + const gchar *type_init; + const gchar *func; + gboolean deprecated; + gboolean is_abstract; + gboolean is_fundamental; + gboolean is_final; + GIObjectInfo *pnode; + GIStructInfo *class_struct; + gint i; + + name = g_base_info_get_name ((GIBaseInfo *)info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + is_abstract = g_object_info_get_abstract (info); + is_fundamental = g_object_info_get_fundamental (info); + is_final = g_object_info_get_final (info); + + type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info); + type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info); + xml_start_element (file, "class"); + xml_printf (file, " name=\"%s\"", name); + + pnode = g_object_info_get_parent (info); + if (pnode) + { + write_type_name_attribute (ns, (GIBaseInfo *)pnode, "parent", file); + g_base_info_unref ((GIBaseInfo *)pnode); + } + + class_struct = g_object_info_get_class_struct (info); + if (class_struct) + { + write_type_name_attribute (ns, (GIBaseInfo*) class_struct, "glib:type-struct", file); + g_base_info_unref ((GIBaseInfo*)class_struct); + } + + if (is_abstract) + xml_printf (file, " abstract=\"1\""); + + if (is_final) + xml_printf (file, " final=\"1\""); + + xml_printf (file, " glib:type-name=\"%s\" glib:get-type=\"%s\"", type_name, type_init); + + if (is_fundamental) + xml_printf (file, " glib:fundamental=\"1\""); + + func = g_object_info_get_unref_function (info); + if (func) + xml_printf (file, " glib:unref-function=\"%s\"", func); + + func = g_object_info_get_ref_function (info); + if (func) + xml_printf (file, " glib:ref-function=\"%s\"", func); + + func = g_object_info_get_set_value_function (info); + if (func) + xml_printf (file, " glib:set-value-function=\"%s\"", func); + + func = g_object_info_get_get_value_function (info); + if (func) + xml_printf (file, " glib:get-value-function=\"%s\"", func); + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + write_attributes (file, (GIBaseInfo*) info); + + if (g_object_info_get_n_interfaces (info) > 0) + { + for (i = 0; i < g_object_info_get_n_interfaces (info); i++) + { + GIInterfaceInfo *imp = g_object_info_get_interface (info, i); + xml_start_element (file, "implements"); + write_type_name_attribute (ns, (GIBaseInfo *)imp, "name", file); + xml_end_element (file, "implements"); + g_base_info_unref ((GIBaseInfo*)imp); + } + } + + for (i = 0; i < g_object_info_get_n_fields (info); i++) + { + GIFieldInfo *field = g_object_info_get_field (info, i); + write_field_info (ns, field, NULL, file); + g_base_info_unref ((GIBaseInfo *)field); + } + + for (i = 0; i < g_object_info_get_n_methods (info); i++) + { + GIFunctionInfo *function = g_object_info_get_method (info, i); + write_function_info (ns, function, file); + g_base_info_unref ((GIBaseInfo *)function); + } + + for (i = 0; i < g_object_info_get_n_properties (info); i++) + { + GIPropertyInfo *prop = g_object_info_get_property (info, i); + write_property_info (ns, prop, file); + g_base_info_unref ((GIBaseInfo *)prop); + } + + for (i = 0; i < g_object_info_get_n_signals (info); i++) + { + GISignalInfo *signal = g_object_info_get_signal (info, i); + write_signal_info (ns, signal, file); + g_base_info_unref ((GIBaseInfo *)signal); + } + + for (i = 0; i < g_object_info_get_n_vfuncs (info); i++) + { + GIVFuncInfo *vfunc = g_object_info_get_vfunc (info, i); + write_vfunc_info (ns, vfunc, file); + g_base_info_unref ((GIBaseInfo *)vfunc); + } + + for (i = 0; i < g_object_info_get_n_constants (info); i++) + { + GIConstantInfo *constant = g_object_info_get_constant (info, i); + write_constant_info (ns, constant, file); + g_base_info_unref ((GIBaseInfo *)constant); + } + + xml_end_element (file, "class"); +} + +static void +write_interface_info (const gchar *ns, + GIInterfaceInfo *info, + Xml *file) +{ + const gchar *name; + const gchar *type_name; + const gchar *type_init; + GIStructInfo *class_struct; + gboolean deprecated; + gint i; + + name = g_base_info_get_name ((GIBaseInfo *)info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info); + type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info); + xml_start_element (file, "interface"); + xml_printf (file, " name=\"%s\" glib:type-name=\"%s\" glib:get-type=\"%s\"", + name, type_name, type_init); + + class_struct = g_interface_info_get_iface_struct (info); + if (class_struct) + { + write_type_name_attribute (ns, (GIBaseInfo*) class_struct, "glib:type-struct", file); + g_base_info_unref ((GIBaseInfo*)class_struct); + } + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + write_attributes (file, (GIBaseInfo*) info); + + if (g_interface_info_get_n_prerequisites (info) > 0) + { + for (i = 0; i < g_interface_info_get_n_prerequisites (info); i++) + { + GIBaseInfo *req = g_interface_info_get_prerequisite (info, i); + + xml_start_element (file, "prerequisite"); + write_type_name_attribute (ns, req, "name", file); + + xml_end_element_unchecked (file); + g_base_info_unref (req); + } + } + + for (i = 0; i < g_interface_info_get_n_methods (info); i++) + { + GIFunctionInfo *function = g_interface_info_get_method (info, i); + write_function_info (ns, function, file); + g_base_info_unref ((GIBaseInfo *)function); + } + + for (i = 0; i < g_interface_info_get_n_properties (info); i++) + { + GIPropertyInfo *prop = g_interface_info_get_property (info, i); + write_property_info (ns, prop, file); + g_base_info_unref ((GIBaseInfo *)prop); + } + + for (i = 0; i < g_interface_info_get_n_signals (info); i++) + { + GISignalInfo *signal = g_interface_info_get_signal (info, i); + write_signal_info (ns, signal, file); + g_base_info_unref ((GIBaseInfo *)signal); + } + + for (i = 0; i < g_interface_info_get_n_vfuncs (info); i++) + { + GIVFuncInfo *vfunc = g_interface_info_get_vfunc (info, i); + write_vfunc_info (ns, vfunc, file); + g_base_info_unref ((GIBaseInfo *)vfunc); + } + + for (i = 0; i < g_interface_info_get_n_constants (info); i++) + { + GIConstantInfo *constant = g_interface_info_get_constant (info, i); + write_constant_info (ns, constant, file); + g_base_info_unref ((GIBaseInfo *)constant); + } + + xml_end_element (file, "interface"); +} + +static void +write_union_info (const gchar *ns, + GIUnionInfo *info, + Xml *file) +{ + const gchar *name; + const gchar *type_name; + const gchar *type_init; + const gchar *func; + gboolean deprecated; + gint i; + gint size; + + name = g_base_info_get_name ((GIBaseInfo *)info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info); + type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info); + + xml_start_element (file, "union"); + xml_printf (file, " name=\"%s\"", name); + + if (type_name) + xml_printf (file, " type-name=\"%s\" get-type=\"%s\"", type_name, type_init); + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + size = g_union_info_get_size (info); + if (file->show_all && size >= 0) + xml_printf (file, " size=\"%d\"", size); + + func = g_union_info_get_copy_function (info); + if (func) + xml_printf (file, " copy-function=\"%s\"", func); + + func = g_union_info_get_free_function (info); + if (func) + xml_printf (file, " free-function=\"%s\"", func); + + write_attributes (file, (GIBaseInfo*) info); + + if (g_union_info_is_discriminated (info)) + { + gint offset; + GITypeInfo *type; + + offset = g_union_info_get_discriminator_offset (info); + type = g_union_info_get_discriminator_type (info); + + xml_start_element (file, "discriminator"); + xml_printf (file, " offset=\"%d\" type=\"", offset); + write_type_info (ns, type, file); + xml_end_element (file, "discriminator"); + g_base_info_unref ((GIBaseInfo *)type); + } + + for (i = 0; i < g_union_info_get_n_fields (info); i++) + { + GIFieldInfo *field = g_union_info_get_field (info, i); + GIConstantInfo *constant = g_union_info_get_discriminator (info, i); + write_field_info (ns, field, constant, file); + g_base_info_unref ((GIBaseInfo *)field); + if (constant) + g_base_info_unref ((GIBaseInfo *)constant); + } + + for (i = 0; i < g_union_info_get_n_methods (info); i++) + { + GIFunctionInfo *function = g_union_info_get_method (info, i); + write_function_info (ns, function, file); + g_base_info_unref ((GIBaseInfo *)function); + } + + xml_end_element (file, "union"); +} + + +/* + * g_ir_writer_write: + * @filename: filename to write to + * @ns: GIR namespace to write + * @needs_prefix: if the filename needs prefixing + * @show_all: if field size calculations should be included + * + * Writes the output of a typelib represented by @ns + * into a GIR xml file named @filename. + */ +void +g_ir_writer_write (const char *filename, + const char *ns, + gboolean needs_prefix, + gboolean show_all) +{ + FILE *ofile; + gint i, j; + char **dependencies; + GIRepository *repository; + Xml *xml; + + repository = g_irepository_get_default (); + + if (filename == NULL) + ofile = stdout; + else + { + gchar *full_filename; + + if (needs_prefix) + full_filename = g_strdup_printf ("%s-%s", ns, filename); + else + full_filename = g_strdup (filename); + ofile = g_fopen (filename, "w"); + + if (ofile == NULL) + { + g_fprintf (stderr, "failed to open '%s': %s\n", + full_filename, g_strerror (errno)); + g_free (full_filename); + + return; + } + + g_free (full_filename); + } + + xml = xml_open (ofile); + xml->show_all = show_all; + xml_printf (xml, "\n"); + xml_start_element (xml, "repository"); + xml_printf (xml, " version=\"1.0\"\n" + " xmlns=\"http://www.gtk.org/introspection/core/1.0\"\n" + " xmlns:c=\"http://www.gtk.org/introspection/c/1.0\"\n" + " xmlns:glib=\"http://www.gtk.org/introspection/glib/1.0\""); + + dependencies = g_irepository_get_immediate_dependencies (repository, ns); + if (dependencies != NULL) + { + for (i = 0; dependencies[i]; i++) + { + char **parts = g_strsplit (dependencies[i], "-", 2); + xml_start_element (xml, "include"); + xml_printf (xml, " name=\"%s\" version=\"%s\"", parts[0], parts[1]); + xml_end_element (xml, "include"); + g_strfreev (parts); + } + } + + if (TRUE) + { + const gchar *shared_library; + const gchar *c_prefix; + const char *cur_ns = ns; + const char *cur_version; + gint n_infos; + + cur_version = g_irepository_get_version (repository, cur_ns); + + shared_library = g_irepository_get_shared_library (repository, cur_ns); + c_prefix = g_irepository_get_c_prefix (repository, cur_ns); + xml_start_element (xml, "namespace"); + xml_printf (xml, " name=\"%s\" version=\"%s\"", cur_ns, cur_version); + if (shared_library) + xml_printf (xml, " shared-library=\"%s\"", shared_library); + if (c_prefix) + xml_printf (xml, " c:prefix=\"%s\"", c_prefix); + + n_infos = g_irepository_get_n_infos (repository, cur_ns); + for (j = 0; j < n_infos; j++) + { + GIBaseInfo *info = g_irepository_get_info (repository, cur_ns, j); + switch (g_base_info_get_type (info)) + { + case GI_INFO_TYPE_FUNCTION: + write_function_info (ns, (GIFunctionInfo *)info, xml); + break; + + case GI_INFO_TYPE_CALLBACK: + write_callback_info (ns, (GICallbackInfo *)info, xml); + break; + + case GI_INFO_TYPE_STRUCT: + case GI_INFO_TYPE_BOXED: + write_struct_info (ns, (GIStructInfo *)info, xml); + break; + + case GI_INFO_TYPE_UNION: + write_union_info (ns, (GIUnionInfo *)info, xml); + break; + + case GI_INFO_TYPE_ENUM: + case GI_INFO_TYPE_FLAGS: + write_enum_info (ns, (GIEnumInfo *)info, xml); + break; + + case GI_INFO_TYPE_CONSTANT: + write_constant_info (ns, (GIConstantInfo *)info, xml); + break; + + case GI_INFO_TYPE_OBJECT: + write_object_info (ns, (GIObjectInfo *)info, xml); + break; + + case GI_INFO_TYPE_INTERFACE: + write_interface_info (ns, (GIInterfaceInfo *)info, xml); + break; + + default: + g_error ("unknown info type %d\n", g_base_info_get_type (info)); + } + + g_base_info_unref (info); + } + + xml_end_element (xml, "namespace"); + } + + xml_end_element (xml, "repository"); + + xml_free (xml); +} diff --git a/girepository/gisignalinfo.c b/girepository/gisignalinfo.c new file mode 100644 index 000000000..0dd26e931 --- /dev/null +++ b/girepository/gisignalinfo.c @@ -0,0 +1,143 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Signal implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" +#include "gisignalinfo.h" + +/** + * SECTION:gisignalinfo + * @title: GISignalInfo + * @short_description: Struct representing a signal + * + * GISignalInfo represents a signal. + * + * It's a sub-struct of #GICallableInfo and contains a set of flags and + * a class closure. + * + * See #GICallableInfo for information on how to retreive arguments + * and other metadata from the signal. + */ + +/** + * g_signal_info_get_flags: + * @info: a #GISignalInfo + * + * Obtain the flags for this signal info. See #GSignalFlags for + * more information about possible flag values. + * + * Returns: the flags + */ +GSignalFlags +g_signal_info_get_flags (GISignalInfo *info) +{ + GSignalFlags flags; + GIRealInfo *rinfo = (GIRealInfo *)info; + SignalBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_SIGNAL_INFO (info), 0); + + blob = (SignalBlob *)&rinfo->typelib->data[rinfo->offset]; + flags = 0; + + if (blob->run_first) + flags = flags | G_SIGNAL_RUN_FIRST; + + if (blob->run_last) + flags = flags | G_SIGNAL_RUN_LAST; + + if (blob->run_cleanup) + flags = flags | G_SIGNAL_RUN_CLEANUP; + + if (blob->no_recurse) + flags = flags | G_SIGNAL_NO_RECURSE; + + if (blob->detailed) + flags = flags | G_SIGNAL_DETAILED; + + if (blob->action) + flags = flags | G_SIGNAL_ACTION; + + if (blob->no_hooks) + flags = flags | G_SIGNAL_NO_HOOKS; + + return flags; +} + +/** + * g_signal_info_get_class_closure: + * @info: a #GISignalInfo + * + * Obtain the class closure for this signal if one is set. The class + * closure is a virtual function on the type that the signal belongs to. + * If the signal lacks a closure %NULL will be returned. + * + * Returns: (transfer full): the class closure or %NULL + */ +GIVFuncInfo * +g_signal_info_get_class_closure (GISignalInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SignalBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_SIGNAL_INFO (info), 0); + + blob = (SignalBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->has_class_closure) + return g_interface_info_get_vfunc ((GIInterfaceInfo *)rinfo->container, blob->class_closure); + + return NULL; +} + +/** + * g_signal_info_true_stops_emit: + * @info: a #GISignalInfo + * + * Obtain if the returning true in the signal handler will + * stop the emission of the signal. + * + * Returns: %TRUE if returning true stops the signal emission + */ +gboolean +g_signal_info_true_stops_emit (GISignalInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SignalBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_SIGNAL_INFO (info), 0); + + blob = (SignalBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->true_stops_emit; +} + diff --git a/girepository/gisignalinfo.h b/girepository/gisignalinfo.h new file mode 100644 index 000000000..31a5fa627 --- /dev/null +++ b/girepository/gisignalinfo.h @@ -0,0 +1,55 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Signal + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +/** + * GI_IS_SIGNAL_INFO + * @info: an info structure + * + * Checks if @info is a #GISignalInfo. + */ +#define GI_IS_SIGNAL_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_SIGNAL) + + +GI_AVAILABLE_IN_ALL +GSignalFlags g_signal_info_get_flags (GISignalInfo *info); + +GI_AVAILABLE_IN_ALL +GIVFuncInfo * g_signal_info_get_class_closure (GISignalInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_signal_info_true_stops_emit (GISignalInfo *info); + +G_END_DECLS diff --git a/girepository/gistructinfo.c b/girepository/gistructinfo.c new file mode 100644 index 000000000..f81069450 --- /dev/null +++ b/girepository/gistructinfo.c @@ -0,0 +1,340 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Struct implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" +#include "gistructinfo.h" + +/** + * SECTION:gistructinfo + * @title: GIStructInfo + * @short_description: Struct representing a C structure + * + * GIStructInfo represents a generic C structure type. + * + * A structure has methods and fields. + */ + +/** + * g_struct_info_get_n_fields: + * @info: a #GIStructInfo + * + * Obtain the number of fields this structure has. + * + * Returns: number of fields + */ +gint +g_struct_info_get_n_fields (GIStructInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_fields; +} + +/** + * g_struct_info_get_field_offset: + * @info: a #GIStructInfo + * @n: index of queried field + * + * Obtain the offset of the specified field. + * + * Returns: field offset in bytes + */ +static gint32 +g_struct_get_field_offset (GIStructInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header = (Header *)rinfo->typelib->data; + guint32 offset = rinfo->offset + header->struct_blob_size; + gint i; + FieldBlob *field_blob; + + for (i = 0; i < n; i++) + { + field_blob = (FieldBlob *)&rinfo->typelib->data[offset]; + offset += header->field_blob_size; + if (field_blob->has_embedded_type) + offset += header->callback_blob_size; + } + + return offset; +} + +/** + * g_struct_info_get_field: + * @info: a #GIStructInfo + * @n: a field index + * + * Obtain the type information for field with specified index. + * + * Returns: (transfer full): the #GIFieldInfo, free it with g_base_info_unref() + * when done. + */ +GIFieldInfo * +g_struct_info_get_field (GIStructInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + + return (GIFieldInfo *) g_info_new (GI_INFO_TYPE_FIELD, (GIBaseInfo*)info, rinfo->typelib, + g_struct_get_field_offset (info, n)); +} + +/** + * g_struct_info_find_field: + * @info: a #GIStructInfo + * @name: a field name + * + * Obtain the type information for field named @name. + * + * Since: 1.46 + * Returns: (transfer full): the #GIFieldInfo or %NULL if not found, + * free it with g_base_info_unref() when done. + */ +GIFieldInfo * +g_struct_info_find_field (GIStructInfo *info, + const gchar *name) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + Header *header = (Header *)rinfo->typelib->data; + guint32 offset = rinfo->offset + header->struct_blob_size; + gint i; + + for (i = 0; i < blob->n_fields; i++) + { + FieldBlob *field_blob = (FieldBlob *)&rinfo->typelib->data[offset]; + const gchar *fname = (const gchar *)&rinfo->typelib->data[field_blob->name]; + + if (strcmp (name, fname) == 0) + { + return (GIFieldInfo *) g_info_new (GI_INFO_TYPE_FIELD, + (GIBaseInfo* )info, + rinfo->typelib, + offset); + } + + offset += header->field_blob_size; + if (field_blob->has_embedded_type) + offset += header->callback_blob_size; + } + + return NULL; +} + +/** + * g_struct_info_get_n_methods: + * @info: a #GIStructInfo + * + * Obtain the number of methods this structure has. + * + * Returns: number of methods + */ +gint +g_struct_info_get_n_methods (GIStructInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_methods; +} + +/** + * g_struct_info_get_method: + * @info: a #GIStructInfo + * @n: a method index + * + * Obtain the type information for method with specified index. + * + * Returns: (transfer full): the #GIFunctionInfo, free it with g_base_info_unref() + * when done. + */ +GIFunctionInfo * +g_struct_info_get_method (GIStructInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + Header *header = (Header *)rinfo->typelib->data; + gint offset; + + offset = g_struct_get_field_offset (info, blob->n_fields) + n * header->function_blob_size; + return (GIFunctionInfo *) g_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_struct_info_find_method: + * @info: a #GIStructInfo + * @name: a method name + * + * Obtain the type information for method named @name. + * + * Returns: (transfer full): the #GIFunctionInfo, free it with g_base_info_unref() + * when done. + */ +GIFunctionInfo * +g_struct_info_find_method (GIStructInfo *info, + const gchar *name) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = g_struct_get_field_offset (info, blob->n_fields); + return _g_base_info_find_method ((GIBaseInfo*)info, offset, blob->n_methods, name); +} + +/** + * g_struct_info_get_size: + * @info: a #GIStructInfo + * + * Obtain the total size of the structure. + * + * Returns: size of the structure in bytes + */ +gsize +g_struct_info_get_size (GIStructInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->size; +} + +/** + * g_struct_info_get_alignment: + * @info: a #GIStructInfo + * + * Obtain the required alignment of the structure. + * + * Returns: required alignment in bytes + */ +gsize +g_struct_info_get_alignment (GIStructInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->alignment; +} + +/** + * g_struct_info_is_foreign: + * @info: TODO + * + * TODO + * + * Returns: TODO + */ +gboolean +g_struct_info_is_foreign (GIStructInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->foreign; +} + +/** + * g_struct_info_is_gtype_struct: + * @info: a #GIStructInfo + * + * Return true if this structure represents the "class structure" for some + * #GObject or #GInterface. This function is mainly useful to hide this kind of structure + * from generated public APIs. + * + * Returns: %TRUE if this is a class struct, %FALSE otherwise + */ +gboolean +g_struct_info_is_gtype_struct (GIStructInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->is_gtype_struct; +} + +/** + * g_struct_info_get_copy_function: + * @info: a struct information blob + * + * Retrieves the name of the copy function for @info, if any is set. + * + * Returns: (transfer none) (nullable): the name of the copy function + * + * Since: 1.76 + */ +const char * +g_struct_info_get_copy_function (GIStructInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_STRUCT_INFO (info), NULL); + + blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->copy_func) + return g_typelib_get_string (rinfo->typelib, blob->copy_func); + + return NULL; +} + +/** + * g_struct_info_get_free_function: + * @info: a struct information blob + * + * Retrieves the name of the free function for @info, if any is set. + * + * Returns: (transfer none) (nullable): the name of the free function + * + * Since: 1.76 + */ +const char * +g_struct_info_get_free_function (GIStructInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_STRUCT_INFO (info), NULL); + + blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->free_func) + return g_typelib_get_string (rinfo->typelib, blob->free_func); + + return NULL; +} diff --git a/girepository/gistructinfo.h b/girepository/gistructinfo.h new file mode 100644 index 000000000..74030c170 --- /dev/null +++ b/girepository/gistructinfo.h @@ -0,0 +1,85 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Struct + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_STRUCT_INFO + * @info: an info structure + * + * Checks if @info is a #GIStructInfo. + */ +#define GI_IS_STRUCT_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_STRUCT) + + +GI_AVAILABLE_IN_ALL +gint g_struct_info_get_n_fields (GIStructInfo *info); + +GI_AVAILABLE_IN_ALL +GIFieldInfo * g_struct_info_get_field (GIStructInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GIFieldInfo * g_struct_info_find_field (GIStructInfo *info, + const gchar *name); + +GI_AVAILABLE_IN_ALL +gint g_struct_info_get_n_methods (GIStructInfo *info); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_struct_info_get_method (GIStructInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_struct_info_find_method (GIStructInfo *info, + const gchar *name); + +GI_AVAILABLE_IN_ALL +gsize g_struct_info_get_size (GIStructInfo *info); + +GI_AVAILABLE_IN_ALL +gsize g_struct_info_get_alignment (GIStructInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_struct_info_is_gtype_struct (GIStructInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_struct_info_is_foreign (GIStructInfo *info); + +GI_AVAILABLE_IN_ALL +const char * g_struct_info_get_copy_function (GIStructInfo *info); + +GI_AVAILABLE_IN_ALL +const char * g_struct_info_get_free_function (GIStructInfo *info); + +G_END_DECLS diff --git a/girepository/gitypeinfo.c b/girepository/gitypeinfo.c new file mode 100644 index 000000000..ae6ff501c --- /dev/null +++ b/girepository/gitypeinfo.c @@ -0,0 +1,571 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Type implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" +#include "gitypeinfo.h" + +/** + * SECTION:gitypeinfo + * @title: GITypeInfo + * @short_description: Struct representing a type + * + * GITypeInfo represents a type. + * + * You can retrieve a type info from an argument (see #GIArgInfo), a + * functions return value (see #GIFunctionInfo), a field (see + * #GIFieldInfo), a property (see #GIPropertyInfo), a constant + * (see #GIConstantInfo) or for a union discriminator (see #GIUnionInfo). + * + * A type can either be a of a basic type which is a standard C primitive + * type or an interface type. For interface types you need to call + * g_type_info_get_interface() to get a reference to the base info for that + * interface. + */ + +/** + * g_type_info_is_pointer: + * @info: a #GITypeInfo + * + * Obtain if the type is passed as a reference. + * + * Note that the types of %GI_DIRECTION_OUT and %GI_DIRECTION_INOUT parameters + * will only be pointers if the underlying type being transferred is a pointer + * (i.e. only if the type of the C function’s formal parameter is a pointer to a + * pointer). + * + * Returns: %TRUE if it is a pointer + */ +gboolean +g_type_info_is_pointer (GITypeInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SimpleTypeBlob *type; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_TYPE_INFO (info), FALSE); + + type = (SimpleTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (type->flags.reserved == 0 && type->flags.reserved2 == 0) + return type->flags.pointer; + else + { + InterfaceTypeBlob *iface = (InterfaceTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + return iface->pointer; + } +} + +/** + * g_type_info_get_tag: + * @info: a #GITypeInfo + * + * Obtain the type tag for the type. See #GITypeTag for a list + * of type tags. + * + * Returns: the type tag + */ +GITypeTag +g_type_info_get_tag (GITypeInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SimpleTypeBlob *type; + + g_return_val_if_fail (info != NULL, GI_TYPE_TAG_BOOLEAN); + g_return_val_if_fail (GI_IS_TYPE_INFO (info), GI_TYPE_TAG_BOOLEAN); + + type = (SimpleTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (rinfo->type_is_embedded) + return GI_TYPE_TAG_INTERFACE; + else if (type->flags.reserved == 0 && type->flags.reserved2 == 0) + return type->flags.tag; + else + { + InterfaceTypeBlob *iface = (InterfaceTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + return iface->tag; + } +} + +/** + * g_type_info_get_param_type: + * @info: a #GITypeInfo + * @n: index of the parameter + * + * Obtain the parameter type @n. + * + * Returns: (transfer full): the param type info + */ +GITypeInfo * +g_type_info_get_param_type (GITypeInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SimpleTypeBlob *type; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_TYPE_INFO (info), NULL); + + type = (SimpleTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (!(type->flags.reserved == 0 && type->flags.reserved2 == 0)) + { + ParamTypeBlob *param = (ParamTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + switch (param->tag) + { + case GI_TYPE_TAG_ARRAY: + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + case GI_TYPE_TAG_GHASH: + return _g_type_info_new ((GIBaseInfo*)info, rinfo->typelib, + rinfo->offset + sizeof (ParamTypeBlob) + + sizeof (SimpleTypeBlob) * n); + break; + default: + break; + } + } + + return NULL; +} + +/** + * g_type_info_get_interface: + * @info: a #GITypeInfo + * + * For types which have #GI_TYPE_TAG_INTERFACE such as GObjects and boxed values, + * this function returns full information about the referenced type. You can then + * inspect the type of the returned #GIBaseInfo to further query whether it is + * a concrete GObject, a GInterface, a structure, etc. using g_base_info_get_type(). + * + * Returns: (transfer full): the #GIBaseInfo, or %NULL. Free it with + * g_base_info_unref() when done. + */ +GIBaseInfo * +g_type_info_get_interface (GITypeInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_TYPE_INFO (info), NULL); + + /* For embedded types, the given offset is a pointer to the actual blob, + * after the end of the field. In that case we know it's a "subclass" of + * CommonBlob, so use that to determine the info type. + */ + if (rinfo->type_is_embedded) + { + CommonBlob *common = (CommonBlob *)&rinfo->typelib->data[rinfo->offset]; + GIInfoType info_type; + + switch (common->blob_type) + { + case BLOB_TYPE_CALLBACK: + info_type = GI_INFO_TYPE_CALLBACK; + break; + default: + g_assert_not_reached (); + return NULL; + } + return (GIBaseInfo *) g_info_new (info_type, (GIBaseInfo*)info, rinfo->typelib, + rinfo->offset); + } + else + { + SimpleTypeBlob *type = (SimpleTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + if (!(type->flags.reserved == 0 && type->flags.reserved2 == 0)) + { + InterfaceTypeBlob *blob = (InterfaceTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->tag == GI_TYPE_TAG_INTERFACE) + return _g_info_from_entry (rinfo->repository, rinfo->typelib, blob->interface); + } + } + + return NULL; +} + +/** + * g_type_info_get_array_length: + * @info: a #GITypeInfo + * + * Obtain the position of the argument which gives the array length of the type. + * The type tag must be a #GI_TYPE_TAG_ARRAY or -1 will be returned. + * + * Returns: the array length, or -1 if the type is not an array + */ +gint +g_type_info_get_array_length (GITypeInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SimpleTypeBlob *type; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_TYPE_INFO (info), -1); + + type = (SimpleTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (!(type->flags.reserved == 0 && type->flags.reserved2 == 0)) + { + ArrayTypeBlob *blob = (ArrayTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->tag == GI_TYPE_TAG_ARRAY) + { + if (blob->has_length) + return blob->dimensions.length; + } + } + + return -1; +} + +/** + * g_type_info_get_array_fixed_size: + * @info: a #GITypeInfo + * + * Obtain the fixed array size of the type. The type tag must be a + * #GI_TYPE_TAG_ARRAY or -1 will be returned. + * + * Returns: the size or -1 if it's not an array + */ +gint +g_type_info_get_array_fixed_size (GITypeInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SimpleTypeBlob *type; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_TYPE_INFO (info), 0); + + type = (SimpleTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (!(type->flags.reserved == 0 && type->flags.reserved2 == 0)) + { + ArrayTypeBlob *blob = (ArrayTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->tag == GI_TYPE_TAG_ARRAY) + { + if (blob->has_size) + return blob->dimensions.size; + } + } + + return -1; +} + +/** + * g_type_info_is_zero_terminated: + * @info: a #GITypeInfo + * + * Obtain if the last element of the array is %NULL. The type tag must be a + * #GI_TYPE_TAG_ARRAY or %FALSE will be returned. + * + * Returns: %TRUE if zero terminated + */ +gboolean +g_type_info_is_zero_terminated (GITypeInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SimpleTypeBlob *type; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_TYPE_INFO (info), FALSE); + + type = (SimpleTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (!(type->flags.reserved == 0 && type->flags.reserved2 == 0)) + { + ArrayTypeBlob *blob = (ArrayTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->tag == GI_TYPE_TAG_ARRAY) + return blob->zero_terminated; + } + + return FALSE; +} + +/** + * g_type_info_get_array_type: + * @info: a #GITypeInfo + * + * Obtain the array type for this type. See #GIArrayType for a list of + * possible values. If the type tag of this type is not array, -1 will be + * returned. + * + * Returns: the array type or -1 + */ +GIArrayType +g_type_info_get_array_type (GITypeInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SimpleTypeBlob *type; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_TYPE_INFO (info), -1); + + type = (SimpleTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (!(type->flags.reserved == 0 && type->flags.reserved2 == 0)) + { + ArrayTypeBlob *blob = (ArrayTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + g_return_val_if_fail (blob->tag == GI_TYPE_TAG_ARRAY, -1); + + return blob->array_type; + } + + return -1; +} + +/** + * g_type_info_get_storage_type: + * @info: a #GITypeInfo + * + * Obtain the type tag corresponding to the underlying storage type in C for + * the type. + * See #GITypeTag for a list of type tags. + * + * Returns: the type tag + * + * Since: 1.66 + */ +GITypeTag +g_type_info_get_storage_type (GITypeInfo *info) +{ + GITypeTag type_tag = g_type_info_get_tag (info); + + if (type_tag == GI_TYPE_TAG_INTERFACE) + { + GIBaseInfo *interface = g_type_info_get_interface (info); + GIInfoType info_type = g_base_info_get_type (interface); + if (info_type == GI_INFO_TYPE_ENUM || info_type == GI_INFO_TYPE_FLAGS) + type_tag = g_enum_info_get_storage_type (interface); + g_base_info_unref (interface); + } + + return type_tag; +} + +/** + * gi_type_tag_argument_from_hash_pointer: + * @storage_type: a #GITypeTag obtained from g_type_info_get_storage_type() + * @hash_pointer: A pointer, such as a #GHashTable data pointer + * @arg: A #GIArgument to fill in + * + * GLib data structures, such as #GList, #GSList, and #GHashTable, all store + * data pointers. + * In the case where the list or hash table is storing single types rather than + * structs, these data pointers may have values stuffed into them via macros + * such as %GPOINTER_TO_INT. + * + * Use this function to ensure that all values are correctly extracted from + * stuffed pointers, regardless of the machine's architecture or endianness. + * + * This function fills in the appropriate field of @arg with the value extracted + * from @hash_pointer, depending on @storage_type. + * + * Since: 1.72 + */ +void +gi_type_tag_argument_from_hash_pointer (GITypeTag storage_type, + gpointer hash_pointer, + GIArgument *arg) +{ + switch (storage_type) + { + case GI_TYPE_TAG_BOOLEAN: + arg->v_boolean = !!GPOINTER_TO_INT (hash_pointer); + break; + case GI_TYPE_TAG_INT8: + arg->v_int8 = (gint8)GPOINTER_TO_INT (hash_pointer); + break; + case GI_TYPE_TAG_UINT8: + arg->v_uint8 = (guint8)GPOINTER_TO_UINT (hash_pointer); + break; + case GI_TYPE_TAG_INT16: + arg->v_int16 = (gint16)GPOINTER_TO_INT (hash_pointer); + break; + case GI_TYPE_TAG_UINT16: + arg->v_uint16 = (guint16)GPOINTER_TO_UINT (hash_pointer); + break; + case GI_TYPE_TAG_INT32: + arg->v_int32 = (gint32)GPOINTER_TO_INT (hash_pointer); + break; + case GI_TYPE_TAG_UINT32: + case GI_TYPE_TAG_UNICHAR: + arg->v_uint32 = (guint32)GPOINTER_TO_UINT (hash_pointer); + break; + case GI_TYPE_TAG_GTYPE: + arg->v_size = GPOINTER_TO_SIZE (hash_pointer); + break; + case GI_TYPE_TAG_UTF8: + case GI_TYPE_TAG_FILENAME: + case GI_TYPE_TAG_INTERFACE: + case GI_TYPE_TAG_ARRAY: + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + case GI_TYPE_TAG_GHASH: + case GI_TYPE_TAG_ERROR: + arg->v_pointer = hash_pointer; + break; + case GI_TYPE_TAG_INT64: + case GI_TYPE_TAG_UINT64: + case GI_TYPE_TAG_FLOAT: + case GI_TYPE_TAG_DOUBLE: + default: + g_critical ("Unsupported storage type for pointer-stuffing: %s", + g_type_tag_to_string (storage_type)); + arg->v_pointer = hash_pointer; + } +} + +/** + * g_type_info_argument_from_hash_pointer: + * @info: a #GITypeInfo + * @hash_pointer: A pointer, such as a #GHashTable data pointer + * @arg: A #GIArgument to fill in + * + * GLib data structures, such as #GList, #GSList, and #GHashTable, all store + * data pointers. + * In the case where the list or hash table is storing single types rather than + * structs, these data pointers may have values stuffed into them via macros + * such as %GPOINTER_TO_INT. + * + * Use this function to ensure that all values are correctly extracted from + * stuffed pointers, regardless of the machine's architecture or endianness. + * + * This function fills in the appropriate field of @arg with the value extracted + * from @hash_pointer, depending on the storage type of @info. + * + * Since: 1.66 + */ +void +g_type_info_argument_from_hash_pointer (GITypeInfo *info, + gpointer hash_pointer, + GIArgument *arg) +{ + GITypeTag storage_type = g_type_info_get_storage_type (info); + gi_type_tag_argument_from_hash_pointer (storage_type, hash_pointer, + arg); +} + +/** + * gi_type_tag_hash_pointer_from_argument: + * @storage_type: a #GITypeTag obtained from g_type_info_get_storage_type() + * @arg: A #GIArgument with the value to stuff into a pointer + * + * GLib data structures, such as #GList, #GSList, and #GHashTable, all store + * data pointers. + * In the case where the list or hash table is storing single types rather than + * structs, these data pointers may have values stuffed into them via macros + * such as %GPOINTER_TO_INT. + * + * Use this function to ensure that all values are correctly stuffed into + * pointers, regardless of the machine's architecture or endianness. + * + * This function returns a pointer stuffed with the appropriate field of @arg, + * depending on @storage_type. + * + * Returns: A stuffed pointer, that can be stored in a #GHashTable, for example + * + * Since: 1.72 + */ +gpointer +gi_type_tag_hash_pointer_from_argument (GITypeTag storage_type, + GIArgument *arg) +{ + switch (storage_type) + { + case GI_TYPE_TAG_BOOLEAN: + return GINT_TO_POINTER (arg->v_boolean); + case GI_TYPE_TAG_INT8: + return GINT_TO_POINTER (arg->v_int8); + case GI_TYPE_TAG_UINT8: + return GUINT_TO_POINTER (arg->v_uint8); + case GI_TYPE_TAG_INT16: + return GINT_TO_POINTER (arg->v_int16); + case GI_TYPE_TAG_UINT16: + return GUINT_TO_POINTER (arg->v_uint16); + case GI_TYPE_TAG_INT32: + return GINT_TO_POINTER (arg->v_int32); + case GI_TYPE_TAG_UINT32: + case GI_TYPE_TAG_UNICHAR: + return GUINT_TO_POINTER (arg->v_uint32); + case GI_TYPE_TAG_GTYPE: + return GSIZE_TO_POINTER (arg->v_size); + case GI_TYPE_TAG_UTF8: + case GI_TYPE_TAG_FILENAME: + case GI_TYPE_TAG_INTERFACE: + case GI_TYPE_TAG_ARRAY: + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + case GI_TYPE_TAG_GHASH: + case GI_TYPE_TAG_ERROR: + return arg->v_pointer; + case GI_TYPE_TAG_INT64: + case GI_TYPE_TAG_UINT64: + case GI_TYPE_TAG_FLOAT: + case GI_TYPE_TAG_DOUBLE: + default: + g_critical ("Unsupported storage type for pointer-stuffing: %s", + g_type_tag_to_string (storage_type)); + return arg->v_pointer; + } +} + +/** + * g_type_info_hash_pointer_from_argument: + * @info: a #GITypeInfo + * @arg: A #GIArgument with the value to stuff into a pointer + * + * GLib data structures, such as #GList, #GSList, and #GHashTable, all store + * data pointers. + * In the case where the list or hash table is storing single types rather than + * structs, these data pointers may have values stuffed into them via macros + * such as %GPOINTER_TO_INT. + * + * Use this function to ensure that all values are correctly stuffed into + * pointers, regardless of the machine's architecture or endianness. + * + * This function returns a pointer stuffed with the appropriate field of @arg, + * depending on the storage type of @info. + * + * Returns: A stuffed pointer, that can be stored in a #GHashTable, for example + * + * Since: 1.66 + */ +gpointer +g_type_info_hash_pointer_from_argument (GITypeInfo *info, + GIArgument *arg) +{ + GITypeTag storage_type = g_type_info_get_storage_type (info); + return gi_type_tag_hash_pointer_from_argument (storage_type, arg); +} diff --git a/girepository/gitypeinfo.h b/girepository/gitypeinfo.h new file mode 100644 index 000000000..8460dab0e --- /dev/null +++ b/girepository/gitypeinfo.h @@ -0,0 +1,159 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Type + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_TYPE_INFO + * @info: an info structure + * + * Checks if @info is a #GITypeInfo. + */ +#define GI_IS_TYPE_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_TYPE) + +/** + * G_TYPE_TAG_IS_BASIC + * @tag: a type tag + * + * Checks if @tag is a basic type. + * + * Deprecated: 1.72: Use GI_TYPE_TAG_IS_BASIC() instead + */ +#define G_TYPE_TAG_IS_BASIC(tag) GI_TYPE_TAG_IS_BASIC(tag) + +/** + * GI_TYPE_TAG_IS_BASIC + * @tag: a type tag + * + * Checks if @tag is a basic type. + * + * Since: 1.72 + */ +#define GI_TYPE_TAG_IS_BASIC(tag) ((tag) < GI_TYPE_TAG_ARRAY || (tag) == GI_TYPE_TAG_UNICHAR) + +/** + * GI_TYPE_TAG_IS_NUMERIC: + * @tag: a type tag + * + * Checks if @tag is a numeric type. That is, integer or floating point. + * + * Since: 1.72 + */ +#define GI_TYPE_TAG_IS_NUMERIC(tag) ((tag) >= GI_TYPE_TAG_INT8 && (tag) <= GI_TYPE_TAG_DOUBLE) + +/** + * GI_TYPE_TAG_IS_NUMERIC: + * @tag: a type tag + * + * Checks if @tag is a numeric type. That is, integer or floating point. + * + * Since: 1.72 + */ +#define GI_TYPE_TAG_IS_NUMERIC(tag) ((tag) >= GI_TYPE_TAG_INT8 && (tag) <= GI_TYPE_TAG_DOUBLE) + +/** + * GI_TYPE_TAG_IS_NUMERIC: + * @tag: a type tag + * + * Checks if @tag is a numeric type. That is, integer or floating point. + * + * Since: 1.72 + */ +#define GI_TYPE_TAG_IS_NUMERIC(tag) ((tag) >= GI_TYPE_TAG_INT8 && (tag) <= GI_TYPE_TAG_DOUBLE) + +/** + * GI_TYPE_TAG_IS_CONTAINER: + * @tag: a type tag + * + * Checks if @tag is a container type. That is, a type which may have a nonnull + * return from g_type_info_get_param_type(). + * + * Since: 1.72 + */ + #define GI_TYPE_TAG_IS_CONTAINER(tag) ((tag) == GI_TYPE_TAG_ARRAY || \ + ((tag) >= GI_TYPE_TAG_GLIST && (tag) <= GI_TYPE_TAG_GHASH)) + +GI_AVAILABLE_IN_ALL +const gchar* g_type_tag_to_string (GITypeTag type); + +GI_AVAILABLE_IN_ALL +const gchar* g_info_type_to_string (GIInfoType type); + + +GI_AVAILABLE_IN_ALL +gboolean g_type_info_is_pointer (GITypeInfo *info); + +GI_AVAILABLE_IN_ALL +GITypeTag g_type_info_get_tag (GITypeInfo *info); + +GI_AVAILABLE_IN_ALL +GITypeInfo * g_type_info_get_param_type (GITypeInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GIBaseInfo * g_type_info_get_interface (GITypeInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_type_info_get_array_length (GITypeInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_type_info_get_array_fixed_size(GITypeInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_type_info_is_zero_terminated (GITypeInfo *info); + +GI_AVAILABLE_IN_ALL +GIArrayType g_type_info_get_array_type (GITypeInfo *info); + +GI_AVAILABLE_IN_ALL +GITypeTag g_type_info_get_storage_type (GITypeInfo *info); + +GI_AVAILABLE_IN_ALL +void g_type_info_argument_from_hash_pointer (GITypeInfo *info, + gpointer hash_pointer, + GIArgument *arg); + +GI_AVAILABLE_IN_ALL +gpointer g_type_info_hash_pointer_from_argument (GITypeInfo *info, + GIArgument *arg); + +GI_AVAILABLE_IN_ALL +void gi_type_tag_argument_from_hash_pointer (GITypeTag storage_type, + gpointer hash_pointer, + GIArgument *arg); + +GI_AVAILABLE_IN_ALL +gpointer gi_type_tag_hash_pointer_from_argument (GITypeTag storage_type, + GIArgument *arg); + +G_END_DECLS diff --git a/girepository/gitypelib-internal.h b/girepository/gitypelib-internal.h new file mode 100644 index 000000000..244e7d996 --- /dev/null +++ b/girepository/gitypelib-internal.h @@ -0,0 +1,1350 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: struct definitions for the binary + * typelib format, validation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#include +#include "girepository.h" + +G_BEGIN_DECLS + +/** + * SECTION:gitypelib-internal + * @title: GITypelib Internals + * @short_description: Layout and accessors for typelib + * @stability: Stable + * + * The "typelib" is a binary, readonly, memory-mappable database + * containing reflective information about a GObject library. + * + * What the typelib describes and the types used are the same for every + * platform so, apart the endianness of its scalar values, the typelib + * database must be considered architecture-independent. + * + * The format of GObject typelib is strongly influenced by the Mozilla XPCOM + * format. + * + * Some of the differences to XPCOM include: + * + * - Type information is stored not quite as compactly (XPCOM stores it inline + * in function descriptions in variable-sized blobs of 1 to n bytes. We store + * 16 bits of type information for each parameter, which is enough to encode + * simple types inline. Complex (e.g. recursive) types are stored out of line + * in a separate list of types. + * - String and complex type data is stored outside of typelib entry blobs, + * references are stored as offsets relative to the start of the typelib. + * One possibility is to store the strings and types in a pools at the end + * of the typelib. + * + * The typelib has the following general format: + * + * |[ + * typelib ::= header, section-index, directory, blobs, attributes, attributedata + * + * directory ::= list of entries + * + * entry ::= blob type, name, namespace, offset + * blob ::= function|callback|struct|boxed|enum|flags|object|interface|constant|union + * attribute ::= offset, key, value + * attributedata ::= string data for attributes + * ]| + * + * Details + * + * We describe the fragments that make up the typelib in the form of C structs + * (although some fall short of being valid C structs since they contain + * multiple flexible arrays). + */ + +/* +TYPELIB HISTORY +----- + +Version 1.1 +- Add ref/unref/set-value/get-value functions to Object, to be able + to support instantiatable fundamental types which are not GObject based. + +Version 1.0 +- Rename class_struct to gtype_struct, add to interfaces + +Changes since 0.9: +- Add padding to structures + +Changes since 0.8: +- Add class struct concept to ObjectBlob +- Add is_class_struct bit to StructBlob + +Changes since 0.7: +- Add dependencies + +Changes since 0.6: +- rename metadata to typelib, to follow xpcom terminology + +Changes since 0.5: +- basic type cleanup: + + remove GString + + add [u]int, [u]long, [s]size_t + + rename string to utf8, add filename +- allow blob_type to be zero for non-local entries + +Changes since 0.4: +- add a UnionBlob + +Changes since 0.3: +- drop short_name for ValueBlob + +Changes since 0.2: +- make inline types 4 bytes after all, remove header->types and allow + types to appear anywhere +- allow error domains in the directory + +Changes since 0.1: + +- drop comments about _GOBJ_METADATA +- drop string pool, strings can appear anywhere +- use 'blob' as collective name for the various blob types +- rename 'type' field in blobs to 'blob_type' +- rename 'type_name' and 'type_init' fields to 'gtype_name', 'gtype_init' +- shrink directory entries to 12 bytes +- merge struct and boxed blobs +- split interface blobs into enum, object and interface blobs +- add an 'unregistered' flag to struct and enum blobs +- add a 'wraps_vfunc' flag to function blobs and link them to + the vfuncs they wrap +- restrict value blobs to only occur inside enums and flags again +- add constant blobs, allow them toplevel, in interfaces and in objects +- rename 'receiver_owns_value' and 'receiver_owns_container' to + 'transfer_ownership' and 'transfer_container_ownership' +- add a 'struct_offset' field to virtual function and field blobs +- add 'dipper' and 'optional' flags to arg blobs +- add a 'true_stops_emit' flag to signal blobs +- add variable blob sizes to header +- store offsets to signature blobs instead of including them directly +- change the type offset to be measured in words rather than bytes +*/ + +/** + * G_IR_MAGIC: + * + * Identifying prefix for the typelib. This was inspired by XPCOM, + * which in turn borrowed from PNG. + */ +#define G_IR_MAGIC "GOBJ\nMETADATA\r\n\032" + +/** + * GTypelibBlobType: + * @BLOB_TYPE_INVALID: Should not appear in code + * @BLOB_TYPE_FUNCTION: A #FunctionBlob + * @BLOB_TYPE_CALLBACK: A #CallbackBlob + * @BLOB_TYPE_STRUCT: A #StructBlob + * @BLOB_TYPE_BOXED: Can be either a #StructBlob or #UnionBlob + * @BLOB_TYPE_ENUM: An #EnumBlob + * @BLOB_TYPE_FLAGS: An #EnumBlob + * @BLOB_TYPE_OBJECT: An #ObjectBlob + * @BLOB_TYPE_INTERFACE: An #InterfaceBlob + * @BLOB_TYPE_CONSTANT: A #ConstantBlob + * @BLOB_TYPE_INVALID_0: Deleted, used to be ErrorDomain. + * @BLOB_TYPE_UNION: A #UnionBlob + * + * The integral value of this enumeration appears in each "Blob" component of + * a typelib to identify its type. + */ +typedef enum { + BLOB_TYPE_INVALID, + BLOB_TYPE_FUNCTION, + BLOB_TYPE_CALLBACK, + BLOB_TYPE_STRUCT, + BLOB_TYPE_BOXED, + BLOB_TYPE_ENUM, + BLOB_TYPE_FLAGS, + BLOB_TYPE_OBJECT, + BLOB_TYPE_INTERFACE, + BLOB_TYPE_CONSTANT, + BLOB_TYPE_INVALID_0, + BLOB_TYPE_UNION +} GTypelibBlobType; + + +#if defined (G_CAN_INLINE) && defined (G_ALWAYS_INLINE) + +G_ALWAYS_INLINE +inline gboolean +_blob_is_registered_type (GTypelibBlobType blob_type) +{ + switch (blob_type) + { + case BLOB_TYPE_STRUCT: + case BLOB_TYPE_UNION: + case BLOB_TYPE_ENUM: + case BLOB_TYPE_FLAGS: + case BLOB_TYPE_OBJECT: + case BLOB_TYPE_INTERFACE: + return TRUE; + default: + return FALSE; + } +} + +#define BLOB_IS_REGISTERED_TYPE(blob) \ + _blob_is_registered_type ((GTypelibBlobType) (blob)->blob_type) + +#else + +#define BLOB_IS_REGISTERED_TYPE(blob) \ + ((blob)->blob_type == BLOB_TYPE_STRUCT || \ + (blob)->blob_type == BLOB_TYPE_UNION || \ + (blob)->blob_type == BLOB_TYPE_ENUM || \ + (blob)->blob_type == BLOB_TYPE_FLAGS || \ + (blob)->blob_type == BLOB_TYPE_OBJECT || \ + (blob)->blob_type == BLOB_TYPE_INTERFACE) + +#endif /* defined (G_CAN_INLINE) && defined (G_ALWAYS_INLINE) */ + +/** + * Header: + * @magic: See #G_IR_MAGIC. + * @major_version: The major version number of the typelib format. Major version + * number changes indicate incompatible changes to the tyeplib format. + * @minor_version: The minor version number of the typelib format. Minor version + * number changes indicate compatible changes and should still allow the + * typelib to be parsed by a parser designed for the same @major_version. + * @reserved: Reserved for future use. + * @n_entries: The number of entries in the directory. + * @n_local_entries: The number of entries referring to blobs in this typelib. + * The local entries must occur before the unresolved entries. + * @directory: Offset of the directory in the typelib. + * @n_attributes: Number of attribute blocks + * @attributes: Offset of the list of attributes in the typelib. + * @dependencies: Offset of a single string, which is the list of immediate + * dependencies, separated by the '|' character. The dependencies are + * required in order to avoid having programs consuming a typelib check for + * an "Unresolved" type return from every API call. + * @size: The size in bytes of the typelib. + * @namespace: Offset of the namespace string in the typelib. + * @nsversion: Offset of the namespace version string in the typelib. + * @shared_library: This field is the set of shared libraries associated with + * the typelib. The entries are separated by the '|' (pipe) character. + * @c_prefix: The prefix for the function names of the library + * @entry_blob_size: The sizes of fixed-size blobs. Recording this information + * here allows to write parser which continue to work if the format is + * extended by adding new fields to the end of the fixed-size blobs. + * @function_blob_size: See @entry_blob_size. + * @callback_blob_size: See @entry_blob_size. + * @signal_blob_size: See @entry_blob_size. + * @vfunc_blob_size: See @entry_blob_size. + * @arg_blob_size: See @entry_blob_size. + * @property_blob_size: See @entry_blob_size. + * @field_blob_size: See @entry_blob_size. + * @value_blob_size: See @entry_blob_size. + * @attribute_blob_size: See @entry_blob_size. + * @constant_blob_size: See @entry_blob_size. + * @error_domain_blob_size: See @entry_blob_size. + * @signature_blob_size: See @entry_blob_size. + * @enum_blob_size: See @entry_blob_size. + * @struct_blob_size: See @entry_blob_size. + * @object_blob_size: See @entry_blob_size. + * @interface_blob_size: For variable-size blobs, the size of the struct up to + * the first flexible array member. Recording this information here allows + * to write parser which continue to work if the format is extended by + * adding new fields before the first flexible array member in + * variable-size blobs. + * @union_blob_size: See @entry_blob_size. + * @sections: Offset of section blob array + * @padding: TODO + * + * The header structure appears exactly once at the beginning of a typelib. It is a + * collection of meta-information, such as the number of entries and dependencies. + */ +typedef struct { + gchar magic[16]; + guint8 major_version; + guint8 minor_version; + guint16 reserved; + guint16 n_entries; + guint16 n_local_entries; + guint32 directory; + guint32 n_attributes; + guint32 attributes; + + guint32 dependencies; + + guint32 size; + guint32 namespace; + guint32 nsversion; + guint32 shared_library; + guint32 c_prefix; + + guint16 entry_blob_size; + guint16 function_blob_size; + guint16 callback_blob_size; + guint16 signal_blob_size; + guint16 vfunc_blob_size; + guint16 arg_blob_size; + guint16 property_blob_size; + guint16 field_blob_size; + guint16 value_blob_size; + guint16 attribute_blob_size; + guint16 constant_blob_size; + guint16 error_domain_blob_size; + + guint16 signature_blob_size; + guint16 enum_blob_size; + guint16 struct_blob_size; + guint16 object_blob_size; + guint16 interface_blob_size; + guint16 union_blob_size; + + guint32 sections; + + guint16 padding[6]; +} Header; + +/** + * SectionType: + * @GI_SECTION_END: TODO + * @GI_SECTION_DIRECTORY_INDEX: TODO + * + * TODO + */ +typedef enum { + GI_SECTION_END = 0, + GI_SECTION_DIRECTORY_INDEX = 1 +} SectionType; + +/** + * Section: + * @id: A #SectionType + * @offset: Integer offset for this section + * + * A section is a blob of data that's (at least theoretically) optional, + * and may or may not be present in the typelib. Presently, just used + * for the directory index. This allows a form of dynamic extensibility + * with different tradeoffs from the format minor version. + */ +typedef struct { + guint32 id; + guint32 offset; +} Section; + + +/** + * DirEntry: + * @blob_type: A #GTypelibBlobType + * @local: Whether this entry refers to a blob in this typelib. + * @reserved: Reserved for future use. + * @name: The name of the entry. + * @offset: If is_local is set, this is the offset of the blob in the typelib. + * Otherwise, it is the offset of the namespace in which the blob has to be + * looked up by name. + * + * References to directory entries are stored as 1-based 16-bit indexes. + * + * All blobs pointed to by a directory entry start with the same layout for + * the first 8 bytes (the reserved flags may be used by some blob types) + */ +typedef struct { + guint16 blob_type; + + guint16 local : 1; + guint16 reserved :15; + guint32 name; + guint32 offset; +} DirEntry; + +/** + * SimpleTypeBlobFlags: + * @reserved: Reserved for future use. + * @reserved2: Reserved for future use. + * @pointer: TODO + * @reserved3: Reserved for future use. + * @tag: A #GITypeTag + * + * TODO + */ +typedef struct { + guint reserved : 8; + guint reserved2 :16; + guint pointer : 1; + guint reserved3 : 2; + guint tag : 5; +} SimpleTypeBlobFlags; + +union _SimpleTypeBlob +{ + SimpleTypeBlobFlags flags; + guint32 offset; +}; + +/** + * SimpleTypeBlob: + * @flags: TODO + * @offset: Offset relative to header->types that points to a TypeBlob. + * Unlike other offsets, this is in words (ie 32bit units) rather + * than bytes. + * + * The SimpleTypeBlob is the general purpose "reference to a type" construct, + * used in method parameters, returns, callback definitions, fields, constants, + * etc. It's actually just a 32 bit integer which you can see from the union + * definition. This is for efficiency reasons, since there are so many + * references to types. + * + * SimpleTypeBlob is divided into two cases; first, if "reserved" and + * "reserved2" are both zero, the type tag for a basic type is embedded in the + * "tag" bits. This allows e.g. GI_TYPE_TAG_UTF8, GI_TYPE_TAG_INT and the like + * to be embedded directly without taking up extra space. + * + * References to "interfaces" (objects, interfaces) are more complicated; + * In this case, the integer is actually an offset into the directory (see + * above). Because the header is larger than 2^8=256 bits, all offsets will + * have one of the upper 24 bits set. + */ +typedef union _SimpleTypeBlob SimpleTypeBlob; + +/** + * ArgBlob: + * @name: A suggested name for the parameter. + * @in: The parameter is an input to the function + * @out: The parameter is used to return an output of the function. Parameters + * can be both in and out. Out parameters implicitly add another level of + * indirection to the parameter type. Ie if the type is uint32 in an out + * parameter, the function actually takes an uint32*. + * @caller_allocates: The parameter is a pointer to a struct or object that + * will receive an output of the function. + * @nullable: Only meaningful for types which are passed as pointers. For an + * in parameter, indicates if it is ok to pass NULL in. Gor an out + * parameter, indicates whether it may return NULL. Note that NULL is a + * valid GList and GSList value, thus allow_none will normally be set + * for parameters of these types. + * @optional: For an out parameter, indicates that NULL may be passed in + * if the value is not needed. + * @transfer_ownership: For an in parameter, indicates that the function takes + * over ownership of the parameter value. For an out parameter, it indicates + * that the caller is responsible for freeing the return value. + * @transfer_container_ownership: For container types, indicates that the + * ownership of the container, but not of its contents is transferred. + * This is typically the case for out parameters returning lists of + * statically allocated things. + * @return_value: The parameter should be considered the return value of the + * function. Only out parameters can be marked as return value, and there + * can be at most one per function call. If an out parameter is marked as + * return value, the actual return value of the function should be either + * void or a boolean indicating the success of the call. + * @scope: A #GIScopeType. If the parameter is of a callback type, this denotes + * the scope of the user_data and the callback function pointer itself + * (for languages that emit code at run-time). + * @skip: Indicates that the parameter is only useful in C and should be skipped. + * @reserved: Reserved for future use. + * @closure: Index of the closure (user_data) parameter associated with the + * callback, or -1. + * @destroy: Index of the destroy notfication callback parameter associated + * with the callback, or -1. + * @padding: TODO + * @arg_type: Describes the type of the parameter. See details below. + * + * Types are specified by four bytes. If the three high bytes are zero, + * the low byte describes a basic type, otherwise the 32bit number is an + * offset which points to a TypeBlob. + */ +typedef struct { + guint32 name; + + guint in : 1; + guint out : 1; + guint caller_allocates : 1; + guint nullable : 1; + guint optional : 1; + guint transfer_ownership : 1; + guint transfer_container_ownership : 1; + guint return_value : 1; + guint scope : 3; + guint skip : 1; + guint reserved :20; + gint8 closure; + gint8 destroy; + + guint16 padding; + + SimpleTypeBlob arg_type; +} ArgBlob; + +/** + * SignatureBlob: + * @return_type: Describes the type of the return value. See details below. + * @may_return_null: Only relevant for pointer types. Indicates whether the + * caller must expect NULL as a return value. + * @caller_owns_return_value: If set, the caller is responsible for freeing + * the return value if it is no longer needed. + * @caller_owns_return_container: This flag is only relevant if the return type + * is a container type. If the flag is set, the caller is resonsible for + * freeing the container, but not its contents. + * @skip_return: Indicates that the return value is only useful in C and should + * be skipped. + * @instance_transfer_ownership: When calling, the function assumes ownership of + * the instance parameter. + * @throws: Denotes the signature takes an additional #GError argument beyond + * the annotated arguments. + * @reserved: Reserved for future use. + * @n_arguments: The number of arguments that this function expects, also the + * length of the array of ArgBlobs. + * @arguments: An array of ArgBlob for the arguments of the function. + * + * TODO + */ +typedef struct { + SimpleTypeBlob return_type; + + guint16 may_return_null : 1; + guint16 caller_owns_return_value : 1; + guint16 caller_owns_return_container : 1; + guint16 skip_return : 1; + guint16 instance_transfer_ownership : 1; + guint16 throws : 1; + guint16 reserved :10; + + guint16 n_arguments; + + ArgBlob arguments[]; +} SignatureBlob; + +/** + * CommonBlob: + * @blob_type: A #GTypelibBlobType + * @deprecated: Whether the blob is deprecated. + * @reserved: Reserved for future use. + * @name: The name of the blob. + * + * The #CommonBlob is shared between #FunctionBlob, + * #CallbackBlob, #SignalBlob. + * + * TODO + */ +typedef struct { + guint16 blob_type; /* 1 */ + + guint16 deprecated : 1; + guint16 reserved :15; + guint32 name; +} CommonBlob; + +/** + * FunctionBlob: + * @blob_type: #BLOB_TYPE_FUNCTION + * @deprecated: The function is deprecated. + * @setter: The function is a setter for a property. Language bindings may + * prefer to not bind individual setters and rely on the generic + * g_object_set(). + * @getter: The function is a getter for a property. Language bindings may + * prefer to not bind individual getters and rely on the generic + * g_object_get(). + * @constructor: The function acts as a constructor for the object it is + * contained in. + * @wraps_vfunc: The function is a simple wrapper for a virtual function. + * @throws: This is now additionally stored in the #SignatureBlob. (deprecated) + * @index: Index of the property that this function is a setter or getter of + * in the array of properties of the containing interface, or index + * of the virtual function that this function wraps. + * @name: TODO + * @symbol: The symbol which can be used to obtain the function pointer with + * dlsym(). + * @signature: Offset of the SignatureBlob describing the parameter types and the + * return value type. + * @is_static: The function is a "static method"; in other words it's a pure + * function whose name is conceptually scoped to the object. + * @reserved: Reserved for future use. + * @reserved2: Reserved for future use. + * + * TODO + */ +typedef struct { + guint16 blob_type; /* 1 */ + + guint16 deprecated : 1; + guint16 setter : 1; + guint16 getter : 1; + guint16 constructor : 1; + guint16 wraps_vfunc : 1; + guint16 throws : 1; + guint16 index :10; + /* Note the bits above need to match CommonBlob + * and are thus exhausted, extend things using + * the reserved block below. */ + + guint32 name; + guint32 symbol; + guint32 signature; + + guint16 is_static : 1; + guint16 reserved : 15; + guint16 reserved2 : 16; +} FunctionBlob; + +/** + * CallbackBlob: + * @blob_type: TODO + * @deprecated: TODO + * @reserved: Reserved for future use. + * @name: TODO + * @signature: Offset of the #SignatureBlob describing the parameter types and + * the return value type. + * + * TODO + */ +typedef struct { + guint16 blob_type; /* 2 */ + + guint16 deprecated : 1; + guint16 reserved :15; + guint32 name; + guint32 signature; +} CallbackBlob; + +/** + * InterfaceTypeBlob: + * @pointer: Whether this type represents an indirection + * @reserved: Reserved for future use. + * @tag: A #GITypeTag + * @reserved2: Reserved for future use. + * @interface: Index of the directory entry for the interface. + * + * If the interface is an enum of flags type, is_pointer is 0, otherwise it is 1. + */ +typedef struct { + guint8 pointer :1; + guint8 reserved :2; + guint8 tag :5; + guint8 reserved2; + guint16 interface; +} InterfaceTypeBlob; + +/** + * ArrayTypeDimension: + * @length: TODO + * @size: TODO + * + * TODO + */ +typedef union { + guint16 length; + guint16 size; +} ArrayTypeDimension; + +/** + * ArrayTypeBlob: + * @pointer: TODO + * @reserved: Reserved for future use. + * @tag: TODO + * @zero_terminated: Indicates that the array must be terminated by a suitable + * #NULL value. + * @has_length: Indicates that length points to a parameter specifying the + * length of the array. If both has_length and zero_terminated are set, the + * convention is to pass -1 for the length if the array is zero-terminated. + * @has_size: Indicates that size is the fixed size of the array. + * @array_type: Indicates whether this is a C array, GArray, GPtrArray, or + * GByteArray. If something other than a C array, the length and element + * size are implicit in the structure. + * @reserved2: Reserved for future use. + * @dimensions: TODO + * @type: TODO + * + * TODO + */ +typedef struct { + guint16 pointer :1; + guint16 reserved :2; + guint16 tag :5; + + guint16 zero_terminated :1; + guint16 has_length :1; + guint16 has_size :1; + guint16 array_type :2; + guint16 reserved2 :3; + + ArrayTypeDimension dimensions; + + SimpleTypeBlob type; +} ArrayTypeBlob; + +/** + * ParamTypeBlob: + * @pointer: TODO + * @reserved: Reserved for future use. + * @tag: TODO + * @reserved2: Reserved for future use. + * @n_types: The number of parameter types to follow. + * @type: Describes the type of the list elements. + * + * TODO + */ +typedef struct { + guint8 pointer :1; + guint8 reserved :2; + guint8 tag :5; + + guint8 reserved2; + guint16 n_types; + + SimpleTypeBlob type[]; +} ParamTypeBlob; + +/** + * ErrorTypeBlob: + * @pointer: TODO + * @reserved: TODO + * @tag: TODO + * @reserved2: TODO + * @n_domains: TODO: must be 0 + * @domains: TODO + * + * TODO + */ +typedef struct { + guint8 pointer :1; + guint8 reserved :2; + guint8 tag :5; + + guint8 reserved2; + + guint16 n_domains; /* Must be 0 */ + guint16 domains[]; +} ErrorTypeBlob; + +/** + * ValueBlob: + * @deprecated: Whether this value is deprecated + * @unsigned_value: if set, value is a 32-bit unsigned integer cast to gint32 + * @reserved: Reserved for future use. + * @name: Name of blob + * @value: The numerical value + * + * Values commonly occur in enums and flags. + */ +typedef struct { + guint32 deprecated : 1; + guint32 unsigned_value : 1; + guint32 reserved :30; + guint32 name; + gint32 value; +} ValueBlob; + +/** + * FieldBlob: + * @name: The name of the field. + * @readable: TODO + * @writable: How the field may be accessed. + * @has_embedded_type: An anonymous type follows the FieldBlob. + * @reserved: Reserved for future use. + * @bits: If this field is part of a bitfield, the number of bits which it + * uses, otherwise 0. + * @struct_offset: The offset of the field in the struct. The value 0xFFFF + * indicates that the struct offset is unknown. + * @reserved2: Reserved for future use. + * @type: The type of the field. + * + * TODO + */ +typedef struct { + guint32 name; + + guint8 readable :1; + guint8 writable :1; + guint8 has_embedded_type :1; + guint8 reserved :5; + guint8 bits; + + guint16 struct_offset; + + guint32 reserved2; + + SimpleTypeBlob type; +} FieldBlob; + +/** + * RegisteredTypeBlob: + * @blob_type: TODO + * @deprecated: TODO + * @unregistered: TODO + * @reserved: Reserved for future use. + * @name: TODO + * @gtype_name: The name under which the type is registered with GType. + * @gtype_init: The symbol name of the get_type() function which registers the + * type. + * + * TODO + */ +typedef struct { + guint16 blob_type; + guint16 deprecated : 1; + guint16 unregistered : 1; + guint16 reserved :14; + guint32 name; + + guint32 gtype_name; + guint32 gtype_init; +} RegisteredTypeBlob; + +/** + * StructBlob: + * @blob_type: #BLOB_TYPE_STRUCT + * @deprecated: Whether this structure is deprecated + * @unregistered: If this is set, the type is not registered with GType. + * @is_gtype_struct: Whether this structure is the class or interface layout + * for a GObject + * @alignment: The byte boundary that the struct is aligned to in memory + * @foreign: If the type is foreign, eg if it's expected to be overridden by + * a native language binding instead of relying of introspected bindings. + * @reserved: Reserved for future use. + * @name: TODO + * @gtype_name: String name of the associated #GType + * @gtype_init: String naming the symbol which gets the runtime #GType + * @size: The size of the struct in bytes. + * @n_fields: TODO + * @n_methods: TODO + * @copy_func: String pointing to a function which can be called to copy + * the contents of this struct type + * @free_func: String pointing to a function which can be called to free + * the contents of this struct type + * + * TODO + */ +typedef struct { + guint16 blob_type; + + guint16 deprecated : 1; + guint16 unregistered : 1; + guint16 is_gtype_struct : 1; + guint16 alignment : 6; + guint16 foreign : 1; + guint16 reserved : 6; + + guint32 name; + + guint32 gtype_name; + guint32 gtype_init; + + guint32 size; + + guint16 n_fields; + guint16 n_methods; + + guint32 copy_func; + guint32 free_func; +} StructBlob; + +/** + * UnionBlob: + * @blob_type: TODO + * @deprecated: TODO + * @unregistered: If this is set, the type is not registered with GType. + * @discriminated: Is set if the union is discriminated + * @alignment: The byte boundary that the union is aligned to in memory + * @reserved: Reserved for future use. + * @name: TODO + * @gtype_name: String name of the associated #GType + * @gtype_init: String naming the symbol which gets the runtime #GType + * @size: TODO + * @n_fields: Length of the arrays + * @n_functions: TODO + * @copy_func: String pointing to a function which can be called to copy + * the contents of this union type + * @free_func: String pointing to a function which can be called to free + * the contents of this union type + * @discriminator_offset: Offset from the beginning of the union where the + * discriminator of a discriminated union is located. The value 0xFFFF + * indicates that the discriminator offset is unknown. + * @discriminator_type: Type of the discriminator + * + * TODO + */ +typedef struct { + guint16 blob_type; + guint16 deprecated : 1; + guint16 unregistered : 1; + guint16 discriminated : 1; + guint16 alignment : 6; + guint16 reserved : 7; + guint32 name; + + guint32 gtype_name; + guint32 gtype_init; + + guint32 size; + + guint16 n_fields; + guint16 n_functions; + + guint32 copy_func; + guint32 free_func; + + gint32 discriminator_offset; + SimpleTypeBlob discriminator_type; +} UnionBlob; + +/** + * EnumBlob: + * @blob_type: TODO + * @deprecated: TODO + * @unregistered: If this is set, the type is not registered with GType. + * @storage_type: The tag of the type used for the enum in the C ABI + * (will be a signed or unsigned integral type) + * @reserved: Reserved for future use. + * @name: TODO + * @gtype_name: String name of the associated #GType + * @gtype_init: String naming the symbol which gets the runtime #GType + * @n_values: The length of the values array. + * @n_methods: The length of the methods array. + * @error_domain: String naming the #GError domain this enum is associated with + * @values: TODO + * + * TODO + */ +typedef struct { + guint16 blob_type; + + guint16 deprecated : 1; + guint16 unregistered : 1; + guint16 storage_type : 5; + guint16 reserved : 9; + + guint32 name; + + guint32 gtype_name; + guint32 gtype_init; + + guint16 n_values; + guint16 n_methods; + + guint32 error_domain; + + ValueBlob values[]; +} EnumBlob; + +#define ACCESSOR_SENTINEL 0x3ff + +/** + * PropertyBlob: + * @name: The name of the property. + * @deprecated: TODO + * @readable: TODO + * @writable: TODO + * @construct: TODO + * @construct_only: The ParamFlags used when registering the property. + * @transfer_ownership: When writing, the type containing the property takes + * ownership of the value. When reading, the returned value needs to be + * released by the caller. + * @transfer_container_ownership: For container types indicates that the + * ownership of the container, but not of its contents, is transferred. + * This is typically the case when reading lists of statically allocated + * things. + * @setter: the index of the setter function for this property, if @writable + * is set; if the method is not known, the value will be set to %ACCESSOR_SENTINEL + * @getter: ths index of the getter function for this property, if @readable + * is set; if the method is not known, the value will be set to %ACCESSOR_SENTINEL + * @reserved: Reserved for future use. + * @reserved2: Reserved for future use. + * @type: Describes the type of the property. + * + * TODO + */ +typedef struct { + guint32 name; + + guint32 deprecated : 1; + guint32 readable : 1; + guint32 writable : 1; + guint32 construct : 1; + guint32 construct_only : 1; + guint32 transfer_ownership : 1; + guint32 transfer_container_ownership : 1; + guint32 setter :10; + guint32 getter :10; + guint32 reserved : 5; + + guint32 reserved2; + + SimpleTypeBlob type; +} PropertyBlob; + +/** + * SignalBlob: + * @deprecated: TODO + * @run_first: TODO + * @run_last: TODO + * @run_cleanup: TODO + * @no_recurse: TODO + * @detailed: TODO + * @action: TODO + * @no_hooks: The flags used when registering the signal. + * @has_class_closure: Set if the signal has a class closure. + * @true_stops_emit: Whether the signal has true-stops-emit semantics + * @reserved: Reserved for future use. + * @class_closure: The index of the class closure in the list of virtual + * functions of the object or interface on which the signal is defined. + * @name: The name of the signal. + * @reserved2: Reserved for future use. + * @signature: Offset of the SignatureBlob describing the parameter types + * and the return value type. + * + * TODO + */ +typedef struct { + guint16 deprecated : 1; + guint16 run_first : 1; + guint16 run_last : 1; + guint16 run_cleanup : 1; + guint16 no_recurse : 1; + guint16 detailed : 1; + guint16 action : 1; + guint16 no_hooks : 1; + guint16 has_class_closure : 1; + guint16 true_stops_emit : 1; + guint16 reserved : 6; + + guint16 class_closure; + + guint32 name; + + guint32 reserved2; + + guint32 signature; +} SignalBlob; + +/** + * VFuncBlob: + * @name: The name of the virtual function. + * @must_chain_up: If set, every implementation of this virtual function must + * chain up to the implementation of the parent class. + * @must_be_implemented: If set, every derived class must override this virtual + * function. + * @must_not_be_implemented: If set, derived class must not override this + * virtual function. + * @class_closure: Set if this virtual function is the class closure of a + * signal. + * @throws: This is now additionally stored in the #SignatureBlob. (deprecated) + * @reserved: Reserved for future use. + * @signal: The index of the signal in the list of signals of the object or + * interface to which this virtual function belongs. + * @struct_offset: The offset of the function pointer in the class struct. + * The value 0xFFFF indicates that the struct offset is unknown. + * @invoker: If a method invoker for this virtual exists, this is the offset + * in the class structure of the method. If no method is known, this value + * will be 0x3ff. + * @reserved2: Reserved for future use. + * @reserved3: Reserved for future use. + * @signature: Offset of the SignatureBlob describing the parameter types and + * the return value type. + * + * TODO + */ +typedef struct { + guint32 name; + + guint16 must_chain_up : 1; + guint16 must_be_implemented : 1; + guint16 must_not_be_implemented : 1; + guint16 class_closure : 1; + guint16 throws : 1; + guint16 reserved :11; + guint16 signal; + + guint16 struct_offset; + guint16 invoker : 10; /* Number of bits matches @index in FunctionBlob */ + guint16 reserved2 : 6; + + guint32 reserved3; + guint32 signature; +} VFuncBlob; + +/** + * ObjectBlob: + * @blob_type: #BLOB_TYPE_OBJECT + * @deprecated: whether the type is deprecated + * @abstract: whether the type can be instantiated + * @fundamental: this object is not a GObject derived type, instead it's + * an additional fundamental type. + * @final_: whether the type can be derived + * @reserved: Reserved for future use. + * @name: TODO + * @gtype_name: String name of the associated #GType + * @gtype_init: String naming the symbol which gets the runtime #GType + * @parent: The directory index of the parent type. This is only set for + * objects. If an object does not have a parent, it is zero. + * @gtype_struct: TODO + * @n_interfaces: TODO + * @n_fields: TODO + * @n_properties: TODO + * @n_methods: TODO + * @n_signals: TODO + * @n_vfuncs: TODO + * @n_constants: The lengths of the arrays.Up to 16bits of padding may be + * inserted between the arrays to ensure that they start on a 32bit + * boundary. + * @n_field_callbacks: The number of n_fields which are also callbacks. + * This is used to calculate the fields section size in constant time. + * @ref_func: String pointing to a function which can be called to increase + * the reference count for an instance of this object type. + * @unref_func: String pointing to a function which can be called to decrease + * the reference count for an instance of this object type. + * @set_value_func: String pointing to a function which can be called to + * convert a pointer of this object to a GValue + * @get_value_func: String pointing to a function which can be called to + * convert extract a pointer to this object from a GValue + * @reserved3: Reserved for future use. + * @reserved4: Reserved for future use. + * @interfaces: An array of indices of directory entries for the implemented + * interfaces. + * + * TODO + */ +typedef struct { + guint16 blob_type; /* 7 */ + guint16 deprecated : 1; + guint16 abstract : 1; + guint16 fundamental : 1; + guint16 final_ : 1; + guint16 reserved :12; + guint32 name; + + guint32 gtype_name; + guint32 gtype_init; + + guint16 parent; + guint16 gtype_struct; + + guint16 n_interfaces; + guint16 n_fields; + guint16 n_properties; + guint16 n_methods; + guint16 n_signals; + guint16 n_vfuncs; + guint16 n_constants; + guint16 n_field_callbacks; + + guint32 ref_func; + guint32 unref_func; + guint32 set_value_func; + guint32 get_value_func; + + guint32 reserved3; + guint32 reserved4; + + guint16 interfaces[]; +} ObjectBlob; + +/** + * InterfaceBlob: + * @blob_type: TODO + * @deprecated: TODO + * @reserved: Reserved for future use. + * @name: TODO + * @gtype_name: TODO + * @gtype_init: TODO + * @gtype_struct: Name of the interface "class" C structure + * @n_prerequisites: Number of prerequisites + * @n_properties: Number of properties + * @n_methods: Number of methods + * @n_signals: Number of signals + * @n_vfuncs: Number of virtual functions + * @n_constants: The lengths of the arrays. Up to 16bits of padding may be + * inserted between the arrays to ensure that they start on a 32bit + * boundary. + * @padding: TODO + * @reserved2: Reserved for future use. + * @reserved3: Reserved for future use. + * @prerequisites: An array of indices of directory entries for required + * interfaces. + * + * TODO + */ +typedef struct { + guint16 blob_type; + guint16 deprecated : 1; + guint16 reserved :15; + guint32 name; + + guint32 gtype_name; + guint32 gtype_init; + guint16 gtype_struct; + + guint16 n_prerequisites; + guint16 n_properties; + guint16 n_methods; + guint16 n_signals; + guint16 n_vfuncs; + guint16 n_constants; + + guint16 padding; + + guint32 reserved2; + guint32 reserved3; + + guint16 prerequisites[]; +} InterfaceBlob; + +/** + * ConstantBlob: + * @blob_type: TODO + * @deprecated: TODO + * @reserved: Reserved for future use. + * @name: TODO + * @type: The type of the value. In most cases this should be a numeric type + * or string. + * @size: The size of the value in bytes. + * @offset: The offset of the value in the typelib. + * @reserved2: Reserved for future use. + * + * TODO + */ +typedef struct { + guint16 blob_type; + guint16 deprecated : 1; + guint16 reserved :15; + guint32 name; + + SimpleTypeBlob type; + + guint32 size; + guint32 offset; + + guint32 reserved2; +} ConstantBlob; + +/** + * AttributeBlob: + * @offset: The offset of the typelib entry to which this attribute refers. + * Attributes are kept sorted by offset, so that the attributes of an + * entry can be found by a binary search. + * @name: The name of the attribute, a string. + * @value: The value of the attribute (also a string) + * + * TODO + */ +typedef struct { + guint32 offset; + guint32 name; + guint32 value; +} AttributeBlob; + +struct _GITypelib { + /*< private >*/ + guchar *data; + gsize len; + gboolean owns_memory; + GMappedFile *mfile; + GList *modules; + gboolean open_attempted; +}; + +DirEntry *g_typelib_get_dir_entry (GITypelib *typelib, + guint16 index); + +DirEntry *g_typelib_get_dir_entry_by_name (GITypelib *typelib, + const char *name); + +DirEntry *g_typelib_get_dir_entry_by_gtype_name (GITypelib *typelib, + const gchar *gtype_name); + +DirEntry *g_typelib_get_dir_entry_by_error_domain (GITypelib *typelib, + GQuark error_domain); + +gboolean g_typelib_matches_gtype_name_prefix (GITypelib *typelib, + const gchar *gtype_name); + + +GI_AVAILABLE_IN_ALL +void g_typelib_check_sanity (void); + +/** + * g_typelib_get_string: + * @typelib: TODO + * @offset: TODO + * + * TODO + * + * Returns: TODO + */ +#define g_typelib_get_string(typelib,offset) ((const gchar*)&(typelib->data)[(offset)]) + + +/** + * GITypelibError: + * @G_TYPELIB_ERROR_INVALID: the typelib is invalid + * @G_TYPELIB_ERROR_INVALID_HEADER: the typelib header is invalid + * @G_TYPELIB_ERROR_INVALID_DIRECTORY: the typelib directory is invalid + * @G_TYPELIB_ERROR_INVALID_ENTRY: a typelib entry is invalid + * @G_TYPELIB_ERROR_INVALID_BLOB: a typelib blob is invalid + * + * A error set while validating the #GITypelib + */ +typedef enum +{ + G_TYPELIB_ERROR_INVALID, + G_TYPELIB_ERROR_INVALID_HEADER, + G_TYPELIB_ERROR_INVALID_DIRECTORY, + G_TYPELIB_ERROR_INVALID_ENTRY, + G_TYPELIB_ERROR_INVALID_BLOB +} GITypelibError; + +/** + * G_TYPELIB_ERROR: + * + * TODO + */ +#define G_TYPELIB_ERROR (g_typelib_error_quark ()) + +GQuark g_typelib_error_quark (void); + + +GI_AVAILABLE_IN_ALL +gboolean g_typelib_validate (GITypelib *typelib, + GError **error); + + +/* defined in gibaseinfo.c */ +AttributeBlob *_attribute_blob_find_first (GIBaseInfo *info, + guint32 blob_offset); + +/** + * GITypelibHashBuilder: + * + * TODO + */ +typedef struct _GITypelibHashBuilder GITypelibHashBuilder; + +GITypelibHashBuilder * _gi_typelib_hash_builder_new (void); + +void _gi_typelib_hash_builder_add_string (GITypelibHashBuilder *builder, const char *str, guint16 value); + +gboolean _gi_typelib_hash_builder_prepare (GITypelibHashBuilder *builder); + +guint32 _gi_typelib_hash_builder_get_buffer_size (GITypelibHashBuilder *builder); + +void _gi_typelib_hash_builder_pack (GITypelibHashBuilder *builder, guint8* mem, guint32 size); + +void _gi_typelib_hash_builder_destroy (GITypelibHashBuilder *builder); + +guint16 _gi_typelib_hash_search (guint8* memory, const char *str, guint n_entries); + + +G_END_DECLS diff --git a/girepository/gitypelib.c b/girepository/gitypelib.c new file mode 100644 index 000000000..315b2cfb9 --- /dev/null +++ b/girepository/gitypelib.c @@ -0,0 +1,2530 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: typelib validation, auxiliary functions + * related to the binary typelib format + * + * Copyright (C) 2005 Matthias Clasen + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include +#include + +#include + +#include "gitypelib-internal.h" +#include "gitypelib.h" + +typedef struct { + GITypelib *typelib; + GSList *context_stack; +} ValidateContext; + +#define ALIGN_VALUE(this, boundary) \ + (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1))) + +static void +push_context (ValidateContext *ctx, const char *name) +{ + ctx->context_stack = g_slist_prepend (ctx->context_stack, (char*)name); +} + +static void +pop_context (ValidateContext *ctx) +{ + g_assert (ctx->context_stack != NULL); + ctx->context_stack = g_slist_delete_link (ctx->context_stack, + ctx->context_stack); +} + +static gboolean +validate_interface_blob (ValidateContext *ctx, + guint32 offset, + GError **error); + +static DirEntry * +get_dir_entry_checked (GITypelib *typelib, + guint16 index, + GError **error) +{ + Header *header = (Header *)typelib->data; + guint32 offset; + + if (index == 0 || index > header->n_entries) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Invalid directory index %d", index); + return FALSE; + } + + offset = header->directory + (index - 1) * header->entry_blob_size; + + if (typelib->len < offset + sizeof (DirEntry)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + return (DirEntry *)&typelib->data[offset]; +} + + +static CommonBlob * +get_blob (GITypelib *typelib, + guint32 offset, + GError **error) +{ + if (typelib->len < offset + sizeof (CommonBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + return (CommonBlob *)&typelib->data[offset]; +} + +static InterfaceTypeBlob * +get_type_blob (GITypelib *typelib, + SimpleTypeBlob *simple, + GError **error) +{ + if (simple->offset == 0) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "Expected blob for type"); + return FALSE; + } + + if (simple->flags.reserved == 0 && simple->flags.reserved2 == 0) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "Expected non-basic type but got %d", + simple->flags.tag); + return FALSE; + } + + return (InterfaceTypeBlob*) get_blob (typelib, simple->offset, error); +} + +/** + * g_typelib_get_dir_entry: + * @typelib: TODO + * @index: TODO + * + * TODO + * + * Returns: TODO + */ +DirEntry * +g_typelib_get_dir_entry (GITypelib *typelib, + guint16 index) +{ + Header *header = (Header *)typelib->data; + + return (DirEntry *)&typelib->data[header->directory + (index - 1) * header->entry_blob_size]; +} + +static Section * +get_section_by_id (GITypelib *typelib, + SectionType section_type) +{ + Header *header = (Header *)typelib->data; + Section *section; + + if (header->sections == 0) + return NULL; + + for (section = (Section*)&typelib->data[header->sections]; + section->id != GI_SECTION_END; + section++) + { + if (section->id == section_type) + return section; + } + return NULL; +} + +/** + * g_typelib_get_dir_entry_by_name: + * @typelib: TODO + * @name: TODO + * + * TODO + * + * Returns: TODO + */ +DirEntry * +g_typelib_get_dir_entry_by_name (GITypelib *typelib, + const char *name) +{ + Section *dirindex; + gint i, n_entries; + const char *entry_name; + DirEntry *entry; + + dirindex = get_section_by_id (typelib, GI_SECTION_DIRECTORY_INDEX); + n_entries = ((Header *)typelib->data)->n_local_entries; + + if (dirindex == NULL) + { + for (i = 1; i <= n_entries; i++) + { + entry = g_typelib_get_dir_entry (typelib, i); + entry_name = g_typelib_get_string (typelib, entry->name); + if (strcmp (name, entry_name) == 0) + return entry; + } + return NULL; + } + else + { + guint8 *hash = (guint8*) &typelib->data[dirindex->offset]; + guint16 index; + + index = _gi_typelib_hash_search (hash, name, n_entries); + entry = g_typelib_get_dir_entry (typelib, index + 1); + entry_name = g_typelib_get_string (typelib, entry->name); + if (strcmp (name, entry_name) == 0) + return entry; + return NULL; + } +} + +/** + * g_typelib_get_dir_entry_by_gtype_name: + * @typelib: TODO + * @gtype_name: TODO + * + * TODO + * + * Returns: TODO + */ +DirEntry * +g_typelib_get_dir_entry_by_gtype_name (GITypelib *typelib, + const gchar *gtype_name) +{ + Header *header = (Header *)typelib->data; + guint i; + + for (i = 1; i <= header->n_local_entries; i++) + { + RegisteredTypeBlob *blob; + const char *type; + DirEntry *entry = g_typelib_get_dir_entry (typelib, i); + if (!BLOB_IS_REGISTERED_TYPE (entry)) + continue; + + blob = (RegisteredTypeBlob *)(&typelib->data[entry->offset]); + if (!blob->gtype_name) + continue; + + type = g_typelib_get_string (typelib, blob->gtype_name); + if (strcmp (type, gtype_name) == 0) + return entry; + } + return NULL; +} + +typedef struct { + const char *s; + const char *separator; + gsize sep_len; + GString buf; +} StrSplitIter; + +static void +strsplit_iter_init (StrSplitIter *iter, + const char *s, + const char *separator) +{ + iter->s = s; + iter->separator = separator; + iter->sep_len = strlen (separator); + iter->buf.str = NULL; + iter->buf.len = 0; + iter->buf.allocated_len = 0; +} + +static gboolean +strsplit_iter_next (StrSplitIter *iter, + const char **out_val) +{ + const char *s = iter->s; + const char *next; + gsize len; + + if (!s) + return FALSE; + next = strstr (s, iter->separator); + if (next) + { + iter->s = next + iter->sep_len; + len = next - s; + } + else + { + iter->s = NULL; + len = strlen (s); + } + if (len == 0) + { + *out_val = ""; + } + else + { + g_string_overwrite_len (&iter->buf, 0, s, (gssize)len); + *out_val = iter->buf.str; + } + return TRUE; +} + +static void +strsplit_iter_clear (StrSplitIter *iter) +{ + g_free (iter->buf.str); +} + +/** + * g_typelib_matches_gtype_name_prefix: + * @typelib: TODO + * @gtype_name: TODO + * + * TODO + * + * Returns: TODO + */ +gboolean +g_typelib_matches_gtype_name_prefix (GITypelib *typelib, + const gchar *gtype_name) +{ + Header *header = (Header *)typelib->data; + const char *c_prefix; + const gchar *prefix; + gboolean ret = FALSE; + StrSplitIter split_iter; + gsize gtype_name_len; + + c_prefix = g_typelib_get_string (typelib, header->c_prefix); + if (c_prefix == NULL || strlen (c_prefix) == 0) + return FALSE; + + gtype_name_len = strlen (gtype_name); + + /* c_prefix is a comma separated string of supported prefixes + * in the typelib. + * We match the specified gtype_name if the gtype_name starts + * with the prefix, and is followed by a capital letter. + * For example, a typelib offering the 'Gdk' prefix does match + * GdkX11Cursor, however a typelib offering the 'G' prefix does not. + */ + strsplit_iter_init (&split_iter, c_prefix, ","); + while (strsplit_iter_next (&split_iter, &prefix)) + { + size_t len = strlen (prefix); + + if (gtype_name_len < len) + continue; + + if (strncmp (prefix, gtype_name, len) != 0) + continue; + + if (g_ascii_isupper (gtype_name[len])) + { + ret = TRUE; + break; + } + } + strsplit_iter_clear (&split_iter); + return ret; +} + +/** + * g_typelib_get_dir_entry_by_error_domain: + * @typelib: TODO + * @error_domain: TODO + * + * TODO + * + * Returns: TODO + */ +DirEntry * +g_typelib_get_dir_entry_by_error_domain (GITypelib *typelib, + GQuark error_domain) +{ + Header *header = (Header *)typelib->data; + guint n_entries = header->n_local_entries; + const char *domain_string = g_quark_to_string (error_domain); + DirEntry *entry; + guint i; + + for (i = 1; i <= n_entries; i++) + { + EnumBlob *blob; + const char *enum_domain_string; + + entry = g_typelib_get_dir_entry (typelib, i); + if (entry->blob_type != BLOB_TYPE_ENUM) + continue; + + blob = (EnumBlob *)(&typelib->data[entry->offset]); + if (!blob->error_domain) + continue; + + enum_domain_string = g_typelib_get_string (typelib, blob->error_domain); + if (strcmp (domain_string, enum_domain_string) == 0) + return entry; + } + return NULL; +} + +/** + * g_typelib_check_sanity: + * + * TODO + */ +void +g_typelib_check_sanity (void) +{ + /* Check that struct layout is as we expect */ + + gboolean size_check_ok = TRUE; + +#define CHECK_SIZE(s,n) \ + if (sizeof(s) != n) \ + { \ + g_printerr ("sizeof("#s") is expected to be %d but is %"G_GSIZE_FORMAT".\n", \ + n, sizeof (s)); \ + size_check_ok = FALSE; \ + } + + /* When changing the size of a typelib structure, you are required to update + * the hardcoded size here. Do NOT change these to use sizeof(); these + * should match whatever is defined in the text specification and serve as + * a sanity check on structure modifications. + * + * Everything else in the code however should be using sizeof(). + */ + + CHECK_SIZE (Header, 112); + CHECK_SIZE (DirEntry, 12); + CHECK_SIZE (SimpleTypeBlob, 4); + CHECK_SIZE (ArgBlob, 16); + CHECK_SIZE (SignatureBlob, 8); + CHECK_SIZE (CommonBlob, 8); + CHECK_SIZE (FunctionBlob, 20); + CHECK_SIZE (CallbackBlob, 12); + CHECK_SIZE (InterfaceTypeBlob, 4); + CHECK_SIZE (ArrayTypeBlob, 8); + CHECK_SIZE (ParamTypeBlob, 4); + CHECK_SIZE (ErrorTypeBlob, 4); + CHECK_SIZE (ValueBlob, 12); + CHECK_SIZE (FieldBlob, 16); + CHECK_SIZE (RegisteredTypeBlob, 16); + CHECK_SIZE (StructBlob, 32); + CHECK_SIZE (EnumBlob, 24); + CHECK_SIZE (PropertyBlob, 16); + CHECK_SIZE (SignalBlob, 16); + CHECK_SIZE (VFuncBlob, 20); + CHECK_SIZE (ObjectBlob, 60); + CHECK_SIZE (InterfaceBlob, 40); + CHECK_SIZE (ConstantBlob, 24); + CHECK_SIZE (AttributeBlob, 12); + CHECK_SIZE (UnionBlob, 40); +#undef CHECK_SIZE + + g_assert (size_check_ok); +} + + +static gboolean +is_aligned (guint32 offset) +{ + return offset == ALIGN_VALUE (offset, 4); +} + +#define MAX_NAME_LEN 2048 + +static const char * +get_string (GITypelib *typelib, guint32 offset, GError **error) +{ + if (typelib->len < offset) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "Buffer is too short while looking up name"); + return NULL; + } + + return (const char*)&typelib->data[offset]; +} + +static const char * +get_string_nofail (GITypelib *typelib, guint32 offset) +{ + const char *ret = get_string (typelib, offset, NULL); + g_assert (ret); + return ret; +} + +static gboolean +validate_name (GITypelib *typelib, + const char *msg, + const guchar *data, guint32 offset, + GError **error) +{ + const char *name; + + name = get_string (typelib, offset, error); + if (!name) + return FALSE; + + if (!memchr (name, '\0', MAX_NAME_LEN)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The %s is too long: %s", + msg, name); + return FALSE; + } + + if (strspn (name, G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "-_") < strlen (name)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The %s contains invalid characters: '%s'", + msg, name); + return FALSE; + } + + return TRUE; +} + +/* Fast path sanity check, operates on a memory blob */ +static gboolean +validate_header_basic (const guint8 *memory, + gsize len, + GError **error) +{ + Header *header = (Header *)memory; + + if (len < sizeof (Header)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The specified typelib length %" G_GSIZE_FORMAT " is too short", + len); + return FALSE; + } + + if (strncmp (header->magic, G_IR_MAGIC, 16) != 0) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_HEADER, + "Invalid magic header"); + return FALSE; + + } + + if (header->major_version != 4) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_HEADER, + "Typelib version mismatch; expected 4, found %d", + header->major_version); + return FALSE; + + } + + if (header->n_entries < header->n_local_entries) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_HEADER, + "Inconsistent entry counts"); + return FALSE; + } + + if (header->size != len) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_HEADER, + "Typelib size %" G_GSIZE_FORMAT " does not match %" G_GSIZE_FORMAT, + (gsize) header->size, len); + return FALSE; + } + + /* This is a sanity check for a specific typelib; it + * prevents us from loading an incompatible typelib. + * + * The hardcoded checks in g_typelib_check_sanity to + * protect against inadvertent or buggy changes to the typelib format + * itself. + */ + + if (header->entry_blob_size != sizeof (DirEntry) || + header->function_blob_size != sizeof (FunctionBlob) || + header->callback_blob_size != sizeof (CallbackBlob) || + header->signal_blob_size != sizeof (SignalBlob) || + header->vfunc_blob_size != sizeof (VFuncBlob) || + header->arg_blob_size != sizeof (ArgBlob) || + header->property_blob_size != sizeof (PropertyBlob) || + header->field_blob_size != sizeof (FieldBlob) || + header->value_blob_size != sizeof (ValueBlob) || + header->constant_blob_size != sizeof (ConstantBlob) || + header->attribute_blob_size != sizeof (AttributeBlob) || + header->signature_blob_size != sizeof (SignatureBlob) || + header->enum_blob_size != sizeof (EnumBlob) || + header->struct_blob_size != sizeof (StructBlob) || + header->object_blob_size != sizeof(ObjectBlob) || + header->interface_blob_size != sizeof (InterfaceBlob) || + header->union_blob_size != sizeof (UnionBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_HEADER, + "Blob size mismatch"); + return FALSE; + } + + if (!is_aligned (header->directory)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_HEADER, + "Misaligned directory"); + return FALSE; + } + + if (!is_aligned (header->attributes)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_HEADER, + "Misaligned attributes"); + return FALSE; + } + + if (header->attributes == 0 && header->n_attributes > 0) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_HEADER, + "Wrong number of attributes"); + return FALSE; + } + + return TRUE; +} + +static gboolean +validate_header (ValidateContext *ctx, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + + if (!validate_header_basic (typelib->data, typelib->len, error)) + return FALSE; + + { + Header *header = (Header*)typelib->data; + if (!validate_name (typelib, "namespace", typelib->data, header->namespace, error)) + return FALSE; + } + + return TRUE; +} + +static gboolean validate_type_blob (GITypelib *typelib, + guint32 offset, + guint32 signature_offset, + gboolean return_type, + GError **error); + +static gboolean +validate_array_type_blob (GITypelib *typelib, + guint32 offset, + guint32 signature_offset, + gboolean return_type, + GError **error) +{ + /* FIXME validate length */ + + if (!validate_type_blob (typelib, + offset + G_STRUCT_OFFSET (ArrayTypeBlob, type), + 0, FALSE, error)) + return FALSE; + + return TRUE; +} + +static gboolean +validate_iface_type_blob (GITypelib *typelib, + guint32 offset, + guint32 signature_offset, + gboolean return_type, + GError **error) +{ + InterfaceTypeBlob *blob; + InterfaceBlob *target; + + blob = (InterfaceTypeBlob*)&typelib->data[offset]; + + target = (InterfaceBlob*) get_dir_entry_checked (typelib, blob->interface, error); + + if (!target) + return FALSE; + if (target->blob_type == 0) /* non-local */ + return TRUE; + + return TRUE; +} + +static gboolean +validate_param_type_blob (GITypelib *typelib, + guint32 offset, + guint32 signature_offset, + gboolean return_type, + gint n_params, + GError **error) +{ + ParamTypeBlob *blob; + gint i; + + blob = (ParamTypeBlob*)&typelib->data[offset]; + + if (!blob->pointer) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Pointer type exected for tag %d", blob->tag); + return FALSE; + } + + if (blob->n_types != n_params) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Parameter type number mismatch"); + return FALSE; + } + + for (i = 0; i < n_params; i++) + { + if (!validate_type_blob (typelib, + offset + sizeof (ParamTypeBlob) + + i * sizeof (SimpleTypeBlob), + 0, FALSE, error)) + return FALSE; + } + + return TRUE; +} + +static gboolean +validate_error_type_blob (GITypelib *typelib, + guint32 offset, + guint32 signature_offset, + gboolean return_type, + GError **error) +{ + ErrorTypeBlob *blob; + + blob = (ErrorTypeBlob*)&typelib->data[offset]; + + if (!blob->pointer) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Pointer type exected for tag %d", blob->tag); + return FALSE; + } + + return TRUE; +} + +static gboolean +validate_type_blob (GITypelib *typelib, + guint32 offset, + guint32 signature_offset, + gboolean return_type, + GError **error) +{ + SimpleTypeBlob *simple; + InterfaceTypeBlob *iface; + + simple = (SimpleTypeBlob *)&typelib->data[offset]; + + if (simple->flags.reserved == 0 && + simple->flags.reserved2 == 0) + { + if (!GI_TYPE_TAG_IS_BASIC(simple->flags.tag)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Invalid non-basic tag %d in simple type", simple->flags.tag); + return FALSE; + } + + if (simple->flags.tag >= GI_TYPE_TAG_UTF8 && + simple->flags.tag != GI_TYPE_TAG_UNICHAR && + !simple->flags.pointer) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Pointer type exected for tag %d", simple->flags.tag); + return FALSE; + } + + return TRUE; + } + + iface = (InterfaceTypeBlob*)&typelib->data[simple->offset]; + + switch (iface->tag) + { + case GI_TYPE_TAG_ARRAY: + if (!validate_array_type_blob (typelib, simple->offset, + signature_offset, return_type, error)) + return FALSE; + break; + case GI_TYPE_TAG_INTERFACE: + if (!validate_iface_type_blob (typelib, simple->offset, + signature_offset, return_type, error)) + return FALSE; + break; + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + if (!validate_param_type_blob (typelib, simple->offset, + signature_offset, return_type, 1, error)) + return FALSE; + break; + case GI_TYPE_TAG_GHASH: + if (!validate_param_type_blob (typelib, simple->offset, + signature_offset, return_type, 2, error)) + return FALSE; + break; + case GI_TYPE_TAG_ERROR: + if (!validate_error_type_blob (typelib, simple->offset, + signature_offset, return_type, error)) + return FALSE; + break; + default: + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Wrong tag in complex type"); + return FALSE; + } + + return TRUE; +} + +static gboolean +validate_arg_blob (GITypelib *typelib, + guint32 offset, + guint32 signature_offset, + GError **error) +{ + ArgBlob *blob; + + if (typelib->len < offset + sizeof (ArgBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (ArgBlob*) &typelib->data[offset]; + + if (!validate_name (typelib, "argument", typelib->data, blob->name, error)) + return FALSE; + + if (!validate_type_blob (typelib, + offset + G_STRUCT_OFFSET (ArgBlob, arg_type), + signature_offset, FALSE, error)) + return FALSE; + + return TRUE; +} + +static SimpleTypeBlob * +return_type_from_signature (GITypelib *typelib, + guint32 offset, + GError **error) +{ + SignatureBlob *blob; + if (typelib->len < offset + sizeof (SignatureBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return NULL; + } + + blob = (SignatureBlob*) &typelib->data[offset]; + if (blob->return_type.offset == 0) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "No return type found in signature"); + return NULL; + } + + return (SimpleTypeBlob *)&typelib->data[offset + G_STRUCT_OFFSET (SignatureBlob, return_type)]; +} + +static gboolean +validate_signature_blob (GITypelib *typelib, + guint32 offset, + GError **error) +{ + SignatureBlob *blob; + gint i; + + if (typelib->len < offset + sizeof (SignatureBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (SignatureBlob*) &typelib->data[offset]; + + if (blob->return_type.offset != 0) + { + if (!validate_type_blob (typelib, + offset + G_STRUCT_OFFSET (SignatureBlob, return_type), + offset, TRUE, error)) + return FALSE; + } + + for (i = 0; i < blob->n_arguments; i++) + { + if (!validate_arg_blob (typelib, + offset + sizeof (SignatureBlob) + + i * sizeof (ArgBlob), + offset, + error)) + return FALSE; + } + + /* FIXME check constraints on return_value */ + /* FIXME check array-length pairs */ + return TRUE; +} + +static gboolean +validate_function_blob (ValidateContext *ctx, + guint32 offset, + guint16 container_type, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + FunctionBlob *blob; + + if (typelib->len < offset + sizeof (FunctionBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (FunctionBlob*) &typelib->data[offset]; + + if (blob->blob_type != BLOB_TYPE_FUNCTION) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Wrong blob type %d, expected function", blob->blob_type); + return FALSE; + } + + if (!validate_name (typelib, "function", typelib->data, blob->name, error)) + return FALSE; + + push_context (ctx, get_string_nofail (typelib, blob->name)); + + if (!validate_name (typelib, "function symbol", typelib->data, blob->symbol, error)) + return FALSE; + + if (blob->constructor) + { + switch (container_type) + { + case BLOB_TYPE_BOXED: + case BLOB_TYPE_STRUCT: + case BLOB_TYPE_UNION: + case BLOB_TYPE_OBJECT: + case BLOB_TYPE_INTERFACE: + break; + default: + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Constructor not allowed"); + return FALSE; + } + } + + if (blob->setter || blob->getter || blob->wraps_vfunc) + { + switch (container_type) + { + case BLOB_TYPE_OBJECT: + case BLOB_TYPE_INTERFACE: + break; + default: + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Setter, getter or wrapper not allowed"); + return FALSE; + } + } + + if (blob->index) + { + if (!(blob->setter || blob->getter || blob->wraps_vfunc)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Must be setter, getter or wrapper"); + return FALSE; + } + } + + /* FIXME: validate index range */ + + if (!validate_signature_blob (typelib, blob->signature, error)) + return FALSE; + + if (blob->constructor) + { + SimpleTypeBlob *simple = return_type_from_signature (typelib, + blob->signature, + error); + InterfaceTypeBlob *iface_type; + + if (!simple) + return FALSE; + iface_type = get_type_blob (typelib, simple, error); + if (!iface_type) + return FALSE; + if (iface_type->tag != GI_TYPE_TAG_INTERFACE && + (container_type == BLOB_TYPE_OBJECT || + container_type == BLOB_TYPE_INTERFACE)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "Invalid return type '%s' for constructor '%s'", + g_type_tag_to_string (iface_type->tag), + get_string_nofail (typelib, blob->symbol)); + return FALSE; + } + } + + pop_context (ctx); + + return TRUE; +} + +static gboolean +validate_callback_blob (ValidateContext *ctx, + guint32 offset, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + CallbackBlob *blob; + + if (typelib->len < offset + sizeof (CallbackBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (CallbackBlob*) &typelib->data[offset]; + + if (blob->blob_type != BLOB_TYPE_CALLBACK) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Wrong blob type"); + return FALSE; + } + + if (!validate_name (typelib, "callback", typelib->data, blob->name, error)) + return FALSE; + + push_context (ctx, get_string_nofail (typelib, blob->name)); + + if (!validate_signature_blob (typelib, blob->signature, error)) + return FALSE; + + pop_context (ctx); + + return TRUE; +} + +static gboolean +validate_constant_blob (GITypelib *typelib, + guint32 offset, + GError **error) +{ + guint value_size[] = { + 0, /* VOID */ + 4, /* BOOLEAN */ + 1, /* INT8 */ + 1, /* UINT8 */ + 2, /* INT16 */ + 2, /* UINT16 */ + 4, /* INT32 */ + 4, /* UINT32 */ + 8, /* INT64 */ + 8, /* UINT64 */ + sizeof (gfloat), + sizeof (gdouble), + 0, /* GTYPE */ + 0, /* UTF8 */ + 0, /* FILENAME */ + 0, /* ARRAY */ + 0, /* INTERFACE */ + 0, /* GLIST */ + 0, /* GSLIST */ + 0, /* GHASH */ + 0, /* ERROR */ + 4 /* UNICHAR */ + }; + ConstantBlob *blob; + SimpleTypeBlob *type; + + g_assert (G_N_ELEMENTS (value_size) == GI_TYPE_TAG_N_TYPES); + + if (typelib->len < offset + sizeof (ConstantBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (ConstantBlob*) &typelib->data[offset]; + + if (blob->blob_type != BLOB_TYPE_CONSTANT) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Wrong blob type"); + return FALSE; + } + + if (!validate_name (typelib, "constant", typelib->data, blob->name, error)) + return FALSE; + + if (!validate_type_blob (typelib, offset + G_STRUCT_OFFSET (ConstantBlob, type), + 0, FALSE, error)) + return FALSE; + + if (!is_aligned (blob->offset)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Misaligned constant value"); + return FALSE; + } + + type = (SimpleTypeBlob *)&typelib->data[offset + G_STRUCT_OFFSET (ConstantBlob, type)]; + if (type->flags.reserved == 0 && type->flags.reserved2 == 0) + { + if (type->flags.tag == 0) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Constant value type void"); + return FALSE; + } + + if (value_size[type->flags.tag] != 0 && + blob->size != value_size[type->flags.tag]) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Constant value size mismatch"); + return FALSE; + } + /* FIXME check string values */ + } + + return TRUE; +} + +static gboolean +validate_value_blob (GITypelib *typelib, + guint32 offset, + GError **error) +{ + ValueBlob *blob; + + if (typelib->len < offset + sizeof (ValueBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (ValueBlob*) &typelib->data[offset]; + + if (!validate_name (typelib, "value", typelib->data, blob->name, error)) + return FALSE; + + return TRUE; +} + +static gboolean +validate_field_blob (ValidateContext *ctx, + guint32 offset, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + Header *header = (Header *)typelib->data; + FieldBlob *blob; + + if (typelib->len < offset + sizeof (FieldBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (FieldBlob*) &typelib->data[offset]; + + if (!validate_name (typelib, "field", typelib->data, blob->name, error)) + return FALSE; + + if (blob->has_embedded_type) + { + if (!validate_callback_blob (ctx, offset + header->field_blob_size, error)) + return FALSE; + } + else if (!validate_type_blob (typelib, + offset + G_STRUCT_OFFSET (FieldBlob, type), + 0, FALSE, error)) + return FALSE; + + return TRUE; +} + +static gboolean +validate_property_blob (GITypelib *typelib, + guint32 offset, + GError **error) +{ + PropertyBlob *blob; + + if (typelib->len < offset + sizeof (PropertyBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (PropertyBlob*) &typelib->data[offset]; + + if (!validate_name (typelib, "property", typelib->data, blob->name, error)) + return FALSE; + + if (!validate_type_blob (typelib, + offset + G_STRUCT_OFFSET (PropertyBlob, type), + 0, FALSE, error)) + return FALSE; + + return TRUE; +} + +static gboolean +validate_signal_blob (GITypelib *typelib, + guint32 offset, + guint32 container_offset, + GError **error) +{ + SignalBlob *blob; + gint n_signals; + + if (typelib->len < offset + sizeof (SignalBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (SignalBlob*) &typelib->data[offset]; + + if (!validate_name (typelib, "signal", typelib->data, blob->name, error)) + return FALSE; + + if ((blob->run_first != 0) + + (blob->run_last != 0) + + (blob->run_cleanup != 0) != 1) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Invalid signal run flags"); + return FALSE; + } + + if (blob->has_class_closure) + { + if (((CommonBlob*)&typelib->data[container_offset])->blob_type == BLOB_TYPE_OBJECT) + { + ObjectBlob *object; + + object = (ObjectBlob*)&typelib->data[container_offset]; + + n_signals = object->n_signals; + } + else + { + InterfaceBlob *iface; + + iface = (InterfaceBlob*)&typelib->data[container_offset]; + + n_signals = iface->n_signals; + } + + if (blob->class_closure >= n_signals) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Invalid class closure index"); + return FALSE; + } + } + + if (!validate_signature_blob (typelib, blob->signature, error)) + return FALSE; + + return TRUE; +} + +static gboolean +validate_vfunc_blob (GITypelib *typelib, + guint32 offset, + guint32 container_offset, + GError **error) +{ + VFuncBlob *blob; + gint n_vfuncs; + + if (typelib->len < offset + sizeof (VFuncBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (VFuncBlob*) &typelib->data[offset]; + + if (!validate_name (typelib, "vfunc", typelib->data, blob->name, error)) + return FALSE; + + if (blob->class_closure) + { + if (((CommonBlob*)&typelib->data[container_offset])->blob_type == BLOB_TYPE_OBJECT) + { + ObjectBlob *object; + + object = (ObjectBlob*)&typelib->data[container_offset]; + + n_vfuncs = object->n_vfuncs; + } + else + { + InterfaceBlob *iface; + + iface = (InterfaceBlob*)&typelib->data[container_offset]; + + n_vfuncs = iface->n_vfuncs; + } + + if (blob->class_closure >= n_vfuncs) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Invalid class closure index"); + return FALSE; + } + } + + if (!validate_signature_blob (typelib, blob->signature, error)) + return FALSE; + + return TRUE; +} + +static gboolean +validate_struct_blob (ValidateContext *ctx, + guint32 offset, + guint16 blob_type, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + StructBlob *blob; + gint i; + guint32 field_offset; + + if (typelib->len < offset + sizeof (StructBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (StructBlob*) &typelib->data[offset]; + + if (blob->blob_type != blob_type) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Wrong blob type"); + return FALSE; + } + + if (!validate_name (typelib, "struct", typelib->data, blob->name, error)) + return FALSE; + + push_context (ctx, get_string_nofail (typelib, blob->name)); + + if (!blob->unregistered) + { + if (!validate_name (typelib, "boxed", typelib->data, blob->gtype_name, error)) + return FALSE; + + if (!validate_name (typelib, "boxed", typelib->data, blob->gtype_init, error)) + return FALSE; + } + else + { + if (blob->gtype_name || blob->gtype_init) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Gtype data in struct"); + return FALSE; + } + } + + if (typelib->len < offset + sizeof (StructBlob) + + blob->n_fields * sizeof (FieldBlob) + + blob->n_methods * sizeof (FunctionBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + field_offset = offset + sizeof (StructBlob); + for (i = 0; i < blob->n_fields; i++) + { + FieldBlob *field_blob = (FieldBlob*) &typelib->data[field_offset]; + + if (!validate_field_blob (ctx, + field_offset, + error)) + return FALSE; + + field_offset += sizeof (FieldBlob); + if (field_blob->has_embedded_type) + field_offset += sizeof (CallbackBlob); + } + + for (i = 0; i < blob->n_methods; i++) + { + if (!validate_function_blob (ctx, + field_offset + + i * sizeof (FunctionBlob), + blob_type, + error)) + return FALSE; + } + + pop_context (ctx); + + return TRUE; +} + +static gboolean +validate_enum_blob (ValidateContext *ctx, + guint32 offset, + guint16 blob_type, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + EnumBlob *blob; + gint i; + guint32 offset2; + + if (typelib->len < offset + sizeof (EnumBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (EnumBlob*) &typelib->data[offset]; + + if (blob->blob_type != blob_type) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Wrong blob type"); + return FALSE; + } + + if (!blob->unregistered) + { + if (!validate_name (typelib, "enum", typelib->data, blob->gtype_name, error)) + return FALSE; + + if (!validate_name (typelib, "enum", typelib->data, blob->gtype_init, error)) + return FALSE; + } + else + { + if (blob->gtype_name || blob->gtype_init) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Gtype data in unregistered enum"); + return FALSE; + } + } + + if (!validate_name (typelib, "enum", typelib->data, blob->name, error)) + return FALSE; + + if (typelib->len < offset + sizeof (EnumBlob) + + blob->n_values * sizeof (ValueBlob) + + blob->n_methods * sizeof (FunctionBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + offset2 = offset + sizeof (EnumBlob); + + push_context (ctx, get_string_nofail (typelib, blob->name)); + + for (i = 0; i < blob->n_values; i++, offset2 += sizeof (ValueBlob)) + { + if (!validate_value_blob (typelib, + offset2, + error)) + return FALSE; + +#if 0 + v1 = (ValueBlob *)&typelib->data[offset2]; + for (j = 0; j < i; j++) + { + v2 = (ValueBlob *)&typelib->data[offset2 + + j * sizeof (ValueBlob)]; + + if (v1->value == v2->value) + { + + /* FIXME should this be an error ? */ + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Duplicate enum value"); + return FALSE; + } + } +#endif + } + + for (i = 0; i < blob->n_methods; i++, offset2 += sizeof (FunctionBlob)) + { + if (!validate_function_blob (ctx, offset2, BLOB_TYPE_ENUM, error)) + return FALSE; + } + + pop_context (ctx); + + return TRUE; +} + +static gboolean +validate_object_blob (ValidateContext *ctx, + guint32 offset, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + Header *header; + ObjectBlob *blob; + gint i; + guint32 offset2; + guint16 n_field_callbacks; + + header = (Header *)typelib->data; + + if (typelib->len < offset + sizeof (ObjectBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (ObjectBlob*) &typelib->data[offset]; + + if (blob->blob_type != BLOB_TYPE_OBJECT) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Wrong blob type"); + return FALSE; + } + + if (!validate_name (typelib, "object", typelib->data, blob->gtype_name, error)) + return FALSE; + + if (!validate_name (typelib, "object", typelib->data, blob->gtype_init, error)) + return FALSE; + + if (!validate_name (typelib, "object", typelib->data, blob->name, error)) + return FALSE; + + if (blob->parent > header->n_entries) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Invalid parent index"); + return FALSE; + } + + if (blob->parent != 0) + { + DirEntry *entry; + + entry = get_dir_entry_checked (typelib, blob->parent, error); + if (!entry) + return FALSE; + if (entry->blob_type != BLOB_TYPE_OBJECT && + (entry->local || entry->blob_type != 0)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Parent not object"); + return FALSE; + } + } + + if (blob->gtype_struct != 0) + { + DirEntry *entry; + + entry = get_dir_entry_checked (typelib, blob->gtype_struct, error); + if (!entry) + return FALSE; + if (entry->blob_type != BLOB_TYPE_STRUCT && entry->local) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Class struct invalid type or not local"); + return FALSE; + } + } + + if (typelib->len < offset + sizeof (ObjectBlob) + + (blob->n_interfaces + blob->n_interfaces % 2) * 2 + + blob->n_fields * sizeof (FieldBlob) + + blob->n_properties * sizeof (PropertyBlob) + + blob->n_methods * sizeof (FunctionBlob) + + blob->n_signals * sizeof (SignalBlob) + + blob->n_vfuncs * sizeof (VFuncBlob) + + blob->n_constants * sizeof (ConstantBlob)) + + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + offset2 = offset + sizeof (ObjectBlob); + + for (i = 0; i < blob->n_interfaces; i++, offset2 += 2) + { + guint16 iface; + DirEntry *entry; + + iface = *(guint16*)&typelib->data[offset2]; + if (iface == 0 || iface > header->n_entries) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Invalid interface index"); + return FALSE; + } + + entry = get_dir_entry_checked (typelib, iface, error); + if (!entry) + return FALSE; + + if (entry->blob_type != BLOB_TYPE_INTERFACE && + (entry->local || entry->blob_type != 0)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Not an interface"); + return FALSE; + } + } + + offset2 += 2 * (blob->n_interfaces %2); + + push_context (ctx, get_string_nofail (typelib, blob->name)); + + n_field_callbacks = 0; + for (i = 0; i < blob->n_fields; i++) + { + FieldBlob *field_blob = (FieldBlob*) &typelib->data[offset2]; + + if (!validate_field_blob (ctx, offset2, error)) + return FALSE; + + offset2 += sizeof (FieldBlob); + /* Special case fields which are callbacks. */ + if (field_blob->has_embedded_type) { + offset2 += sizeof (CallbackBlob); + n_field_callbacks++; + } + } + + if (blob->n_field_callbacks != n_field_callbacks) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Incorrect number of field callbacks; expected " + "%" G_GUINT16_FORMAT ", got %" G_GUINT16_FORMAT, + blob->n_field_callbacks, n_field_callbacks); + return FALSE; + } + + for (i = 0; i < blob->n_properties; i++, offset2 += sizeof (PropertyBlob)) + { + if (!validate_property_blob (typelib, offset2, error)) + return FALSE; + } + + for (i = 0; i < blob->n_methods; i++, offset2 += sizeof (FunctionBlob)) + { + if (!validate_function_blob (ctx, offset2, BLOB_TYPE_OBJECT, error)) + return FALSE; + } + + for (i = 0; i < blob->n_signals; i++, offset2 += sizeof (SignalBlob)) + { + if (!validate_signal_blob (typelib, offset2, offset, error)) + return FALSE; + } + + for (i = 0; i < blob->n_vfuncs; i++, offset2 += sizeof (VFuncBlob)) + { + if (!validate_vfunc_blob (typelib, offset2, offset, error)) + return FALSE; + } + + for (i = 0; i < blob->n_constants; i++, offset2 += sizeof (ConstantBlob)) + { + if (!validate_constant_blob (typelib, offset2, error)) + return FALSE; + } + + pop_context (ctx); + + return TRUE; +} + +static gboolean +validate_interface_blob (ValidateContext *ctx, + guint32 offset, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + Header *header; + InterfaceBlob *blob; + gint i; + guint32 offset2; + + header = (Header *)typelib->data; + + if (typelib->len < offset + sizeof (InterfaceBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (InterfaceBlob*) &typelib->data[offset]; + + if (blob->blob_type != BLOB_TYPE_INTERFACE) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Wrong blob type; expected interface, got %d", blob->blob_type); + return FALSE; + } + + if (!validate_name (typelib, "interface", typelib->data, blob->gtype_name, error)) + return FALSE; + + if (!validate_name (typelib, "interface", typelib->data, blob->gtype_init, error)) + return FALSE; + + if (!validate_name (typelib, "interface", typelib->data, blob->name, error)) + return FALSE; + + if (typelib->len < offset + sizeof (InterfaceBlob) + + (blob->n_prerequisites + blob->n_prerequisites % 2) * 2 + + blob->n_properties * sizeof (PropertyBlob) + + blob->n_methods * sizeof (FunctionBlob) + + blob->n_signals * sizeof (SignalBlob) + + blob->n_vfuncs * sizeof (VFuncBlob) + + blob->n_constants * sizeof (ConstantBlob)) + + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + offset2 = offset + sizeof (InterfaceBlob); + + for (i = 0; i < blob->n_prerequisites; i++, offset2 += 2) + { + DirEntry *entry; + guint16 req; + + req = *(guint16*)&typelib->data[offset2]; + if (req == 0 || req > header->n_entries) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Invalid prerequisite index"); + return FALSE; + } + + entry = g_typelib_get_dir_entry (typelib, req); + if (entry->blob_type != BLOB_TYPE_INTERFACE && + entry->blob_type != BLOB_TYPE_OBJECT && + (entry->local || entry->blob_type != 0)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Not an interface or object"); + return FALSE; + } + } + + offset2 += 2 * (blob->n_prerequisites % 2); + + push_context (ctx, get_string_nofail (typelib, blob->name)); + + for (i = 0; i < blob->n_properties; i++, offset2 += sizeof (PropertyBlob)) + { + if (!validate_property_blob (typelib, offset2, error)) + return FALSE; + } + + for (i = 0; i < blob->n_methods; i++, offset2 += sizeof (FunctionBlob)) + { + if (!validate_function_blob (ctx, offset2, BLOB_TYPE_INTERFACE, error)) + return FALSE; + } + + for (i = 0; i < blob->n_signals; i++, offset2 += sizeof (SignalBlob)) + { + if (!validate_signal_blob (typelib, offset2, offset, error)) + return FALSE; + } + + for (i = 0; i < blob->n_vfuncs; i++, offset2 += sizeof (VFuncBlob)) + { + if (!validate_vfunc_blob (typelib, offset2, offset, error)) + return FALSE; + } + + for (i = 0; i < blob->n_constants; i++, offset2 += sizeof (ConstantBlob)) + { + if (!validate_constant_blob (typelib, offset2, error)) + return FALSE; + } + + pop_context (ctx); + + return TRUE; +} + +static gboolean +validate_union_blob (GITypelib *typelib, + guint32 offset, + GError **error) +{ + return TRUE; +} + +static gboolean +validate_blob (ValidateContext *ctx, + guint32 offset, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + CommonBlob *common; + + if (typelib->len < offset + sizeof (CommonBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + common = (CommonBlob*)&typelib->data[offset]; + + switch (common->blob_type) + { + case BLOB_TYPE_FUNCTION: + if (!validate_function_blob (ctx, offset, 0, error)) + return FALSE; + break; + case BLOB_TYPE_CALLBACK: + if (!validate_callback_blob (ctx, offset, error)) + return FALSE; + break; + case BLOB_TYPE_STRUCT: + case BLOB_TYPE_BOXED: + if (!validate_struct_blob (ctx, offset, common->blob_type, error)) + return FALSE; + break; + case BLOB_TYPE_ENUM: + case BLOB_TYPE_FLAGS: + if (!validate_enum_blob (ctx, offset, common->blob_type, error)) + return FALSE; + break; + case BLOB_TYPE_OBJECT: + if (!validate_object_blob (ctx, offset, error)) + return FALSE; + break; + case BLOB_TYPE_INTERFACE: + if (!validate_interface_blob (ctx, offset, error)) + return FALSE; + break; + case BLOB_TYPE_CONSTANT: + if (!validate_constant_blob (typelib, offset, error)) + return FALSE; + break; + case BLOB_TYPE_UNION: + if (!validate_union_blob (typelib, offset, error)) + return FALSE; + break; + default: + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_ENTRY, + "Invalid blob type"); + return FALSE; + } + + return TRUE; +} + +static gboolean +validate_directory (ValidateContext *ctx, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + Header *header = (Header *)typelib->data; + DirEntry *entry; + gint i; + + if (typelib->len < header->directory + header->n_entries * sizeof (DirEntry)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + for (i = 0; i < header->n_entries; i++) + { + entry = g_typelib_get_dir_entry (typelib, i + 1); + + if (!validate_name (typelib, "entry", typelib->data, entry->name, error)) + return FALSE; + + if ((entry->local && entry->blob_type == BLOB_TYPE_INVALID) || + entry->blob_type > BLOB_TYPE_UNION) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_DIRECTORY, + "Invalid entry type"); + return FALSE; + } + + if (i < header->n_local_entries) + { + if (!entry->local) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_DIRECTORY, + "Too few local directory entries"); + return FALSE; + } + + if (!is_aligned (entry->offset)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_DIRECTORY, + "Misaligned entry"); + return FALSE; + } + + if (!validate_blob (ctx, entry->offset, error)) + return FALSE; + } + else + { + if (entry->local) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_DIRECTORY, + "Too many local directory entries"); + return FALSE; + } + + if (!validate_name (typelib, "namespace", typelib->data, entry->offset, error)) + return FALSE; + } + } + + return TRUE; +} + +static gboolean +validate_attributes (ValidateContext *ctx, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + Header *header = (Header *)typelib->data; + + if (header->size < header->attributes + header->n_attributes * sizeof (AttributeBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + return TRUE; +} + +static void +prefix_with_context (GError **error, + const char *section, + ValidateContext *ctx) +{ + GString *str; + GSList *link; + char *buf; + + link = ctx->context_stack; + if (!link) + { + g_prefix_error (error, "In %s:", section); + return; + } + + str = g_string_new (NULL); + + for (; link; link = link->next) + { + g_string_append (str, link->data); + if (link->next) + g_string_append_c (str, '/'); + } + g_string_append_c (str, ')'); + buf = g_string_free (str, FALSE); + g_prefix_error (error, "In %s (Context: %s): ", section, buf); + g_free (buf); +} + +/** + * g_typelib_validate: + * @typelib: TODO + * @error: TODO + * + * TODO + * + * Returns: TODO + */ +gboolean +g_typelib_validate (GITypelib *typelib, + GError **error) +{ + ValidateContext ctx; + ctx.typelib = typelib; + ctx.context_stack = NULL; + + if (!validate_header (&ctx, error)) + { + prefix_with_context (error, "In header", &ctx); + return FALSE; + } + + if (!validate_directory (&ctx, error)) + { + prefix_with_context (error, "directory", &ctx); + return FALSE; + } + + if (!validate_attributes (&ctx, error)) + { + prefix_with_context (error, "attributes", &ctx); + return FALSE; + } + + return TRUE; +} + +/** + * g_typelib_error_quark: + * + * TODO + * + * Returns: TODO + */ +GQuark +g_typelib_error_quark (void) +{ + static GQuark quark = 0; + if (quark == 0) + quark = g_quark_from_static_string ("g-typelib-error-quark"); + return quark; +} + +static GSList *library_paths; + +/** + * g_irepository_prepend_library_path: + * @directory: (type filename): a single directory to scan for shared libraries + * + * Prepends @directory to the search path that is used to + * search shared libraries referenced by imported namespaces. + * Multiple calls to this function all contribute to the final + * list of paths. + * The list of paths is unique and shared for all #GIRepository + * instances across the process, but it doesn't affect namespaces + * imported before the call. + * + * If the library is not found in the directories configured + * in this way, loading will fall back to the system library + * path (ie. LD_LIBRARY_PATH and DT_RPATH in ELF systems). + * See the documentation of your dynamic linker for full details. + * + * Since: 1.36 + */ +void +g_irepository_prepend_library_path (const char *directory) +{ + library_paths = g_slist_prepend (library_paths, + g_strdup (directory)); +} + +/* Note on the GModule flags used by this function: + + * Glade's autoconnect feature and OpenGL's extension mechanism + * as used by Clutter rely on g_module_open(NULL) to work as a means of + * accessing the app's symbols. This keeps us from using + * G_MODULE_BIND_LOCAL. BIND_LOCAL may have other issues as well; + * in general libraries are not expecting multiple copies of + * themselves and are not expecting to be unloaded. So we just + * load modules globally for now. + */ +static GModule * +load_one_shared_library (const char *shlib) +{ + GSList *p; + GModule *m; + +#ifdef __APPLE__ + /* On macOS, @-prefixed shlib paths (@rpath, @executable_path, @loader_path) + need to be treated as absolute; trying to combine them with a + configured library path produces a mangled path that is unresolvable + and may cause unintended side effects (such as loading the library + from a fall-back location on macOS 12.0.1). + */ + if (!g_path_is_absolute (shlib) && !g_str_has_prefix (shlib, "@")) +#else + if (!g_path_is_absolute (shlib)) +#endif + { + /* First try in configured library paths */ + for (p = library_paths; p; p = p->next) + { + char *path = g_build_filename (p->data, shlib, NULL); + + m = g_module_open (path, G_MODULE_BIND_LAZY); + + g_free (path); + if (m != NULL) + return m; + } + } + + /* Then try loading from standard paths */ + /* Do not attempt to fix up shlib to replace .la with .so: + it's done by GModule anyway. + */ + return g_module_open (shlib, G_MODULE_BIND_LAZY); +} + +static void +_g_typelib_do_dlopen (GITypelib *typelib) +{ + Header *header; + const char *shlib_str; + + header = (Header *) typelib->data; + /* note that NULL shlib means to open the main app, which is allowed */ + if (header->shared_library) + shlib_str = g_typelib_get_string (typelib, header->shared_library); + else + shlib_str = NULL; + + if (shlib_str != NULL && shlib_str[0] != '\0') + { + gchar **shlibs; + gint i; + + /* shared-library is a comma-separated list of libraries */ + shlibs = g_strsplit (shlib_str, ",", 0); + + /* We load all passed libs unconditionally as if the same library is loaded + * again with g_module_open(), the same file handle will be returned. See bug: + * http://bugzilla.gnome.org/show_bug.cgi?id=555294 + */ + for (i = 0; shlibs[i]; i++) + { + GModule *module; + + module = load_one_shared_library (shlibs[i]); + + if (module == NULL) + { + g_warning ("Failed to load shared library '%s' referenced by the typelib: %s", + shlibs[i], g_module_error ()); + } + else + { + typelib->modules = g_list_append (typelib->modules, module); + } + } + + g_strfreev (shlibs); + } + else + { + /* If there's no shared-library entry for this module, assume that + * the module is for the application. Some of the hand-written .gir files + * in gobject-introspection don't have shared-library entries, but no one + * is really going to be calling g_module_symbol on them either. + */ + GModule *module = g_module_open (NULL, 0); + if (module == NULL) + g_warning ("gtypelib.c: Failed to g_module_open (NULL): %s", g_module_error ()); + else + typelib->modules = g_list_prepend (typelib->modules, module); + } +} + +static inline void +_g_typelib_ensure_open (GITypelib *typelib) +{ + if (typelib->open_attempted) + return; + typelib->open_attempted = TRUE; + _g_typelib_do_dlopen (typelib); +} + +/** + * g_typelib_new_from_memory: (skip) + * @memory: address of memory chunk containing the typelib + * @len: length of memory chunk containing the typelib + * @error: a #GError + * + * Creates a new #GITypelib from a memory location. The memory block + * pointed to by @typelib will be automatically g_free()d when the + * repository is destroyed. + * + * Returns: the new #GITypelib + */ +GITypelib * +g_typelib_new_from_memory (guint8 *memory, + gsize len, + GError **error) +{ + GITypelib *meta; + + if (!validate_header_basic (memory, len, error)) + return NULL; + + meta = g_slice_new0 (GITypelib); + meta->data = memory; + meta->len = len; + meta->owns_memory = TRUE; + meta->modules = NULL; + + return meta; +} + +/** + * g_typelib_new_from_const_memory: (skip) + * @memory: address of memory chunk containing the typelib + * @len: length of memory chunk containing the typelib + * @error: A #GError + * + * Creates a new #GITypelib from a memory location. + * + * Returns: the new #GITypelib + */ +GITypelib * +g_typelib_new_from_const_memory (const guchar *memory, + gsize len, + GError **error) +{ + GITypelib *meta; + + if (!validate_header_basic (memory, len, error)) + return NULL; + + meta = g_slice_new0 (GITypelib); + meta->data = (guchar *) memory; + meta->len = len; + meta->owns_memory = FALSE; + meta->modules = NULL; + + return meta; +} + +/** + * g_typelib_new_from_mapped_file: (skip) + * @mfile: a #GMappedFile, that will be free'd when the repository is destroyed + * @error: a #GError + * + * Creates a new #GITypelib from a #GMappedFile. + * + * Returns: the new #GITypelib + */ +GITypelib * +g_typelib_new_from_mapped_file (GMappedFile *mfile, + GError **error) +{ + GITypelib *meta; + guint8 *data = (guint8 *) g_mapped_file_get_contents (mfile); + gsize len = g_mapped_file_get_length (mfile); + + if (!validate_header_basic (data, len, error)) + return NULL; + + meta = g_slice_new0 (GITypelib); + meta->mfile = mfile; + meta->owns_memory = FALSE; + meta->data = data; + meta->len = len; + + return meta; +} + +/** + * g_typelib_free: + * @typelib: a #GITypelib + * + * Free a #GITypelib. + */ +void +g_typelib_free (GITypelib *typelib) +{ + if (typelib->mfile) + g_mapped_file_unref (typelib->mfile); + else + if (typelib->owns_memory) + g_free (typelib->data); + if (typelib->modules) + { + g_list_foreach (typelib->modules, (GFunc) (void *) g_module_close, NULL); + g_list_free (typelib->modules); + } + g_slice_free (GITypelib, typelib); +} + +/** + * g_typelib_get_namespace: + * @typelib: TODO + * + * TODO + * + * Returns: TODO + */ +const gchar * +g_typelib_get_namespace (GITypelib *typelib) +{ + return g_typelib_get_string (typelib, ((Header *) typelib->data)->namespace); +} + +/** + * g_typelib_symbol: + * @typelib: the typelib + * @symbol_name: name of symbol to be loaded + * @symbol: returns a pointer to the symbol value + * + * Loads a symbol from #GITypelib. + * + * Returns: #TRUE on success + */ +gboolean +g_typelib_symbol (GITypelib *typelib, const char *symbol_name, gpointer *symbol) +{ + GList *l; + + _g_typelib_ensure_open (typelib); + + /* + * The reason for having multiple modules dates from gir-repository + * when it was desired to inject code (accessors, etc.) into an + * existing library. In that situation, the first module listed + * will be the custom one, which overrides the main one. A bit + * inefficient, but the problem will go away when gir-repository + * does. + * + * For modules with no shared library, we dlopen'd the current + * process above. + */ + for (l = typelib->modules; l; l = l->next) + { + GModule *module = l->data; + + if (g_module_symbol (module, symbol_name, symbol)) + return TRUE; + } + + return FALSE; +} diff --git a/girepository/gitypelib.h b/girepository/gitypelib.h new file mode 100644 index 000000000..8c6c879b4 --- /dev/null +++ b/girepository/gitypelib.h @@ -0,0 +1,78 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Public typelib API + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +#include + +G_BEGIN_DECLS + +/** + * SECTION:gitypelib + * @title: GITypelib + * @short_description: TODO + * + * TODO + */ + +/** + * GITypelib: + * + * TODO + */ +typedef struct _GITypelib GITypelib; + +GI_AVAILABLE_IN_ALL +GITypelib * g_typelib_new_from_memory (guint8 *memory, + gsize len, + GError **error); + +GI_AVAILABLE_IN_ALL +GITypelib * g_typelib_new_from_const_memory (const guint8 *memory, + gsize len, + GError **error); + +GI_AVAILABLE_IN_ALL +GITypelib * g_typelib_new_from_mapped_file (GMappedFile *mfile, + GError **error); + +GI_AVAILABLE_IN_ALL +void g_typelib_free (GITypelib *typelib); + +GI_AVAILABLE_IN_ALL +gboolean g_typelib_symbol (GITypelib *typelib, + const gchar *symbol_name, + gpointer *symbol); + +GI_AVAILABLE_IN_ALL +const gchar * g_typelib_get_namespace (GITypelib *typelib); + + +G_END_DECLS diff --git a/girepository/gitypes.h b/girepository/gitypes.h new file mode 100644 index 000000000..704dbf054 --- /dev/null +++ b/girepository/gitypes.h @@ -0,0 +1,512 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: types + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +G_BEGIN_DECLS + +typedef struct _GIBaseInfoStub { + /*< private >*/ + gint32 dummy1; + gint32 dummy2; + gpointer dummy3; + gpointer dummy4; + gpointer dummy5; + guint32 dummy6; + guint32 dummy7; + gpointer padding[4]; +} GIBaseInfo; + +/** + * GICallableInfo: + * + * Represents a callable, either #GIFunctionInfo, #GICallbackInfo or + * #GIVFuncInfo. + */ +typedef GIBaseInfo GICallableInfo; + +/** + * GIFunctionInfo: + * + * Represents a function, eg arguments and return value. + */ +typedef GIBaseInfo GIFunctionInfo; + +/** + * SECTION:gicallbackinfo + * @title: GICallbackInfo + * @short_description: Struct representing a callback + * + * GICallbackInfo represents a callback. + */ + +/** + * GICallbackInfo: + * + * Represents a callback, eg arguments and return value. + */ +typedef GIBaseInfo GICallbackInfo; + +/** + * GIRegisteredTypeInfo: + * + * Represent a registered type. + */ +typedef GIBaseInfo GIRegisteredTypeInfo; + +/** + * GIStructInfo: + * + * Represents a struct. + */ +typedef GIBaseInfo GIStructInfo; + +/** + * GIUnionInfo: + * + * Represents a union. + */ +typedef GIBaseInfo GIUnionInfo; + +/** + * GIEnumInfo: + * + * Represents an enum or a flag. + */ +typedef GIBaseInfo GIEnumInfo; + +/** + * GIObjectInfo: + * + * Represents an object. + */ +typedef GIBaseInfo GIObjectInfo; + +/** + * GIInterfaceInfo: + * + * Represents an interface. + */ +typedef GIBaseInfo GIInterfaceInfo; + +/** + * GIConstantInfo: + * + * Represents a constant. + */ +typedef GIBaseInfo GIConstantInfo; + +/** + * SECTION:givalueinfo + * @title: GIValueInfo + * @short_description: Struct representing a value + * + * GIValueInfo represents a value. + */ + +/** + * GIValueInfo: + * + * Represents a enum value of a #GIEnumInfo. + */ +typedef GIBaseInfo GIValueInfo; + +/** + * GISignalInfo: + * + * Represents a signal. + */ +typedef GIBaseInfo GISignalInfo; + +/** + * GIVFuncInfo: + * + * Represents a virtual function. + */ +typedef GIBaseInfo GIVFuncInfo; + +/** + * GIPropertyInfo: + * + * Represents a property of a #GIObjectInfo or a #GIInterfaceInfo. + */ +typedef GIBaseInfo GIPropertyInfo; + +/** + * GIFieldInfo: + * + * Represents a field of a #GIStructInfo or a #GIUnionInfo. + */ +typedef GIBaseInfo GIFieldInfo; + +/** + * GIArgInfo: + * + * Represents an argument. + */ +typedef GIBaseInfo GIArgInfo; + +/** + * GITypeInfo: + * + * Represents type information, direction, transfer etc. + */ +typedef GIBaseInfo GITypeInfo; + +/** + * GIUnresolvedInfo: + * + * Represents a unresolved type in a typelib. + */ +typedef struct _GIUnresolvedInfo GIUnresolvedInfo; + +union _GIArgument +{ + gboolean v_boolean; + gint8 v_int8; + guint8 v_uint8; + gint16 v_int16; + guint16 v_uint16; + gint32 v_int32; + guint32 v_uint32; + gint64 v_int64; + guint64 v_uint64; + gfloat v_float; + gdouble v_double; + gshort v_short; + gushort v_ushort; + gint v_int; + guint v_uint; + glong v_long; + gulong v_ulong; + gssize v_ssize; + gsize v_size; + gchar * v_string; + gpointer v_pointer; +}; + +/** + * GIArgument: + * @v_boolean: TODO + * @v_int8: TODO + * @v_uint8: TODO + * @v_int16: TODO + * @v_uint16: TODO + * @v_int32: TODO + * @v_uint32: TODO + * @v_int64: TODO + * @v_uint64: TODO + * @v_float: TODO + * @v_double: TODO + * @v_short: TODO + * @v_ushort: TODO + * @v_int: TODO + * @v_uint: TODO + * @v_long: TODO + * @v_ulong: TODO + * @v_ssize: TODO + * @v_size: TODO + * @v_string: TODO + * @v_pointer: TODO + * + * Stores an argument of varying type + */ +typedef union _GIArgument GIArgument; + +/** + * GIInfoType: + * @GI_INFO_TYPE_INVALID: invalid type + * @GI_INFO_TYPE_FUNCTION: function, see #GIFunctionInfo + * @GI_INFO_TYPE_CALLBACK: callback, see #GIFunctionInfo + * @GI_INFO_TYPE_STRUCT: struct, see #GIStructInfo + * @GI_INFO_TYPE_BOXED: boxed, see #GIStructInfo or #GIUnionInfo + * @GI_INFO_TYPE_ENUM: enum, see #GIEnumInfo + * @GI_INFO_TYPE_FLAGS: flags, see #GIEnumInfo + * @GI_INFO_TYPE_OBJECT: object, see #GIObjectInfo + * @GI_INFO_TYPE_INTERFACE: interface, see #GIInterfaceInfo + * @GI_INFO_TYPE_CONSTANT: contant, see #GIConstantInfo + * @GI_INFO_TYPE_INVALID_0: deleted, used to be GI_INFO_TYPE_ERROR_DOMAIN. + * @GI_INFO_TYPE_UNION: union, see #GIUnionInfo + * @GI_INFO_TYPE_VALUE: enum value, see #GIValueInfo + * @GI_INFO_TYPE_SIGNAL: signal, see #GISignalInfo + * @GI_INFO_TYPE_VFUNC: virtual function, see #GIVFuncInfo + * @GI_INFO_TYPE_PROPERTY: GObject property, see #GIPropertyInfo + * @GI_INFO_TYPE_FIELD: struct or union field, see #GIFieldInfo + * @GI_INFO_TYPE_ARG: argument of a function or callback, see #GIArgInfo + * @GI_INFO_TYPE_TYPE: type information, see #GITypeInfo + * @GI_INFO_TYPE_UNRESOLVED: unresolved type, a type which is not present in + * the typelib, or any of its dependencies. + * + * The type of a GIBaseInfo struct. + */ +typedef enum +{ + GI_INFO_TYPE_INVALID, + GI_INFO_TYPE_FUNCTION, + GI_INFO_TYPE_CALLBACK, + GI_INFO_TYPE_STRUCT, + GI_INFO_TYPE_BOXED, + GI_INFO_TYPE_ENUM, /* 5 */ + GI_INFO_TYPE_FLAGS, + GI_INFO_TYPE_OBJECT, + GI_INFO_TYPE_INTERFACE, + GI_INFO_TYPE_CONSTANT, + GI_INFO_TYPE_INVALID_0, /* 10 */ + GI_INFO_TYPE_UNION, + GI_INFO_TYPE_VALUE, + GI_INFO_TYPE_SIGNAL, + GI_INFO_TYPE_VFUNC, + GI_INFO_TYPE_PROPERTY, /* 15 */ + GI_INFO_TYPE_FIELD, + GI_INFO_TYPE_ARG, + GI_INFO_TYPE_TYPE, + GI_INFO_TYPE_UNRESOLVED +} GIInfoType; + +/** + * GITransfer: + * @GI_TRANSFER_NOTHING: transfer nothing from the callee (function or the type + * instance the property belongs to) to the caller. The callee retains the + * ownership of the transfer and the caller doesn't need to do anything to free + * up the resources of this transfer. + * @GI_TRANSFER_CONTAINER: transfer the container (list, array, hash table) from + * the callee to the caller. The callee retains the ownership of the individual + * items in the container and the caller has to free up the container resources + * (g_list_free()/g_hash_table_destroy() etc) of this transfer. + * @GI_TRANSFER_EVERYTHING: transfer everything, eg the container and its + * contents from the callee to the caller. This is the case when the callee + * creates a copy of all the data it returns. The caller is responsible for + * cleaning up the container and item resources of this transfer. + * + * The transfer is the exchange of data between two parts, from the callee to + * the caller. The callee is either a function/method/signal or an + * object/interface where a property is defined. The caller is the side + * accessing a property or calling a function. + * #GITransfer specifies who's responsible for freeing the resources after the + * ownership transfer is complete. In case of a containing type such as a list, + * an array or a hash table the container itself is specified differently from + * the items within the container itself. Each container is freed differently, + * check the documentation for the types themselves for information on how to + * free them. + */ +typedef enum { + GI_TRANSFER_NOTHING, + GI_TRANSFER_CONTAINER, + GI_TRANSFER_EVERYTHING +} GITransfer; + +/** + * GIDirection: + * @GI_DIRECTION_IN: in argument. + * @GI_DIRECTION_OUT: out argument. + * @GI_DIRECTION_INOUT: in and out argument. + * + * The direction of a #GIArgInfo. + */ +typedef enum { + GI_DIRECTION_IN, + GI_DIRECTION_OUT, + GI_DIRECTION_INOUT +} GIDirection; + +/** + * GIScopeType: + * @GI_SCOPE_TYPE_INVALID: The argument is not of callback type. + * @GI_SCOPE_TYPE_CALL: The callback and associated user_data is only + * used during the call to this function. + * @GI_SCOPE_TYPE_ASYNC: The callback and associated user_data is + * only used until the callback is invoked, and the callback. + * is invoked always exactly once. + * @GI_SCOPE_TYPE_NOTIFIED: The callback and associated + * user_data is used until the caller is notfied via the destroy_notify. + * @GI_SCOPE_TYPE_FOREVER: The callback and associated user_data is + * used until the process terminates + * + * Scope type of a #GIArgInfo representing callback, determines how the + * callback is invoked and is used to decided when the invoke structs + * can be freed. + */ +typedef enum { + GI_SCOPE_TYPE_INVALID, + GI_SCOPE_TYPE_CALL, + GI_SCOPE_TYPE_ASYNC, + GI_SCOPE_TYPE_NOTIFIED, + GI_SCOPE_TYPE_FOREVER +} GIScopeType; + +/** + * GITypeTag: + * @GI_TYPE_TAG_VOID: void + * @GI_TYPE_TAG_BOOLEAN: boolean + * @GI_TYPE_TAG_INT8: 8-bit signed integer + * @GI_TYPE_TAG_UINT8: 8-bit unsigned integer + * @GI_TYPE_TAG_INT16: 16-bit signed integer + * @GI_TYPE_TAG_UINT16: 16-bit unsigned integer + * @GI_TYPE_TAG_INT32: 32-bit signed integer + * @GI_TYPE_TAG_UINT32: 32-bit unsigned integer + * @GI_TYPE_TAG_INT64: 64-bit signed integer + * @GI_TYPE_TAG_UINT64: 64-bit unsigned integer + * @GI_TYPE_TAG_FLOAT: float + * @GI_TYPE_TAG_DOUBLE: double floating point + * @GI_TYPE_TAG_GTYPE: a #GType + * @GI_TYPE_TAG_UTF8: a UTF-8 encoded string + * @GI_TYPE_TAG_FILENAME: a filename, encoded in the same encoding + * as the native filesystem is using. + * @GI_TYPE_TAG_ARRAY: an array + * @GI_TYPE_TAG_INTERFACE: an extended interface object + * @GI_TYPE_TAG_GLIST: a #GList + * @GI_TYPE_TAG_GSLIST: a #GSList + * @GI_TYPE_TAG_GHASH: a #GHashTable + * @GI_TYPE_TAG_ERROR: a #GError + * @GI_TYPE_TAG_UNICHAR: Unicode character + * + * The type tag of a #GITypeInfo. + */ +typedef enum { + /* Basic types */ + GI_TYPE_TAG_VOID = 0, + GI_TYPE_TAG_BOOLEAN = 1, + GI_TYPE_TAG_INT8 = 2, /* Start of GI_TYPE_TAG_IS_NUMERIC types */ + GI_TYPE_TAG_UINT8 = 3, + GI_TYPE_TAG_INT16 = 4, + GI_TYPE_TAG_UINT16 = 5, + GI_TYPE_TAG_INT32 = 6, + GI_TYPE_TAG_UINT32 = 7, + GI_TYPE_TAG_INT64 = 8, + GI_TYPE_TAG_UINT64 = 9, + GI_TYPE_TAG_FLOAT = 10, + GI_TYPE_TAG_DOUBLE = 11, /* End of numeric types */ + GI_TYPE_TAG_GTYPE = 12, + GI_TYPE_TAG_UTF8 = 13, + GI_TYPE_TAG_FILENAME = 14, + /* Non-basic types; compare with GI_TYPE_TAG_IS_BASIC */ + GI_TYPE_TAG_ARRAY = 15, /* container (see GI_TYPE_TAG_IS_CONTAINER) */ + GI_TYPE_TAG_INTERFACE = 16, + GI_TYPE_TAG_GLIST = 17, /* container */ + GI_TYPE_TAG_GSLIST = 18, /* container */ + GI_TYPE_TAG_GHASH = 19, /* container */ + GI_TYPE_TAG_ERROR = 20, + /* Another basic type */ + GI_TYPE_TAG_UNICHAR = 21 + /* Note - there is currently only room for 32 tags */ +} GITypeTag; + +/** + * GI_TYPE_TAG_N_TYPES: + * + * TODO + */ +#define GI_TYPE_TAG_N_TYPES (GI_TYPE_TAG_UNICHAR+1) + +#ifndef __GTK_DOC_IGNORE__ +/* These were removed and no longer appear in the typelib; + * instead, the machine-specific versions like INT32 are + * always used. + */ +#define GI_TYPE_TAG_SHORT GI_TYPE_TAG_SHORT_WAS_REMOVED +#define GI_TYPE_TAG_INT GI_TYPE_TAG_INT_WAS_REMOVED +#define GI_TYPE_TAG_LONG GI_TYPE_TAG_LONG_WAS_REMOVED +#endif + +/** + * GIArrayType: + * @GI_ARRAY_TYPE_C: a C array, char[] for instance + * @GI_ARRAY_TYPE_ARRAY: a @GArray array + * @GI_ARRAY_TYPE_PTR_ARRAY: a #GPtrArray array + * @GI_ARRAY_TYPE_BYTE_ARRAY: a #GByteArray array + * + * The type of array in a #GITypeInfo. + */ +typedef enum { + GI_ARRAY_TYPE_C, + GI_ARRAY_TYPE_ARRAY, + GI_ARRAY_TYPE_PTR_ARRAY, + GI_ARRAY_TYPE_BYTE_ARRAY +} GIArrayType; + +/** + * GIFieldInfoFlags: + * @GI_FIELD_IS_READABLE: field is readable. + * @GI_FIELD_IS_WRITABLE: field is writable. + * + * Flags for a #GIFieldInfo. + */ + +typedef enum +{ + GI_FIELD_IS_READABLE = 1 << 0, + GI_FIELD_IS_WRITABLE = 1 << 1 +} GIFieldInfoFlags; + +/** + * GIVFuncInfoFlags: + * @GI_VFUNC_MUST_CHAIN_UP: chains up to the parent type + * @GI_VFUNC_MUST_OVERRIDE: overrides + * @GI_VFUNC_MUST_NOT_OVERRIDE: does not override + * @GI_VFUNC_THROWS: Includes a #GError + * + * Flags of a #GIVFuncInfo struct. + */ +typedef enum +{ + GI_VFUNC_MUST_CHAIN_UP = 1 << 0, + GI_VFUNC_MUST_OVERRIDE = 1 << 1, + GI_VFUNC_MUST_NOT_OVERRIDE = 1 << 2, + GI_VFUNC_THROWS = 1 << 3 +} GIVFuncInfoFlags; + +/** + * GIFunctionInfoFlags: + * @GI_FUNCTION_IS_METHOD: is a method. + * @GI_FUNCTION_IS_CONSTRUCTOR: is a constructor. + * @GI_FUNCTION_IS_GETTER: is a getter of a #GIPropertyInfo. + * @GI_FUNCTION_IS_SETTER: is a setter of a #GIPropertyInfo. + * @GI_FUNCTION_WRAPS_VFUNC: represents a virtual function. + * @GI_FUNCTION_THROWS: the function may throw an error. + * + * Flags for a #GIFunctionInfo struct. + */ +typedef enum +{ + GI_FUNCTION_IS_METHOD = 1 << 0, + GI_FUNCTION_IS_CONSTRUCTOR = 1 << 1, + GI_FUNCTION_IS_GETTER = 1 << 2, + GI_FUNCTION_IS_SETTER = 1 << 3, + GI_FUNCTION_WRAPS_VFUNC = 1 << 4, + GI_FUNCTION_THROWS = 1 << 5 +} GIFunctionInfoFlags; + +#ifndef __GI_SCANNER__ +#ifndef __GTK_DOC_IGNORE__ +/* backwards compatibility */ +typedef GIArgument GArgument; +typedef struct _GITypelib GTypelib; +#endif +#endif + +G_END_DECLS diff --git a/girepository/giunioninfo.c b/girepository/giunioninfo.c new file mode 100644 index 000000000..8cd1cffa4 --- /dev/null +++ b/girepository/giunioninfo.c @@ -0,0 +1,326 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Union implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" +#include "giunioninfo.h" + +/** + * SECTION:giunioninfo + * @title: GIUnionInfo + * @short_description: Struct representing a union. + * + * GIUnionInfo represents a union type. + * + * A union has methods and fields. Unions can optionally have a + * discriminator, which is a field deciding what type of real union + * fields is valid for specified instance. + */ + +/** + * g_union_info_get_n_fields: + * @info: a #GIUnionInfo + * + * Obtain the number of fields this union has. + * + * Returns: number of fields + */ +gint +g_union_info_get_n_fields (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_fields; +} + +/** + * g_union_info_get_field: + * @info: a #GIUnionInfo + * @n: a field index + * + * Obtain the type information for field with specified index. + * + * Returns: (transfer full): the #GIFieldInfo, free it with g_base_info_unref() + * when done. + */ +GIFieldInfo * +g_union_info_get_field (GIUnionInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header = (Header *)rinfo->typelib->data; + + return (GIFieldInfo *) g_info_new (GI_INFO_TYPE_FIELD, (GIBaseInfo*)info, rinfo->typelib, + rinfo->offset + header->union_blob_size + + n * header->field_blob_size); +} + +/** + * g_union_info_get_n_methods: + * @info: a #GIUnionInfo + * + * Obtain the number of methods this union has. + * + * Returns: number of methods + */ +gint +g_union_info_get_n_methods (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_functions; +} + +/** + * g_union_info_get_method: + * @info: a #GIUnionInfo + * @n: a method index + * + * Obtain the type information for method with specified index. + * + * Returns: (transfer full): the #GIFunctionInfo, free it with g_base_info_unref() + * when done. + */ +GIFunctionInfo * +g_union_info_get_method (GIUnionInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + Header *header = (Header *)rinfo->typelib->data; + gint offset; + + offset = rinfo->offset + header->union_blob_size + + blob->n_fields * header->field_blob_size + + n * header->function_blob_size; + return (GIFunctionInfo *) g_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_union_info_is_discriminated: + * @info: a #GIUnionInfo + * + * Return true if this union contains discriminator field. + * + * Returns: %TRUE if this is a discriminated union, %FALSE otherwise + */ +gboolean +g_union_info_is_discriminated (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->discriminated; +} + +/** + * g_union_info_get_discriminator_offset: + * @info: a #GIUnionInfo + * + * Returns offset of the discriminator field in the structure. + * + * Returns: offset in bytes of the discriminator + */ +gint +g_union_info_get_discriminator_offset (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->discriminator_offset; +} + +/** + * g_union_info_get_discriminator_type: + * @info: a #GIUnionInfo + * + * Obtain the type information of the union discriminator. + * + * Returns: (transfer full): the #GITypeInfo, free it with g_base_info_unref() + * when done. + */ +GITypeInfo * +g_union_info_get_discriminator_type (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + + return _g_type_info_new ((GIBaseInfo*)info, rinfo->typelib, rinfo->offset + 24); +} + +/** + * g_union_info_get_discriminator: + * @info: a #GIUnionInfo + * @n: a union field index + * + * Obtain discriminator value assigned for n-th union field, i.e. n-th + * union field is the active one if discriminator contains this + * constant. + * + * Returns: (transfer full): the #GIConstantInfo, free it with g_base_info_unref() + * when done. + */ +GIConstantInfo * +g_union_info_get_discriminator (GIUnionInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->discriminated) + { + Header *header = (Header *)rinfo->typelib->data; + gint offset; + + offset = rinfo->offset + header->union_blob_size + + blob->n_fields * header->field_blob_size + + blob->n_functions * header->function_blob_size + + n * header->constant_blob_size; + + return (GIConstantInfo *) g_info_new (GI_INFO_TYPE_CONSTANT, (GIBaseInfo*)info, + rinfo->typelib, offset); + } + + return NULL; +} + +/** + * g_union_info_find_method: + * @info: a #GIUnionInfo + * @name: a method name + * + * Obtain the type information for method named @name. + * + * Returns: (transfer full): the #GIFunctionInfo, free it with g_base_info_unref() + * when done. + */ +GIFunctionInfo * +g_union_info_find_method (GIUnionInfo *info, + const gchar *name) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header = (Header *)rinfo->typelib->data; + UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->union_blob_size + + blob->n_fields * header->field_blob_size; + + return _g_base_info_find_method ((GIBaseInfo*)info, offset, blob->n_functions, name); +} + +/** + * g_union_info_get_size: + * @info: a #GIUnionInfo + * + * Obtain the total size of the union. + * + * Returns: size of the union in bytes + */ +gsize +g_union_info_get_size (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->size; +} + +/** + * g_union_info_get_alignment: + * @info: a #GIUnionInfo + * + * Obtain the required alignment of the union. + * + * Returns: required alignment in bytes + */ +gsize +g_union_info_get_alignment (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->alignment; +} + +/** + * g_union_info_get_copy_function: + * @info: a union information blob + * + * Retrieves the name of the copy function for @info, if any is set. + * + * Returns: (transfer none) (nullable): the name of the copy function + * + * Since: 1.76 + */ +const char * +g_union_info_get_copy_function (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_UNION_INFO (info), NULL); + + blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->copy_func) + return g_typelib_get_string (rinfo->typelib, blob->copy_func); + + return NULL; +} + +/** + * g_union_info_get_free_function: + * @info: a union information blob + * + * Retrieves the name of the free function for @info, if any is set. + * + * Returns: (transfer none) (nullable): the name of the free function + * + * Since: 1.76 + */ +const char * +g_union_info_get_free_function (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_UNION_INFO (info), NULL); + + blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->free_func) + return g_typelib_get_string (rinfo->typelib, blob->free_func); + + return NULL; +} diff --git a/girepository/giunioninfo.h b/girepository/giunioninfo.h new file mode 100644 index 000000000..1c5b40599 --- /dev/null +++ b/girepository/giunioninfo.h @@ -0,0 +1,87 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Union + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_UNION_INFO + * @info: an info structure + * + * Checks if @info is a #GIUnionInfo. + */ +#define GI_IS_UNION_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_UNION) + +GI_AVAILABLE_IN_ALL +gint g_union_info_get_n_fields (GIUnionInfo *info); + +GI_AVAILABLE_IN_ALL +GIFieldInfo * g_union_info_get_field (GIUnionInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +gint g_union_info_get_n_methods (GIUnionInfo *info); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_union_info_get_method (GIUnionInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +gboolean g_union_info_is_discriminated (GIUnionInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_union_info_get_discriminator_offset (GIUnionInfo *info); + +GI_AVAILABLE_IN_ALL +GITypeInfo * g_union_info_get_discriminator_type (GIUnionInfo *info); + +GI_AVAILABLE_IN_ALL +GIConstantInfo * g_union_info_get_discriminator (GIUnionInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_union_info_find_method (GIUnionInfo *info, + const gchar *name); + +GI_AVAILABLE_IN_ALL +gsize g_union_info_get_size (GIUnionInfo *info); + +GI_AVAILABLE_IN_ALL +gsize g_union_info_get_alignment (GIUnionInfo *info); + +GI_AVAILABLE_IN_ALL +const char * g_union_info_get_copy_function (GIUnionInfo *info); + +GI_AVAILABLE_IN_ALL +const char * g_union_info_get_free_function (GIUnionInfo *info); + +G_END_DECLS diff --git a/girepository/givfuncinfo.c b/girepository/givfuncinfo.c new file mode 100644 index 000000000..891a843fc --- /dev/null +++ b/girepository/givfuncinfo.c @@ -0,0 +1,348 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Virtual Function implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include "config.h" + +#include + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" +#include "givfuncinfo.h" + +/** + * SECTION:givfuncinfo + * @title: GIVFuncInfo + * @short_description: Struct representing a virtual function + * + * GIVfuncInfo represents a virtual function. + * + * A virtual function is a callable object that belongs to either a + * #GIObjectInfo or a #GIInterfaceInfo. + */ + +GIVFuncInfo * +_g_base_info_find_vfunc (GIRealInfo *rinfo, + guint32 offset, + gint n_vfuncs, + const gchar *name) +{ + /* FIXME hash */ + Header *header = (Header *)rinfo->typelib->data; + gint i; + + for (i = 0; i < n_vfuncs; i++) + { + VFuncBlob *fblob = (VFuncBlob *)&rinfo->typelib->data[offset]; + const gchar *fname = (const gchar *)&rinfo->typelib->data[fblob->name]; + + if (strcmp (name, fname) == 0) + return (GIVFuncInfo *) g_info_new (GI_INFO_TYPE_VFUNC, (GIBaseInfo*) rinfo, + rinfo->typelib, offset); + + offset += header->vfunc_blob_size; + } + + return NULL; +} + +/** + * g_vfunc_info_get_flags: + * @info: a #GIVFuncInfo + * + * Obtain the flags for this virtual function info. See #GIVFuncInfoFlags for + * more information about possible flag values. + * + * Returns: the flags + */ +GIVFuncInfoFlags +g_vfunc_info_get_flags (GIVFuncInfo *info) +{ + GIVFuncInfoFlags flags; + GIRealInfo *rinfo = (GIRealInfo *)info; + VFuncBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_VFUNC_INFO (info), 0); + + blob = (VFuncBlob *)&rinfo->typelib->data[rinfo->offset]; + + flags = 0; + + if (blob->must_chain_up) + flags = flags | GI_VFUNC_MUST_CHAIN_UP; + + if (blob->must_be_implemented) + flags = flags | GI_VFUNC_MUST_OVERRIDE; + + if (blob->must_not_be_implemented) + flags = flags | GI_VFUNC_MUST_NOT_OVERRIDE; + + if (blob->throws) + flags = flags | GI_VFUNC_THROWS; + + return flags; +} + +/** + * g_vfunc_info_get_offset: + * @info: a #GIVFuncInfo + * + * Obtain the offset of the function pointer in the class struct. The value + * 0xFFFF indicates that the struct offset is unknown. + * + * Returns: the struct offset or 0xFFFF if it's unknown + */ +gint +g_vfunc_info_get_offset (GIVFuncInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + VFuncBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_VFUNC_INFO (info), 0); + + blob = (VFuncBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->struct_offset; +} + +/** + * g_vfunc_info_get_signal: + * @info: a #GIVFuncInfo + * + * Obtain the signal for the virtual function if one is set. + * The signal comes from the object or interface to which + * this virtual function belongs. + * + * Returns: (transfer full): the signal or %NULL if none set + */ +GISignalInfo * +g_vfunc_info_get_signal (GIVFuncInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + VFuncBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_VFUNC_INFO (info), 0); + + blob = (VFuncBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->class_closure) + return g_interface_info_get_signal ((GIInterfaceInfo *)rinfo->container, blob->signal); + + return NULL; +} + +/** + * g_vfunc_info_get_invoker: + * @info: a #GIVFuncInfo + * + * If this virtual function has an associated invoker method, this + * method will return it. An invoker method is a C entry point. + * + * Not all virtuals will have invokers. + * + * Returns: (transfer full): the #GIVFuncInfo or %NULL. Free it with + * g_base_info_unref() when done. + */ +GIFunctionInfo * +g_vfunc_info_get_invoker (GIVFuncInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + VFuncBlob *blob; + GIBaseInfo *container; + GIInfoType parent_type; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_VFUNC_INFO (info), 0); + + blob = (VFuncBlob *)&rinfo->typelib->data[rinfo->offset]; + + /* 1023 = 0x3ff is the maximum of the 10 bits for invoker index */ + if (blob->invoker == 1023) + return NULL; + + container = rinfo->container; + parent_type = g_base_info_get_type (container); + if (parent_type == GI_INFO_TYPE_OBJECT) + return g_object_info_get_method ((GIObjectInfo*)container, blob->invoker); + else if (parent_type == GI_INFO_TYPE_INTERFACE) + return g_interface_info_get_method ((GIInterfaceInfo*)container, blob->invoker); + else + g_assert_not_reached (); +} + +/** + * g_vfunc_info_get_address: + * @info: a #GIVFuncInfo + * @implementor_gtype: #GType implementing this virtual function + * @error: return location for a #GError + * + * This method will look up where inside the type struct of @implementor_gtype + * is the implementation for @info. + * + * Returns: address to a function or %NULL if an error happened + */ +gpointer +g_vfunc_info_get_address (GIVFuncInfo *vfunc_info, + GType implementor_gtype, + GError **error) +{ + GIBaseInfo *container_info; + GIInterfaceInfo *interface_info; + GIObjectInfo *object_info; + GIStructInfo *struct_info; + GIFieldInfo *field_info = NULL; + int length, i, offset; + gpointer implementor_class, implementor_vtable; + gpointer func = NULL; + + container_info = g_base_info_get_container (vfunc_info); + if (g_base_info_get_type (container_info) == GI_INFO_TYPE_OBJECT) + { + object_info = (GIObjectInfo*) container_info; + interface_info = NULL; + struct_info = g_object_info_get_class_struct (object_info); + } + else + { + interface_info = (GIInterfaceInfo*) container_info; + object_info = NULL; + struct_info = g_interface_info_get_iface_struct (interface_info); + } + + length = g_struct_info_get_n_fields (struct_info); + for (i = 0; i < length; i++) + { + field_info = g_struct_info_get_field (struct_info, i); + + if (strcmp (g_base_info_get_name ( (GIBaseInfo*) field_info), + g_base_info_get_name ( (GIBaseInfo*) vfunc_info)) != 0) { + g_base_info_unref (field_info); + field_info = NULL; + continue; + } + + break; + } + + if (field_info == NULL) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_SYMBOL_NOT_FOUND, + "Couldn't find struct field for this vfunc"); + goto out; + } + + implementor_class = g_type_class_ref (implementor_gtype); + + if (object_info) + { + implementor_vtable = implementor_class; + } + else + { + GType interface_type; + + interface_type = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo*) interface_info); + implementor_vtable = g_type_interface_peek (implementor_class, interface_type); + } + + offset = g_field_info_get_offset (field_info); + func = *(gpointer*) G_STRUCT_MEMBER_P (implementor_vtable, offset); + g_type_class_unref (implementor_class); + g_base_info_unref (field_info); + + if (func == NULL) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_SYMBOL_NOT_FOUND, + "Class %s doesn't implement %s", + g_type_name (implementor_gtype), + g_base_info_get_name ( (GIBaseInfo*) vfunc_info)); + goto out; + } + + out: + g_base_info_unref ((GIBaseInfo*) struct_info); + + return func; +} + +/** + * g_vfunc_info_invoke: (skip) + * @info: a #GIVFuncInfo describing the virtual function to invoke + * @implementor: #GType of the type that implements this virtual function + * @in_args: (array length=n_in_args): an array of #GIArguments, one for each in + * parameter of @info. If there are no in parameter, @in_args + * can be %NULL + * @n_in_args: the length of the @in_args array + * @out_args: (array length=n_out_args): an array of #GIArguments, one for each out + * parameter of @info. If there are no out parameters, @out_args + * may be %NULL + * @n_out_args: the length of the @out_args array + * @return_value: return location for the return value of the + * function. If the function returns void, @return_value may be + * %NULL + * @error: return location for detailed error information, or %NULL + * + * Invokes the function described in @info with the given + * arguments. Note that inout parameters must appear in both + * argument lists. + * + * Returns: %TRUE if the function has been invoked, %FALSE if an + * error occurred. + */ +gboolean +g_vfunc_info_invoke (GIVFuncInfo *info, + GType implementor, + const GIArgument *in_args, + int n_in_args, + const GIArgument *out_args, + int n_out_args, + GIArgument *return_value, + GError **error) +{ + gpointer func; + + func = g_vfunc_info_get_address (info, implementor, error); + if (*error != NULL) + return FALSE; + + return g_callable_info_invoke ((GICallableInfo*) info, + func, + in_args, + n_in_args, + out_args, + n_out_args, + return_value, + TRUE, + FALSE, + error); +} diff --git a/girepository/givfuncinfo.h b/girepository/givfuncinfo.h new file mode 100644 index 000000000..a1ec37ce6 --- /dev/null +++ b/girepository/givfuncinfo.h @@ -0,0 +1,71 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Virtual Functions + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#pragma once + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_VFUNC_INFO + * @info: an info structure + * + * Checks if @info is a #GIVfuncInfo. + */ +#define GI_IS_VFUNC_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_VFUNC) + +GI_AVAILABLE_IN_ALL +GIVFuncInfoFlags g_vfunc_info_get_flags (GIVFuncInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_vfunc_info_get_offset (GIVFuncInfo *info); + +GI_AVAILABLE_IN_ALL +GISignalInfo * g_vfunc_info_get_signal (GIVFuncInfo *info); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_vfunc_info_get_invoker (GIVFuncInfo *info); + +GI_AVAILABLE_IN_ALL +gpointer g_vfunc_info_get_address (GIVFuncInfo *info, + GType implementor_gtype, + GError **error); + +GI_AVAILABLE_IN_ALL +gboolean g_vfunc_info_invoke (GIVFuncInfo *info, + GType implementor, + const GIArgument *in_args, + int n_in_args, + const GIArgument *out_args, + int n_out_args, + GIArgument *return_value, + GError **error); + +G_END_DECLS diff --git a/girepository/gthash-test.c b/girepository/gthash-test.c new file mode 100644 index 000000000..9ae3f5d7e --- /dev/null +++ b/girepository/gthash-test.c @@ -0,0 +1,67 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Test typelib hashing + * + * Copyright (C) 2010 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include +#include "gitypelib-internal.h" + +static void +test_build_retrieve (void) +{ + GITypelibHashBuilder *builder; + guint32 bufsize; + guint8* buf; + + builder = _gi_typelib_hash_builder_new (); + + _gi_typelib_hash_builder_add_string (builder, "Action", 0); + _gi_typelib_hash_builder_add_string (builder, "ZLibDecompressor", 42); + _gi_typelib_hash_builder_add_string (builder, "VolumeMonitor", 9); + _gi_typelib_hash_builder_add_string (builder, "FileMonitorFlags", 31); + + if (!_gi_typelib_hash_builder_prepare (builder)) + g_assert_not_reached (); + + bufsize = _gi_typelib_hash_builder_get_buffer_size (builder); + + buf = g_malloc (bufsize); + + _gi_typelib_hash_builder_pack (builder, buf, bufsize); + + _gi_typelib_hash_builder_destroy (builder); + + g_assert (_gi_typelib_hash_search (buf, "Action", 4) == 0); + g_assert (_gi_typelib_hash_search (buf, "ZLibDecompressor", 4) == 42); + g_assert (_gi_typelib_hash_search (buf, "VolumeMonitor", 4) == 9); + g_assert (_gi_typelib_hash_search (buf, "FileMonitorFlags", 4) == 31); +} + +int +main(int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/gthash/build-retrieve", test_build_retrieve); + + return g_test_run (); +} + diff --git a/girepository/gthash.c b/girepository/gthash.c new file mode 100644 index 000000000..8a0ee305d --- /dev/null +++ b/girepository/gthash.c @@ -0,0 +1,225 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Typelib hashing + * + * Copyright (C) 2010 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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 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. + */ + +#include +#include +#include + +#include "cmph/cmph.h" +#include "gitypelib-internal.h" + +#define ALIGN_VALUE(this, boundary) \ + (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1))) + +/* + * String hashing in the typelib. We have a set of static (fixed) strings, + * and given one, we need to find its index number. This problem is perfect + * hashing: http://en.wikipedia.org/wiki/Perfect_hashing + * + * I chose CMPH (http://cmph.sourceforge.net/) as it seemed high + * quality, well documented, and easy to embed. + * + * CMPH provides a number of algorithms; I chose BDZ, because while CHD + * appears to be the "best", the simplicitly of BDZ appealed, and really, + * we're only talking about thousands of strings here, not millions, so + * a few microseconds is no big deal. + * + * In memory, the format is: + * INT32 mph_size + * MPH (mph_size bytes) + * (padding for alignment to uint32 if necessary) + * INDEX (array of guint16) + * + * Because BDZ is not order preserving, we need a lookaside table which + * maps the hash value into the directory index. + */ + +struct _GITypelibHashBuilder { + gboolean prepared; + gboolean buildable; + cmph_t *c; + GHashTable *strings; + guint32 dirmap_offset; + guint32 packed_size; +}; + +GITypelibHashBuilder * +_gi_typelib_hash_builder_new (void) +{ + GITypelibHashBuilder *builder = g_slice_new0 (GITypelibHashBuilder); + builder->c = NULL; + builder->strings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + return builder; +} + +void +_gi_typelib_hash_builder_add_string (GITypelibHashBuilder *builder, + const char *str, + guint16 value) +{ + g_return_if_fail (builder->c == NULL); + g_hash_table_insert (builder->strings, g_strdup (str), GUINT_TO_POINTER ((guint) value)); +} + +gboolean +_gi_typelib_hash_builder_prepare (GITypelibHashBuilder *builder) +{ + char **strs; + GHashTableIter hashiter; + gpointer key, value; + cmph_io_adapter_t *io; + cmph_config_t *config; + guint32 num_elts; + guint32 offset; + guint i; + + if (builder->prepared) + return builder->buildable; + g_assert (builder->c == NULL); + + num_elts = g_hash_table_size (builder->strings); + g_assert (num_elts <= 65536); + + strs = (char**) g_new (char *, num_elts + 1); + + i = 0; + g_hash_table_iter_init (&hashiter, builder->strings); + while (g_hash_table_iter_next (&hashiter, &key, &value)) + { + const char *str = key; + + strs[i++] = g_strdup (str); + } + strs[i++] = NULL; + + io = cmph_io_vector_adapter (strs, num_elts); + config = cmph_config_new (io); + cmph_config_set_algo (config, CMPH_BDZ); + + builder->c = cmph_new (config); + builder->prepared = TRUE; + if (!builder->c) + { + builder->buildable = FALSE; + goto out; + } + builder->buildable = TRUE; + g_assert (cmph_size (builder->c) == num_elts); + + /* Pack a size counter at front */ + offset = sizeof(guint32) + cmph_packed_size (builder->c); + builder->dirmap_offset = ALIGN_VALUE (offset, 4); + builder->packed_size = builder->dirmap_offset + (num_elts * sizeof(guint16)); + out: + cmph_config_destroy (config); + cmph_io_vector_adapter_destroy (io); + return builder->buildable; +} + +guint32 +_gi_typelib_hash_builder_get_buffer_size (GITypelibHashBuilder *builder) +{ + g_return_val_if_fail (builder != NULL, 0); + g_return_val_if_fail (builder->prepared, 0); + g_return_val_if_fail (builder->buildable, 0 ); + + return builder->packed_size; +} + +void +_gi_typelib_hash_builder_pack (GITypelibHashBuilder *builder, guint8* mem, guint32 len) +{ + guint16 *table; + GHashTableIter hashiter; + gpointer key, value; + guint32 num_elts; + guint8 *packed_mem; + + g_return_if_fail (builder != NULL); + g_return_if_fail (builder->prepared); + g_return_if_fail (builder->buildable); + + g_assert (len >= builder->packed_size); + g_assert ((((size_t)mem) & 0x3) == 0); + + memset (mem, 0, len); + + *((guint32*) mem) = builder->dirmap_offset; + packed_mem = (guint8*)(mem + sizeof(guint32)); + cmph_pack (builder->c, packed_mem); + + table = (guint16*) (mem + builder->dirmap_offset); + + num_elts = g_hash_table_size (builder->strings); + g_hash_table_iter_init (&hashiter, builder->strings); + while (g_hash_table_iter_next (&hashiter, &key, &value)) + { + const char *str = key; + guint16 strval = (guint16)GPOINTER_TO_UINT(value); + guint32 hashv; + + hashv = cmph_search_packed (packed_mem, str, strlen (str)); + g_assert (hashv < num_elts); + table[hashv] = strval; + } +} + +void +_gi_typelib_hash_builder_destroy (GITypelibHashBuilder *builder) +{ + if (builder->c) + { + cmph_destroy (builder->c); + builder->c = NULL; + } + g_hash_table_destroy (builder->strings); + g_slice_free (GITypelibHashBuilder, builder); +} + +guint16 +_gi_typelib_hash_search (guint8* memory, const char *str, guint n_entries) +{ + guint32 *mph; + guint16 *table; + guint32 dirmap_offset; + guint32 offset; + + g_assert ((((size_t)memory) & 0x3) == 0); + mph = ((guint32*)memory)+1; + + offset = cmph_search_packed (mph, str, strlen (str)); + + /* Make sure that offset always lies in the entries array. cmph + cometimes generates offset larger than number of entries (for + 'str' argument which is not in the hashed list). In this case, + fake the correct result and depend on caller's final check that + the entry is really the one that the caller wanted. */ + if (offset >= n_entries) + offset = 0; + + dirmap_offset = *((guint32*)memory); + table = (guint16*) (memory + dirmap_offset); + + return table[offset]; +} + diff --git a/girepository/meson.build b/girepository/meson.build new file mode 100644 index 000000000..0c4e9d100 --- /dev/null +++ b/girepository/meson.build @@ -0,0 +1,216 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# SPDX-FileCopyrightText: 2017 Patrick Griffis +# SPDX-FileCopyrightText: 2017 Danny Forghieri +# SPDX-FileCopyrightText: 2017 Nirbheek Chauhan +# SPDX-FileCopyrightText: 2017, 2021, 2022 Emmanuele Bassi +# SPDX-FileCopyrightText: 2018 Mathieu Duponchelle +# SPDX-FileCopyrightText: 2018, 2019, 2020 Christoph Reiter +# SPDX-FileCopyrightText: 2018 Kai Kang +# SPDX-FileCopyrightText: 2018 Carlos Garnacho +# SPDX-FileCopyrightText: 2018, 2019, 2020 Alexander Kanavin +# SPDX-FileCopyrightText: 2019, 2020 Chun-wei Fan +# SPDX-FileCopyrightText: 2019 Aaron Boxer +# SPDX-FileCopyrightText: 2019 Thibault Saunier +# SPDX-FileCopyrightText: 2019 Joshua Watt +# SPDX-FileCopyrightText: 2020 Xavier Claessens +# SPDX-FileCopyrightText: 2020 Philip Chimento +# SPDX-FileCopyrightText: 2021 John Ericson +# SPDX-FileCopyrightText: 2021 Cimbali +# SPDX-FileCopyrightText: 2021, 2023 Simon McVittie +# SPDX-FileCopyrightText: 2022 Andoni Morales Alastruey + +subdir('cmph') + +gir_dir_prefix = get_option('gir_dir_prefix') +if gir_dir_prefix == '' or gir_dir_prefix == get_option('datadir') + gir_dir_prefix = get_option('datadir') + gir_dir_pc_prefix = '${datadir}' +else + gir_dir_pc_prefix = join_paths('${prefix}', gir_dir_prefix) +endif + +glib_girdir = get_option('prefix') / gir_dir_prefix / 'gir-1.0' + +gir_includedir = glib_includedir / 'girepository' + +gi_visibility_h = custom_target( + output: 'gi-visibility.h', + command: [gen_visibility_macros, meson.project_version(), 'visibility-macros', 'GI', '@OUTPUT@'], + install: true, + install_dir: gir_includedir, + install_tag: 'devel', +) + +girepo_headers = files( + 'giarginfo.h', + 'gibaseinfo.h', + 'gicallableinfo.h', + 'giconstantinfo.h', + 'gienuminfo.h', + 'gifieldinfo.h', + 'gifunctioninfo.h', + 'giinterfaceinfo.h', + 'giobjectinfo.h', + 'gipropertyinfo.h', + 'giregisteredtypeinfo.h', + 'girepository.h', + 'girffi.h', + 'gisignalinfo.h', + 'gistructinfo.h', + 'gitypeinfo.h', + 'gitypelib.h', + 'gitypes.h', + 'giunioninfo.h', + 'givfuncinfo.h', +) + +install_headers(girepo_headers, install_dir: gir_includedir) + +gir_c_args = [ + '-DGI_COMPILATION', + '-DG_LOG_DOMAIN="GLib-GIRepository"', + '-DGIR_SUFFIX="gir-1.0"', + '-DGIR_DIR="@0@"'.format(glib_girdir), + '-DGOBJECT_INTROSPECTION_LIBDIR="@0@"'.format(glib_libdir), + '-DGOBJECT_INTROSPECTION_DATADIR="@0@"'.format(glib_datadir), +] + +custom_c_args = [] + +if cc.get_id() != 'msvc' + custom_c_args = cc.get_supported_arguments([ + '-Wno-old-style-definition', + '-Wno-cast-align', + ]) +endif + +girepo_gthash_lib = static_library('girepository-gthash', + sources: ['gthash.c', gi_visibility_h], + include_directories : [configinc, girepoinc], + c_args: gir_c_args + custom_c_args, + dependencies: [ + cmph_dep, + libglib_dep, + libgmodule_dep, + libgobject_dep, + ], +) + +girepo_gthash_dep = declare_dependency( + link_with: girepo_gthash_lib, + dependencies: [libglib_dep, libgmodule_dep, libgobject_dep], + include_directories: [girepoinc], +) + +if cc.get_id() != 'msvc' + custom_c_args = cc.get_supported_arguments([ + '-Wno-unused-parameter', + '-Wno-duplicated-branches', + '-Wno-cast-align', + ]) +endif + +libgirepo_internals = static_library('girepository-internals', + sources: [ + 'girmodule.c', + 'girnode.c', + 'giroffsets.c', + 'girparser.c', + 'girwriter.c', + gi_visibility_h, + ], + c_args: gir_c_args + custom_c_args, + include_directories : [configinc, girepoinc], + dependencies: [girepo_gthash_dep, libffi_dep], +) + +libgirepo_internals_dep = declare_dependency( + link_with: libgirepo_internals, + dependencies: libffi_dep, + include_directories: [girepoinc], +) + +girepo_sources = files( + 'gdump.c', + 'giarginfo.c', + 'gibaseinfo.c', + 'gicallableinfo.c', + 'giconstantinfo.c', + 'gienuminfo.c', + 'gifieldinfo.c', + 'gifunctioninfo.c', + 'ginvoke.c', + 'giinterfaceinfo.c', + 'giobjectinfo.c', + 'gipropertyinfo.c', + 'giregisteredtypeinfo.c', + 'girepository.c', + 'girffi.c', + 'gisignalinfo.c', + 'gistructinfo.c', + 'gitypeinfo.c', + 'gitypelib.c', + 'giunioninfo.c', + 'givfuncinfo.c', +) + +if cc.get_id() != 'msvc' + custom_c_args = cc.get_supported_arguments([ + '-Wno-unused-parameter', + '-Wno-duplicated-branches', + '-Wno-type-limits', + '-Wno-cast-align', + '-Wno-missing-field-initializers', + ]) +endif + +libgirepo = shared_library('girepository-2.0', + sources: girepo_sources + [gi_visibility_h], + include_directories: [configinc, girepoinc], + c_args: gir_c_args, + version: library_version, + soversion: soversion, + darwin_versions: darwin_versions, + gnu_symbol_visibility: 'hidden', + link_args: glib_link_flags, + dependencies: [ + libglib_dep, + libgobject_dep, + libgmodule_dep, + libgio_dep, + libgirepo_internals_dep, + ], + install: true, +) + +libgirepo_dep = declare_dependency( + link_with: libgirepo, + dependencies: [libglib_dep, libgobject_dep, libgio_dep, libgmodule_dep], + include_directories: [girepoinc], +) + +executable('gi-dump-types', + sources: 'gi-dump-types.c', + dependencies: [ + libgirepo_dep, + libgiounix_dep, + libgiowin32_dep + ], +) + +pkgconfig_variables = [ + 'gidatadir=${datadir}/gobject-introspection-1.0', + 'girdir=' + gir_dir_pc_prefix / 'gir-1.0', + 'typelibdir=${libdir}/girepository-1.0', +] + +pkg.generate(libgirepo, + name: 'girepository', + version: glib_version, + filebase: 'girepository-2.0', + install_dir: glib_pkgconfigreldir, + description: 'GObject Introspection repository parser', + variables: pkgconfig_variables, + libraries: [libglib_dep, libgobject_dep], +) + diff --git a/meson.build b/meson.build index dbfa8ff35..901ae6dfe 100644 --- a/meson.build +++ b/meson.build @@ -87,6 +87,7 @@ glibinc = include_directories('glib') gobjectinc = include_directories('gobject') gmoduleinc = include_directories('gmodule') gioinc = include_directories('gio') +girepoinc = include_directories('girepository') glib_prefix = get_option('prefix') glib_bindir = join_paths(glib_prefix, get_option('bindir')) @@ -2483,6 +2484,7 @@ subdir('gobject') subdir('gthread') subdir('gmodule') subdir('gio') +subdir('girepository') subdir('fuzzing') # xgettext is optional (on Windows for instance) diff --git a/meson_options.txt b/meson_options.txt index c2c9e3427..e448748cd 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -126,6 +126,10 @@ option('multiarch', value : false, description : 'Install some helper executables in per-architecture locations') +option('gir_dir_prefix', + type: 'string', + description: 'Intermediate prefix for gir installation under ${prefix}') + option('introspection', type: 'feature', value: 'auto',