From bf4630076762d9c20c16c80c1c791f352b046dd1 Mon Sep 17 00:00:00 2001 From: Brad Bishop Date: Mon, 30 Jun 2014 22:10:16 -0500 Subject: Port FFS tools over from Building Block repository. --- LICENSE | 339 ++++++++ Makefile | 15 + NOTICE | 3 + clib/.gitignore | 21 + clib/Doxyfile | 1721 ++++++++++++++++++++++++++++++++++++++ clib/Makefile | 10 + clib/Rules.cunit.mk | 21 + clib/Rules.mk | 50 ++ clib/Rules.test.mk | 69 ++ clib/align.h | 49 ++ clib/array.h | 439 ++++++++++ clib/array_iter.h | 216 +++++ clib/assert.h | 42 + clib/attribute.h | 105 +++ clib/bb_trace.h | 419 ++++++++++ clib/bitset.h | 359 ++++++++ clib/builtin.h | 136 +++ clib/checksum.h | 43 + clib/compare.h | 43 + clib/crc32.h | 54 ++ clib/cunit/clib.c | 49 ++ clib/cunit/ecc.c | 91 ++ clib/cunit/ecc.h | 19 + clib/cunit/map.c | 301 +++++++ clib/cunit/map.h | 19 + clib/cunit/slab.c | 63 ++ clib/cunit/slab.h | 19 + clib/cunit/splay.c | 384 +++++++++ clib/cunit/splay.h | 19 + clib/cunit/tree.c | 398 +++++++++ clib/cunit/tree.h | 19 + clib/cunit/vector.c | 169 ++++ clib/cunit/vector.h | 19 + clib/db.h | 442 ++++++++++ clib/dispatch.h | 93 ++ clib/ecc.h | 217 +++++ clib/err.h | 290 +++++++ clib/exception.h | 299 +++++++ clib/hash.h | 248 ++++++ clib/heap.h | 70 ++ clib/ident.h | 53 ++ clib/libclib.h | 42 + clib/list.h | 537 ++++++++++++ clib/list_iter.h | 227 +++++ clib/map.h | 238 ++++++ clib/map_iter.h | 157 ++++ clib/max.h | 38 + clib/memory_leak_detection.h | 387 +++++++++ clib/min.h | 38 + clib/misc.h | 73 ++ clib/mq.h | 144 ++++ clib/nargs.h | 51 ++ clib/queue.h | 57 ++ clib/raii.h | 46 + clib/signal.h | 72 ++ clib/slab.h | 222 +++++ clib/src/array.c | 490 +++++++++++ clib/src/array_iter.c | 180 ++++ clib/src/checksum.c | 43 + clib/src/crc32.c | 150 ++++ clib/src/crc32_main.c | 76 ++ clib/src/db.c | 400 +++++++++ clib/src/dispatch.c | 155 ++++ clib/src/ecc.c | 456 ++++++++++ clib/src/err.c | 208 +++++ clib/src/exception.c | 145 ++++ clib/src/heap.c | 152 ++++ clib/src/list.c | 49 ++ clib/src/list_iter.c | 139 +++ clib/src/memory_leak_detection.c | 1277 ++++++++++++++++++++++++++++ clib/src/misc.c | 234 ++++++ clib/src/mq.c | 208 +++++ clib/src/signal.c | 135 +++ clib/src/slab.c | 388 +++++++++ clib/src/table.c | 679 +++++++++++++++ clib/src/table_iter.c | 138 +++ clib/src/timer.c | 133 +++ clib/src/trace_indent.c | 185 ++++ clib/src/tree.c | 630 ++++++++++++++ clib/src/tree_iter.c | 137 +++ clib/src/value.c | 143 ++++ clib/src/vector.c | 662 +++++++++++++++ clib/src/vector_iter.c | 177 ++++ clib/src/watch.c | 135 +++ clib/stack.h | 99 +++ clib/table.h | 272 ++++++ clib/table_iter.h | 172 ++++ clib/test/array.c | 70 ++ clib/test/checksum.c | 117 +++ clib/test/dispatch.c | 63 ++ clib/test/ecc.c | 220 +++++ clib/test/err.c | 73 ++ clib/test/exc_throw.c | 47 ++ clib/test/exception.c | 73 ++ clib/test/heap.c | 38 + clib/test/list.c | 71 ++ clib/test/map.c | 76 ++ clib/test/mq.c | 74 ++ clib/test/slab.c | 51 ++ clib/test/splay.c | 96 +++ clib/test/table.c | 102 +++ clib/test/tree.c | 84 ++ clib/test/vector.c | 83 ++ clib/test/watch.c | 44 + clib/timer.h | 77 ++ clib/trace_indent.h | 130 +++ clib/tree.h | 455 ++++++++++ clib/tree_iter.h | 188 +++++ clib/type.h | 82 ++ clib/value.h | 558 ++++++++++++ clib/vector.h | 432 ++++++++++ clib/vector_iter.h | 181 ++++ clib/version.h | 32 + clib/watch.h | 87 ++ clib/x86/Makefile | 5 + clib/x86/cunit/Makefile | 7 + clib/x86/test/Makefile | 7 + ecc/.gitignore | 1 + ecc/Makefile | 10 + ecc/Rules.mk | 20 + ecc/src/main.c | 625 ++++++++++++++ ecc/src/main.h | 78 ++ ecc/x86/Makefile | 6 + fcp/.gitignore | 3 + fcp/Makefile | 10 + fcp/Rules.mk | 30 + fcp/fcp.sh | 313 +++++++ fcp/ppc/Makefile | 9 + fcp/src/cmd_copy.c | 582 +++++++++++++ fcp/src/cmd_erase.c | 203 +++++ fcp/src/cmd_list.c | 166 ++++ fcp/src/cmd_probe.c | 179 ++++ fcp/src/cmd_read.c | 187 +++++ fcp/src/cmd_trunc.c | 163 ++++ fcp/src/cmd_user.c | 201 +++++ fcp/src/cmd_write.c | 202 +++++ fcp/src/main.c | 709 ++++++++++++++++ fcp/src/main.h | 120 +++ fcp/src/misc.c | 937 +++++++++++++++++++++ fcp/src/misc.h | 72 ++ fcp/x86/Makefile | 7 + ffs/.gitignore | 3 + ffs/Makefile | 10 + ffs/Rules.mk | 30 + ffs/Rules.test.mk | 25 + ffs/ffs.h | 148 ++++ ffs/libffs.h | 174 ++++ ffs/libffs2.h | 332 ++++++++ ffs/src/ffs-fsp.h | 59 ++ ffs/src/libffs.c | 1520 +++++++++++++++++++++++++++++++++ ffs/src/libffs2.c | 489 +++++++++++ ffs/test/Makefile | 8 + ffs/test/ffs_tool_test.sh | 244 ++++++ ffs/test/libffs_test.sh | 248 ++++++ ffs/test/test_libffs.c | 654 +++++++++++++++ ffs/test/test_libffs.h | 45 + ffs/x86/Makefile | 7 + fpart/.gitignore | 1 + fpart/Makefile | 10 + fpart/Rules.mk | 30 + fpart/Rules.test.mk | 25 + fpart/fpart.sh | 398 +++++++++ fpart/ppc/Makefile | 9 + fpart/src/cmd_add.c | 102 +++ fpart/src/cmd_compare.c | 231 +++++ fpart/src/cmd_copy.c | 282 +++++++ fpart/src/cmd_create.c | 113 +++ fpart/src/cmd_delete.c | 78 ++ fpart/src/cmd_erase.c | 206 +++++ fpart/src/cmd_hexdump.c | 153 ++++ fpart/src/cmd_list.c | 168 ++++ fpart/src/cmd_read.c | 166 ++++ fpart/src/cmd_trunc.c | 134 +++ fpart/src/cmd_user.c | 156 ++++ fpart/src/cmd_write.c | 229 +++++ fpart/src/command.c | 296 +++++++ fpart/src/main.c | 597 +++++++++++++ fpart/src/main.h | 125 +++ fpart/x86/Makefile | 7 + 179 files changed, 34179 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 NOTICE create mode 100644 clib/.gitignore create mode 100644 clib/Doxyfile create mode 100644 clib/Makefile create mode 100644 clib/Rules.cunit.mk create mode 100644 clib/Rules.mk create mode 100644 clib/Rules.test.mk create mode 100644 clib/align.h create mode 100644 clib/array.h create mode 100644 clib/array_iter.h create mode 100644 clib/assert.h create mode 100644 clib/attribute.h create mode 100644 clib/bb_trace.h create mode 100644 clib/bitset.h create mode 100644 clib/builtin.h create mode 100644 clib/checksum.h create mode 100644 clib/compare.h create mode 100644 clib/crc32.h create mode 100644 clib/cunit/clib.c create mode 100644 clib/cunit/ecc.c create mode 100644 clib/cunit/ecc.h create mode 100644 clib/cunit/map.c create mode 100644 clib/cunit/map.h create mode 100644 clib/cunit/slab.c create mode 100644 clib/cunit/slab.h create mode 100644 clib/cunit/splay.c create mode 100644 clib/cunit/splay.h create mode 100644 clib/cunit/tree.c create mode 100644 clib/cunit/tree.h create mode 100644 clib/cunit/vector.c create mode 100644 clib/cunit/vector.h create mode 100644 clib/db.h create mode 100644 clib/dispatch.h create mode 100644 clib/ecc.h create mode 100644 clib/err.h create mode 100644 clib/exception.h create mode 100644 clib/hash.h create mode 100644 clib/heap.h create mode 100644 clib/ident.h create mode 100644 clib/libclib.h create mode 100644 clib/list.h create mode 100644 clib/list_iter.h create mode 100644 clib/map.h create mode 100644 clib/map_iter.h create mode 100644 clib/max.h create mode 100644 clib/memory_leak_detection.h create mode 100644 clib/min.h create mode 100644 clib/misc.h create mode 100644 clib/mq.h create mode 100644 clib/nargs.h create mode 100644 clib/queue.h create mode 100644 clib/raii.h create mode 100644 clib/signal.h create mode 100644 clib/slab.h create mode 100644 clib/src/array.c create mode 100644 clib/src/array_iter.c create mode 100644 clib/src/checksum.c create mode 100644 clib/src/crc32.c create mode 100644 clib/src/crc32_main.c create mode 100644 clib/src/db.c create mode 100644 clib/src/dispatch.c create mode 100644 clib/src/ecc.c create mode 100644 clib/src/err.c create mode 100644 clib/src/exception.c create mode 100644 clib/src/heap.c create mode 100644 clib/src/list.c create mode 100644 clib/src/list_iter.c create mode 100644 clib/src/memory_leak_detection.c create mode 100644 clib/src/misc.c create mode 100644 clib/src/mq.c create mode 100644 clib/src/signal.c create mode 100644 clib/src/slab.c create mode 100644 clib/src/table.c create mode 100644 clib/src/table_iter.c create mode 100644 clib/src/timer.c create mode 100644 clib/src/trace_indent.c create mode 100644 clib/src/tree.c create mode 100644 clib/src/tree_iter.c create mode 100644 clib/src/value.c create mode 100644 clib/src/vector.c create mode 100644 clib/src/vector_iter.c create mode 100644 clib/src/watch.c create mode 100644 clib/stack.h create mode 100644 clib/table.h create mode 100644 clib/table_iter.h create mode 100644 clib/test/array.c create mode 100644 clib/test/checksum.c create mode 100644 clib/test/dispatch.c create mode 100644 clib/test/ecc.c create mode 100644 clib/test/err.c create mode 100644 clib/test/exc_throw.c create mode 100644 clib/test/exception.c create mode 100644 clib/test/heap.c create mode 100644 clib/test/list.c create mode 100644 clib/test/map.c create mode 100644 clib/test/mq.c create mode 100644 clib/test/slab.c create mode 100644 clib/test/splay.c create mode 100644 clib/test/table.c create mode 100644 clib/test/tree.c create mode 100644 clib/test/vector.c create mode 100644 clib/test/watch.c create mode 100644 clib/timer.h create mode 100644 clib/trace_indent.h create mode 100644 clib/tree.h create mode 100644 clib/tree_iter.h create mode 100644 clib/type.h create mode 100644 clib/value.h create mode 100644 clib/vector.h create mode 100644 clib/vector_iter.h create mode 100644 clib/version.h create mode 100644 clib/watch.h create mode 100644 clib/x86/Makefile create mode 100644 clib/x86/cunit/Makefile create mode 100644 clib/x86/test/Makefile create mode 100644 ecc/.gitignore create mode 100644 ecc/Makefile create mode 100644 ecc/Rules.mk create mode 100644 ecc/src/main.c create mode 100644 ecc/src/main.h create mode 100644 ecc/x86/Makefile create mode 100644 fcp/.gitignore create mode 100644 fcp/Makefile create mode 100644 fcp/Rules.mk create mode 100755 fcp/fcp.sh create mode 100644 fcp/ppc/Makefile create mode 100644 fcp/src/cmd_copy.c create mode 100644 fcp/src/cmd_erase.c create mode 100644 fcp/src/cmd_list.c create mode 100644 fcp/src/cmd_probe.c create mode 100644 fcp/src/cmd_read.c create mode 100644 fcp/src/cmd_trunc.c create mode 100644 fcp/src/cmd_user.c create mode 100644 fcp/src/cmd_write.c create mode 100644 fcp/src/main.c create mode 100644 fcp/src/main.h create mode 100644 fcp/src/misc.c create mode 100644 fcp/src/misc.h create mode 100644 fcp/x86/Makefile create mode 100644 ffs/.gitignore create mode 100644 ffs/Makefile create mode 100644 ffs/Rules.mk create mode 100644 ffs/Rules.test.mk create mode 100644 ffs/ffs.h create mode 100644 ffs/libffs.h create mode 100644 ffs/libffs2.h create mode 100644 ffs/src/ffs-fsp.h create mode 100644 ffs/src/libffs.c create mode 100644 ffs/src/libffs2.c create mode 100644 ffs/test/Makefile create mode 100755 ffs/test/ffs_tool_test.sh create mode 100755 ffs/test/libffs_test.sh create mode 100644 ffs/test/test_libffs.c create mode 100644 ffs/test/test_libffs.h create mode 100644 ffs/x86/Makefile create mode 100644 fpart/.gitignore create mode 100644 fpart/Makefile create mode 100644 fpart/Rules.mk create mode 100644 fpart/Rules.test.mk create mode 100755 fpart/fpart.sh create mode 100644 fpart/ppc/Makefile create mode 100644 fpart/src/cmd_add.c create mode 100644 fpart/src/cmd_compare.c create mode 100644 fpart/src/cmd_copy.c create mode 100644 fpart/src/cmd_create.c create mode 100644 fpart/src/cmd_delete.c create mode 100644 fpart/src/cmd_erase.c create mode 100644 fpart/src/cmd_hexdump.c create mode 100644 fpart/src/cmd_list.c create mode 100644 fpart/src/cmd_read.c create mode 100644 fpart/src/cmd_trunc.c create mode 100644 fpart/src/cmd_user.c create mode 100644 fpart/src/cmd_write.c create mode 100644 fpart/src/command.c create mode 100644 fpart/src/main.c create mode 100644 fpart/src/main.h create mode 100644 fpart/x86/Makefile diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 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. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies 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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, 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. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2869524 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +SUBDIRS = clib fcp ecc ffs fpart + +.PHONY: subdirs $(SUBDIRS) +subdirs: $(SUBDIRS) + +fpart:: ffs clib +fcp:: ffs clib +ecc:: clib + +$(SUBDIRS):: + $(MAKE) -C $@ $(MAKECMDGOALS) + +all install: $(SUBDIRS) + +clean distclean: $(SUBDIRS) diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..5982501 --- /dev/null +++ b/NOTICE @@ -0,0 +1,3 @@ +All files contained within are licensed under the terms of the GNU +General Public License v2.0. A copy may be obtained at: + http://www.gnu.org/licenses/gpl-2.0 diff --git a/clib/.gitignore b/clib/.gitignore new file mode 100644 index 0000000..1a7d377 --- /dev/null +++ b/clib/.gitignore @@ -0,0 +1,21 @@ +*.gch +*.a +*.o +*.so* +*/test/array +*/test/dispatch +*/test/exception +*/test/list +*/test/map +*/test/mq +*/test/slab +*/test/splay +*/test/table +*/test/tree +*/test/vector +*/test/watch +*/test/checksum +*/test/ecc +*/test/err +*/cunit/clib +*/crc32 diff --git a/clib/Doxyfile b/clib/Doxyfile new file mode 100644 index 0000000..f355ead --- /dev/null +++ b/clib/Doxyfile @@ -0,0 +1,1721 @@ +# Doxyfile 1.7.4 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is adviced to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# If the HTML_TIMESTAMP tag is set to YES then the generated HTML +# documentation will contain the timesstamp. + +HTML_TIMESTAMP = NO + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the stylesheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the +# mathjax.org site, so you can quickly see the result without installing +# MathJax, but it is strongly recommended to install a local copy of MathJax +# before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will write a font called Helvetica to the output +# directory and reference it in all dot files that doxygen generates. +# When you want a differently looking font you can specify the font name +# using DOT_FONTNAME. You need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/clib/Makefile b/clib/Makefile new file mode 100644 index 0000000..e08925f --- /dev/null +++ b/clib/Makefile @@ -0,0 +1,10 @@ +SUBDIRS=x86 + +.PHONY: subdirs $(SUBDIRS) + +subdirs: $(SUBDIRS) + +$(SUBDIRS):: + $(MAKE) -C $@ $(MAKECMDGOALS) + +all clean install: $(SUBDIRS) diff --git a/clib/Rules.cunit.mk b/clib/Rules.cunit.mk new file mode 100644 index 0000000..961412a --- /dev/null +++ b/clib/Rules.cunit.mk @@ -0,0 +1,21 @@ +CFLAGS += -m32 -std=gnu99 -D__USE_GNU -I$(DEPTH)/apps +LDFLAGS = -rdynamic -L$(DEPTH)/apps/clib/$(ARCH_DEP_DIR) + +TARGETS=clib + +OBJS=slab.o splay.o map.o vector.o ecc.o tree.o clib.o + +vpath %.h ../../src +vpath %.c ../../cunit + +all: $(TARGETS) + +clib: $(OBJS) $(DEPTH)/apps/clib/$(ARCH_DEP_DIR)/libclib.a + $(CC) $(LDFLAGS) -o $@ $^ -lcunit -lrt + +clean: + $(RM) $(OBJS) $(TARGETS) + +install: + $(INSTALL) -d $(CLIB_INSTALL) + $(INSTALL) $(TARGETS) $(CLIB_INSTALL) diff --git a/clib/Rules.mk b/clib/Rules.mk new file mode 100644 index 0000000..308d1d5 --- /dev/null +++ b/clib/Rules.mk @@ -0,0 +1,50 @@ +CFLAGS += -m32 -D_GNU_SOURCE -std=gnu99 -fshort-enums -D_FILE_OFFSET_BITS=64 +CFLAGS += -iquote$(DEPTH) + +LDFLAGS=-L. -m32 + +# array.o array_iter.o dispatch.o watch.o signal.o heap.o + +OBJS = err.o crc32.o misc.o ecc.o \ + exception.o slab.o \ + list.o list_iter.o \ + tree.o tree_iter.o \ + vector.o vector_iter.o \ + table.o table_iter.o \ + value.o mq.o \ + memory_leak_detection.o \ + trace_indent.o checksum.o + + +TARGETS=libclib.so libclib.a crc32 + +vpath %.c ../src +vpath %.h .. + +all: $(TARGETS) + +libclib.so: $(OBJS) + $(CC) $(LDFLAGS) -fPIC -shared -Wl,-soname,$@ -o $@ $^ -lpthread \ + -lrt + +libclib.a: $(OBJS) + $(AR) -r $@ $^ + +db.o: db.c db.h + $(CC) $(CFLAGS) -DSQLITE3 -c $^ + +crc32: crc32.c crc32_main.c + $(CC) $(CFLAGS) -o $@ $^ + +clean distclean: + $(RM) *.so *.o *.gch *.CKP *.lst $(TARGETS) + $(RM) html latex + +install: $(TARGETS) ../*.h + $(INSTALL) -d $(CLIB_INSTALL)/include/clib $(CLIB_INSTALL)/{bin,lib} + $(INSTALL) ../crc32.h $(CLIB_INSTALL)/include + $(INSTALL) ../*.h $(CLIB_INSTALL)/include/clib + $(INSTALL) libclib.so libclib.a $(CLIB_INSTALL)/lib + $(INSTALL) crc32 $(CLIB_INSTALL)/bin + $(MAKE) -C test $@ + $(MAKE) -C cunit $@ diff --git a/clib/Rules.test.mk b/clib/Rules.test.mk new file mode 100644 index 0000000..35ab9a4 --- /dev/null +++ b/clib/Rules.test.mk @@ -0,0 +1,69 @@ +CFLAGS += -m32 -std=gnu99 -D__USE_GNU -I$(DEPTH)/apps +LDFLAGS = -rdynamic -L$(DEPTH)/apps/clib/$(ARCH_DEP_DIR) + +TARGETS=exception \ + slab \ + list \ + ecc \ + tree \ + splay \ + map \ + table \ + mq \ + vector \ + checksum \ + err + +vpath %.h ../../src +vpath %.c ../../test + +all: $(TARGETS) + +exception: exception.o ../libclib.so + $(CC) $(LDFLAGS) -o $@ $^ + +slab: slab.o ../libclib.so + $(CC) $(LDFLAGS) -o $@ $^ + +list: list.o ../libclib.so + $(CC) $(LDFLAGS) -o $@ $^ + +tree: tree.o ../libclib.so + $(CC) $(LDFLAGS) -o $@ $^ + +splay: splay.o ../libclib.so + $(CC) $(LDFLAGS) -o $@ $^ + +map: map.o ../libclib.so + $(CC) $(LDFLAGS) -o $@ $^ + +mq: mq.o ../libclib.so + $(CC) $(LDFLAGS) -o $@ $^ + +table: table.o ../libclib.so + $(CC) $(LDFLAGS) -o $@ $^ + +watch: watch.o ../libclib.so + $(CC) $(LDFLAGS) -o $@ $^ + +dispatch: dispatch.o ../libclib.so + $(CC) $(LDFLAGS) -o $@ $^ + +vector: vector.o ../libclib.so + $(CC) $(LDFLAGS) -o $@ $^ + +checksum: checksum.o ../libclib.so + $(CC) $(LDFLAGS) -o $@ $^ + +err: err.o ../libclib.so + $(CC) $(LDFLAGS) -o $@ $^ + +ecc: ecc.o ../libclib.so + $(CC) $(LDFLAGS) -o $@ $^ + +clean: + $(RM) $(TARGETS:=.o) $(TARGETS) + +install: + $(INSTALL) -d $(CLIB_INSTALL) + $(INSTALL) $(TARGETS) $(CLIB_INSTALL) diff --git a/clib/align.h b/clib/align.h new file mode 100644 index 0000000..b2a8a6c --- /dev/null +++ b/clib/align.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! + * @file align.h + * @brief Alignment helpers + * @author Shaun Wetzstein + * @date 2010-2011 + */ + +#ifndef __ALIGN_H__ +#define __ALIGN_H__ + +/*! + * @def alignof(t) + * @hideinitializer + * @brief Returns the alignment of an object or minimum alignment required by a type + * @param t [in] Object or type name + */ +#define alignof(t) __alignof__(t) + +#ifndef offsetof + +/*! + * @def offsetof(t,m) + * @hideinitializer + * @brief Returns the offset of a member within a structure + * @param t [in] Structure type name + * @param m [in] Member name within a structure + */ +#define offsetof(t,m) __builtin_offsetof(t, m) +#endif + +#endif /* __ALIGN_H__ */ diff --git a/clib/array.h b/clib/array.h new file mode 100644 index 0000000..8fce5ee --- /dev/null +++ b/clib/array.h @@ -0,0 +1,439 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! + * @file array.h + * @brief Array container + * @details An array class whose elements are partitioned into data `pages' + * that are allocated upon first-touch, possibly leading to a pages + * that are sparsely populated. + * @details For example, + * @code + * #include + * #include + * + * int main(const int argc, const char * argv[]) { + * array_t a; + * array_init(&a, 4, 1024); + * + * array_dump(&a, stdout); + * + * array_put(&a, 52, (uint32_t[]){52}); + * array_put(&a, 53, (uint32_t[]){53}); + * array_put(&a, 167, (uint32_t[]){167}); + * array_put(&a, 223, (uint32_t[]){223}); + * array_put(&a, 78, (uint32_t[]){78}); + * array_put(&a, 205, (uint32_t[]){205}); + * array_put(&a, 183, (uint32_t[]){183}); + * array_put(&a, 150, (uint32_t[]){150}); + * array_put(&a, 90, (uint32_t[]){90}); + * array_put(&a, 66, (uint32_t[]){66}); + * array_put(&a, 91, (uint32_t[]){91}); + * array_put(&a, 45, (uint32_t[]){45}); + * array_put(&a, 211, (uint32_t[]){211}); + * + * uint32_t arr[] = {985,986,987,988,990,991,992,993,994}; + * array_put(&a, 985, arr, 9); + * + * array_iter_t it; + * array_iter_init(&it, &a, AI_FLAG_FWD); + * + * uint32_t * j; + * array_for_each(&it, j) { + * printf("arr[%d]\n", *j); + * } + * + * array_dump(&a, stdout); + * array_delete(&a); + * + * return 0; + * } + * @endcode + * @author Shaun Wetzstein + * @date 2010-2011 + */ + +#ifndef __ARRAY_H__ +#define __ARRAY_H__ + +#include +#include + +#include "builtin.h" +#include "mqueue.h" +#include "tree.h" +#include "nargs.h" + +/* ======================================================================= */ + +typedef struct array array_t; //!< Alias for the @em array class + +/*! + * @brief Array container class + */ +struct array { + uint32_t magic; //!< Array magic number + + uint32_t page_size; //!< Array data page size (in bytes) + + uint16_t elem_size; //!< Array element size (in bytes) + uint16_t elem_num; //!< Array element count (per page) + + size_t size; //!< Number of initialized elements + size_t pages; //!< Number of data pages allocated (currently) + + size_t low; //!< @private + size_t high; //!< @private + + size_t map_size; //!< @private + tree_t tree; //!< @private +}; + +/* ======================================================================= */ + +/*! + * @brief Constructs an @em array container object + * @details For example, + * @code + * ... + * array_t ar; + * array_init(&ar, 4, 1024); + * ... + * @endcode + * @memberof array + * @param self [in] array object @em self pointer + * @param elem_size [in] array element size, in bytes + * @param elem_num [in] array element number, per data page + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern void array_init(array_t * self, size_t elem_size, size_t elem_num) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Destructs an @em array container object + * @details Deallocate all backing storage associated with this \em array object + * @details For example, + * @code + * ... + * array_init(&ar, 4, 1024); + * array_put(&ar, 524, &count); + * array_delete(&ar); + * ... + * @endcode + * @memberof array + * @param self [in] array object @em self pointer + * @return None + */ +extern void array_delete(array_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Returns a reference to the element at position @em idx in the @em array + * @details For example, + * @code + * ... + * array_init(&ar, 4, 1024); + * array_put(&ar, 524, &count); + * printf("ar[524] = %d\n", *(int *)array_at(&ar, 524)); + * array_delete(&ar); + * ... + * @endcode + * @memberof array + * @param self [in] array object @em self pointer + * @param elem_num [in] array element index + * @return Reference to array element at @em idx on SUCCESS + * @throws UNEXPECTED if @em self pointer is NULL + * @throws UNEXPECTED if array element at @em idx is uninitialized + */ +extern const void *array_at(array_t * self, size_t elem_num) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @fn void array_get(array_t * self, size_t elem_off, const void * ptr, size_t elem_num=1) + * @brief Copy content from the @em array + * @details Copies @em elem_num element(s) starting at position @em elem_off in the source @em array to destination pointer @em ptr + * @note If the fourth parameter is omitted, it defaults to 1 + * @details For example, + * @code + * ... + * array_init(&ar, 4, 1024); + * array_put(&ar, 524, &count); + * array_get(&ar, 524, &count); + * array_delete(&ar); + * ... + * @endcode + * @memberof array + * @param self [in] array object @em self pointer + * @param elem_off [in] array element index + * @param ptr [out] Destination storage pointer + * @param elem_num [in] Desgination element count (optional) + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define array_put(...) STRCAT(array_put, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern void array_get3(array_t * self, size_t elem_off, void *ptr) +/*! @cond */ +__nonnull((1, 3)) /*! @endcond */ ; +extern void array_get4(array_t * self, size_t elem_off, void *ptr, + size_t elem_num) +/*! @cond */ +__nonnull((1, 3)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @fn void array_put(array_t * self, size_t elem_off, const void * ptr, size_t elem_num=1) + * @brief Assign new content to the @em array + * @details Copies @em elem_num element(s) from source pointer @em ptr to the destination @em array starting at position @em elem_off + * @note If the fourth parameter is omitted, it defaults to 1 + * @details For example, + * @code + * ... + * array_init(&ar, 4, 1024); + * array_put(&ar, 524, &count); + * array_get(&ar, 524, &count); + * array_delete(&ar); + * ... + * @endcode + * @memberof array + * @param self [in] array object @em self pointer + * @param elem_off [in] array element index + * @param ptr [in] Source storage pointer + * @param elem_num [in] Source element count (optional) + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define array_put(...) STRCAT(array_put, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern void array_put3(array_t * self, size_t elem_off, const void *ptr) +/*! @cond */ +__nonnull((1, 3)) /*! @endcond */ ; +extern void array_put4(array_t * self, size_t elem_off, const void *ptr, + size_t elem_num) +/*! @cond */ +__nonnull((1, 3)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @fn bool array_status(array_t * self, size_t elem_num, bool state) + * @brief Set or return the initialized status of the @em array element at position @em idx + * @note If the third parameter is omitted, the element's current status is returned + * @details For example, + * @code + * ... + * array_init(&ar, 4, 1024); + * array_put(&ar, 524, &count); + * printf("%d %d\n", array_status(&ar, 524), array_status(&ar, 600)); + * array_delete(&ar); + * ... + * @endcode + * @memberof array + * @param self [in] array object @em self pointer + * @param elem_num [in] array element index + * @param state [in] Element state (optional) + * @return None + * @throws UNEXPECTED if @em idx is out of bounds + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define array_status(...) STRCAT(array_status, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern bool array_status2(array_t * self, size_t elem_num) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern bool array_status3(array_t * self, size_t elem_num, bool state) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @fn size_t array_size(array_t * self, size_t size = 1) + * @brief Return or set the size of the @em array + * @details Return or set the number of allocated elements in the @em array + * @details For example, + * @code + * ... + * array_init(&ar, 4, 1024); + * array_size(&ar, 2040); + * array_delete(&ar); + * ... + * @endcode + * @memberof array + * @param self [in] array object @em self pointer + * @param size [in] New array size + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +#define array_size(...) STRCAT(array_size, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern size_t array_size1(array_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern void array_size2(array_t * self, size_t size) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return pages of the @em array container + * @details Return the number of pages in the @em array container + * @details For example, + * @code + * ... + * array_init(&ar, 4, 1024); + * array_size(&ar, 2040); + * printf("pages = %d\n", array_pages(&ar)); + * array_delete(&ar); + * ... + * @endcode + * @memberof array + * @param self [in] array object @em self pointer + * @return The number of pages that conform the array's content + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern size_t array_pages(array_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return capacity of the @em array + * @details Return the number of allocated and unallocated elements in the @em array container + * @details For example, + * @code + * ... + * array_init(&ar, 4, 1024); + * array_size(&ar, 2040); + * printf("capacity = %d\n", array_capacity(&ar)); + * array_delete(&ar); + * ... + * @endcode + * @memberof array + * @param self [in] array object @em self pointer + * @return The number of total elements that conform the array's content + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern size_t array_capacity(array_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return @em array index lower bound + * @details For example, + * @code + * ... + * array_init(&ar, 4, 1024); + * array_put(&ar, 7, &count); + * array_put(&ar, 7000, &count); + * printf("low = %d\n", array_low(&ar)); + * array_delete(&ar); + * ... + * @endcode + * @memberof array + * @param self [in] array object @em self pointer + * @return non-0 on success + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern size_t array_low(array_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return @em array index upper bound + * @details For example, + * @code + * ... + * array_init(&ar, 4, 1024); + * array_put(&ar, 7, &count); + * array_put(&ar, 7000, &count); + * printf("high = %d\n", array_high(&ar)); + * array_delete(&ar); + * ... + * @endcode + * @memberof array + * @param self [in] array object @em self pointer + * @return non-0 on success + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern size_t array_high(array_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Send (write) an @em array object to a message queue + * @details For example, + * @code + * ... + * array_init(&ar, 4, 1024); + * array_put(&ar, 7, &count); + * array_put(&ar, 7000, &count); + * ... + * mqueue mq; + * mqueue_init(&mq, "my_server"); + * mqueue_create(&mq, gettid()); + * ... + * array_send(&ar, &mq); + * array_delete(&ar); + * ... + * @endcode + * @memberof array + * @param self [in] array object @em self pointer + * @return non-0 on success + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern void array_send(array_t * self, mqueue_t * mq) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief receive (read) an @em array object from a message queue + * @details For example, + * @code + * ... + * array ar; + * ... + * mqueue mq; + * mqueue_open(&mq, path); + * ... + * array_receive(&ar, &mq); + * array_dump(&ar); + * ... + * @endcode + * @memberof array + * @param self [in] array object @em self pointer + * @return non-0 on success + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern void array_receive(array_t * self, mqueue_t * mq) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Pretty print the contents of an @em array to stdout + * @memberof array + * @param self [in] array object @em self pointer + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern void array_dump(array_t * self, FILE * out) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/* ======================================================================= */ + +#endif /* __ARRAY_H__ */ diff --git a/clib/array_iter.h b/clib/array_iter.h new file mode 100644 index 0000000..efb6e0c --- /dev/null +++ b/clib/array_iter.h @@ -0,0 +1,216 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! @file array_iter.h + * @brief Sparse Array Iterator + * @details An array class whose elements are partitioned into data `pages' + * that are allocated upon first-touch. + * @code + * ... + * array_t arr; + * array_init(&arr, sizeof(uint32_t), 1024); + * ... + * array_iter_t it; + * array_iter_init(&it, &arr, VI_FLAG_FWD); + * + * uint32_t * j; + * array_for_each(&it, j) { + * printf("arr[%d] = %d\n", it.idx, *j); + * } + * ... + * @endcode + * @author Shaun Wetzstein + * @date 2010-2011 + */ + +#ifndef __ARRAY_ITER_H__ +#define __ARRAY_ITER_H__ + +#include +#include + +#include "builtin.h" +#include "array.h" + +/* ======================================================================= */ + +typedef struct array_iter array_iter_t; //!< Alias for the @em array_iter class + +/*! + * @brief Array iterator + * @details Array iterator class +*/ +struct array_iter { + array_t *array; //!< Reference to the target array object + int32_t idx; //!< Current position of the iterator + uint32_t flags; //!< Iterator configuration flags +}; + +/* ======================================================================= */ + +#define AI_FLAG_NONE 0x00000000 //!< No flag mask +#define AI_FLAG_FWD 0x00000000 //!< Forward (FWD) flag mask +#define AI_FLAG_BWD 0x00000002 //!< Backward (BWD) flag mask +#define AI_FLAG_MASK 0x00000003 //!< All flags mask + +/*! + * @brief Initializes an @em array_iter iterator object + * @details For example, + * @code + * ... + * array_t ar; + * array_init(&ar, 4, 1024); + * ... + * array_iter_t it; + * array_iter_init(&it, &ar, AI_FLAG_FWD); + * ... + * @endcode + * @memberof array_iter + * @param self [in] array_iter object @em self pointer + * @param array [in] array container object to iterate + * @param flags [in] iterator configuration flags + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + * @throws UNEXPECTED if @em array pointer is NULL + */ +extern void array_iter_init(array_iter_t * self, array_t * array, + uint32_t flags) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Resets an @em array iterator object + * @details For example, + * @code + * ... + * array_t ar; + * array_init(&ar, 4, 1024); + * ... + * array_iter_t it; + * array_iter_init(&it, &ar, AI_FLAG_FWD); + * array_iter_clear(&it); + * ... + * @endcode + * @memberof array_iter + * @param self [in] array_iter object @em self pointer + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern void array_iter_clear(array_iter_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return a pointer to @em array element bytes at the current iterator position + * @details For example, + * @code + * ... + * array_t ar; + * array_init(&ar, 4, 1024); + * ... + * array_iter_t it; + * array_iter_init(&it, &ar, AI_FLAG_FWD); + * ... + * data_t * d = (data_t *)array_iter_elem(&it); + * ... + * @endcode + * @memberof array_iter + * @param self [in] array_iter object @em self pointer + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern const void *array_iter_elem(array_iter_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @fn const void * array_iter_inc(array_iter_t * self, size_t count = 1) + * @brief Increment the position of an @em array iterator + * @details If the second (2nd) parameter is omitted, the iterator is incremented by one (1) position. + * @details For example, + * @code + * ... + * array_t ar; + * array_init(&ar, 4, 1024); + * ... + * array_iter_t it; + * array_iter_init(&it, &ar, AI_FLAG_FWD); + * array_iter_inc(&it); + * ... + * @endcode + * @memberof array_iter + * @param self [in] array_iter object @em self pointer + * @param count [in] Number of positions to increment (optional) + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define array_iter_inc(...) STRCAT(array_iter_inc, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern const void *array_iter_inc1(array_iter_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern const void *array_iter_inc2(array_iter_t * self, size_t count) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @fn const void * array_iter_dec(array_iter_t * self, size_t count = 1) + * @brief decrement the position of an @em array iterator + * @note If the second (2nd) parameter is omitted, the iterator is decremented by one (1) position. + * @details For example, + * @code + * ... + * array_t ar; + * array_init(&ar, 4, 1024); + * ... + * array_iter_t it; + * array_iter_init(&it, &ar, AI_FLAG_FWD); + * array_iter_dec(&it, 3); + * ... + * @endcode + * @memberof array_iter + * @param self [in] array_iter object @em self pointer + * @param count [in] Number of positions to decrement (optional) + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define array_iter_dec(...) STRCAT(array_iter_dec, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern const void *array_iter_dec1(array_iter_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern const void *array_iter_dec2(array_iter_t * self, size_t count) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @def array_for_each(it, i) + * @hideinitializer + * @brief Array for-each algorithm + * @param it [in] Tree iterator object + * @param i [in] Tree element variable + */ +#define array_for_each(it, i) \ + for (i = (typeof(i))array_iter_elem(it); \ + i != NULL; \ + i = (typeof(i))array_iter_inc(it, 1)) + +/* ======================================================================= */ + +#endif /* __ARRAY_ITER_H__ */ diff --git a/clib/assert.h b/clib/assert.h new file mode 100644 index 0000000..8740eda --- /dev/null +++ b/clib/assert.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! @file assert.h + * @brief Assertion helpers + * @author Shaun Wetzstein + * @date 2010-2011 + */ + +#ifdef assert +#undef assert +#endif + +/*! + * @def assert(e) + * @hideinitializer + * @brief Check for program assertion failures + * @param e [in] assertion expression + * @throws ASSERTION iff expression @em e evaluates @em false + */ + +#ifdef NDEBUG +#define assert(e) ((void)0) +#else +#include "exception.h" +#define assert(e) ((void)((e) ? (void)0 : throw_bytes(ASSERTION, __STRING((e)), strlen(__STRING((e)))))) +#endif diff --git a/clib/attribute.h b/clib/attribute.h new file mode 100644 index 0000000..2213096 --- /dev/null +++ b/clib/attribute.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: attribute.h + * Author: Shaun Wetzstein + * Descr: GCC attribute helpers + * Note: + * Date: 08/29/10 + */ +/*! @file attribute.h + * @brief Attribute helpers + * @author Shaun Wetzstein + * @date 2010-2011 + */ + +#ifndef __ATTRIBUTE_H__ +#define __ATTRIBUTE_H__ + +/*! + * @def __constructor + * @hideinitializer + * @brief Constructor function attribute + */ +#define __constructor __attribute__ ((constructor)) + +/*! + * @def __destructor + * @hideinitializer + * @brief Desstructor function attribute + */ +#define __destructor __attribute__ ((destructor)) + +/*! + * @def __deprecated + * @hideinitializer + * @brief Deprecated function attribute + */ +#define __deprecated __attribute__ ((deprecated)) + +/*! + * @def __inline + * @hideinitializer + * @brief Inline function attribute + */ +#define __inline __attribute__ ((always_inline)) + +/*! + * @def __used + * @hideinitializer + * @brief Used identifier attribute + */ +#define __used __attribute__ ((__used__)) + +/*! + * @def __used + * @hideinitializer + * @brief Used identifier attribute + */ +#define __unused__ __attribute__ ((__unused__)) + +/*! + * @def __const + * @hideinitializer + * @brief Const identifier attribute + */ +#define __const __attribute__ ((__const__)) + +/*! + * @def __must_check + * @hideinitializer + * @brief Warning about "unused" identifier attribute + */ +#define __must_check __attribute__ ((warn_unused_result)) + +/*! + * @def __packed + * @hideinitializer + * @brief Packed structure identifier attribute + */ +#define __packed __attribute__ ((packed)) + +/*! + * @def __cleanup + * @hideinitializer + * @brief Clenaup variable attribute + */ +#define __cleanup(f) __attribute__ ((cleanup(f))) + +#endif /* __ATTRIBUTE_H__ */ diff --git a/clib/bb_trace.h b/clib/bb_trace.h new file mode 100644 index 0000000..a3557c4 --- /dev/null +++ b/clib/bb_trace.h @@ -0,0 +1,419 @@ +/* + * Copyright (c) International Business Machines Corp., 2011 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: B. Rafanello + * + * Module: bb_trace.h + * + * Implements a simple trace system. + * + * Version 0.0.0.1 + */ + +#ifndef BB_TRACE_H + +#define BB_TRACE_H + +#include +#include "trace_indent.h" + +#if (ENABLE_API_TRACE || ENABLE_FUNCTION_TRACE || ENABLE_FUNCTION_PARAMETER_TRACE || ENABLE_API_TRACE || ENABLE_API_PARAMETER_TRACE || ENABLE_EXTRA_CHECKS || ENABLE_USER_TRACE ) + +#define ENABLE_BB_TRACE_BASE + +#endif + +#ifdef ENABLE_ALL_BB_TRACE + +#define ENABLE_API_TRACE +#define ENABLE_FUNCTION_TRACE +#define ENABLE_API_PARAMETER_TRACE +#define ENABLE_FUNCTION_PARAMETER_TRACE +#define ENABLE_EXTRA_CHECKS +#define ENABLE_USER_TRACE +#define ENABLE_BB_TRACE_BASE + +#endif + +#ifdef ENABLE_BB_TRACE_BASE + + /* The trace level is a bitmask. The following bits, if 1, activate the corresponding trace function: + * + * 0 : API Entry & Exit + * 1 : Local function entry & exit + * 2 : API parameters will be listed on entry and return values listed on exit + * 3 : Local function parameters and return values will be listed + * 4 : Low bit, extra checks level number. + * 5 : High bit, extra checks level number. + * 6 : Low bit, user defined trace level number. + * 7 : High bit, user defined trace level number. + */ + +#define API_ENTRY_EXIT_TRACE 0x01 +#define FUNCTION_ENTRY_EXIT_TRACE 0x02 +#define API_PARAMETER_TRACE 0x04 +#define FUNCTION_PARAMETER_TRACE 0x08 +#define EXTRA_CHECKS_LEVEL_1 0x10 +#define EXTRA_CHECKS_LEVEL_2 0x20 +#define EXTRA_CHECKS_LEVEL_3 0x30 +#define USER_TRACE_LEVEL_1 0x40 +#define USER_TRACE_LEVEL_2 0x80 +#define USER_TRACE_LEVEL_3 0xC0 + +extern unsigned char BB_TRACE_LEVEL; +extern unsigned char BB_PREVIOUS_TRACE_LEVEL; + +#define TRACE_INDENT() if (BB_TRACE_LEVEL != 0) \ + Indent_Trace_Output(); + +#define TRACE_OUTDENT() if ( BB_TRACE_LEVEL != 0 ) \ + Outdent_Trace_Output(); + +#define TRACE_STOP() if ( BB_TRACE_LEVEL != 0 ) \ + { \ + BB_PREVIOUS_TRACE_LEVEL = BB_TRACE_LEVEL; \ + BB_TRACE_LEVEL = 0; \ + } + +#define TRACE_START() if ( BB_TRACE_LEVEL == 0 ) \ + { \ + BB_TRACE_LEVEL = BB_PREVIOUS_TRACE_LEVEL; \ + } + +#define SET_TRACE_LEVEL( Level ) BB_TRACE_LEVEL = Level; + +#else + +#define API_ENTRY_EXIT_TRACE +#define FUNCTION_ENTRY_EXIT_TRACE +#define API_PARAMETER_TRACE +#define FUNCTION_PARAMETER_TRACE +#define EXTRA_CHECKS_LEVEL_1 +#define EXTRA_CHECKS_LEVEL_2 +#define EXTRA_CHECKS_LEVEL_3 +#define USER_TRACE_LEVEL_1 +#define USER_TRACE_LEVEL_2 +#define USER_TRACE_LEVEL_3 + +#define TRACE_INDENT() + +#define TRACE_OUTDENT() + +#define TRACE_STOP() + +#define TRACE_START() + +#define SET_TRACE_LEVEL( Level ) + +#endif + +#ifdef ENABLE_FUNCTION_TRACE + +#define FUNCTION_ENTRY( ) if ( BB_TRACE_LEVEL & FUNCTION_ENTRY_EXIT_TRACE ) \ + { \ + fprintf( stderr, " \n\n"); \ + Do_Indent(); \ + fprintf( stderr, "-------------------- \n"); \ + Do_Indent(); \ + fprintf( stderr, "Entering %s.\n", __func__ ); \ + Do_Indent(); \ + fprintf( stderr, "-------------------- \n\n"); \ + Indent_Trace_Output(); \ + } + +#define FUNCTION_EXIT( ) if ( BB_TRACE_LEVEL & FUNCTION_ENTRY_EXIT_TRACE ) \ + { \ + fprintf( stderr, " \n\n"); \ + Outdent_Trace_Output(); \ + Do_Indent(); \ + fprintf( stderr, "-------------------- \n"); \ + Do_Indent(); \ + fprintf( stderr, "Leaving %s.\n", __func__ ); \ + Do_Indent(); \ + fprintf( stderr, "-------------------- \n\n"); \ + } \ + return; + +#define FUNCTION_RETURN( Format, Value ) \ + if ( BB_TRACE_LEVEL & FUNCTION_ENTRY_EXIT_TRACE ) \ + { \ + fprintf( stderr, " \n\n"); \ + Outdent_Trace_Output(); \ + Do_Indent(); \ + fprintf( stderr, "-------------------- \n"); \ + Do_Indent(); \ + fprintf( stderr, "Leaving %s with return value " Format "\n", __func__ , Value); \ + Do_Indent(); \ + fprintf( stderr, "-------------------- \n\n"); \ + } \ + return Value; + +#else + +#define FUNCTION_ENTRY( ) + +#define FUNCTION_EXIT( ) return; + +#define FUNCTION_RETURN( Format, Value ) return Value; + +#endif + +#ifdef ENABLE_FUNCTION_PARAMETER_TRACE + +#define PRINT_FUNCTION_PARAMETER( ... ) if ( BB_TRACE_LEVEL & FUNCTION_PARAMETER_TRACE ) \ + { \ + Do_Indent(); \ + fprintf( stderr, __VA_ARGS__ ); \ + fprintf( stderr, "\n"); \ + } + +#else + +#define PRINT_FUNCTION_PARAMETER( ... ) + +#endif + +#ifdef ENABLE_API_TRACE + +#define API_FUNCTION_ENTRY( ) if ( BB_TRACE_LEVEL & API_ENTRY_EXIT_TRACE ) \ + { \ + fprintf( stderr, " \n\n"); \ + Do_Indent(); \ + fprintf( stderr, "==================== \n"); \ + Do_Indent(); \ + fprintf( stderr, "Entering %s API.\n", __func__ ); \ + Do_Indent(); \ + fprintf( stderr, "==================== \n\n"); \ + Indent_Trace_Output(); \ + } + +#define API_FUNCTION_EXIT( ) if ( BB_TRACE_LEVEL & API_ENTRY_EXIT_TRACE ) \ + { \ + fprintf( stderr, " \n\n"); \ + Outdent_Trace_Output(); \ + Do_Indent(); \ + fprintf( stderr, "==================== \n"); \ + Do_Indent(); \ + fprintf( stderr, "Leaving %s API.\n", __func__ ); \ + Do_Indent(); \ + fprintf( stderr, "==================== \n\n"); \ + } \ + return; + +#define API_FUNCTION_RETURN( Format, Value ) \ + if ( BB_TRACE_LEVEL & API_ENTRY_EXIT_TRACE ) \ + { \ + fprintf( stderr, " \n\n"); \ + Outdent_Trace_Output(); \ + Do_Indent(); \ + fprintf( stderr, "==================== \n"); \ + Do_Indent(); \ + fprintf( stderr, "Leaving %s API with return value " Format "\n", __func__, Value); \ + Do_Indent(); \ + fprintf( stderr, "==================== \n\n"); \ + } \ + return Value; + +#else + +#define API_FUNCTION_ENTRY( ) + +#define API_FUNCTION_EXIT( ) return; + +#define API_FUNCTION_RETURN( Format, Value ) return Value; + +#endif + +#ifdef ENABLE_API_PARAMETER_TRACE + +#define PRINT_API_PARAMETER( ... ) if ( BB_TRACE_LEVEL & API_PARAMETER_TRACE ) \ + { \ + Do_Indent(); \ + fprintf( stderr, __VA_ARGS__ ); \ + fprintf( stderr, "\n"); \ + } + +#else + +#define PRINT_API_PARAMETER( ... ) + +#endif + +#ifdef ENABLE_EXTRA_CHECKS + +#define LEVEL1_EXTRA_CHECK( ... ) if ( BB_TRACE_LEVEL & EXTRA_CHECKS_LEVEL_1 ) \ + { \ + if ( __VA_ARGS__ ) \ + { \ + fprintf( stderr, "\n"); \ + Do_Indent(); \ + fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \ + Do_Indent(); \ + fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \ + Do_Indent(); \ + fprintf( stderr, "Extra check in file %s, line %d has failed!\n", \ + __FILE__, __LINE__ ); \ + Do_Indent(); \ + fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \ + Do_Indent(); \ + fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \ + } \ + \ + } + +#define LEVEL1_EC_PRINT_LINE( ... ) if ( BB_TRACE_LEVEL & EXTRA_CHECKS_LEVEL_1 ) \ + { \ + Do_Indent(); \ + fprintf( stderr, __VA_ARGS__ ); \ + fprintf( stderr, "\n"); \ + } + +#define LEVEL2_EXTRA_CHECK( ... ) if ( BB_TRACE_LEVEL & EXTRA_CHECKS_LEVEL_2 ) \ + { \ + if ( __VA_ARGS__ ) \ + { \ + fprintf( stderr, "\n"); \ + Do_Indent(); \ + fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \ + Do_Indent(); \ + fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \ + Do_Indent(); \ + fprintf( stderr, "Extra check in file %s, line %d has failed!\n", \ + __FILE__, __LINE__ ); \ + Do_Indent(); \ + fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \ + Do_Indent(); \ + fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \ + } \ + \ + } + +#define LEVEL2_EC_PRINT_LINE( ... ) if ( BB_TRACE_LEVEL & EXTRA_CHECKS_LEVEL_2 ) \ + { \ + Do_Indent(); \ + fprintf( stderr, __VA_ARGS__ ); \ + fprintf( stderr, "\n"); \ + } + +#define LEVEL3_EXTRA_CHECK( ... ) if ( BB_TRACE_LEVEL & EXTRA_CHECKS_LEVEL_3 ) \ + { \ + if ( __VA_ARGS__ ) \ + { \ + fprintf( stderr, "\n"); \ + Do_Indent(); \ + fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \ + Do_Indent(); \ + fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \ + Do_Indent(); \ + fprintf( stderr, "Extra check in file %s, line %d has failed!\n", \ + __FILE__, __LINE__ ); \ + Do_Indent(); \ + fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \ + Do_Indent(); \ + fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \ + } \ + \ + } + +#define LEVEL3_EC_PRINT_LINE( ... ) if ( BB_TRACE_LEVEL & EXTRA_CHECKS_LEVEL_3 ) \ + { \ + Do_Indent(); \ + fprintf( stderr, __VA_ARGS__ ); \ + fprintf( stderr, "\n"); \ + } + +#define EXTRA_CHECK( Trace_Level, ... ) if ( BB_TRACE_LEVEL & Trace_Level ) \ + { \ + if ( __VA_ARGS__ ) \ + { \ + fprintf( stderr, "\n"); \ + Do_Indent(); \ + fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \ + Do_Indent(); \ + fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \ + Do_Indent(); \ + fprintf( stderr, "Extra check in file %s, line %d has failed!\n", \ + __FILE__, __LINE__ ); \ + Do_Indent(); \ + fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \ + Do_Indent(); \ + fprintf( stderr, "!!!!!!!!!!!!!!!!!!!! \n"); \ + } \ + \ + } + +#else + +#define LEVEL1_EXTRA_CHECK( ... ) + +#define LEVEL2_EXTRA_CHECK( ... ) + +#define LEVEL3_EXTRA_CHECK( ... ) + +#define LEVEL1_EC_PRINT_LINE( ... ) + +#define LEVEL2_EC_PRINT_LINE( ... ) + +#define LEVEL3_EC_PRINT_LINE( ... ) + +#define EXTRA_CHECK( Trace_Level, ... ) + +#endif + +#ifdef ENABLE_USER_TRACE + +#define USER1_PRINT_LINE( ... ) if ( BB_TRACE_LEVEL & USER_TRACE_LEVEL_1 ) \ + { \ + Do_Indent(); \ + fprintf( stderr, __VA_ARGS__ ); \ + fprintf( stderr, "\n"); \ + } + +#define USER2_PRINT_LINE( ... ) if ( BB_TRACE_LEVEL & USER_TRACE_LEVEL_2 ) \ + { \ + Do_Indent(); \ + fprintf( stderr, __VA_ARGS__ ); \ + fprintf( stderr, "\n"); \ + } + +#define USER3_PRINT_LINE( ... ) if ( BB_TRACE_LEVEL & USER_TRACE_LEVEL_3 ) \ + { \ + Do_Indent(); \ + fprintf( stderr, __VA_ARGS__ ); \ + fprintf( stderr, "\n"); \ + } + +#define TRACE_PRINT_LINE( Trace_Level, ... ) if ( BB_TRACE_LEVEL & Trace_Level ) \ + { \ + Do_Indent(); \ + fprintf( stderr, __VA_ARGS__ ); \ + fprintf( stderr, "\n"); \ + } + +#else + +#define USER1_PRINT_LINE( ... ) + +#define USER2_PRINT_LINE( ... ) + +#define USER3_PRINT_LINE( ... ) + +#define TRACE_PRINT_LINE( Trace_Level, ... ) + +#endif + +#endif diff --git a/clib/bitset.h b/clib/bitset.h new file mode 100644 index 0000000..d1947aa --- /dev/null +++ b/clib/bitset.h @@ -0,0 +1,359 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! + * @file bitset.h + * @brief bitset container + * @details bitsets are a special container that are design to store + * bits (elements with only 2 possible values: 0 or 1) + * @details For example, + * @code + * #include + * #include + * + * int main(const int argc, const char * argv[]) { + * bitset_t a; + * bitset_init(&a, 4, 1024); + * + * bitset_size(&a, 10); + * + * int i; + * for (i=0; i<10; i++) + * bitset_put(&a, i, &i); + * bitset_put(&a, 223, (uint32_t[]){223}); + * + * bitset_iter_t it; + * bitset_iter_init(&it, &a, VI_FLAG_FWD); + * + * uint32_t * j; + * bitset_for_each(&it, j) { + * printf("vec[%d]\n", *j); + * } + * + * bitset_dump(&a, stdout); + * bitset_delete(&a); + * + * return 0; + * } + * @endcode + * @author Shaun Wetzstein + * @date 2010-2011 + */ + +#ifndef __bitset_H__ +#define __bitset_H__ + +#include +#include + +#include "builtin.h" + +#include "nargs.h" +#include "mqueue.h" +#include "tree.h" + +/* ======================================================================= */ + +typedef struct bitset bitset_t; //!< Alias for the @em bitset class + +/*! + * @brief bitset container + * @details bitset container + */ +struct bitset { //! The bitset class + uint32_t magic; //!< bitset magic number + + uint32_t page_size; //!< bitset data page size (in bytes) + + uint16_t elem_size; //!< bitset element size (in bytes) + uint16_t elem_num; //!< bitset element count (per page) + + size_t size; //!< Number of initialized elements + size_t pages; //!< Number of data pages allocated (currently) + + tree_t tree; //!< @private +}; + +/* ======================================================================= */ + +/*! + * @brief Constructs an @em bitset container object + * @details For example, + * @code + * ... + * bitset_t ar; + * bitset_init(&ar, 4, 1024); + * ... + * @endcode + * @memberof bitset + * @param self [in] bitset object @em self pointer + * @param elem_size [in] bitset element size, in bytes + * @param elem_num [in] bitset element number, per data page + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define bitset_init(...) STRCAT(bitset_init, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern void bitset_init2(bitset_t * self, size_t elem_size) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern void bitset_init3(bitset_t * self, size_t elem_size, size_t page_size) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @brief Destructs an @em bitset container object + * @details Deallocate all backing storage associated with this \em bitset object + * @details For example, + * @code + * ... + * bitset_init(&ar, 4, 1024); + * bitset_put(&ar, 524, &count); + * bitset_delete(&ar); + * ... + * @endcode + * @memberof bitset + * @param self [in] bitset object @em self pointer + * @return None + */ +extern void bitset_delete(bitset_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Returns a reference to the element at position @em idx in the @em bitset + * @details For example, + * @code + * ... + * bitset_init(&ar, 4, 1024); + * bitset_put(&ar, 524, &count); + * printf("ar[524] = %d\n", *(int *)bitset_at(&ar, 524)); + * bitset_delete(&ar); + * ... + * @endcode + * @memberof bitset + * @param self [in] bitset object @em self pointer + * @param elem_num [in] bitset element index + * @return Reference to bitset element at @em idx on SUCCESS + * @throws UNEXPECTED if @em self pointer is NULL + * @throws UNEXPECTED if bitset element at @em idx is uninitialized + */ +extern const void *bitset_at(bitset_t * self, size_t elem_num) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @fn void bitset_get(bitset_t * self, size_t elem_off, const void * ptr, size_t elem_num=1) + * @brief Copy content from the @em bitset + * @details Copies @em elem_num element(s) starting at position @em elem_off in the source @em bitset to destination pointer @em ptr + * @note If the fourth parameter is omitted, it defaults to 1 + * @details For example, + * @code + * ... + * bitset_init(&ar, 4, 1024); + * bitset_put(&ar, 524, &count); + * bitset_get(&ar, 524, &count); + * bitset_delete(&ar); + * ... + * @endcode + * @memberof bitset + * @param self [in] bitset object @em self pointer + * @param elem_off [in] bitset element index + * @param ptr [out] Destination storage pointer + * @param elem_num [in] Desgination element count (optional) + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define bitset_get(...) STRCAT(bitset_get, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern void bitset_get3(bitset_t * self, size_t elem_off, void *ptr) +/*! @cond */ +__nonnull((1, 3)) /*! @endcond */ ; +extern void bitset_get4(bitset_t * self, size_t elem_off, void *ptr, + size_t elem_num) +/*! @cond */ +__nonnull((1, 3)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @fn void bitset_put(bitset_t * self, size_t elem_off, const void * ptr, size_t elem_num=1) + * @brief Assign new content to the @em bitset + * @details Copies @em elem_num element(s) from source pointer @em ptr to the destination @em bitset starting at position @em elem_off + * @note If the fourth parameter is omitted, it defaults to 1 + * @details For example, + * @code + * ... + * bitset_init(&ar, 4, 1024); + * bitset_put(&ar, 524, &count); + * bitset_get(&ar, 524, &count); + * bitset_delete(&ar); + * ... + * @endcode + * @memberof bitset + * @param self [in] bitset object @em self pointer + * @param elem_off [in] bitset element index + * @param ptr [in] Source storage pointer + * @param elem_num [in] Source element count (optional) + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define bitset_put(...) STRCAT(bitset_put, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern void bitset_put3(bitset_t * self, size_t elem_off, const void *ptr) +/*! @cond */ +__nonnull((1, 3)) /*! @endcond */ ; +extern void bitset_put4(bitset_t * self, size_t elem_off, const void *ptr, + size_t elem_num) +/*! @cond */ +__nonnull((1, 3)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @fn size_t bitset_size(bitset_t * self, size_t size = 1) + * @brief Return or set the size of the @em bitset + * @details Return or set the number of allocated elements in the @em bitset + * @details For example, + * @code + * ... + * bitset_init(&ar, 4, 1024); + * bitset_size(&ar, 2040); + * bitset_delete(&ar); + * ... + * @endcode + * @memberof bitset + * @param self [in] bitset object @em self pointer + * @param size [in] New bitset size + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define bitset_size(...) STRCAT(bitset_size, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern size_t bitset_size1(bitset_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern void bitset_size2(bitset_t * self, size_t size) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @brief Return pages of the @em bitset container + * @details Return the number of pages in the @em bitset container + * @details For example, + * @code + * ... + * bitset_init(&ar, 4, 1024); + * bitset_size(&ar, 2040); + * printf("pages = %d\n", bitset_pages(&ar)); + * bitset_delete(&ar); + * ... + * @endcode + * @memberof bitset + * @param self [in] bitset object @em self pointer + * @return The number of pages that conform the bitset's content + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern size_t bitset_pages(bitset_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return capacity of the @em bitset + * @details Return the number of allocated and unallocated elements in the @em bitset container + * @details For example, + * @code + * ... + * bitset_init(&ar, 4, 1024); + * bitset_size(&ar, 2040); + * printf("capacity = %d\n", bitset_capacity(&ar)); + * bitset_delete(&ar); + * ... + * @endcode + * @memberof bitset + * @param self [in] bitset object @em self pointer + * @return The number of total elements that conform the bitset's content + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern size_t bitset_capacity(bitset_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Send (write) an @em bitset object to a message queue + * @details For example, + * @code + * ... + * bitset_init(&ar, 4, 1024); + * bitset_put(&ar, 7, &count); + * bitset_put(&ar, 7000, &count); + * ... + * mqueue mq; + * mqueue_init(&mq, "my_server"); + * mqueue_create(&mq, gettid()); + * ... + * bitset_send(&ar, &mq); + * bitset_delete(&ar); + * ... + * @endcode + * @memberof bitset + * @param self [in] bitset object @em self pointer + * @return non-0 on success + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern void bitset_send(bitset_t * self, mqueue_t * mq) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief receive (read) an @em bitset object from a message queue + * @details For example, + * @code + * ... + * bitset ar; + * ... + * mqueue mq; + * mqueue_open(&mq, path); + * ... + * bitset_receive(&ar, &mq); + * bitset_dump(&ar); + * ... + * @endcode + * @memberof bitset + * @param self [in] bitset object @em self pointer + * @return non-0 on success + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern void bitset_receive(bitset_t * self, mqueue_t * mq) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Pretty print the contents of an @em bitset to stdout + * @memberof bitset + * @param self [in] bitset object @em self pointer + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern void bitset_dump(bitset_t * self, FILE * out) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/* ======================================================================= */ + +#endif /* __bitset_H__ */ diff --git a/clib/builtin.h b/clib/builtin.h new file mode 100644 index 0000000..5827fb9 --- /dev/null +++ b/clib/builtin.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! @file builtin.h + * @brief Builtin Function Macros + * @author Shaun Wetzstein + * @date 2010-2011 + */ + +#ifndef __BUILTIN_H__ +#define __BUILTIN_H__ + +/*! + * @def likely(x) + * @hideinitializer + * @brief Indicate an expression is likely to occur + * @param x [in] Expression + */ +#define likely(x) __builtin_expect(!!(x), 1) + +/*! + * @def unlikely(x) + * @hideinitializer + * @brief Indicate an expression is unlikely to occur + * @param x [in] Expression + */ +#define unlikely(x) __builtin_expect(!!(x), 0) + +/*! + * @def parity(x) + * @hideinitializer + * @brief Return parity of the input + * @param x [in] Object + */ +#define parity(x) __builtin_parity((x)) +#define ffs(x) __builtin_ffs((x)) + +/*! + * @def popcount(x) + * @hideinitializer + * @brief Return number of 0b'1' bits of an int + * @param x [in] Object + */ +#define popcount(x) __builtin_popcount((x)) + +/*! + * @def ctz(x) + * @hideinitializer + * @brief Count trailing zeros of an int + * @param x [in] @em int value + */ +#define ctz(x) __builtin_ctz((x)) + +/*! + * @def ctzl(x) + * @hideinitializer + * @brief Count trailing zeros of a long + * @param x [in] @em long value + */ +#define ctzl(x) __builtin_ctzl((x)) + +/*! + * @def ctzll(x) + * @hideinitializer + * @brief Count trailing zeros of a long long + * @param x [in] @em long @em long value + */ +#define ctzll(x) __builtin_ctzll((x)) + +/*! + * @def clz(x) + * @hideinitializer + * @brief Count leading zeros of an int + * @param x [in] @em int value + */ +#define clz(x) __builtin_clzl((x)) + +/*! + * @def clzl(x) + * @hideinitializer + * @brief Count leading zeros of a long + * @param x [in] @em long value + */ +#define clzl(x) __builtin_clzl((x)) + +/*! + * @def clzll(x) + * @hideinitializer + * @brief Count leading zeros of a long long + * @param x [in] @em long long value + */ +#define clzll(x) __builtin_clzll((x)) + +/*! + * @def caller(x) + * @hideinitializer + * @brief Return callers return address + * @param x [in] Function name + */ +#define caller(x) __builtin_return_address((x)) + +#define choose_expr(x,y,z) __builtin_choose_expr((x),(y),(z)) + +/*! + * @def const_expr(x) + * @hideinitializer + * @brief Return callers return address + * @param x [in] Function name + */ +#define const_expr(x) __builtin_constant_p((x)) + +/*! + * @def compatible_type(x) + * @hideinitializer + * @brief Return @em true if typeof(x) and typeof(y) are compatible + * @param x [in] Type name + * @param y [in] Type name + */ +#define compatible_type(x,y) __builtin_types_compatible_p((x),(y)) + +#endif /* __BUILTIN_H__ */ diff --git a/clib/checksum.h b/clib/checksum.h new file mode 100644 index 0000000..528b77e --- /dev/null +++ b/clib/checksum.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! @file checksum.h + * @brief Simple XOR checksum + * @author Mike Kobler + * @date 2007-2012 + */ + +#ifndef __CHECKSUM_H__ +#define __CHECKSUM_H__ + +#include + +/*! + * @brief Copy bytes from the source reference to the destination reference + * while computing a 32-bit checksum + * @param __dst [in] Destination reference + * @param __src [in] Source reference (must be 4 byte aligned) + * @param __n [in] Number of bytes to copy / compute + * @return 32-bit Checksum value + */ +extern uint32_t memcpy_checksum(void *__restrict __dst, + const void *__restrict __src, size_t __n) +/*! @cond */ +__THROW __nonnull((2)) /*! @endcond */ ; + +#endif /* __CHECKSUM_H__ */ diff --git a/clib/compare.h b/clib/compare.h new file mode 100644 index 0000000..0461b51 --- /dev/null +++ b/clib/compare.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! + * @file compare.h + * @brief Generic compare function + * @author Shaun Wetzstein + * @date 2010-2011 + */ + +#ifndef __COMPARE_H__ +#define __COMPARE_H__ + +/*! + * @brief Pointer to a generic compare function + */ +typedef int (*compare_f) (const void *, const void *); + +/*! + * @brief Default compare function + */ +static inline int default_compare(const void *v1, const void *v2) +{ + const int i1 = (const int)v1, i2 = (const int)v2; + return i1 - i2; +} + +#endif /* __COMPARE_H__ */ diff --git a/clib/crc32.h b/clib/crc32.h new file mode 100644 index 0000000..ce77a31 --- /dev/null +++ b/clib/crc32.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! @file crc32.h + * @brief 32-bit CRC + * @details Gary Brown's CRC32 algorithm for polynomial: + * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 + * @author Gary Brown + * @date 2010-2011 + */ + +#ifndef __CRC32_H__ +#define __CRC32_H__ + +#include +#include + +/*! + * @brief Compute a 32-bit CRC value for an octet + * @param c [in] Input data (octet) + * @param crc [in] Input 32-bit CRC value + * @return 32-bit CRC value + */ +extern uint32_t clib_crc32(unsigned char c, uint32_t crc); + +/*! + * @brief Copy bytes from the source reference to the destination reference + * while computing a 32-bit CRC + * @param __dst [in] Destination reference + * @param __src [in] Source reference + * @param __n [in] Number of bytes to copy / compute + * @return 32-bit CRC value + */ +extern uint32_t memcpy32(void *__restrict __dst, const void *__restrict __src, + size_t __n) +/*! @cond */ +__THROW __nonnull((2)) /*! @endcond */ ; + +#endif /* __CRC32_H__ */ diff --git a/clib/cunit/clib.c b/clib/cunit/clib.c new file mode 100644 index 0000000..e9f1053 --- /dev/null +++ b/clib/cunit/clib.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include + +#include "vector.h" +#include "slab.h" +#include "tree.h" +#include "splay.h" +#include "map.h" +#include "ecc.h" + +int main(void) { + /* initialize the CUnit test registry */ + if (CUE_SUCCESS != CU_initialize_registry()) + return CU_get_error(); + + vector_test(); + slab_test(); + tree_test(); + splay_test(); + map_test(); + ecc_test(); + + /* Run all tests using the CUnit Basic interface */ + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + CU_cleanup_registry(); + + return CU_get_error(); +} diff --git a/clib/cunit/ecc.c b/clib/cunit/ecc.c new file mode 100644 index 0000000..3ae6303 --- /dev/null +++ b/clib/cunit/ecc.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#define COUNT 10000 +#define SEED 41 + +static int init_ecc(void) { + return 0; +} + +static int clean_ecc(void) { + return 0; +} + +static void ecc_1(void) { + int size[] = {7, 8, 16, 1024+16, 4096+32, 8*1024*4+64, 0}; + + for (int * s = size; *s != 0; s++) { + unsigned char in[*s]; + unsigned char out[*s + (*s / 8)]; + + ssize_t in_sz = sizeof in; + ssize_t out_sz = sizeof out; + + memset(in, 0, in_sz); + memset(out, 0, out_sz); + + FILE * f = fopen("/dev/urandom", "r"); + CU_ASSERT_FATAL(f != NULL); + CU_ASSERT_FATAL(fread(in, in_sz, 1, f) == 1); + fclose(f); + + ssize_t rc = sfc_ecc_inject(out, out_sz, in, in_sz); + if ((*s % 8) != 0) { + CU_ASSERT(rc == -1); + } else { + CU_ASSERT(rc == in_sz + (in_sz / 8)); + } + + unsigned char cmp[*s]; + ssize_t cmp_sz = sizeof cmp; + memset(cmp, 0, cmp_sz); + + rc = sfc_ecc_remove(cmp, cmp_sz, out, out_sz); + if ((out_sz % 9) != 0) { + CU_ASSERT(rc == -1); + } else { + CU_ASSERT(rc == in_sz) + CU_ASSERT_FATAL(memcmp(in, cmp, in_sz) == 0); +#ifdef DEBUG + dump_memory(stdout, 0, in, in_sz); + dump_memory(stdout, 0, cmp, cmp_sz); +#endif + } + } +} + +void ecc_test(void) { + CU_pSuite suite = CU_add_suite("ecc", init_ecc, clean_ecc); + if (NULL == suite) + return; + + if (CU_add_test(suite, "test of --> ecc_1", ecc_1) == NULL) return; +} diff --git a/clib/cunit/ecc.h b/clib/cunit/ecc.h new file mode 100644 index 0000000..0eec8da --- /dev/null +++ b/clib/cunit/ecc.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void ecc_test(void); diff --git a/clib/cunit/map.c b/clib/cunit/map.c new file mode 100644 index 0000000..3761b1a --- /dev/null +++ b/clib/cunit/map.c @@ -0,0 +1,301 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#define COUNT 20000 +#define SEED 22 + +slab_t slab; + +typedef struct { + map_node_t node; + int i; + float f; +} data_t; + +static int init_map(void) { + slab_init(&slab, "my_slab", sizeof(data_t), 4096); + return 0; +} + +static int clean_map(void) { + slab_delete(&slab); + return 0; +} + +static void __insert(map_t * t, int i) { + data_t * d = (data_t *)slab_alloc(&slab); + + i %= INT32_MAX; + + d->i = i; + d->f = (float)i; + map_node_init(&d->node, (const void *)(intptr_t)(d->i)); + + if (map_insert(t, &d->node) < 0) { + err_t * err = err_get(); + fprintf(stderr, "%s(%d): %.*s\n", + err_file(err), err_line(err), err_size(err), + (const char *)err_data(err)); + } +} + +static data_t * __remove(map_t * t, int i) { + + i %= INT32_MAX; + + map_node_t * n = map_find(t, (const void *)i); + if (n == NULL) map_dump(t, stdout); + CU_ASSERT_PTR_NOT_NULL_FATAL(n); + + map_remove(t, n); + + data_t * d = container_of(n, data_t, node); + CU_ASSERT_PTR_NOT_NULL_FATAL(n); + + if (0 <= i) + CU_ASSERT((int)d->node.key == i); + + return d; +} + +static int compare(const void * v1, const void * v2) { + const int i1 = (const int)v1, i2 = (const int)v2; + return i1 - i2; +} + +static void map_1(void) { + map_t t; + map_init(&t, compare); + + CU_ASSERT(map_min(&t) == NULL); + CU_ASSERT(map_max(&t) == NULL); + CU_ASSERT(map_empty(&t) == true); + CU_ASSERT(map_size(&t) == 0); +} + +static void map_2(void) { + map_t t; + map_init(&t, compare); + + CU_ASSERT(map_empty(&t) == true); + CU_ASSERT(map_size(&t) == 0); + + for (int i=1; i<=COUNT; i++) + __insert(&t, i); + + CU_ASSERT(map_empty(&t) == false); + CU_ASSERT(map_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + CU_ASSERT(i == (int)map_min(&t)->key); + CU_ASSERT(COUNT == (int)map_max(&t)->key); + __remove(&t, (int)map_min(&t)->key); + CU_ASSERT(map_size(&t) + i == COUNT); + } + + CU_ASSERT(map_empty(&t) == true); + CU_ASSERT(map_size(&t) == 0); + + for (int i=1; i<=COUNT; i++) + __insert(&t, i); + + CU_ASSERT(map_empty(&t) == false); + CU_ASSERT(map_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + CU_ASSERT(1 == (int)map_min(&t)->key); + CU_ASSERT(COUNT - i + 1 == (int)map_max(&t)->key); + __remove(&t, (int)map_max(&t)->key); + CU_ASSERT(map_size(&t) + i == COUNT); + } + + CU_ASSERT(map_empty(&t) == true); + CU_ASSERT(map_size(&t) == 0); +} + +static void map_3(void) { + map_t t; + map_init(&t, compare); + + CU_ASSERT(map_empty(&t) == true); + CU_ASSERT(map_size(&t) == 0); + + for (int i=1; i<=COUNT; i++) + __insert(&t, COUNT - i + 1); + + CU_ASSERT(map_empty(&t) == false); + CU_ASSERT(map_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + CU_ASSERT(1 == (int)map_min(&t)->key); + CU_ASSERT(COUNT - i + 1 == (int)map_max(&t)->key); + __remove(&t, (int)map_max(&t)->key); + CU_ASSERT(map_size(&t) + i == COUNT); + } + + CU_ASSERT(map_empty(&t) == true); + CU_ASSERT(map_size(&t) == 0); + + for (int i=1; i<=COUNT; i++) + __insert(&t, COUNT - i + 1); + + CU_ASSERT(map_empty(&t) == false); + CU_ASSERT(map_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + CU_ASSERT(i == (int)map_min(&t)->key); + CU_ASSERT(COUNT == (int)map_max(&t)->key); + __remove(&t, (int)map_min(&t)->key); + CU_ASSERT(map_size(&t) + i == COUNT); + } + + CU_ASSERT(map_empty(&t) == true); + CU_ASSERT(map_size(&t) == 0); +} + +static void map_4(void) { + map_t t; + map_init(&t, compare); + + CU_ASSERT(map_empty(&t) == true); + CU_ASSERT(map_size(&t) == 0); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) + __insert(&t, (int)random()); + + CU_ASSERT(map_empty(&t) == false); + CU_ASSERT(map_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + __remove(&t, (int)map_min(&t)->key); + CU_ASSERT(map_size(&t) + i == COUNT); + } + + CU_ASSERT(map_empty(&t) == true); + CU_ASSERT(map_size(&t) == 0); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) + __insert(&t, (int)random()); + + CU_ASSERT(map_empty(&t) == false); + CU_ASSERT(map_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + __remove(&t, (int)map_max(&t)->key); + CU_ASSERT(map_size(&t) + i == COUNT); + } + + CU_ASSERT(map_empty(&t) == true); + CU_ASSERT(map_size(&t) == 0); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) + __insert(&t, (int)random()); + + CU_ASSERT(map_empty(&t) == false); + CU_ASSERT(map_size(&t) == COUNT); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) { + __remove(&t, (int)random()); + CU_ASSERT(map_size(&t) + i == COUNT); + } + + CU_ASSERT(map_empty(&t) == true); + CU_ASSERT(map_size(&t) == 0); +} + +static void map_5(void) { + map_t t; + map_init(&t, compare); + + CU_ASSERT(map_empty(&t) == true); + CU_ASSERT(map_size(&t) == 0); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) + __insert(&t, (int)random()); + + CU_ASSERT(map_empty(&t) == false); + CU_ASSERT(map_size(&t) == COUNT); + + data_t * d; + int key = 0; + + map_iter_t it; + map_iter_init(&it, &t, MI_FLAG_FWD); + map_for_each(&it, d, node) { + CU_ASSERT(key < (int)d->node.key); + key = (int)d->node.key; + } + + key = INT32_MAX; + map_iter_init(&it, &t, MI_FLAG_BWD); + map_for_each(&it, d, node) { + CU_ASSERT((int)d->node.key < key); + key = (int)d->node.key; + } +} + +static void map_6(void) { + map_t t; + map_init(&t, compare); + + CU_ASSERT(map_empty(&t) == true); + CU_ASSERT(map_size(&t) == 0); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) + __insert(&t, (int)random()); + + CU_ASSERT(map_empty(&t) == false); + CU_ASSERT(map_size(&t) == COUNT); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) { + map_node_t * node = map_find(&t, (const void *)random()); + CU_ASSERT_PTR_NOT_NULL_FATAL(node); + } +} + +void map_test(void) { + CU_pSuite suite = CU_add_suite("map", init_map, clean_map); + if (NULL == suite) + return; + + if (CU_add_test(suite, "test of --> map_1", map_1) == NULL) return; + if (CU_add_test(suite, "test of --> map_2", map_2) == NULL) return; + if (CU_add_test(suite, "test of --> map_3", map_3) == NULL) return; + if (CU_add_test(suite, "test of --> map_4", map_4) == NULL) return; + if (CU_add_test(suite, "test of --> map_5", map_5) == NULL) return; + if (CU_add_test(suite, "test of --> map_6", map_6) == NULL) return; +} diff --git a/clib/cunit/map.h b/clib/cunit/map.h new file mode 100644 index 0000000..9996b54 --- /dev/null +++ b/clib/cunit/map.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void map_test(void); diff --git a/clib/cunit/slab.c b/clib/cunit/slab.c new file mode 100644 index 0000000..c9585d3 --- /dev/null +++ b/clib/cunit/slab.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include +#include +#include + +#include + +#include + +#define COUNT 10000 +#define SEED 41 + +static int init_slab(void) { + return 0; +} + +static int clean_slab(void) { + return 0; +} + +static void slab_1(void) { + slab_t s[SLAB_ALLOC_MAX+1] = {INIT_SLAB,}; + + for (int i=SLAB_ALLOC_MIN; i<=SLAB_ALLOC_MAX; i+=3) { + slab_init(&s[i], "my_slab", i); +//slab_dump(&s[i], stdout); + + for (int j=0; j slab_1", slab_1) == NULL) return; +} diff --git a/clib/cunit/slab.h b/clib/cunit/slab.h new file mode 100644 index 0000000..32e2aa6 --- /dev/null +++ b/clib/cunit/slab.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void slab_test(void); diff --git a/clib/cunit/splay.c b/clib/cunit/splay.c new file mode 100644 index 0000000..302310e --- /dev/null +++ b/clib/cunit/splay.c @@ -0,0 +1,384 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#define COUNT 30000 +#define SEED 22 + +slab_t slab; + +typedef struct { + tree_node_t node; + int i; + float f; +} data_t; + +static int init_splay(void) { + slab_init(&slab, "my_slab", sizeof(data_t), 4096); + return 0; +} + +static int clean_splay(void) { + slab_delete(&slab); + return 0; +} + +static void __insert(tree_t * t, int i) { + data_t * d = (data_t *)slab_alloc(&slab); + + d->i = i; + d->f = (float)i; + + tree_node_init(&d->node, (const void *)(d->i)); + + if (splay_insert(t, &d->node) < 0) { + tree_dump(t, stdout); + + fprintf(stdout, "key: %d root->key: %d\n", + i, (int)tree_root(t)->key); + + err_t * err = err_get(); + fprintf(stderr, "%s(%d): %.*s\n", + err_file(err), err_line(err), err_size(err), + (const char *)err_data(err)); + } +} + +static data_t * __remove(tree_t * t, int i) { + tree_node_t * n = tree_find(t, (const void *)i); + if (n == NULL) tree_dump(t, stdout); + CU_ASSERT_PTR_NOT_NULL_FATAL(n); + + splay_remove(t, n); + CU_ASSERT_PTR_NULL(tree_node_parent(n)); + CU_ASSERT_PTR_NULL(tree_node_left(n)); + CU_ASSERT_PTR_NULL(tree_node_right(n)); + CU_ASSERT_PTR_NOT_NULL(tree_node_key(n)); + + data_t * d = container_of(n, data_t, node); + CU_ASSERT_PTR_NOT_NULL_FATAL(n); + + if (0 <= i) + CU_ASSERT(d->i == i); + + return d; +} + +static int compare(const void * v1, const void * v2) { + const int i1 = (const int)v1, i2 = (const int)v2; + return i1 - i2; +} + +static void splay_1(void) { + tree_t t; + tree_init(&t, compare); + + CU_ASSERT(tree_min(&t) == NULL); + CU_ASSERT(tree_max(&t) == NULL); + CU_ASSERT(t.compare != NULL); + + CU_ASSERT(tree_root(&t) == NULL); + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); +} + +static void splay_2(void) { + tree_t t; + tree_init(&t, compare); + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + for (int i=1; i<=COUNT; i++) + __insert(&t, i); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_root(&t) != NULL); + CU_ASSERT(tree_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + CU_ASSERT(i == (int)tree_min(&t)->key); + CU_ASSERT(COUNT == (int)tree_max(&t)->key); + __remove(&t, (int)tree_min(&t)->key); + CU_ASSERT(tree_size(&t) + i == COUNT); + } + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + for (int i=1; i<=COUNT; i++) + __insert(&t, i); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_root(&t) != NULL); + CU_ASSERT(tree_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + CU_ASSERT(1 == (int)tree_min(&t)->key); + CU_ASSERT(COUNT - i + 1 == (int)tree_max(&t)->key); + __remove(&t, (int)tree_max(&t)->key); + CU_ASSERT(tree_size(&t) + i == COUNT); + } + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); +} + +static void splay_3(void) { + tree_t t; + tree_init(&t, compare); + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + for (int i=1; i<=COUNT; i++) + __insert(&t, COUNT - i + 1); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_root(&t) != NULL); + CU_ASSERT(tree_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + CU_ASSERT(1 == (int)tree_min(&t)->key); + CU_ASSERT(COUNT - i + 1 == (int)tree_max(&t)->key); + __remove(&t, (int)tree_max(&t)->key); + CU_ASSERT(tree_size(&t) + i == COUNT); + } + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + for (int i=1; i<=COUNT; i++) + __insert(&t, COUNT - i + 1); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_root(&t) != NULL); + CU_ASSERT(tree_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + CU_ASSERT(i == (int)tree_min(&t)->key); + CU_ASSERT(COUNT == (int)tree_max(&t)->key); + __remove(&t, (int)tree_min(&t)->key); + CU_ASSERT(tree_size(&t) + i == COUNT); + } + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); +} + +static void splay_4(void) { + tree_t t; + tree_init(&t, compare); + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) + __insert(&t, random()); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + __remove(&t, (int)tree_min(&t)->key); + CU_ASSERT(tree_size(&t) + i == COUNT); + } + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) + __insert(&t, random()); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + __remove(&t, (int)tree_max(&t)->key); + CU_ASSERT(tree_size(&t) + i == COUNT); + } + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) + __insert(&t, random()); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + __remove(&t, (int)tree_root(&t)->key); + CU_ASSERT(tree_size(&t) + i == COUNT); + } + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) + __insert(&t, random()); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_size(&t) == COUNT); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) { + __remove(&t, random()); + CU_ASSERT(tree_size(&t) + i == COUNT); + } + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); +} + +static void splay_5(void) { + tree_t t; + tree_init(&t, compare); + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) + __insert(&t, random()); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_size(&t) == COUNT); + data_t * d; + + tree_iter_t it; + + int key = 0; + tree_iter_init(&it, &t, TI_FLAG_FWD); + tree_for_each(&it, d, node) { + CU_ASSERT(key < d->i); + key = d->i; + } + + key = INT32_MAX; + tree_iter_init(&it, &t, TI_FLAG_BWD); + tree_for_each(&it, d, node) { + CU_ASSERT(d->i < key); + key = d->i; + } +} + +static void splay_6(void) { + tree_t t; + tree_init(&t, compare); + + data_t * d; + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) + __insert(&t, random()); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + tree_iter_t it; + tree_iter_init(&it, &t, TI_FLAG_FWD); + + int key = 0; + tree_for_each(&it, d, node) { + CU_ASSERT(key < d->i); + key = d->i; + } + + __remove(&t, (int)tree_min(&t)->key); + + if (0 < tree_size(&t)) { + CU_ASSERT(tree_min(&t) != NULL); + } else if (tree_size(&t) <= 0) { + CU_ASSERT(tree_min(&t) == NULL); + } + + CU_ASSERT(tree_size(&t) + i == COUNT); + } +} + +static void splay_7(void) { + tree_t t; + tree_init(&t, compare); + + data_t * d; + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) + __insert(&t, random()); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + tree_iter_t it; + + int key = INT32_MAX; + tree_iter_init(&it, &t, TI_FLAG_BWD); + tree_for_each(&it, d, node) { + CU_ASSERT(d->i < key); + key = d->i; + } + + __remove(&t, (int)tree_max(&t)->key); + + if (0 < tree_size(&t)) { + CU_ASSERT(tree_max(&t) != NULL); + } else if ( tree_size(&t) <= 0) { + CU_ASSERT(tree_max(&t) == NULL); + } + + CU_ASSERT(tree_size(&t) + i == COUNT); + } +} + +void splay_test(void) { + CU_pSuite suite = CU_add_suite("splay", init_splay, clean_splay); + if (NULL == suite) + return; + + if (CU_add_test(suite, "test of --> splay_1", splay_1) == NULL) return; + if (CU_add_test(suite, "test of --> splay_2", splay_2) == NULL) return; + if (CU_add_test(suite, "test of --> splay_3", splay_3) == NULL) return; + if (CU_add_test(suite, "test of --> splay_4", splay_4) == NULL) return; + if (CU_add_test(suite, "test of --> splay_5", splay_5) == NULL) return; + if (CU_add_test(suite, "test of --> splay_6", splay_6) == NULL) return; + if (CU_add_test(suite, "test of --> splay_7", splay_7) == NULL) return; +} diff --git a/clib/cunit/splay.h b/clib/cunit/splay.h new file mode 100644 index 0000000..2cc256d --- /dev/null +++ b/clib/cunit/splay.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void splay_test(void); diff --git a/clib/cunit/tree.c b/clib/cunit/tree.c new file mode 100644 index 0000000..2f67ab7 --- /dev/null +++ b/clib/cunit/tree.c @@ -0,0 +1,398 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#define COUNT 30000 +#define SEED 22 + +slab_t slab; + +typedef struct { + tree_node_t node; + int i; + float f; +} data_t; + +static int init_tree(void) { + slab_init(&slab, "my_slab", sizeof(data_t), 4096); + return 0; +} + +static int clean_tree(void) { + slab_delete(&slab); + return 0; +} + +static void __insert(tree_t * t, int i) { + data_t * d = (data_t *)slab_alloc(&slab); + + d->i = i; + d->f = (float)i; + + tree_node_init(&d->node, (const void *)(d->i)); + + if (tree_insert(t, &d->node) < 0) { + err_t * err = err_get(); + fprintf(stderr, "%s(%d): UNEXPECTED: %.*s\n", + err_file(err), err_line(err), err_size(err), + (const char *)err_data(err)); + } +} + +static data_t * __remove(tree_t * t, int i) { + tree_node_t * n = tree_find(t, (const void *)i); + if (n == NULL) tree_dump(t, stdout); + CU_ASSERT_PTR_NOT_NULL_FATAL(n); + + tree_remove(t, n); + CU_ASSERT_PTR_NULL(tree_node_parent(n)); + CU_ASSERT_PTR_NULL(tree_node_left(n)); + CU_ASSERT_PTR_NULL(tree_node_right(n)); + CU_ASSERT_PTR_NOT_NULL(tree_node_key(n)); + + data_t * d = container_of(n, data_t, node); + CU_ASSERT_PTR_NOT_NULL_FATAL(n); + + if (0 <= i) + CU_ASSERT(d->i == i); + + return d; +} + +static int compare(const void * v1, const void * v2) { + const int i1 = (const int)v1, i2 = (const int)v2; + return i1 - i2; +} + +static void tree_1(void) { + tree_t t; + tree_init(&t, compare); + + CU_ASSERT(tree_min(&t) == NULL); + CU_ASSERT(tree_max(&t) == NULL); + CU_ASSERT(t.compare != NULL); + + CU_ASSERT(tree_root(&t) == NULL); + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); +} + +static void tree_2(void) { + tree_t t; + tree_init(&t, compare); + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + for (int i=1; i<=COUNT; i++) + __insert(&t, i); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_root(&t) != NULL); + CU_ASSERT(tree_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + CU_ASSERT(i == (int)tree_min(&t)->key); + CU_ASSERT(COUNT == (int)tree_max(&t)->key); + __remove(&t, (int)tree_min(&t)->key); + CU_ASSERT(tree_size(&t) + i == COUNT); + } + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + for (int i=1; i<=COUNT; i++) + __insert(&t, i); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_root(&t) != NULL); + CU_ASSERT(tree_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + CU_ASSERT(1 == (int)tree_min(&t)->key); + CU_ASSERT(COUNT - i + 1 == (int)tree_max(&t)->key); + __remove(&t, (int)tree_max(&t)->key); + CU_ASSERT(tree_size(&t) + i == COUNT); + } + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); +} + +static void tree_3(void) { + tree_t t; + tree_init(&t, compare); + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + for (int i=1; i<=COUNT; i++) + __insert(&t, COUNT - i + 1); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_root(&t) != NULL); + CU_ASSERT(tree_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + CU_ASSERT(1 == (int)tree_min(&t)->key); + CU_ASSERT(COUNT - i + 1 == (int)tree_max(&t)->key); + __remove(&t, (int)tree_max(&t)->key); + CU_ASSERT(tree_size(&t) + i == COUNT); + } + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + for (int i=1; i<=COUNT; i++) + __insert(&t, COUNT - i + 1); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_root(&t) != NULL); + CU_ASSERT(tree_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + CU_ASSERT(i == (int)tree_min(&t)->key); + CU_ASSERT(COUNT == (int)tree_max(&t)->key); + __remove(&t, (int)tree_min(&t)->key); + CU_ASSERT(tree_size(&t) + i == COUNT); + } + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); +} + +static void tree_4(void) { + tree_t t; + tree_init(&t, compare); + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) + __insert(&t, random()); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + __remove(&t, (int)tree_min(&t)->key); + CU_ASSERT(tree_size(&t) + i == COUNT); + } + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) + __insert(&t, random()); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + __remove(&t, (int)tree_max(&t)->key); + CU_ASSERT(tree_size(&t) + i == COUNT); + } + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) + __insert(&t, random()); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + __remove(&t, (int)tree_root(&t)->key); + CU_ASSERT(tree_size(&t) + i == COUNT); + } + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) + __insert(&t, random()); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_size(&t) == COUNT); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) { + __remove(&t, random()); + CU_ASSERT(tree_size(&t) + i == COUNT); + } + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); +} + +static void tree_5(void) { + tree_t t; + tree_init(&t, compare); + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) + __insert(&t, random()); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_size(&t) == COUNT); + + data_t * d; + + tree_iter_t it; + + int key = 0; + tree_iter_init(&it, &t, TI_FLAG_FWD); + tree_for_each(&it, d, node) { + CU_ASSERT(key < d->i); + key = d->i; + } + + key = INT32_MAX; + tree_iter_init(&it, &t, TI_FLAG_BWD); + tree_for_each(&it, d, node) { + CU_ASSERT(d->i < key); + key = d->i; + } +} + +static void tree_6(void) { + tree_t t; + tree_init(&t, compare); + + data_t * d; + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) + __insert(&t, random()); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + tree_iter_t it; + tree_iter_init(&it, &t, TI_FLAG_FWD); + + int key = 0; + tree_for_each(&it, d, node) { + CU_ASSERT(key < d->i); + key = d->i; + } + + __remove(&t, (int)tree_min(&t)->key); + + if (0 < tree_size(&t)) { + CU_ASSERT(tree_min(&t) != NULL); + } else if (tree_size(&t) <= 0) { + CU_ASSERT(tree_min(&t) == NULL); + } + + CU_ASSERT(tree_size(&t) + i == COUNT); + } +} + +static void tree_7(void) { + tree_t t; + tree_init(&t, compare); + + data_t * d; + + CU_ASSERT(tree_empty(&t) == true); + CU_ASSERT(tree_size(&t) == 0); + + srandom(SEED); + for (int i=1; i<=COUNT; i++) + __insert(&t, random()); + + CU_ASSERT(tree_empty(&t) == false); + CU_ASSERT(tree_size(&t) == COUNT); + + for (int i=1; i<=COUNT; i++) { + tree_iter_t it; + + int key = INT32_MAX; + tree_iter_init(&it, &t, TI_FLAG_BWD); + tree_for_each(&it, d, node) { + CU_ASSERT(d->i < key); + key = d->i; + } + + __remove(&t, (int)tree_max(&t)->key); + + if (0 < tree_size(&t)) { + CU_ASSERT(tree_max(&t) != NULL); + } else if ( tree_size(&t) <= 0) { + CU_ASSERT(tree_max(&t) == NULL); + } + + CU_ASSERT(tree_size(&t) + i == COUNT); + } +} + + +void tree_test(void) { + CU_pSuite suite = CU_add_suite("tree", init_tree, clean_tree); + if (NULL == suite) + return; + + if (CU_add_test(suite, "test of --> tree_1", tree_1) == NULL) + goto error; + if (CU_add_test(suite, "test of --> tree_2", tree_2) == NULL) + goto error; + if (CU_add_test(suite, "test of --> tree_3", tree_3) == NULL) + goto error; + if (CU_add_test(suite, "test of --> tree_4", tree_4) == NULL) + goto error; + if (CU_add_test(suite, "test of --> tree_5", tree_5) == NULL) + goto error; + if (CU_add_test(suite, "test of --> tree_6", tree_6) == NULL) + goto error; + if (CU_add_test(suite, "test of --> tree_7", tree_7) == NULL) + goto error; + + if (false) { + err_t * err; +error: + err = err_get(); + fprintf(stderr, "%s(%d): UNEXPECTED: %.*s\n", + err_file(err), err_line(err), err_size(err), + (const char *)err_data(err)); + } + +} diff --git a/clib/cunit/tree.h b/clib/cunit/tree.h new file mode 100644 index 0000000..264670c --- /dev/null +++ b/clib/cunit/tree.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void tree_test(void); diff --git a/clib/cunit/vector.c b/clib/cunit/vector.c new file mode 100644 index 0000000..7ead584 --- /dev/null +++ b/clib/cunit/vector.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include + +#include +#include + +#include + +#define COUNT 10000 +#define SEED 41 + +static int init_vector(void) { + return 0; +} + +static int clean_vector(void) { + return 0; +} + +static void vector_1(void) { + vector_t v; + + CU_ASSERT(vector_init(&v, "my_vector", 0) == -1) + err_t * err = err_get(); + fprintf(stderr, "%s(%d): %.*s\n", + err_file(err), err_line(err), err_size(err), + (const char *)err_data(err)); + + for (size_t i=VECTOR_ELEM_MIN; i<=VECTOR_ELEM_MAX; i++) { + CU_ASSERT(vector_init(&v, "my_vector", i) == 0); + + CU_ASSERT(vector_size(&v) == 0); + CU_ASSERT(vector_pages(&v) == 0); + CU_ASSERT(vector_capacity(&v) == 0); + CU_ASSERT(vector_elem_size(&v) == i); + CU_ASSERT(vector_elem_size(&v) * v.hdr.elem_num <= v.hdr.page_size); + + CU_ASSERT(vector_delete(&v) == 0); + } +} + +uint32_t crc2; + +static void vector_2(void) { + vector_t v; + + CU_ASSERT(vector_init(&v, "my_vector.bin", 1) == 0); + CU_ASSERT(vector_size(&v) == 0); + CU_ASSERT(vector_pages(&v) == 0); + CU_ASSERT(vector_capacity(&v) == 0); + CU_ASSERT(vector_elem_size(&v) == 1); + + CU_ASSERT(vector_size(&v, COUNT) == COUNT); + CU_ASSERT(COUNT <= vector_capacity(&v)); + CU_ASSERT(COUNT <= vector_size(&v)); + CU_ASSERT(3 <= vector_pages(&v)); + + for (int i=0; i + * @date 2010-2011 + */ + +#ifndef __DB_H__ +#define __DB_H__ + +#include +#include +#include + +#include "exception.h" + +/* ======================================================================= */ + +#define DB_ROW SQLITE_ROW //!< Statement contains another row @hideinitializer +#define DB_DONE SQLITE_DONE //!< Statement contains no more rows @hideinitializer +#define DB_OK SQLITE_OK //!< Database command ok @hideinitializer + +#define DB 4 //!< Database exception class +#define SQL 5 //!< Statement exception class + +typedef struct db db_t; //!< Alias for the @em db class +typedef struct statement statement_t; //!< Alias for the @em statement class + +/*! + * @brief Database access class + */ +struct db { +#ifdef SQLITE3 + const char *path; //!< @private + sqlite3 *db; //!< @private +#endif +}; + +typedef enum transaction_type transaction_type_t; //!< Alias for the @em transaction_type enum + +/*! + * @brief Transaction types + */ +enum transaction_type { + tt_ERROR = -1, //!< Invalid / unknown transaction + tt_DEFERRED, //!< Defferred locking transaction + tt_IMMEDIATE, //!< Immediate locking transaction + tt_EXCLUSIVE, //!< Exclusive locking transaction +}; + +#ifdef SQLITE3 +typedef sqlite3_context db_context_t; //!< User-defined database context +typedef sqlite3_value db_value_t; //!< User-defined value +#else +#error MUST define db_context_t and db_value_t +#endif + +/*! + * @brief User-defined function + */ +typedef void (*db_f) (db_context_t *, int, db_value_t **); + +/*! + * @brief Statement class + */ +struct statement { + db_t *db; //!< Database object +#ifdef SQLITE3 + sqlite3_stmt *stmt; //!< @private +#endif +}; + +/* ======================================================================= */ + +#if 0 +extern db_t *db_new(const char *) __nonnull((1)); +#endif + +/*! + * @brief Constructs a @em db object + * @memberof db + * @param self [in] db object @em self pointer + * @param path [in] db file name + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int db_init(db_t * self, const char *path) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Destructs a @em db object + * @memberof db + * @param self [in] db object @em self pointer + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int db_delete(db_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Execute a SQL txt statement + * @memberof db + * @param self [in] db object @em self pointer + * @param fmt [in] printf-like format string + * @param ... [in] printf-like arguments + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int db_execute(db_t * self, const char *fmt, ...) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Open a @em db object + * @memberof db + * @param self [in] db object @em self pointer + * @param flags [in] Open flagsprintf-like format string + * @return 0 on success, non-0 otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int db_open(db_t * self, int flags) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Close a @em db object + * @memberof db + * @param self [in] db object @em self pointer + * @return 0 on success, non-0 otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int db_close(db_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Register a user-defined function on a @em db object + * @memberof db + * @param self [in] db object @em self pointer + * @param name [in] db function name + * @param argc [in] Function argument count + * @param func [in] User-defined function + * @param data [in] User-defined data passed to @em finc + * @return 0 on siccess, non-0 otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int db_register_function(db_t * self, const char *name, int argc, + db_f func, void *data) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Start a database transaction + * @memberof db + * @param self [in] db object @em self pointer + * @param type [in] Transaction type + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int db_begin(db_t * self, transaction_type_t type) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Commit a database transaction + * @memberof db + * @param self [in] db object @em self pointer + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int db_commit(db_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Rollback a database transaction + * @memberof db + * @param self [in] db object @em self pointer + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int db_rollback(db_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/* ======================================================================= */ + +#if 0 +extern statement_t *statement_new(db_t *) __nonnull((1)); +#endif + +/*! + * @brief Constructs a @em statement object + * @memberof statement + * @param self [in] statement object @em self pointer + * @param db [in] Pointer to owning database object + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int statement_init(statement_t * self, db_t * db) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Destructs a @em statement object + * @memberof statement + * @param self [in] statement object @em self pointer + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int statement_delete(statement_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Prepare a @em statement object for execution + * @memberof statement + * @param self [in] statement object @em self pointer + * @return DB_ROW when a row is available, DB_DONE when no more rows left + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int statement_prepare(statement_t * self, const char *sql) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Execute the next step of @em statement object + * @memberof statement + * @param self [in] statement object @em self pointer + * @return DB_OK on success, non-0 otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int statement_step(statement_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Reset a @em statement object + * @memberof statement + * @param self [in] statement object @em self pointer + * @return DB_OK on success, non-0 otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int statement_reset(statement_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Finalize a @em statement object + * @memberof statement + * @param self [in] statement object @em self pointer + * @return DB_OK on success, non-0 otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int statement_finalize(statement_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Binds an integer to a bound variable of a @em statement object + * @memberof statement + * @param self [in] statement object @em self pointer + * @param pos [in] Bound varialble position number + * @param val [in] Integer value + * @return DB_OK on success, non-0 otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int statement_bind_int(statement_t * self, int pos, int val) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Binds a long long integer to a bound variable of a @em statement object + * @memberof statement + * @param self [in] statement object @em self pointer + * @param pos [in] Bound varialble position number + * @param val [in] Long Long Integer value + * @return DB_OK on success, non-0 otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int statement_bind_int64(statement_t * self, int pos, int64_t val) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Binds a text string to a bound variable of a @em statement object + * @memberof statement + * @param self [in] statement object @em self pointer + * @param pos [in] Bound varialble position number + * @param val [in] Text string value + * @return DB_OK on success, non-0 otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int statement_bind_text(statement_t * self, int pos, const char *val) +/*! @cond */ +__nonnull((1, 3)) /*! @endcond */ ; + +/*! + * @brief Binds a blob to a bound variable of a @em statement object + * @memberof statement + * @param self [in] statement object @em self pointer + * @param pos [in] Bound varialble position number + * @param val [in] Blob value + * @param len [in] Lengh of blob value (in bytes) + * @return DB_OK on success, non-0 otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int statement_bind_blob(statement_t * self, int pos, const void *val, + int len) +/*! @cond */ +__nonnull((1, 3)) /*! @endcond */ ; + +/*! + * @brief Return an integer from the result set of a @em statement object + * @memberof statement + * @param self [in] statement object @em self pointer + * @param pos [in] Column position number + * @return DB_OK on success, non-0 otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int statement_column_int(statement_t * self, int pos) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return an long long integer from the result set of a @em statement object + * @memberof statement + * @param self [in] statement object @em self pointer + * @param pos [in] Column position number + * @return DB_OK on success, non-0 otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int64_t statement_column_int64(statement_t * self, int pos) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the length of a column from the result set of a @em statement object + * @memberof statement + * @param self [in] statement object @em self pointer + * @param pos [in] Column position number + * @return DB_OK on success, non-0 otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int statement_column_bytes(statement_t * self, int pos) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return a text string from the result set of a @em statement object + * @memberof statement + * @param self [in] statement object @em self pointer + * @param pos [in] Column position number + * @return DB_OK on success, non-0 otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern const unsigned char *statement_column_text(statement_t * self, int pos) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return a blob from the result set of a @em statement object + * @memberof statement + * @param self [in] statement object @em self pointer + * @param pos [in] Column position number + * @return DB_OK on success, non-0 otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern const void *statement_column_blob(statement_t * self, int pos) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +#ifdef SQLITE3 +#define DBERR(d) ({ \ + err_t * _e = (err_t *)malloc(sizeof(*_e) + ERR_DATA_SIZE); \ + memset(_e, 0, sizeof(*_e) + ERR_DATA_SIZE); \ + _e->size = snprintf(_e->data, ERR_DATA_SIZE, "%s (code=%d)", \ + sqlite3_errmsg(d), sqlite3_errcode(d)); \ + _e->magic = ERR_MAGIC, _e->type = DB, _e->code = sqlite3_errcode(d); \ + _e->file = __FILE__, _e->line = __LINE__; \ + err_put(_e); \ + }) + +#define SQLERR(d,s) ({ \ + err_t * _e = (err_t *)malloc(sizeof(*_e) + ERR_DATA_SIZE); \ + memset(_e, 0, sizeof(*_e) + ERR_DATA_SIZE); \ + _e->size = snprintf(_e->data, ERR_DATA_SIZE, "'%s' => %s (code=%d)", \ + sqlite3_sql(s), sqlite3_errmsg(d), \ + sqlite3_errcode(d)); \ + _e->magic = ERR_MAGIC, _e->type = SQL, _e->code = sqlite3_errcode(d); \ + _e->file = __FILE__, _e->line = __LINE__; \ + err_put(_e); \ + }) +#if 0 +#define throw_db(x) ({ \ + char __e[strlen(sqlite3_errmsg((x))) + 32]; \ + __exc_throw(DB,((void*)__e), \ + sprintf(__e, "(%d) %s", \ + sqlite3_errcode((x)), \ + sqlite3_errmsg((x))), \ + __FILE__, __LINE__); \ + }) + +#define throw_statement(x,s) ({ \ + char __e[strlen(sqlite3_errmsg((x))) + \ + strlen(sqlite3_sql((s))) + 32]; \ + __exc_throw(STATEMENT,((void*)__e), \ + sprintf(__e, "(%d) %s\n%s", \ + sqlite3_errcode((x)), \ + sqlite3_errmsg((x)), \ + sqlite3_sql((s))), \ + __FILE__, __LINE__); \ + }) +#endif +#else +#error FIX ME +#endif + +/* ======================================================================= */ + +#endif /* __DB_H__ */ diff --git a/clib/dispatch.h b/clib/dispatch.h new file mode 100644 index 0000000..9cb82ef --- /dev/null +++ b/clib/dispatch.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: dispatch.h + * Author: Shaun Wetzstein + * Descr: File descriptor activity callback dispatcher + * Note: + * Date: 10/03/10 + */ + +#ifndef __DISPATCH_H__ +#define __DISPATCH_H__ + +#include +#include + +#include + +#include "attribute.h" +#include "builtin.h" +#include "assert.h" + +#include "tree.h" +#include "array.h" + +#define DISPATCH_READ EPOLLIN +#define DISPATCH_WRITE EPOLLOUT +#define DISPATCH_READ_HUP EPOLLRDHUP +#define DISPATCH_READ_PRIORITY EPOLLPRI +#define DISPATCH_ERROR EPOLLERR +#define DISPATCH_WRITE_HUP EPOLLRDHUP +#define DISPATCH_EDGE_TRIGGER EPOLLET +#define DISPATCH_ONE_SHOT EPOLLONESHOT + +/* ======================================================================= */ + +typedef struct dispatch dispatch_t; +typedef struct dispatch_callback dispatch_callback_t; +typedef struct dispatch_event dispatch_event_t; + +typedef int (*dispatch_f) (dispatch_event_t *, void *); + +struct dispatch { + int fd; + array_t events; +}; + +struct dispatch_callback { + int fd; + void *data; + dispatch_f func; +}; + +struct dispatch_event { + int fd; + uint32_t events; +}; + +/* ======================================================================= */ + +extern void dispatch_init(dispatch_t *) __nonnull((1)); + +extern void dispatch_delete(dispatch_t *) __nonnull((1)); + +extern int dispatch_fileno(dispatch_t *) __nonnull((1)); + +extern int dispatch_add(dispatch_t *, int, uint32_t, dispatch_callback_t *) +__nonnull((1, 4)); +extern void dispatch_remove(dispatch_t *, int) __nonnull((1)); + +#define dispatch_wait(...) STRCAT(dispatch_wait, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern void dispatch_wait1(dispatch_t *) __nonnull((1)); +extern void dispatch_wait2(dispatch_t *, int) __nonnull((1)); + +/* ======================================================================= */ + +#endif /* __DISPATCH_H__ */ diff --git a/clib/ecc.h b/clib/ecc.h new file mode 100644 index 0000000..c6f67f7 --- /dev/null +++ b/clib/ecc.h @@ -0,0 +1,217 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! + * @file ecc.h + * @brief FSP-2 & P8 ECC functions + * @details These functions are used to insert and remove FSP-2 & P8 ECC + * bytes. 8-bits of ECC is inserted every 8 bytes of data such as: + * XXxxXXxxXXxxXXxxYY (where XXxx is 4 nibbles of data and YY is + * 2 nibbles of ECC) + * @author Shaun Wetzstein + * @date 2011 + */ + +#ifndef __ECC_H__ +#define __ECC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +/** Status for the ECC removal function. */ +enum ecc_status + { + CLEAN=0, //< No ECC Error was detected. + CORRECTED=1, //< ECC error detected and corrected. + UNCORRECTABLE=2 //< ECC error detected and uncorrectable. + }; +typedef enum ecc_status ecc_status_t; + +enum ecc_bitfields + { + GD = 0xff, //< Good, ECC matches. + UE = 0xfe, //< Uncorrectable. + E0 = 71, //< Error in ECC bit 0 + E1 = 70, //< Error in ECC bit 1 + E2 = 69, //< Error in ECC bit 2 + E3 = 68, //< Error in ECC bit 3 + E4 = 67, //< Error in ECC bit 4 + E5 = 66, //< Error in ECC bit 5 + E6 = 65, //< Error in ECC bit 6 + E7 = 64 //< Error in ECC bit 7 + }; + +/*! + * @brief Compute the 8-bit ECC (SFC) value given an array of 8 + * unsigned char data values + * @param data [in] Input data + * @return 8-bit SFC ECC value + */ + extern uint8_t sfc_ecc(uint8_t __data[8]); + +/*! + * @brief Copy bytes from the source buffer to the destination buffer while + * computing and injecting an 8-bit SFC ECC value for every 8-bytes + * of source buffer read + * @param __dst [in] Destination buffer + * @param __dst_sz [in] Destination buffer size (in bytes) which must be large + * enough to store both the data and the ECC bytes + * @param __src [in] Source buffer + * @param __src_sz [in] Source buffer size (in bytes) which must be a multiple + * of 8 bytes + * @return -1 if an error occurs, number of bytes copied (including ECC bytes) + * otherwise. + * EINVAL if __src_sz is 0 or not a multiple of 8 bytes + * ENOBUFS if __dst_sz is not large enough to store the ECC bytes + * + */ + extern ssize_t sfc_ecc_inject(void *__restrict __dst, size_t __dst_sz, + const void *__restrict __src, + size_t __src_sz) +/*! @cond */ + __nonnull((1, 3)) /*! @endcond */ ; + +/*! + * @brief Copy bytes from the source buffer to the destination buffer while + * computing and removing an 8-bit SFC ECC value for every 9-bytes + * of source buffer read + * @param __dst [in] Destination buffer + * @param __dst_sz [in] Destination buffer size (in bytes) which must be large + * enough to store the data (after ECC removal) + * @param __src [in] Source buffer + * @param __src_sz [in] Source buffer size (in bytes) which must be a multiple + * 9 bytes + * @return -1 if an error occurs, number of bytes copied (excluding ECC bytes) + * otherwise. if an ECC mismatch error occurs the function returns + * immediately and the return code indicates the number of bytes processed + * prior to the ECC error. + * EINVAL if __src_sz is 0 or not a multiple of 9 bytes + * ENOBUFS if __dst_sz is not large enough to store the ECC bytes + */ + extern ssize_t sfc_ecc_remove(void *__restrict __dst, size_t __dst_sz, + const void *__restrict __src, + size_t __src_sz) +/*! @cond */ + __nonnull((1, 3)) /*! @endcond */ ; + +/*! + * @brief Hexdump the contents of a memory buffer to an output stream. + * This is a buck-standard hexdump except it issolates the ECC value + * in a separate column for easy debug. + * @param __out [in] Output stream + * @param __addr [in] Starting put to display (in bytes) + * @param __buf [in] Data buffer + * @param __buf_sz [in] Data buffer size (in bytes) + * @return -1 if an error occurs, number of bytes copied (excluding ECC bytes) + * otherwise. if an ECC mismatch error occurs the function highlights the + * the corrupted data with red ANSI. + */ + extern void sfc_ecc_dump(FILE * __out, uint32_t __addr, + void *__restrict __buf, size_t __buf_sz) +/*! @cond */ + __nonnull((1, 3)) /*! @endcond */ ; + +/*! + * @brief Copy bytes from the source buffer to the destination buffer while + * computing and injecting an 8-bit P8 ECC value for every 8-bytes + * of source buffer read + * @param __dst [in] Destination buffer + * @param __dst_sz [in] Destination buffer size (in bytes) which must be large + * enough to store both the data and the ECC bytes + * @param __src [in] Source buffer + * @param __src_sz [in] Source buffer size (in bytes) which must be a multiple + * of 8 bytes + * @return -1 if an error occurs, number of bytes copied (including ECC bytes) + * otherwise. + * EINVAL if __src_sz is 0 or not a multiple of 8 bytes + * ENOBUFS if __dst_sz is not large enough to store the ECC bytes + * + */ + extern ssize_t p8_ecc_inject(void *__restrict __dst, size_t __dst_sz, + const void *__restrict __src, + size_t __src_sz) +/*! @cond */ + __nonnull((1, 3)) /*! @endcond */ ; + +/*! + * @brief Copy bytes from the source buffer to the destination buffer while + * computing and removing an 8-bit P8 ECC value for every 9-bytes + * of source buffer read + * @param __dst [in] Destination buffer + * @param __dst_sz [in] Destination buffer size (in bytes) which must be large + * enough to store the data (after ECC removal) + * @param __src [in] Source buffer + * @param __src_sz [in] Source buffer size (in bytes) which must be a multiple + * 9 bytes + * @return 0 - CLEAN for success + * 1 - CORRECTED error - _src [in] buffer changed + * 2 - UNCORRECTABLE error. + */ + extern ecc_status_t p8_ecc_remove(void *__restrict __dst, size_t __dst_sz, + void *__restrict __src, + size_t __src_sz) + +/*! @cond */ + __nonnull((1, 3)) /*! @endcond */ ; + +/*! + * @brief Copy bytes from the source buffer to the destination buffer while + * computing and removing an 8-bit P8 ECC value for every 9-bytes + * of source buffer read + * @param __dst [in] Destination buffer + * @param __dst_sz [in] Destination buffer size (in bytes) which must be large + * enough to store the data (after ECC removal) + * @param __src [in] Source buffer + * @param __src_sz [in] Source buffer size (in bytes) which must be a multiple + * 9 bytes + * @return -1 if an error occurs, number of bytes copied (excluding ECC bytes) + * otherwise. if an ECC mismatch error occurs the function returns + * immediately and the return code indicates zero number of bytes + * processed + * EINVAL if __src_sz is 0 or not a multiple of 9 bytes + * ENOBUFS if __dst_sz is not large enough to store the ECC bytes + */ + extern ssize_t p8_ecc_remove_size(void *__restrict __dst, size_t __dst_sz, + void *__restrict __src, size_t __src_sz) + +/*! @cond */ + __nonnull((1, 3)) /*! @endcond */ ; + +/*! + * @brief Hexdump the contents of a memory buffer to an output stream. + * This is a buck-standard hexdump except it issolates the P8 ECC + * value in a separate column for easy debug. + * @param __out [in] Output stream + * @param __addr [in] Starting put to display (in bytes) + * @param __buf [in] Data buffer + * @param __buf_sz [in] Data buffer size (in bytes) + * @return -1 if an error occurs, number of bytes copied (excluding ECC bytes) + * otherwise. if an ECC mismatch error occurs the function highlights the + * the corrupted data with red ANSI. + */ + extern void p8_ecc_dump(FILE * __out, uint32_t __addr, + void *__restrict __buf, size_t __buf_sz) +/*! @cond */ + __nonnull((1, 3)) /*! @endcond */ ; + +#ifdef __cplusplus +} +#endif +#endif /* __ECC_H__ */ diff --git a/clib/err.h b/clib/err.h new file mode 100644 index 0000000..5b326d7 --- /dev/null +++ b/clib/err.h @@ -0,0 +1,290 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! + * @file err.h + * @brief CLib error container + * @details Error is a special container that contains a library error + * @details For example, + * @code + * #include + * #include + * #include + * + * FIX ME + * + * } + * @endcode + * @author Shaun Wetzstein + * @date 2013 + */ + +#ifndef __ERR_H__ +#define __ERR_H__ + +#include + +#include +#include + +#include "nargs.h" +#include "list.h" + +/* ======================================================================= */ + +#define ERR_NONE 0 +#define ERR_ERRNO 1 +#define ERR_UNEXPECTED 2 +#define ERR_VERSION 3 +#define ERR_LAST 4 + +#define ERR_MAGIC 0x45525220 // "ERR " +#define ERR_DATA_SIZE 1000 + +#define INIT_ERR {ERR_MAGIC, 0, 0, NULL, 0, {0,}, 0} + +#define ERROR(t,c,f, ...) ({ \ + err_t * _e = (err_t *)malloc(sizeof(*_e) + ERR_DATA_SIZE); \ + memset(_e, 0, sizeof(*_e) + ERR_DATA_SIZE); \ + _e->size = snprintf(_e->data, ERR_DATA_SIZE, (f), ##__VA_ARGS__); \ + _e->magic = ERR_MAGIC, _e->type = (t), _e->code = (c); \ + _e->file = __FILE__, _e->line = __LINE__; \ + err_put(_e); \ + }) + +#define ERRNO(e) \ + ERROR(ERR_ERRNO, (e), "%s => %s (errno=%d)", __func__, strerror(e), e) + +#define UNEXPECTED(f, ...) \ + ERROR(ERR_UNEXPECTED, -1, (f), ##__VA_ARGS__) + +#define VERSION(v,f, ...) \ + ERROR(ERR_VERSION, v, (f), ##__VA_ARGS__) + +/*! + * @brief error container + */ +struct err { //!< The error class + unsigned long magic; //!< Error magic number + list_node_t node; //!@ private + + int type, code; //!< Error type & code + const char *file; //!< Error source file name + int line; //!< Error source line number + + int size; //!< Extended error data size + char data[]; //!< Extended error data +}; +typedef struct err err_t; //!< Alias for the @em error class + +/* ======================================================================= */ + +extern void err_delete(err_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the last library error + * @details For example, + * @code + * ... + * err_clear(err_get()); + * ... + * @endcode + * @memberof error + * @param self [in] error object @em self pointer + * @return None + */ +extern err_t *err_get(void); + +/*! + * @brief Set the last library error + * @details For example, + * @code + * ... + * err_out(&e); + * ... + * @endcode + * @memberof error + * @param self [in] error object @em self pointer + * @return None + */ +extern void err_put(err_t *); + +/*! + * @fn int err_type(err_t *self, [ int type ]) + * @brief Set or get the type from an @em error + * @details For example, + * @code + * ... + * err_type(&e, MY_ERROR_TYPE); + * if (err_type(&e) == MY_ERROR_TYPE) + * ... + * @endcode + * @memberof error + * @param self [in] error object @em self pointer + * @param type [in] error type + * @return current error type + */ +/*! @cond */ +#define err_type(...) STRCAT(err_type, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern int err_type1(err_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern void err_type2(err_t *, int) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @fn int err_code(err_t *self, [ int code ]) + * @brief Set or get the code from an @em error + * @details For example, + * @code + * ... + * err_code(&e, errno); + * if (err_code(&e) == EINVAL) + * ... + * @endcode + * @memberof error + * @param self [in] error object @em self pointer + * @param code [in] error code + * @return current error code + */ +/*! @cond */ +#define err_code(...) STRCAT(err_code, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern int err_code1(err_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern void err_code2(err_t *, int) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @fn int err_file(err_t *self, [ const char * file ]) + * @brief Set or get the file name from an @em error + * @details For example, + * @code + * ... + * err_file(&e, "example.txt"); + * if (strcmp(err_file(&e), "example.txt") == 0) + * ... + * @endcode + * @memberof error + * @param self [in] error object @em self pointer + * @param file [in] error file name + * @return current file name + */ +/*! @cond */ +#define err_file(...) STRCAT(err_file, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern const char *err_file1(err_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern void err_file2(err_t *, const char *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @fn int err_line(err_t *self, [ int line ]) + * @brief Set or get the line number from an @em error + * @details For example, + * @code + * ... + * err_line(&e, errno); + * if (err_line(&e) == EINVAL) + * ... + * @endcode + * @memberof error + * @param self [in] error object @em self pointer + * @param line [in] error line + * @return current error line + */ +/*! @cond */ +#define err_line(...) STRCAT(err_line, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern int err_line1(err_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern void err_line2(err_t *, int) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @fn int err_data(err_t *self, [ const void *data ]) + * @brief Set or get the extended data from an @em error + * @details For example, + * @code + * ... + * err_data(&e, extended_data); + * memcpy(err_log, err_data(&e), err_size(&e)); + * ... + * @endcode + * @memberof error + * @param self [in] error object @em self pointer + * @param data [in] error extended data + * @return current error extended data + */ +/*! @cond */ +#define err_data(...) STRCAT(err_data, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern const void *err_data1(err_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern void err_data2(err_t *, int, const void *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @brief Get the data size from an @em error + * @details For example, + * @code + * ... + * err_size(&e, data_size); + * if (err_size(&e) <= 0) + * ... + * @endcode + * @memberof error + * @param self [in] error object @em self pointer + * @return current error data size + */ +extern int err_size(err_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Get the type name from an @em error + * @details For example, + * @code + * ... + * if (strcmp(err_name(&e), "errno") == 0) + * ... + * @endcode + * @memberof error + * @param self [in] error object @em self pointer + * @return current error data size + */ +/*! @cond */ +extern const char *err_type_name(err_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/* ======================================================================= */ + +#endif /* __ERR_H__ */ diff --git a/clib/exception.h b/clib/exception.h new file mode 100644 index 0000000..26eaec2 --- /dev/null +++ b/clib/exception.h @@ -0,0 +1,299 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! + * @file exception.h + * @brief Exceptions for C + * @details This file implements setjump / longjump based exceptions + * @note Using these macros will create an exception context in each thread + * @author Shaun Wetzstein + * @date 2008-2011 + */ + +#ifndef __EXCEPTION_H__ +#define __EXCEPTION_H__ + +#include +#include +#include +#include + +#define EX_PAGE_SIZE 4096 //!< Max. size of an exception payload +#define EXC_MAGIC 0x45584350 //!< Magic number, i.e. "EXCP" + +#define ASSERTION 1 //!< Assertion exception class +//#define UNEXPECTED 2 //!< Unexpected result exception class +//#define ERRNO 3 //!< @em errno error exception class +#define EXC_LAST 4 + +typedef struct exception_frame exception_frame_t; //!< Alias for the @em exception_frame class + +/*! + * @brief Exception class + */ +typedef struct { + int ex; //!< Exception class + + const char *file; //!< Source file exception was thrown + int line; //!< Source line exception was thrown + + void *data; //!< User-defined data associated with the exception + int size; //!< Size (in bytes) of the user-defined data +} exception_t; + +/*! + * @brief Exception frame class + */ +struct exception_frame { + unsigned long magic; //!< @private + exception_frame_t *prev; //!< @private + jmp_buf jmp; //!< @private + exception_t exc; //!< @private +}; + +/*! + * @def try + * @brief Simulate a try {...} catch {...} else {...} end_try block + * @hideinitializer + * @details For example, + * @code + * ... + * #define ERROR 4 + * ... + * exception_t * ex; + * ... + * try { + * ... + * } catch (ERROR, ex) { + * fprintf(stderr, "caught ERROR exception at: %s(%d)\n", + * ex->file, ex->line); + * struct ErrorStruct * err = (struct ErrorStruct *)ex->data + * ...format and log err... + * } end_try + * @endcode + */ +#define try \ +do { \ + __exc_init(); \ + exception_frame_t __frame; \ + memset(&__frame, 0, sizeof(__frame)); \ + __frame.magic = EXC_MAGIC, __frame.prev = __exc_get_frame(); \ + __exc_set_frame(&__frame); \ + volatile int __flag = setjmp(__frame.jmp); \ + if (__flag == 0) { + +/*! + * @def catch + * @brief Simulate a try {...} catch {...} else {...} end_try block + * @hideinitializer + * @details For example, + * @code + * ... + * exception_t ex; + * ... + * try { + * .... + * } catch (TYPE1, ex) { + * ... + * } catch (TYPE2, ex) { + * ... + * } catch (TYPE3, ex) { + * ... + * } else (ex) { + * ... + * } end_try + * @endcode + */ +#define catch(x, e) \ + } else if (__flag == (x)) { \ + exception_frame_t * __tmp = __exc_get_frame(); \ + (e) = __tmp->exc, (e).ex = (__flag); \ + +/*! + * @def else + * @brief Simulate a try {...} catch {...} else {...} end_try block + * @hideinitializer + * @details For example, + * @code + * ... + * exception_t ex; + * ... + * try { + * .... + * } catch (TYPE1, ex) { + * ... + * } catch (TYPE2, ex) { + * ... + * } catch (TYPE3, ex) { + * ... + * } else (ex) { + * ... + * } end_try + * @endcode + */ +#define else(e) catch(__flag, (e)) + +/*! + * @def end_try + * @brief Simulate a try {...} catch {...} else {...} end_try block + * @hideinitializer + */ +#define end_try \ + } else { \ + __exc_set_frame(__frame.prev); \ + throw_bytes(__flag, __frame.exc.data, __frame.exc.size); \ + } \ + __exc_set_frame(__frame.prev); \ +} while (0); + +/*! + * @def throw(x, f, ...) + * @brief Throw a C exception with printf-like formatting + * @hideinitializer + * @param x [in] Exception class + * @param d [in] Pointer to user-defined data + * @param s [in] Size of user-defined data (in bytes) + * @details For example, + * @code + * ... + * #define ERRNO_STRING + * ... + * #define throw_errno_string(x) \ + * throw(ERRNO_STRING, "errno=%d : %s", (x), + * (void*)strerror((x)), __FILE__, __LINE__) + * ... + * if (rc < 0) + * throw_errno_string(errno); + * ... + * exception_t ex; + * ... + * catch (ERRNO_STRING ex) { + * fprintf(strerr, "EXCEPTION: errno: %s in file: %s on line: %d\n", + * (char *)ex.data, ex.file, ex.line); + * exit(1); + * } + * @endcode + */ +#define throw(x, f, ...) ({ \ + char __d[EX_PAGE_SIZE]; \ + __exc_throw((x), __d, snprintf(__d, sizeof __d, (f), ##__VA_ARGS__),\ + __FILE__, __LINE__); \ +}) + +/*! + * @def rethrow(x, f, ...) + * @brief Rethrow a C exception from within a try ... catch ... end_try block + * @hideinitializer + * @param e [in] Exception object + * @details For example, + * @code + * ... + * exception_t ex; + * ... + * catch (ERRNO_STRING ex) { + * fprintf(strerr, "EXCEPTION: errno: %s in file: %s on line: %d\n", + * (char *)ex.data, ex.file, ex.line); + * rethrow(ex); + * } + * @endcode + */ +#define rethrow(e) ({ \ + __exc_rethrow((e).ex, (e).data, (e).size, (e).file, (e).line); \ +}) + +/*! + * @def throw_bytes(x, d, s) + * @brief Throw a C exception with user-defined data + * @hideinitializer + * @param x [in] Exception class + * @param d [in] Pointer to user-defined data + * @param s [in] Size of user-defined data (in bytes) + * @details For example, + * @code + * ... + * #define ERRNO_STRING + * ... + * #define throw_errno_string(x) \ + * throw_bytes(ERRNO_STRING, (void*)strerror((x)), + * strlen(strerror((x)), __FILE__, __LINE__) + * ... + * if (rc < 0) + * throw_errno_string(errno); + * ... + * exception_t ex; + * ... + * catch (ERRNO_STRING ex) { + * fprintf(strerr, "EXCEPTION: errno: %s in file: %s on line: %d\n", + * (char *)ex.data, ex.file, ex.line); + * exit(1); + * } + * @endcode + */ +#define throw_bytes(x, d, s) \ + __exc_throw((x), (d), (s), __FILE__, __LINE__) + +/*! + * @def throw_unexpected(x) + * @brief Throw a message (NULL terminated C string), due to an @em + * unexpected error + * @hideinitializer + * @param x [in] Unexpected error message + * @details For example, + * @code + * ... + * rc = foo_function(); + * if (rc < 0) + * throw_unexpected("Invalid return code from foo_function()"); + * ... + * @endcode + */ +#define throw_unexpected(x) ({ \ + __exc_throw(UNEXPECTED,((void*)x), strlen((x)), \ + __FILE__, __LINE__); \ + }) + +/*! + * @def throw_errno(x) + * @brief Throw an errno number, due to an invalid function result. + * @hideinitializer + * @param x [in] Unexpected errno number + * @details For example, + * @code + * ... + * rc = open(...); + * if (rc == -1) + * throw_errno(errno); + * ... + * @endcode + */ +#define throw_errno(x) ({ \ + __exc_throw(ERRNO,(void*)(x), 0, \ + __FILE__, __LINE__); \ + }) + +/*! @cond */ +extern void __exc_init(void); +extern exception_frame_t *__exc_get_frame(void); +extern void __exc_set_frame(exception_frame_t *); +extern int __exc_throw(int, void *, int, const char *, int); +extern int __exc_rethrow(int, void *, int, const char *, int); +extern void __exc_backtrace(const char *, ...); +extern const char *__exc_name(int exc); +/*! @endcond */ + +#endif /* __EXCEPTION_H__ */ diff --git a/clib/hash.h b/clib/hash.h new file mode 100644 index 0000000..2f1cb9d --- /dev/null +++ b/clib/hash.h @@ -0,0 +1,248 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: hash.h + * Author: Shaun Wetzstein + * Descr: Various int32 hash functions + * Note: + * Date: 10/03/10 + */ + +#ifndef __HASH_H__ +#define __HASH_H__ + +#include + +/* ======================================================================= */ + +static inline int32_t int32_hash1(int32_t); +static inline int32_t int32_hash2(int32_t); +static inline int32_t int32_hash3(int32_t); + +typedef uint32_t(*hash_t) (char *, uint32_t); + +static inline uint32_t rs_hash(char *, uint32_t); +static inline uint32_t js_hash(char *, uint32_t); +static inline uint32_t pjw_hash(char *, uint32_t); +static inline uint32_t elf_hash(char *, uint32_t); +static inline uint32_t bkdr_hash(char *, uint32_t); +static inline uint32_t sdbm_hash(char *, uint32_t); +static inline uint32_t djb_hash(char *, uint32_t); +static inline uint32_t dek_hash(char *, uint32_t); +static inline uint32_t bp_hash(char *, uint32_t); +static inline uint32_t fnv_hash(char *, uint32_t); +static inline uint32_t ap_hash(char *, uint32_t); + +/* ======================================================================= */ + +static inline int32_t int32_hash1(int32_t key) +{ + key = ~key + (key << 15); + key = key ^ (key >> 12); + key = key + (key << 2); + key = key ^ (key >> 4); + key = key * 2057; + key = key ^ (key >> 16); + + return key; +} + +static inline int32_t int32_hash2(int32_t key) +{ + key = (key + 0x7ed55d16) + (key << 12); + key = (key ^ 0xc761c23c) ^ (key >> 19); + key = (key + 0x165667b1) + (key << 5); + key = (key + 0xd3a2646c) ^ (key << 9); + key = (key + 0xfd7046c5) + (key << 3); + key = (key ^ 0xb55a4f09) ^ (key >> 16); + + return key; +} + +static inline int32_t int32_hash3(int32_t key) +{ + int32_t c2 = 0x27d4eb2d; + + key = (key ^ 61) ^ (key >> 16); + key = key + (key << 3); + key = key ^ (key >> 4); + key = key * c2; + key = key ^ (key >> 15); + + return key; +} + +/* ======================================================================= */ + +static inline uint32_t rs_hash(char *str, uint32_t len) +{ + uint32_t b = 378551; + uint32_t a = 63689; + uint32_t hash = 0; + uint32_t i = 0; + + for (i = 0; i < len; str++, i++) { + hash = hash * a + (*str); + a = a * b; + } + + return hash; +} + +static inline uint32_t js_hash(char *str, uint32_t len) +{ + uint32_t hash = 1315423911; + uint32_t i = 0; + + for (i = 0; i < len; str++, i++) { + hash ^= ((hash << 5) + (*str) + (hash >> 2)); + } + + return hash; +} + +static inline uint32_t pjw_hash(char *str, uint32_t len) +{ + const uint32_t BitsInUnsignedInt = (uint32_t) (sizeof(uint32_t) * 8); + const uint32_t ThreeQuarters = (uint32_t) ((BitsInUnsignedInt * 3) / 4); + const uint32_t OneEighth = (uint32_t) (BitsInUnsignedInt / 8); + const uint32_t HighBits = + (uint32_t) (0xFFFFFFFF) << (BitsInUnsignedInt - OneEighth); + uint32_t hash = 0; + uint32_t test = 0; + uint32_t i = 0; + + for (i = 0; i < len; str++, i++) { + hash = (hash << OneEighth) + (*str); + + if ((test = hash & HighBits) != 0) { + hash = ((hash ^ (test >> ThreeQuarters)) & (~HighBits)); + } + } + + return hash; +} + +static inline uint32_t elf_hash(char *str, uint32_t len) +{ + uint32_t hash = 0; + uint32_t x = 0; + uint32_t i = 0; + + for (i = 0; i < len; str++, i++) { + hash = (hash << 4) + (*str); + if ((x = hash & 0xF0000000L) != 0) { + hash ^= (x >> 24); + } + hash &= ~x; + } + + return hash; +} + +static inline uint32_t bkdr_hash(char *str, uint32_t len) +{ + uint32_t seed = 131; /* 31 131 1313 13131 131313 etc.. */ + uint32_t hash = 0; + uint32_t i = 0; + + for (i = 0; i < len; str++, i++) { + hash = (hash * seed) + (*str); + } + + return hash; +} + +static inline uint32_t sdbm_hash(char *str, uint32_t len) +{ + uint32_t hash = 0; + uint32_t i = 0; + + for (i = 0; i < len; str++, i++) { + hash = (*str) + (hash << 6) + (hash << 16) - hash; + } + + return hash; +} + +static inline uint32_t djb_hash(char *str, uint32_t len) +{ + uint32_t hash = 5381; + uint32_t i = 0; + + for (i = 0; i < len; str++, i++) { + hash = ((hash << 5) + hash) + (*str); + } + + return hash; +} + +static inline uint32_t dek_hash(char *str, uint32_t len) +{ + uint32_t hash = len; + uint32_t i = 0; + + for (i = 0; i < len; str++, i++) { + hash = ((hash << 5) ^ (hash >> 27)) ^ (*str); + } + return hash; +} + +static inline uint32_t bp_hash(char *str, uint32_t len) +{ + uint32_t hash = 0; + uint32_t i = 0; + + for (i = 0; i < len; str++, i++) { + hash = hash << 7 ^ (*str); + } + + return hash; +} + +static inline uint32_t fnv_hash(char *str, uint32_t len) +{ + const uint32_t fnv_prime = 0x811C9DC5; + uint32_t hash = 0; + uint32_t i = 0; + + for (i = 0; i < len; str++, i++) { + hash *= fnv_prime; + hash ^= (*str); + } + + return hash; +} + +static inline uint32_t ap_hash(char *str, uint32_t len) +{ + uint32_t hash = 0xAAAAAAAA; + uint32_t i = 0; + + for (i = 0; i < len; str++, i++) { + hash ^= ((i & 1) == 0) ? ((hash << 7) ^ (*str) * (hash >> 3)) : + (~((hash << 11) + ((*str) ^ (hash >> 5)))); + } + + return hash; +} + +/* ======================================================================= */ + +#endif /* __HASH_H__ */ diff --git a/clib/heap.h b/clib/heap.h new file mode 100644 index 0000000..3f82acb --- /dev/null +++ b/clib/heap.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: heap.h + * Author: Shaun Wetzstein + * Descr: Arena based (slab) memory allocator + * Note: + * Date: 07/28/10 + */ + +#ifndef __HEAP_H__ +#define __HEAP_H__ + +#include + +#include "slab.h" + +/* ======================================================================= */ + +typedef struct heap heap_t; + +struct heap { + size_t page_size; + size_t alloc_size; + + size_t slab_size; + slab_t *slab[]; +}; + +/* ======================================================================= */ + +#define heap_init(a,c) (__heap_init((h),(a),(c),__FILE__,__LINE__)) +extern void __heap_init(heap_t * self, size_t alloc_size, size_t cache_size, + const char *file, int line); + +#define heap_new(a,c) (__heap_new((a),(c),__FILE__,__LINE__)) +extern heap_t *__heap_new(size_t alloc_size, size_t cache_size, + const char *file, int line); +#define heap_delete(h) (__heap_delete((h),__FILE__,__LINE__)) +extern void __heap_delete(heap_t * self, const char *file, + int line) __nonnull((1)); + +#define heap_alloc(h,s) (__heap_alloc((h),(s),__FILE__,__LINE__)) +extern void *__heap_alloc(heap_t * self, size_t size, const char *file, + int line) __nonnull((1)); +#define heap_free(h,p) (__heap_free((h),(p),__FILE__,__LINE__)) +extern void __heap_free(heap_t * self, void *ptr, const char *file, + int line) __nonnull((1, 2)); + +extern void heap_dump(heap_t * self, FILE * out) __nonnull((1)); + +/* ======================================================================= */ + +#endif /* __HEAP_H__ */ diff --git a/clib/ident.h b/clib/ident.h new file mode 100644 index 0000000..a4b5394 --- /dev/null +++ b/clib/ident.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! + * @file ident.h + * @brief Identification object + * @author Shaun Wetzstein + * @date 2008-2011 + */ + +#ifndef __IDENT_H__ +#define __IDENT_H__ + +#include + +#define IDENT_MAGIC_0 0 +#define IDENT_MAGIC_1 1 +#define IDENT_MAGIC_2 2 +#define IDENT_MAGIC_3 3 +#define IDENT_MAJOR 4 +#define IDENT_MINOR 5 +#define IDENT_PATCH 6 +#define IDENT_FLAGS 7 +#define IDENT_SIZE 8 + +#define INIT_IDENT {0,} + +#define MAGIC_CHECK(i, m) ({ \ + bool rc = (((i)[IDENT_MAGIC_0] != (m)[IDENT_MAGIC_0]) || \ + ((i)[IDENT_MAGIC_1] != (m)[IDENT_MAGIC_1]) || \ + ((i)[IDENT_MAGIC_2] != (m)[IDENT_MAGIC_2]) || \ + ((i)[IDENT_MAGIC_3] != (m)[IDENT_MAGIC_3])); \ + rc; \ + }) + +typedef uint8_t ident_t[IDENT_SIZE]; + +#endif /* __IDENT_H__ */ diff --git a/clib/libclib.h b/clib/libclib.h new file mode 100644 index 0000000..6f6bedc --- /dev/null +++ b/clib/libclib.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __LIBCLIB_H__ +#define __LIBCLIB_H__ + +#define CLIB_MAJOR 0x01 +#define CLIB_MINOR 0x00 +#define CLIB_PATCH 0x00 + +#include + +#include "attribute.h" +#include "version.h" +#include "builtin.h" +#include "assert.h" +#include "ident.h" +#include "nargs.h" +#include "hash.h" +#include "misc.h" +#include "type.h" +#include "min.h" +#include "max.h" +#include "err.h" +#include "raii.h" + +#endif /* __LIBCLIB_H__ */ diff --git a/clib/list.h b/clib/list.h new file mode 100644 index 0000000..db8ef59 --- /dev/null +++ b/clib/list.h @@ -0,0 +1,537 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! + * @file list.h + * @brief List container + * @details + * A List is a data structure (container) used for collecting a sequence of elements. + * List allow for efficient insertion, removal and retreival of elements. + * + * @details For example, + * @code + * #include + * #include + * + * int main(const int argc, const char * argv[]) { + * typedef struct { + * list_node_t node; + * int i; + * float f; + * } data_t; + * + * slab_t s; + * slab_init(&s, sizeof(data_t), 0); + * + * list_t a; + * list_init(&a); + * + * int i; + * for (i=0; i<10; i++) { + * data_t * d = (data_t *)slab_alloc(&s); + * + * d->i = i; + * d->f = (float)i; + * + * list_add_tail(&l, &d->node); + * } + * + * data_t * d; + * list_for_each(&l, d, node) { + * printf("i: %d f: %f\n", d->i, d->f); + * } + * + * list_dump(&l, stdout); + * slab_delete(&s); + * + * return 0; + * } + * @endcode + * @date 2010-2011 + */ + +#ifndef __LIST_H__ +#define __LIST_H__ + +#include +#include +#include + +#include "builtin.h" +#include "assert.h" +#include "type.h" + +/* ==================================================================== */ + +#define INIT_LIST_NODE {NULL,NULL} +#define INIT_LIST {INIT_LIST_NODE} + +typedef struct list_node list_node_t; //!< Alias for the @em list_node class +typedef struct list list_t; //!< Alias for the @em list class + +/*! + * @brief list node + * @details Primitive types cannot be stored in the @em list container, instead the user must + * embed a @em list_node object within the stored object. + */ +struct list_node { + list_node_t *prev; //!< Reference the previous list_node + list_node_t *next; //!< Reference the next list_node +}; + +/*! + * @brief list container + */ +struct list { + list_node_t node; +}; + +/* ==================================================================== */ + +/*! + * @brief Return a pointer to the node's next node + * @details For example, + * @code + * ... + * list_node_t * node = list_head(&l); + * list_node_t * next = list_node_next(node); + * ... + * @endcode + * @memberof list + * @param self [in] list node object @em self pointer + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +static inline list_node_t *list_node_next(list_node_t * self) +/*! @cond */ __nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return a pointer to the node's previous node + * @details For example, + * @code + * ... + * list_node_t * node = list_head(&l); + * list_node_t * next = list_node_prev(node); + * ... + * @endcode + * @memberof list + * @param self [in] list node object @em self pointer + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +static inline list_node_t *list_node_prev(list_node_t * self) +/*! @cond */ __nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Constructs an @em list container object + * @details For example, + * @code + * ... + * list_t l; + * list_init(&l); + * ... + * @endcode + * @memberof list + * @param self [in] list object @em self pointer + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +static inline void list_init(list_t * self) +/*! @cond */ __nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Inserts a node at the head of the @em list container + * @details For example, + * @code + * ... + * typedef struct { + * ... + * list_node_t node; + * ... + * } data_t; + * ... + * list_t l; + * list_init(&l); + * + * data_t * d = (data_t *) MALLOC(sizeof(*d)); + * list_add_head(&l, &d->node); + * ... + * @endcode + * @memberof list + * @param self [in] list object @em self pointer + * @param node [in] list_node object to insert + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +static inline void list_add_head(list_t * self, list_node_t * node) +/*! @cond */ __nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Inserts a node at the tail of the @em list container + * @details For example, + * @code + * ... + * typedef struct { + * ... + * list_node_t node; + * ... + * } data_t; + * ... + * list_t l; + * list_init(&l); + * + * data_t * d = (data_t *) MALLOC(sizeof(*d)); + * list_add_tail(&l, &d->node); + * ... + * @endcode + * @memberof list + * @param self [in] list object @em self pointer + * @param node [in] list_node object to insert + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +static inline void list_add_tail(list_t * self, list_node_t * node) +/*! @cond */ __nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Remove a node at the head of the @em list container + * @details For example, + * @code + * ... + * typedef struct { + * ... + * list_node_t node; + * ... + * } data_t; + * ... + * list_t l; + * list_init(&l); + * ... + * data_t * d = (data_t *)list_remove_head(&l); + * ... + * @endcode + * @memberof list + * @param self [in] list object @em self pointer + * @return non-NULL on success, NULL otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +static inline list_node_t *list_remove_head(list_t * self) +/*! @cond */ __nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Remove a node at the tail of the @em list container + * @details For example, + * @code + * ... + * typedef struct { + * ... + * list_node_t node; + * ... + * } data_t; + * ... + * list_t l; + * list_init(&l); + * ... + * data_t * d = (data_t *)list_remove_tail(&l); + * ... + * @endcode + * @memberof list + * @param self [in] list object @em self pointer + * @return non-NULL on success, NULL otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +static inline list_node_t *list_remove_tail(list_t * self) +/*! @cond */ __nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Remove the list_node referenced by @em node from the @em list container + * @details For example, + * @code + * ... + * typedef struct { + * ... + * list_node_t node; + * ... + * } data_t; + * ... + * list_t l; + * list_init(&l); + * ... + * data_t * d = (data_t *) MALLOC(sizeof(*d)); + * list_add_tail(&l, &d->node); + * list_remove_node(&l, &d->node); + * ... + * @endcode + * @memberof list + * @param self [in] list object @em self pointer + * @return non-NULL on success, NULL otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +static inline list_node_t *list_remove_node(list_t * self, list_node_t * node) +/*! @cond */ __nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Test whether a @em list container is empty + * @details For example, + * @code + * ... + * typedef struct { + * ... + * list_node_t node; + * ... + * } data_t; + * ... + * list_t l; + * list_init(&l); + * ... + * if (list_empty(&l)) { + * ... + * } + * ... + * @endcode + * @memberof list + * @param self [in] list object @em self pointer + * @return true if empty, false otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +static inline bool list_empty(const list_t * self) +/*! @cond */ __nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the head list_node from the @em list container + * @details For example, + * @code + * ... + * typedef struct { + * ... + * list_node_t node; + * ... + * } data_t; + * ... + * list_t l; + * list_init(&l); + * ... + * list_node_t * node = list_head(&l); + * ... + * @endcode + * @memberof list + * @param self [in] list object @em self pointer + * @return non-NULL on success, NULL otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +static inline list_node_t *list_head(list_t * self) +/*! @cond */ __nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the tail list_node from the @em list container + * @details For example, + * @code + * ... + * typedef struct { + * ... + * list_node_t node; + * ... + * } data_t; + * ... + * list_t l; + * list_init(&l); + * ... + * list_node_t * node = list_tail(&l); + * ... + * @endcode + * @memberof list + * @param self [in] list object @em self pointer + * @return non-NULL on success, NULL otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +static inline list_node_t *list_tail(list_t * self) +/*! @cond */ __nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Pretty print and dump the contents of the @em list to output stream @em out + * @details For example, + * @code + * ... + * typedef struct { + * ... + * list_node_t node; + * ... + * } data_t; + * ... + * list_t l; + * list_init(&l); + * ... + * list_dump(&l, stdout) { + * ... + * } + * ... + * @endcode + * @memberof list + * @param self [in] list object @em self pointer + * @param out [in] output stream + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern void list_dump(list_t * self, FILE *) +/*! @cond */ __nonnull((1)) /*! @endcond */ ; + +/*! + * @def list_entry(node_pointer, containing_type, member) + * @hideinitializer + * @brief Returns a pointer to the containing structure of a node + * @param n [in] Pointer to a list_node object + * @param T [in] Type of the containing structure + * @param m [in] Name of the list_node member + */ +#define list_entry(n, T, m) container_of(n, T, m) + +#if 0 +/*! + * @def list_head(list_pointer, containing_type, member) + * @hideinitializer + * @brief Returns a pointer to the head element in the @em list + * @param n [in] Pointer to a list_node object + * @param T [in] Type of the containing structure + * @param m [in] Name of the list_node member + */ +#define list_head(l, T, m) ({ \ + list_node_t * __h = (list_empty(l) ? NULL : list_entry((l)->node.next, T, m)) \ + __h; \ + }) + +/*! + * @def list_tail(list_pointer, containing_type, member) + * @hideinitializer + * @brief Returns a pointer to the tail element in the @em list + * @param n [in] Pointer to a list_node object + * @param T [in] Type of the containing structure + * @param m [in] Name of the list_node member + */ +#define list_tail(h, T, m) ({ \ + list_node_t * __t = (list_empty(l) ? NULL : list_entry((l)->node.prev, T, m)) \ + __t; \ + }) +#endif + +/* ==================================================================== */ + +/*! @cond */ +static inline list_node_t *list_node_next(list_node_t * self) +{ + assert(self != NULL); + return self->next; +} + +static inline list_node_t *list_node_prev(list_node_t * self) +{ + assert(self != NULL); + return self->prev; +} + +static inline void list_init(list_t * self) +{ + assert(self != NULL); + self->node.prev = self->node.next = &self->node; +} + +static inline void list_add_head(list_t * self, list_node_t * node) +{ + assert(self != NULL); + assert(node != NULL); + + node->next = self->node.next; + node->prev = &self->node; + self->node.next->prev = node; + self->node.next = node; +} + +static inline void list_add_tail(list_t * self, list_node_t * node) +{ + assert(self != NULL); + assert(node != NULL); + + node->next = &self->node; + node->prev = self->node.prev; + self->node.prev->next = node; + self->node.prev = node; +} + +static inline list_node_t *list_remove_head(list_t * self) +{ + assert(self != NULL); + + list_node_t *node = self->node.next; + + if (node != NULL) { + self->node.next = self->node.next->next; + self->node.next->prev = node->prev; + node->next = node->prev = NULL; + } + + return node; +} + +static inline list_node_t *list_remove_tail(list_t * self) +{ + assert(self != NULL); + + list_node_t *node = self->node.prev; + + if (node != NULL) { + self->node.prev = self->node.prev->prev; + self->node.prev->next = node->next; + node->prev = node->next = NULL; + } + + return node; +} + +static inline list_node_t *list_remove_node(list_t * self, list_node_t * node) +{ + assert(self != NULL); + assert(node != NULL); + + node->next->prev = node->prev; + node->prev->next = node->next; + node->next = node->prev = NULL; + + return node; +} + +static inline bool list_empty(const list_t * self) +{ + return self->node.next == &self->node; +} + +static inline list_node_t *list_head(list_t * self) +{ + assert(self != NULL); + return self->node.next; +} + +static inline list_node_t *list_tail(list_t * self) +{ + assert(self != NULL); + return self->node.prev; +} + +/*! @endcond */ + +/* ==================================================================== */ + +#endif /* __LIST_H__ */ diff --git a/clib/list_iter.h b/clib/list_iter.h new file mode 100644 index 0000000..d9cfcec --- /dev/null +++ b/clib/list_iter.h @@ -0,0 +1,227 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! @file list_iter.h + * @brief List iterator + * @details For example, + * @code + * ... + * data_t * d; + * ... + * list_iter_t it; + * list_iter_init(&it, &t, LI_FLAG_FWD); + * ... + * list_for_each(&it, d, node) { + * printf("forward i[%d] f[%f]\n", d->i, d->f); + * } + * ... + * @endcode + * @author Shaun Wetzstein + * @date 2010-2011 + */ + +#ifndef __LIST_ITER_H__ +#define __LIST_ITER_H__ + +#include +#include + +#include "builtin.h" +#include "list.h" +#include "nargs.h" + +/* ======================================================================= */ + +typedef struct list_iter list_iter_t; //!< Alias for the @em list_iter class + +/*! + * @brief list iterator + * @details list iterator class +*/ +struct list_iter { + list_t *list; //!< Reference to the target list object + list_node_t *node; //!< Current position of the iterator + uint32_t flags; //!< Iterator configuration flags +}; + +/* ======================================================================= */ + +#define LI_FLAG_NONE 0x00000000 //!< No flag mask +#define LI_FLAG_FWD 0x00000000 //!< Forward (FWD) flag mask +#define LI_FLAG_BWD 0x00000002 //!< Backward (BWD) flag mask +#define LI_FLAG_MASK 0x00000003 //!< All flags mask + +/*! + * @brief Initializes an @em list_iter iterator object + * @details For example, + * @code + * ... + * list_t l; + * list_init(&l); + * ... + * list_iter_t it; + * list_iter_init(&it, &l, LI_FLAG_FWD); + * ... + * @endcode + * @memberof list_iter + * @param self [in] list_iter object @em self pointer + * @param list [in] list container object to iterate + * @param flags [in] iterator configuration flags + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + * @throws UNEXPECTED if @em list pointer is NULL + */ +extern void list_iter_init(list_iter_t * self, list_t * list, uint32_t flags) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Resets a @em list iterator object + * @details For example, + * @code + * ... + * list_t l; + * list_init(&l); + * ... + * list_iter_t it; + * list_iter_init(&it, &l, l_FLAG_FWD); + * list_iter_clear(&it); + * ... + * @endcode + * @memberof list_iter + * @param self [in] list_iter object @em self pointer + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern void list_iter_clear(list_iter_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return a pointer to @em list element bytes at the current iterator position + * @details For example, + * @code + * ... + * list_t l; + * list_init(&l, 4, 1024); + * ... + * list_iter_t it; + * list_iter_init(&it, &l, LI_FLAG_FWD); + * ... + * data_t * d = (data_t *)list_iter_elem(&it); + * ... + * @endcode + * @memberof list_iter + * @param self [in] list_iter object @em self pointer + * @return non-NULL on success, NULL otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern list_node_t *list_iter_elem(list_iter_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @fn list_node_t * list_iter_inc(list_iter_t * self, size_t count = 1) + * @brief Increment the position of an @em list iterator + * @details If the second (2nd) parameter is omitted, the iterator is incremented by one (1) position. + * @details For example, + * @code + * ... + * list_t l; + * list_init(&l); + * ... + * list_iter_t it; + * list_iter_init(&it, &l, LI_FLAG_FWD); + * ... + * list_iter_inc(&it); + * ... + * @endcode + * @memberof list_iter + * @param self [in] list_iter object @em self pointer + * @param count [in] Number of positions to increment (optional) + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define list_iter_inc(...) STRCAT(list_iter_inc, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern list_node_t *list_iter_inc1(list_iter_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern list_node_t *list_iter_inc2(list_iter_t * self, size_t count) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @fn list_node_td * list_iter_dec(list_iter_t * self, size_t count = 1) + * @brief decrement the position of an @em list iterator + * @note If the second (2nd) parameter is omitted, the iterator is decremented by one (1) position. + * @details For example, + * @code + * ... + * list_t l; + * list_init(&l); + * ... + * list_iter_t it; + * list_iter_init(&it, &l, LI_FLAG_FWD); + * ... + * list_iter_dec(&it, 3); + * ... + * @endcode + * @memberof list_iter + * @param self [in] list_iter object @em self pointer + * @param count [in] Number of positions to decrement (optional) + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define list_iter_dec(...) STRCAT(list_iter_dec, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern list_node_t *list_iter_dec1(list_iter_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern list_node_t *list_iter_dec2(list_iter_t * self, size_t count) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @def list_for_each(it, i, m) + * @hideinitializer + * @brief List for-each algorithm + * @param it [in] Tree iterator object + * @param i [in] Tree element variable + * @param m [in] Member name + */ +#if 0 +#define list_for_each(l, i, m) \ + for (i = container_of_var(l->node.next, i, m); \ + &i->m != &(l)->node; \ + i = container_of_var(i->m.next, i, m)) +#define list_for_each_safe(l, i, n, m) \ + for (i = container_of_var((l)->node.next, i, m), \ + n = container_of_var(i->m.next, i, m); \ + &i->m != &(l)->node; \ + i = n, n = container_of_var(i->m.next, i, m)) +#endif + +#define list_for_each(it, i, m) \ + for (i = container_of_var(list_iter_elem(it), i, m); \ + list_iter_inc(it) != NULL; \ + i = container_of_var(list_iter_elem(it), i, m)) + +#endif /* __LIST_ITER_H__ */ diff --git a/clib/map.h b/clib/map.h new file mode 100644 index 0000000..80911be --- /dev/null +++ b/clib/map.h @@ -0,0 +1,238 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: map.h + * Author: Shaun Wetzstein + * Descr: Map container + * Note: + * Date: 10/22/10 + */ + +#ifndef __MAP_H__ +#define __MAP_H__ + +#include +#include + +#include "builtin.h" +#include "compare.h" +#include "type.h" + +#include "tree.h" + +/* ==================================================================== */ + +typedef tree_t map_t; //!< Alias for the @em map class +typedef tree_node_t map_node_t; //!< Alias for the @em map_node class + +/*! + * @brief Constructs a @em map_node object + * @memberof map_node + * @param self [in] map_node object @em self pointer + * @param key [in] pointer to key bytes + * @return Reference to an initialized map_node object on SUCCESS + */ +static inline map_node_t *map_node_init(map_node_t *, const void *) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @fn void map_init(map_t * self, compare_f cmp = default_compare) + * @brief Constructs a @em map object + * @memberof map + * @param self [in] map_node object @em self pointer + * @param cmp [in] Reference to the @em map_node compare function + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define map_init(...) STRCAT(map_init, NARGS(__VA_ARGS__))(__VA_ARGS__) +static inline int map_init1(map_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +static inline int map_init2(map_t *, compare_f) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @brief Insert a new @em map_node into the @em map container + * @memberof map + * @param self [in] map object @em self pointer + * @param node [in] Reference to the @em map_node to insert + * @return @em true if the @em map_node was inserted, @em false otherwise + * @throws UNEXPECTED if @em self pointer is NULL + * @throws UNEXPECTED if @em map_node.key points to a duplicate key + */ +static inline int map_insert(map_t *, map_node_t *) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Removes a @em map_node from the @em map container + * @memberof map + * @param self [in] map object @em self pointer + * @param node [in] Reference to the @em map_node to remove + * @return @em true if the @em map_node was removed, @em false otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +static inline int map_remove(map_t *, map_node_t *) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Find a @em map_node within the @em map container + * @memberof map + * @param self [in] map object @em self pointer + * @param key [in] Reference to the @em key to find + * @return Reference to a @em map_node on SUCCESS, false otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +static inline map_node_t *map_find(map_t *, const void *) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Hexdump the contents of a @em map to @em out output stream + * @memberof map + * @param self [in] map object @em self pointer + * @param out [in] Reference to the @em out output stream + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +static inline void map_dump(map_t *, FILE *) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Return whether a @em map container is empty + * @memberof map + * @param self [in] map object @em self pointer + * @return @em true if @em map is empty, false otherwise + */ +static inline bool map_empty(map_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the node count of a @em map container + * @memberof map + * @param self [in] map object @em self pointer + * @return @em 0 if @em map is empty, @em non-0 otherwise + */ +static inline size_t map_size(map_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the minimum @em map_node of a @em map container + * @memberof map + * @param self [in] map object @em self pointer + * @return Reference to a @em map_node if @em self is non-NULL, NULL otherwise + */ +static inline map_node_t *map_min(map_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the maximum @em map_node of a @em map container + * @memberof map + * @param self [in] map object @em self pointer + * @return Reference to a @em map_node if @em self is non-NULL, NULL otherwise + */ +static inline map_node_t *map_max(map_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/* ==================================================================== */ + +static inline map_node_t *map_node_init(map_node_t * self, const void *key) +{ + return (map_node_t *) tree_node_init((tree_node_t *) self, key); +} + +/* ==================================================================== */ + +static inline int map_init1(map_t * self) +{ + assert(self != NULL); + return tree_init((tree_t *) self, default_compare); +} + +static inline int map_init2(map_t * self, compare_f cmp) +{ + assert(self != NULL); + assert(cmp != NULL); + return tree_init((tree_t *) self, cmp); +} + +static inline int map_insert(map_t * self, map_node_t * node) +{ + assert(self != NULL); + assert(node != NULL); + return splay_insert((tree_t *) self, (tree_node_t *) node); +} + +static inline int map_remove(map_t * self, map_node_t * node) +{ + assert(self != NULL); + assert(node != NULL); + return splay_remove((tree_t *) self, (tree_node_t *) node); +} + +static inline map_node_t *map_find(map_t * self, const void *key) +{ + assert(self != NULL); + assert(key != NULL); + return (map_node_t *) tree_find((tree_t *) self, key); +} + +static inline bool map_empty(map_t * self) +{ + assert(self != NULL); + return tree_empty((tree_t *) self); +} + +static inline void map_dump(map_t * self, FILE * out) +{ + assert(self != NULL); + tree_dump((tree_t *) self, out ? out : stdout); +} + +static inline size_t map_size(map_t * self) +{ + assert(self != NULL); + return tree_size((tree_t *) self); +} + +static inline map_node_t *map_min(map_t * self) +{ + assert(self != NULL); + return tree_min((tree_t *) self); +} + +static inline map_node_t *map_max(map_t * self) +{ + assert(self != NULL); + return tree_max((tree_t *) self); +} + +/* ==================================================================== */ + +#endif /* __MAP_H__ */ diff --git a/clib/map_iter.h b/clib/map_iter.h new file mode 100644 index 0000000..5954e63 --- /dev/null +++ b/clib/map_iter.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: map_iter.h + * Author: Shaun Wetzstein + * Descr: Iterator for the Map container + * Note: + * Date: 10/22/10 + */ + +#ifndef __MAP_ITER_H__ +#define __MAP_ITER_H__ + +#include +#include + +#include "builtin.h" + +#include "map.h" +#include "tree_iter.h" + +/* ==================================================================== */ + +typedef tree_iter_t map_iter_t; + +/* ==================================================================== */ + +#define MI_FLAG_NONE TI_FLAG_NONE +#define MI_FLAG_FWD TI_FLAG_FWD +#define MI_FLAG_BWD TI_FLAG_BWD + +/*! + * @brief Initializes an @em map_iter iterator object + * @memberof map_iter + * @param self [in] map_iter object @em self pointer + * @param map [in] map container object to iterate + * @param flags [in] iterator configuration flags + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + * @throws UNEXPECTED if @em map pointer is NULL + */ +static inline int map_iter_init(map_iter_t *, map_t *, uint32_t) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Resets an @em map iterator object + * @memberof map_iter + * @param self [in] map_iter object @em self pointer + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +static inline int map_iter_clear(map_iter_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return a pointer to a @em map_node element at the current iterator + * position + * @memberof map_iter + * @param self [in] map_iter object @em self pointer + * @return non-NULL on success, NULL otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +static inline map_node_t *map_iter_elem(map_iter_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Increment the position of an @em map iterator + * @memberof map_iter + * @param self [in] map_iter object @em self pointer + * @param count [in] Number of positions to increment + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +static inline map_node_t *map_iter_inc(map_iter_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Decrement the position of an @em map iterator + * @memberof map_iter + * @param self [in] map_iter object @em self pointer + * @param count [in] Number of positions to decrement + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +static inline map_node_t *map_iter_dec(map_iter_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @def map_for_each(it, i, m) + * @hideinitializer + * @brief Tree for-each algorithm + * @param it [in] Tree iterator object + * @param i [in] Tree element variable + * @param m [in] Member name + */ +#define map_for_each(it, i, m) \ + for (map_iter_clear(it), i = container_of_var(map_iter_elem(it), i, m); \ + map_iter_elem(it) != NULL; \ + i = container_of_var(map_iter_inc(it), i, m)) + +/* ==================================================================== */ + +static inline int map_iter_init(map_iter_t * self, map_t * map, uint32_t flags) +{ + assert(self != NULL); + assert(map != NULL); + return tree_iter_init((tree_iter_t *) self, (tree_t *) map, flags); +} + +int map_iter_clear(map_iter_t * self) +{ + assert(self != NULL); + return tree_iter_clear((tree_iter_t *) self); +} + +static inline map_node_t *map_iter_elem(map_iter_t * self) +{ + assert(self != NULL); + return (map_node_t *) tree_iter_elem((tree_iter_t *) self); +} + +static inline map_node_t *map_iter_inc(map_iter_t * self) +{ + assert(self != NULL); + return (map_node_t *) tree_iter_inc((tree_iter_t *) self); +} + +static inline map_node_t *map_iter_dec(map_iter_t * self) +{ + assert(self != NULL); + return (map_node_t *) tree_iter_dec((tree_iter_t *) self); +} + +/* ==================================================================== */ + +#endif /* __MAP_ITER_H__ */ diff --git a/clib/max.h b/clib/max.h new file mode 100644 index 0000000..56957a5 --- /dev/null +++ b/clib/max.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: max.h + * Author: + * Descr: typesafe max function + * Note: + * Date: 10/22/10 + */ + +#ifndef __MAX_H__ +#define __MAX_H__ + +#define max(x,y) \ +({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + (void)((void*)&_x == (void*)&_y); \ + _x < _y ? _y : _x; \ +}) + +#endif /* __MAX_H__ */ diff --git a/clib/memory_leak_detection.h b/clib/memory_leak_detection.h new file mode 100644 index 0000000..5c1afb1 --- /dev/null +++ b/clib/memory_leak_detection.h @@ -0,0 +1,387 @@ +/* + * Copyright (c) International Business Machines Corp., 2011 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Module: memory_leak_detection.h + * + * + * Functions: + * + * Description: This module defines macros which should be used in place + * of malloc and free. These macros default to malloc and + * free, but, when FIND_MEMORY_LEAK is defined, they + * default to functions which wrapper malloc and free to + * track memory allocations and find memory leaks. Several + * other useful functions are defined as well. + * + * Author: B. Rafanello + * + * Version 0.0.0.1 + */ + +#ifndef MEMORY_LEAK_DETECTION_H + +#define MEMORY_LEAK_DETECTION_H 1 + +#if ( FIND_MEMORY_LEAK > 0 ) + +#warning Memory Leak Detection enabled! + +/* ================================================================= */ +/* ================================================================= */ +/* */ +/* The following functions are not meant to be called directly! Use */ +/* macros defined after the functions instead of the functions */ +/* themselves. */ +/* ================================================================= */ +/* ================================================================= */ + +/*********************************************************************/ +/* */ +/* Function Name: MEMORY_func */ +/* */ +/* Descriptive Name: This function acts as a replacement for */ +/* malloc. It allocates memory (using malloc) */ +/* and enters that memory into a tracking */ +/* structure so that memory leaks, if any, may */ +/* be found. */ +/* */ +/* Input: size_t sz - The number of bytes to be allocated. */ +/* unsigned int Alignment - 0 for non-aligned (normal) */ +/* malloc, > 0 to return an */ +/* address aligned on a specific */ +/* memory boundary. If > 0, then */ +/* Alignment must be a power of 2 */ +/* and a multiple of sizeof(void *)*/ +/* void ** Memory_Location - The address of a variable which*/ +/* will hold the address of the */ +/* allocated by this function. */ +/* const char * mod_name - The name of the module from which*/ +/* this function was called. */ +/* const char * func - The name of the function from which */ +/* this function was called. */ +/* const int line - The line number of the code containing */ +/* the call to this function. */ +/* */ +/* Output: If Success : The function return value will be 0. */ +/* *Memory_Location will be set to the address*/ +/* of the first byte of the user portion of */ +/* any memory that was allocated. */ +/* */ +/* If Failure : The function return value will be EINVAL or*/ +/* ENOMEM. Errors may be reported on stderr. */ +/* *Memory_Location will be set to NULL. */ +/* */ +/* Error Handling: This function will abort if an error is */ +/* is detected. Errors could be lack of memory, or*/ +/* corruption of the internal structures used to */ +/* track allocated blocks of memory. */ +/* */ +/* Side Effects: Memory may be allocated, errors may be reported */ +/* on stderr. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +int MEMORY_func(size_t sz, + unsigned int Alignment, + void **Memory_Location, + const char *mod_name, const char *func, const int line); + +/*********************************************************************/ +/* */ +/* Function Name: MALLOC_func */ +/* */ +/* Descriptive Name: This function acts as a replacement for */ +/* malloc. It allocates memory (using malloc) */ +/* and enters that memory into a tracking */ +/* structure so that memory leaks, if any, may */ +/* be found. */ +/* */ +/* Input: size_t sz - The number of bytes to be allocated. */ +/* const char * mod_name - The name of the module from which*/ +/* this function was called. */ +/* const char * func - The name of the function from which */ +/* this function was called. */ +/* const int line - The line number of the code containing */ +/* the call to this function. */ +/* */ +/* Output: If Success : The function return value will be non-NULL.*/ +/* */ +/* If Failure : The function return value will be NULL. */ +/* Errors may be reported on stderr. */ +/* */ +/* Error Handling: This function will abort if an error is */ +/* is detected. Errors could be lack of memory, or*/ +/* corruption of the internal structures used to */ +/* track allocated blocks of memory. */ +/* */ +/* Side Effects: Memory may be allocated, errors may be reported */ +/* on stderr. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +void *MALLOC_func(size_t sz, const char *mod_name, const char *func, + const int line); + +/*********************************************************************/ +/* */ +/* Function Name: FREE_func */ +/* */ +/* Descriptive Name: This function frees a block of memory being */ +/* tracked by this module and removes the block */ +/* from its tracking structures. */ +/* */ +/* Input: const void * p - The address of the block of memory to */ +/* be freed. */ +/* const char * mod_name - The name of the module requesting*/ +/* the block of memory be freed. */ +/* const char * func - The name of the function requesting */ +/* the block of memory be freed. */ +/* const int line - The line number of the line of code in */ +/* module calling this function. */ +/* */ +/* Output: If Success : None. */ +/* */ +/* If Failure : Errors may be reported to stderr. */ +/* */ +/* Error Handling: This function causes the internal structures */ +/* of this module to be checked as part of the */ +/* process of freeing the address p. This may */ +/* cause errors to be reported on stderr. If any */ +/* errors are found, then the address p may not be */ +/* freed. */ +/* */ +/* Side Effects: The block of memory associated with the address p */ +/* will be freed and available for reallocation. */ +/* Also, the memory tracking structures in this */ +/* module will undergo a series of checks. */ +/* */ +/* Notes: This function was not intended to be called directly but */ +/* rather through the macro FREE. */ +/* */ +/*********************************************************************/ +void FREE_func(const void *p, const char *mod_name, const char *func, + const int line); + +/*********************************************************************/ +/* */ +/* Function Name: Print_Leak_List */ +/* */ +/* Descriptive Name: This function walks the list of allocated */ +/* memory blocks and prints information about */ +/* each one. If this is done at program exit, */ +/* the resulting list of memory blocks most */ +/* likely represents leaked memory. */ +/* */ +/* Input: None. */ +/* */ +/* Output: If Success : If there are any memory blocks being */ +/* tracked by this module, information about */ +/* block still being tracked will be sent to */ +/* stderr. */ +/* */ +/* If Failure : Error messages may be sent to stderr. */ +/* */ +/* Error Handling: If errors are detected, then error messages are */ +/* output on stderr. */ +/* */ +/* Side Effects: The internal structures of this module are checked*/ +/* for errors with any errors being reported on */ +/* stderr. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +void Print_Leak_List(void); + +/*********************************************************************/ +/* */ +/* Function Name: Test_Address_Allocation */ +/* */ +/* Descriptive Name: This function tests the specified address to */ +/* to see if it lies within an allocated block */ +/* tracked by this module. */ +/* */ +/* Input: void * p - The address to be tested. */ +/* */ +/* Output: If Success : If the address p was found, then 0 will be */ +/* returned if the address is the start of */ +/* a block of allocated memory. If the */ +/* address p was found within an allocated */ +/* block of memory, then 1 is returned. */ +/* */ +/* If Failure : If the address p was NOT found, then 2 is */ +/* returned. If there was an error in the */ +/* memory tracking system then 3 will be */ +/* returned. */ +/* */ +/* Error Handling: This function relies on the error handling */ +/* built into the Check_Leak_List function and */ +/* has no error handling of its own. */ +/* */ +/* Side Effects: If the list of memory allocations contains errors */ +/* then those errors will be detected and reported */ +/* on stderr. */ +/* */ +/* Notes: If NULL is passed in as the address to test, then the */ +/* integrity of the internal tracking structures will be */ +/* checked, in which case a return value of 0 signifies */ +/* that the internal tracking structures have passed the */ +/* checks and a return value of 3 indicates that errors */ +/* were found. */ +/* */ +/*********************************************************************/ +unsigned int Test_Address_Allocation(void *p); + +/*********************************************************************/ +/* */ +/* Function Name: Duplicate_String */ +/* */ +/* Descriptive Name: This function duplicates a string. The memory*/ +/* allocated for the duplicate is allocated */ +/* using the MALLOC_func routine in this module */ +/* and is thus tracked by this module. */ +/* */ +/* Input: const char * Source - The string to be copied. */ +/* const char * mod_name - The name of the module containing*/ +/* the function which called this */ +/* function. */ +/* const char * func - The name of the function calling */ +/* this function. */ +/* const int line - The line number of the line of code in */ +/* module calling this function. */ +/* */ +/* Output: If Success : The function return value will be non-NULL */ +/* and will point to a duplicate of the */ +/* string given in Source. */ +/* */ +/* If Failure : The function return value will be NULL. */ +/* */ +/* Error Handling: Any errors detected by this function result in */ +/* a function return value of NULL. */ +/* */ +/* Side Effects: The memory tracking features of this module are */ +/* employed to allocate memory for the duplicate */ +/* string produced by this funciton. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +char *Duplicate_String(const char *Source, + const char *mod_name, const char *func, const int line); + +/*********************************************************************/ +/* */ +/* Function Name: Realloc_func */ +/* */ +/* Descriptive Name: This function performs the same function as */ +/* the realloc function in the ANSI C library. */ +/* */ +/* Input: const void * p - The address of the block of memory to */ +/* be reallocated. */ +/* size_t size - The size of the memory block to return. */ +/* const char * mod_name - The name of the module requesting*/ +/* the block of memory be freed. */ +/* const char * func - The name of the function requesting */ +/* the block of memory be freed. */ +/* const int line - The line number of the line of code in */ +/* module calling this function. */ +/* */ +/* Output: If Success : The function return value will be a pointer*/ +/* to the new block of memory. */ +/* */ +/* If Failure : NULL will be returned and errno will be set*/ +/* to a non-null error code. */ +/* */ +/* Error Handling: This function causes the internal structures */ +/* of this module to be checked. This may */ +/* cause errors to be reported on stderr. If any */ +/* errors are found, then the address p may not be */ +/* freed. */ +/* */ +/* Side Effects: A new block of memory of size bytes will be */ +/* allocated, the contents of the current block will */ +/* be copied to the new block (at least as much as */ +/* will fit, and the current block will be freed. */ +/* This will cause internal structures in this module*/ +/* to be modified accordingly. */ +/* */ +/* Notes: This function was not intended to be called directly but */ +/* rather through the macro REALLOC. */ +/* */ +/* If p is NULL, then this will cause this function to */ +/* behave like malloc. */ +/* */ +/* If size is 0, then this will cause this function to */ +/* behave like free. */ +/* */ +/*********************************************************************/ +void *Realloc_func(void *p, + size_t size, + const char *mod_name, const char *func, const int line); + +/* ================================================================= */ +/* ================================================================= */ +/* */ +/* Macros to use for accessing the functions in this module. */ +/* */ +/* ================================================================= */ +/* ================================================================= */ + +#ifdef MALLOC +#undef MALLOC +#endif +#ifdef FREE +#undef FREE +#endif + +/* This macro should be used in place of posix_memalign. */ +#define POSIX_MEMALIGN(a, b, c ) MEMORY_func( c, b, a, __FILE__, __func__, __LINE__ ) + +/* This macro should be used in place of the malloc function. */ +#define MALLOC( a ) MALLOC_func( a, __FILE__, __func__, __LINE__ ) + +/* This macro should be used in place of the free function. */ +#define FREE( a ) FREE_func( a, __FILE__, __func__, __LINE__ ) + +/* This macro should be used to print a list of allocated memory blocks. */ +#define PRINT_ALLOCATION_LIST Print_Leak_List() + +/* This macro should be used to see if an address lies within a block of + memory being tracked by this module. */ +#define ADDRESS_CHECK( p ) Test_Address_Allocation( p ) + +/* This macro should be used in place of the strdup function. */ +#define STRDUP( p ) Duplicate_String( p, __FILE__, __func__, __LINE__ ) + +/* This macro should be used in place of the realloc function. */ +#define REALLOC( p, s ) Realloc_func( p, s, __FILE__, __func__, __LINE__ ) + +#else + +#define POSIX_MEMALIGN(a, b, c ) posix_memalign( a, b, c ) +#define MALLOC(s) malloc(s) +#define FREE(p) free(p) +#define PRINT_ALLOCATION_LIST +#define ADDRESS_CHECK( p ) 0 +#define STRDUP( p ) strdup( p ) +#define REALLOC( p, s ) realloc( p, s ) + +#endif + +#endif diff --git a/clib/min.h b/clib/min.h new file mode 100644 index 0000000..69633bc --- /dev/null +++ b/clib/min.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: min.h + * Author: + * Descr: typesafe min function + * Note: + * Date: 10/22/10 + */ + +#ifndef __MIN_H__ +#define __MIN_H__ + +#define min(x,y) \ +({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + (void)((void*)&_x == (void*)&_y); \ + _x < _y ? _x : _y; \ +}) + +#endif /* __MIN_H__ */ diff --git a/clib/misc.h b/clib/misc.h new file mode 100644 index 0000000..ad8f261 --- /dev/null +++ b/clib/misc.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: misc.h + * Author: Shaun Wetzstein +#include + +#include + +#include "attribute.h" + +#include "align.h" +#include "min.h" +#include "max.h" + +#define INT32_BIT (CHAR_BIT*sizeof(int32_t)) + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#ifndef gettid +#define gettid() ({ \ + pid_t __tid = (pid_t)syscall(SYS_gettid); \ + __tid; \ + }) +#endif + +#define is_pow2(x) \ +({ \ + typeof(x) _x = (x); \ + (((_x) != 0) && !((_x) & ((_x) - 1))); \ +}) + +#define __align(x,y) \ +({ \ + assert(is_pow2((y))); \ + typeof((y)) _y = (y) - 1; \ + typeof((x)) _x = ((x)+_y) & ~_y; \ + _x; \ +}) + +extern void prefetch(void *addr, size_t len, ...) __nonnull((1)); +extern void print_binary(FILE *, void *, size_t); +extern void dump_memory(FILE *, uint32_t, const void *__restrict, + size_t) __nonnull((1, 3)); +extern unsigned long align(unsigned long size, unsigned long offset); +extern int __round_pow2(int size); + +#endif /* __MISC_H__ */ diff --git a/clib/mq.h b/clib/mq.h new file mode 100644 index 0000000..95cca4d --- /dev/null +++ b/clib/mq.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! + * @file mqueue.h + * @brief Message queues + * @author Shaun Wetzstein + * @date 2010-2011 + */ + +#ifndef __MQUEUE_H__ +#define __MQUEUE_H__ + +#include +#include +#include + +#include "attribute.h" + +/* ==================================================================== */ + +typedef struct mqueue mqueue_t; //!< Alias for the @em mqueue class +typedef struct mq_attr mqueue_attr_t; + +#define INIT_MQUEUE {NULL,-1,-1} + +/*! + * @brief POSIX message queues + */ +struct mqueue { + char *service; //!< Message queue name + + mqd_t in; //!< Inbound message queue + mqd_t out; //!< Outbound message queue +}; + +/* ==================================================================== */ + +/*! + * @brief Constructs a @em mqueue object + * @memberof mqueue + * @param self [in] mqueue object @em self pointer + * @param service [in] mqueue service name + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int mqueue_init(mqueue_t *, const char *) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Destructs a @em mqueue object + * @details Closes all message queues associated with this \em mqueue object + * @memberof mqueue + * @param self [in] mqueue object @em self pointer + * @return None + */ +extern int mqueue_delete(mqueue_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Create the server-side of a @em mqueue object + * @memberof mqueue + * @param self [in] mqueue object @em self pointer + * @param tid [in] thread id + * @return None + */ +extern int mqueue_create(mqueue_t *, pid_t) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Create the client-side of a @em mqueue object + * @memberof mqueue + * @param self [in] mqueue object @em self pointer + * @param path [in] FIX ME + * @return None + */ +extern int mqueue_open(mqueue_t *, char *) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Close a @em mqueue object + * @memberof mqueue + * @param self [in] mqueue object @em self pointer + * @param path [in] FIX ME + * @return None + */ +extern int mqueue_close(mqueue_t *, char *) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Get message queue operating attributes of a @em mqueue object + * @memberof mqueue + * @param self [in] mqueue object @em self pointer + * @return Initialized mqueue_attr object + */ +extern mqueue_attr_t mqueue_getattr(mqueue_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Write (send) a message to a @em mqueue object + * @memberof mqueue + * @param self [in] mqueue object @em self pointer + * @param ptr [in] Reference to data bytes to send + * @param ptr [in] Length (in bytes) of the data bytes + * @return non-0 on success, 0 othereise + */ +extern int mqueue_send(mqueue_t *, void *, size_t) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Read (receive) a message from a @em mqueue object + * @memberof mqueue + * @param self [in] mqueue object @em self pointer + * @param ptr [out] Reference to data bytes to send + * @param ptr [in] Length (in bytes) of the buffer referenced by @em ptr + * @return non-0 on success, 0 othereise + */ +extern int mqueue_receive(mqueue_t *, void *, size_t) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +#endif /* __MQUEUE_H__ */ diff --git a/clib/nargs.h b/clib/nargs.h new file mode 100644 index 0000000..3a8666d --- /dev/null +++ b/clib/nargs.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! + * @file nargs.h + * @brief Macro utilities + * @author Shaun Wetzstein + * @date 2010-2011 + */ + +#ifndef __NARGS_H__ +#define __NARGS_H__ + +/*! + * @def STRCAT(x,y) + * @hideinitializer + * @brief C string concatination of @em x and @em y + * @param x [in] C-style string + * @param y [in] C-style string + */ +/*! @cond */ +#define STRCAT(x,y) __C__(x, y) +#define __C__(x,y) x ## y +/*! @endcond */ + +/*! + * @def NARGS(...) + * @hideinitializer + * @brief Return the number of pre-process macro parameters + */ +/*! @cond */ +#define __NARGS__(junk, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, _, ...) _ +#define NARGS(...) __NARGS__(junk, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +/*! @endcond */ + +#endif /* __NARGS_H__ */ diff --git a/clib/queue.h b/clib/queue.h new file mode 100644 index 0000000..8e3a5c6 --- /dev/null +++ b/clib/queue.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: queue.h + * Author: Shaun Wetzstein + * Descr: Queue container + * Note: This is an adaptor of the list container + * Date: 10/07/10 + */ + +#ifndef __QUEUE_H__ +#define __QUEUE_H__ + +#include "list.h" +#include "type.h" + +typedef list queue; +typedef list_node queue_node; + +#define queue_init(q) list_init((list *)(q)) +#define queue_push(q,n) list_add_tail((list *)(q),(n)) +#define queue_pop(q) list_remove_head((list *)(q)) +#define stack_empty(q) list_empty((list *)(q)) +#define queue_dump(q,s) list_dump((list *)(q),(s)) + +#define queue_entry(n, t, m) list_entry((n),(t),(m)) +#define queue_top(q) list_head((list *)(q)) +#define queue_bottom(q) list_tail((list *)(q)) + +#define queue_for_each(q, i, m) \ + for (i = container_of_var(q->node.next, i, m); \ + &i->m != &(q)->node; \ + i = container_of_var(i->m.next, i, m)) + +#define queue_for_each_safe(q, i, n, m) \ + for (i = container_of_var((q)->node.next, i, m), \ + n = container_of_var(i->m.next, i, m); \ + &i->m != &(q)->node; \ + i = n, n = container_of_var(i->m.next, i, m)) + +#endif /* __QUEUE_H__ */ diff --git a/clib/raii.h b/clib/raii.h new file mode 100644 index 0000000..8fdb1ce --- /dev/null +++ b/clib/raii.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: raii.h + * Author: + * Descr: RAII macro + * Note: + * Date: 04/03/13 + */ + +#ifndef __RAII_H__ +#define __RAII_H__ + +#include "attribute.h" + +#define CLEANUP(type,name,func) \ + void __cleanup_ ## name (type * __v) { \ + if (__v != NULL) \ + func(__v); \ + } \ + type name __cleanup(__cleanup_##name) + +#define RAII(type,name,ctor,dtor) \ + void __cleanup_##name (type * __v) { \ + if (__v != NULL && *__v != NULL) \ + dtor(*__v); \ + } \ + type name __cleanup(__cleanup_##name) = (ctor) + +#endif /* __RAII_H__ */ diff --git a/clib/signal.h b/clib/signal.h new file mode 100644 index 0000000..7c7be18 --- /dev/null +++ b/clib/signal.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: signal.h + * Author: Shaun Wetzstein + * Descr: File descriptor activity callback signaler + * Note: + * Date: 10/03/10 + */ + +#ifndef __SIGNAL_H__ +#define __SIGNAL_H__ + +#include +#include + +#include +#include + +#include "attribute.h" +#include "builtin.h" +#include "assert.h" + +#include "tree.h" +#include "array.h" + +/* ======================================================================= */ + +typedef struct signal signal_t; +typedef struct signalfd_siginfo signal_event_t; + +struct signal { + int fd; + + sigset_t mask; + int flags; +}; + +/* ======================================================================= */ + +extern void signal_init(signal_t * self) __nonnull((1)); +extern void signal_delete(signal_t * self) __nonnull((1)); + +extern int signal_fileno(signal_t * self) __nonnull((1)); + +extern int signal_setmask(signal_t * self, const sigset_t mask) __nonnull((1)); + +#if 0 +#define signalfd_wait(...) STRCAT(signalfd_wait, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern void signal_wait1(signal_t *) __nonnull((1)); +extern void signal_wait2(_sisignal_tgnal *, int) __nonnull((1)); +#endif + +/* ======================================================================= */ + +#endif /* __SIGNAL_H__ */ diff --git a/clib/slab.h b/clib/slab.h new file mode 100644 index 0000000..7b52bfd --- /dev/null +++ b/clib/slab.h @@ -0,0 +1,222 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! + * @file slab.h + * @brief Slab allocator + * @details A slab allocator is a bit-mapped allocator for which all allocations are the same size + * @details For example, + * @code + * #include + * + * #define SIZE 20000 + * + * int main(const int argc, const char * argv[]) { + * slab_t s = INIT_SLAB; + * slab_init(&s, "my_slab", 16, 4096); + * + * void * ptr[SIZE] = {NULL,}; + * + * int i; + * for (i=0; i + * @date 2010-2011 + */ + +#ifndef __SLAB_H__ +#define __SLAB_H__ + +#include +#include + +#include "ident.h" +#include "nargs.h" +#include "tree.h" + +/* ======================================================================= */ + +#define SLAB_MAGIC "SLAB" + +#define SLAB_NAME_SIZE 40 //!< Maximum vector name size (in bytes) + +#define SLAB_FLAG_LSB 0x01 //!< Little-endian header data +#define SLAB_FLAG_MSB 0x02 //!< Big-endian header data + +#define SLAB_ALLOC_MIN 4 +#define SLAB_ALLOC_MAX 8192 + +#define INIT_SLAB_HEADER {INIT_IDENT,{0,},0,0,0,0,0,0} +#define INIT_SLAB {INIT_SLAB_HEADER,INIT_TREE} + +/*! + * @brief Slab allocator header + */ +struct slab_header { + ident_t id; //!< identification + char name[SLAB_NAME_SIZE]; //!< slab name + + size_t page_size; //!< page size + size_t align_size; //!< page alignment size + size_t page_count; //!< page count + + size_t alloc_size; //!< allocation size + size_t data_size; //!< data size + size_t bitmap_size; //!< bitmap size +}; +typedef struct slab_header slab_header_t; + +/*! + * @brief Slab allocator + */ +struct slab { //!< The slab class + slab_header_t hdr; + + tree_t tree; //!< @private +}; +typedef struct slab slab_t; //!< Alias for the @em slab class + +/* ======================================================================= */ + +/*! + * @brief Constructs a @em slab allocator + * @memberof slab + * @param self [in] self object + * @name name [in] name strnig ('\0' terminated) + * @param alloc_size [in] allocation size (in bytes) + * @param page_size [in] page size (in bytes) + * @return None + */ +/*! @cond */ +#define slab_init(...) STRCAT(slab_init, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern int slab_init3(slab_t * self, const char *name, size_t alloc_size) +__nonnull((1, 2)); +extern int slab_init4(slab_t * self, const char *name, size_t alloc_size, + size_t page_size) __nonnull((1, 2)); +extern int slab_init5(slab_t * self, const char *name, size_t alloc_size, + size_t page_size, size_t align_size) __nonnull((1, 2)); +/*! @endcond */ + +/*! + * @brief Deallocate the memory associated with a @em slab allocator + * @memberof slab + * @param self [in] slab object @em self pointer + * @note Upon success, the cache tree is deallocated from the heap + * @return None + */ +extern int slab_delete(slab_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Allocate first available slot in the @em slab allocator + * @memberof slab + * @param self [in] slab object @em self pointer + * @return non-NULL on success, NULL otherwise + */ +extern void *slab_alloc(slab_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Deallocate a slot in the @em slab allocator + * @memberof slab + * @param self [in] slab object @em self pointer + * @param ptr [in] pointer to a slab allocation + * @note Upon success, a slab slot is deallocated from the slab object + * @return None + */ +extern int slab_free(slab_t * self, void *ptr) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Return the allocation (i.e. slot) size of the @em slab allocator + * @memberof slab + * @param self [in] slab object @em self pointer + * @return non-0 on success, 0 otherwise + */ +extern size_t slab_alloc_size(slab_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the page (i.e. slot) size of the @em slab allocator + * @memberof slab + * @param self [in] slab object @em self pointer + * @return non-0 on success, 0 otherwise + */ +extern size_t slab_page_size(slab_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the data size of the @em slab allocator + * @memberof slab + * @param self [in] slab object @em self pointer + * @return non-0 on success, 0 otherwise + */ +extern size_t slab_data_size(slab_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the map (i.e. slot) size of the @em slab allocator + * @memberof slab + * @param self [in] slab object @em self pointer + * @return non-0 on success, 0 otherwise + */ +extern size_t slab_bitmap_size(slab_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the alignment size of the @em slab allocator + * @memberof slab + * @param self [in] slab object @em self pointer + * @return non-0 on success, 0 otherwise + */ +extern size_t slab_align_size(slab_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Pretty print the contents of a @em slab to stdout + * @memberof slab + * @param self [in] slab object @em self pointer + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern void slab_dump(slab_t * self, FILE * out) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/* ======================================================================= */ + +#endif /* __SLAB_H__ */ diff --git a/clib/src/array.c b/clib/src/array.c new file mode 100644 index 0000000..55e261c --- /dev/null +++ b/clib/src/array.c @@ -0,0 +1,490 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: array.c + * Author: Shaun Wetzstein + * Descr: Sparse Array + * Note: + * Date: 08/29/10 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "assert.h" +#include "misc.h" +#include "hash.h" +#include "array.h" +#include "slab.h" +#include "mqueue.h" +#include "tree.h" +#include "tree_iter.h" +#include "bb_trace.h" +#include "memory_leak_detection.h" + +/*! @cond */ +#define ARRAY_ELEM_SIZE_MIN 1 +#define ARRAY_ELEM_NUM_MIN 64 + +#define ARRAY_MAGIC 0x48444152 +#define ARRAY_NODE_MAGIC 0x4E444152 +/*! @endcond */ + +/* ======================================================================= */ + +/*! @cond */ +typedef struct array_node array_node_t; + +struct array_node { + uint32_t magic; + uint32_t address; + + tree_node_t node; +}; + +const char *__array_msg[] = { + "array: unexpected NULL pointer", + "array: unexpected cache and/or alloc size", + "array: out-of-memory", + "array: uninitialized array element", + "array: unexpected magic number", + "array: unexpected NULL message channel", +}; + +#define ARRAY_NULL (__array_msg[0]) +#define ARRAY_SIZE (__array_msg[1]) +#define ARRAY_OOM (__array_msg[2]) +#define ARRAY_UNINIT_ELEM (__array_msg[3]) +#define ARRAY_MAGIC_CHECK (__array_msg[4]) +#define ARRAY_MQ_NULL (__array_msg[5]) +/*! @endcond */ + +/* ======================================================================= */ + +#define __index_to_page(i,s) \ +({ \ + typeof(i) _p = ((i) / (s)); \ + (i) >= 0 ? _p :_p - 1; \ +}) + +#define __index_to_page_hashed(i,s) \ +({ \ + typeof(i) _h = int32_hash1(__index_to_page((i),(s))); \ + _h; \ +}) + +#define __page_to_low(p,s) \ +({ \ + typeof(p) _l = (p) * (s); \ + (p) >= 0 ? _l : _l - 1; \ +}) + +#define __page_to_high(p,s) \ +({ \ + typeof(p) _h = (p) * (s) + (s) - (typeof(p))1; \ + _h; \ +}) + +/* ======================================================================= */ + +static array_node_t *__array_grow(array_t * self, size_t idx) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_NULL); + + array_node_t *node = NULL; + switch (POSIX_MEMALIGN + ((void **)&node, self->page_size, self->page_size)) { + case EINVAL: + throw_unexpected(ARRAY_SIZE); + case ENOMEM: + throw_unexpected(ARRAY_OOM); + } + assert(node != NULL); + + node->magic = ARRAY_NODE_MAGIC; + node->address = (uint32_t) node; + + const void *key = + (const void *)__index_to_page_hashed(idx, self->elem_num); + tree_node_init(&node->node, key); + + splay_insert(&self->tree, &node->node); + self->pages++; + + size_t page = __index_to_page(idx, self->elem_num); + + self->low = min(self->low, __page_to_low(page, self->elem_num)); + self->high = max(self->high, __page_to_high(page, self->elem_num)); + + return node; +} + +static int __array_compare(const int i1, const int i2) +{ + return i1 - i2; +} + +void array_init(array_t * self, size_t elem_size, size_t elem_num) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_NULL); + + self->magic = ARRAY_MAGIC; + self->high = 0; + self->low = UINT32_MAX; + self->size = 0; + self->pages = 0; + + self->elem_size = max(elem_size, ARRAY_ELEM_SIZE_MIN); + self->elem_num = max(elem_num, ARRAY_ELEM_NUM_MIN); + + self->page_size = __round_pow2(elem_size * elem_num); + + self->map_size = align(elem_num / CHAR_BIT, sizeof(uint32_t)); + + self->elem_num = + (self->page_size - sizeof(array_node_t) - + self->map_size) / self->elem_size; + + tree_init(&self->tree, (compare_f) __array_compare); +} + +static void __array_delete(array_t * self) +{ + if (self != NULL) { + while (self->tree.root != NULL) { + array_node_t *node; + node = + container_of(tree_root(&self->tree), array_node_t, + node); + splay_remove(&self->tree, &node->node); + FREE(node); + } + tree_init(&self->tree, (compare_f) __array_compare); + } +} + +void array_delete(array_t * self) +{ + __array_delete(self); +} + +static void *__array_find_page(array_t * self, size_t idx) +{ + const void *hash = + (const void *)__index_to_page_hashed(idx, self->elem_num); + tree_node_t *node = tree_find(&self->tree, hash); + + if (node == NULL) + return (void *)__array_grow(self, idx); + else + return (void *)container_of(node, array_node_t, node); + + return NULL; +} + +static uint8_t *__array_find_map(array_t * self, size_t idx) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_NULL); + + const void *hash = + (const void *)__index_to_page_hashed(idx, self->elem_num); + uint8_t *map = (uint8_t *) tree_find(&self->tree, hash); + if (map != NULL) + map += sizeof(tree_node_t); + + return map; +} + +const void *array_at(array_t * self, size_t idx) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_NULL); + + uint32_t offset = idx % self->elem_num; + + uint8_t *map = __array_find_page(self, idx); + map += sizeof(array_node_t); + + uint8_t byte = offset / CHAR_BIT; + uint8_t mask = 0x80 >> (offset % CHAR_BIT); + + if ((map[byte] & mask) == 0) + throw_unexpected(ARRAY_UNINIT_ELEM); + + return map + self->map_size + (self->elem_size * offset); +} + +void array_get3(array_t * self, size_t elem_off, void *ptr) +{ + return array_get4(self, elem_off, ptr, 1); +} + +void array_get4(array_t * self, size_t elem_off, void *ptr, size_t elem_num) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_NULL); + + while (0 < elem_num) { + memcpy(ptr, array_at(self, elem_off), self->elem_size); + + elem_off++; + elem_num--; + + ptr += self->elem_size; + } +} + +void __array_put(array_t * self, size_t elem_off, const void *ptr) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_NULL); + + uint32_t offset = elem_off % self->elem_num; + + uint8_t *map = __array_find_page(self, elem_off); + map += sizeof(array_node_t); + + uint8_t byte = offset / CHAR_BIT; + uint8_t mask = 0x80 >> (offset % CHAR_BIT); + + if ((map[byte] & mask) == 0) + self->size++; + + map[byte] |= mask; + + memcpy(map + self->map_size + (self->elem_size * offset), + ptr, self->elem_size); +} + +void array_put3(array_t * self, size_t elem_off, const void *ptr) +{ + return array_put4(self, elem_off, ptr, 1); +} + +void array_put4(array_t * self, size_t elem_off, const void *ptr, + size_t elem_num) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_NULL); + + while (0 < elem_num) { + __array_put(self, elem_off, ptr); + + elem_off++; + elem_num--; + + ptr += self->elem_size; + } +} + +bool array_status2(array_t * self, size_t idx) +{ + uint8_t *map = __array_find_map(self, idx); + + uint32_t offset = idx % self->elem_num; + uint8_t byte = offset / CHAR_BIT; + uint8_t mask = 0x80 >> (offset % CHAR_BIT); + + return ! !(map[byte] & mask); +} + +bool array_status3(array_t * self, size_t idx, bool status) +{ + uint8_t *map = __array_find_map(self, idx); + + if (map == NULL) { + map = (uint8_t *) __array_grow(self, idx); + map += sizeof(array_node_t); + } + + uint32_t offset = idx % self->elem_num; + uint8_t byte = offset / CHAR_BIT; + uint8_t mask = 0x80 >> (offset % CHAR_BIT); + + bool ret = ! !(map[byte] & mask); + + map[byte] &= ~mask; + if (status) + map[byte] |= mask; + + return ret; +} + +void array_resize(array_t * self, size_t size) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_NULL); + while (0 < size) { + (void)__array_grow(self, self->high + 1); + size /= self->elem_num; + } +} + +size_t array_size(array_t * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_NULL); + return self->size; +} + +size_t array_pages(array_t * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_NULL); + return self->pages; +} + +size_t array_capacity(array_t * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_NULL); + return self->high - self->low + 1; +} + +size_t array_low(array_t * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_NULL); + return self->low; +} + +size_t array_high(array_t * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_NULL); + return self->high; +} + +void array_send(array_t * self, mqueue_t * mq) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_NULL); + if (unlikely(mq == NULL)) + throw_unexpected(ARRAY_MQ_NULL); + + mqueue_send(mq, (char *)self, sizeof(*self)); + + tree_iter_t it; + tree_iter_init(&it, &self->tree, TI_FLAG_FWD); + + array_node_t *node; + tree_for_each(&it, node, node) { + if (node->magic != ARRAY_NODE_MAGIC) + throw_unexpected(ARRAY_MAGIC_CHECK); + + mqueue_send(mq, (char *)node, self->page_size); + } +} + +void array_receive(array_t * self, mqueue_t * mq) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_NULL); + if (unlikely(mq == NULL)) + throw_unexpected(ARRAY_MQ_NULL); + + __array_delete(self); + + mqueue_attr_t attr = mqueue_getattr(mq); + + array_node_t *node = NULL; + switch (POSIX_MEMALIGN + ((void **)&node, attr.mq_msgsize, attr.mq_msgsize)) { + case EINVAL: + throw_unexpected(ARRAY_SIZE); + case ENOMEM: + throw_unexpected(ARRAY_OOM); + } + assert(node != NULL); + + ssize_t len = mqueue_receive(mq, (void *)node, attr.mq_msgsize); + assert(0 < len); + + memcpy(self, (void *)node, sizeof(*self)); + tree_init(&self->tree, (compare_f) __array_compare); + + for (int i = 0; i < array_pages(self); i++) { + if (node == NULL) { + switch (POSIX_MEMALIGN + ((void **)&node, attr.mq_msgsize, + attr.mq_msgsize)) { + case EINVAL: + throw_unexpected(ARRAY_SIZE); + case ENOMEM: + throw_unexpected(ARRAY_OOM); + } + } + + len = mqueue_receive(mq, (void *)node, attr.mq_msgsize); + assert(0 < len); + + node->address = (uint32_t) node; + tree_node_init(&node->node, node->node.key); + splay_insert(&self->tree, &node->node); + + node = NULL; + } +} + +void array_dump(array_t * self, FILE * out) +{ + if (self != NULL) { + fprintf(out, + "array: [ page_size: %5u pages: %5u map_size: %5u capacity: %10u ]\n", + self->page_size, self->pages, self->map_size, + array_capacity(self)); + fprintf(out, + " [ elem_size: %5u elem_num: %5u size: %10u range: (%u....%u) ]\n", + self->elem_size, self->elem_num, array_size(self), + array_low(self), array_high(self)); + + dump_memory(out, (unsigned long)self, self, sizeof(*self)); + + tree_iter_t it; + for (tree_iter_init(&it, &self->tree, TI_FLAG_FWD); + tree_iter_elem(&it); tree_iter_inc(&it)) { + array_node_t *node; + node = + container_of(tree_iter_elem(&it), array_node_t, + node); + + fprintf(out, "magic[%x] address[%x]\n", + node->magic, node->address); + fprintf(out, + "n[%p] left[%p] right[%p] parent[%p] key[%p] \n", + &node->node, node->node.left, node->node.right, + node->node.parent, node->node.key); + + dump_memory(out, (unsigned long)node, node, + self->page_size); + } + } +} + +/* ======================================================================= */ diff --git a/clib/src/array_iter.c b/clib/src/array_iter.c new file mode 100644 index 0000000..921bf97 --- /dev/null +++ b/clib/src/array_iter.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: array_iter.c + * Author: Shaun Wetzstein + * Descr: dynamic array + * Note: + * Date: 10/22/10 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "assert.h" +#include "misc.h" +#include "hash.h" +#include "nargs.h" +#include "array_iter.h" + +/* ======================================================================= */ + +const char *__array_iter_msg[] = { + "array_iter: unexpected NULL pointer", + "array_iter: index out-of-range", +}; + +#define ARRAY_ITER_NULL (__array_iter_msg[0]) +#define ARRAY_ITER_RANGE (__array_iter_msg[1]) + +/* ======================================================================= */ + +static inline const void *__array_iter_bwd(array_iter_t * self) +{ + size_t low = array_low(self->array); + const void *ret = NULL; + + if (low < self->idx) + self->idx--; + + while (low < self->idx) { + if (array_status(self->array, self->idx)) + break; + self->idx--; + } + + if (low < self->idx) + ret = array_at(self->array, self->idx); + + return ret; +} + +static inline const void *__array_iter_fwd(array_iter_t * self) +{ + size_t high = array_high(self->array); + const void *ret = NULL; + + if (self->idx < high) + self->idx++; + + while (self->idx < high) { + if (array_status(self->array, self->idx)) + break; + self->idx++; + } + + if (self->idx < high) + ret = array_at(self->array, self->idx); + + return ret; +} + +void array_iter_init(array_iter_t * self, array_t * array, uint32_t flags) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_ITER_NULL); + if (unlikely(array == NULL)) + throw_unexpected(ARRAY_ITER_NULL); + + self->flags = flags; + self->array = array; + + if (self->flags & AI_FLAG_BWD) { + self->idx = array_high(self->array); + __array_iter_bwd(self); + } else { + self->idx = array_low(self->array); + __array_iter_fwd(self); + } +} + +void array_iter_clear(array_iter_t * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_ITER_NULL); + + if (self->flags & AI_FLAG_BWD) + self->idx = array_high(self->array); + else + self->idx = array_low(self->array); + + self->array = NULL; +} + +const void *array_iter_elem(array_iter_t * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_ITER_NULL); + if (self->idx < array_low(self->array)) + throw_unexpected(ARRAY_ITER_RANGE); + if (array_high(self->array) <= self->idx) + throw_unexpected(ARRAY_ITER_RANGE); + + return array_status(self->array, self->idx) ? + array_at(self->array, self->idx) : NULL; +} + +const void *array_iter_inc1(array_iter_t * self) +{ + return array_iter_inc2(self, 1); +} + +const void *array_iter_inc2(array_iter_t * self, size_t inc) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_ITER_NULL); + + const void *ret = NULL; + + if (self->flags & AI_FLAG_BWD) + ret = __array_iter_bwd(self); + else + ret = __array_iter_fwd(self); + + return ret; +} + +const void *array_iter_dec1(array_iter_t * self) +{ + return array_iter_dec2(self, 1); +} + +const void *array_iter_dec2(array_iter_t * self, size_t dec) +{ + if (unlikely(self == NULL)) + throw_unexpected(ARRAY_ITER_NULL); + + const void *ret = NULL; + + if (self->flags & AI_FLAG_BWD) + ret = __array_iter_fwd(self); + else + ret = __array_iter_bwd(self); + + return ret; +} + +/* ======================================================================= */ diff --git a/clib/src/checksum.c b/clib/src/checksum.c new file mode 100644 index 0000000..fea96b4 --- /dev/null +++ b/clib/src/checksum.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include "assert.h" +#include "checksum.h" + +uint32_t memcpy_checksum(void *__restrict __dst, const void *__restrict __src, + size_t __n) +{ + uint8_t sum[4] = { 0, }; + + /* assert(((uintptr_t)__src & 3) == 0); */ + + size_t i; + + if (__dst == NULL) + for (i = 0; i < __n; i++) + sum[i & 3] ^= ((uint8_t *) __src)[i]; + else + for (i = 0; i < __n; i++) + sum[i & 3] ^= ((uint8_t *) __src)[i], + ((uint8_t *) __dst)[i] = ((uint8_t *) __src)[i]; + + return (sum[0] << 24) | (sum[1] << 16) | (sum[2] << 8) | sum[3]; +} diff --git a/clib/src/crc32.c b/clib/src/crc32.c new file mode 100644 index 0000000..d0cf657 --- /dev/null +++ b/clib/src/crc32.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Crc - 32 BIT ANSI X3.66 CRC checksum files */ + +#include +#include + +#include "crc32.h" + +/**********************************************************************\ +|* Demonstration program to compute the 32-bit CRC used as the frame *| +|* check sequence in ADCCP (ANSI X3.66, also known as FIPS PUB 71 *| +|* and FED-STD-1003, the U.S. versions of CCITT's X.25 link-level *| +|* protocol). The 32-bit FCS was added via the Federal Register, *| +|* 1 June 1982, p.23798. I presume but don't know for certain that *| +|* this polynomial is or will be included in CCITT V.41, which *| +|* defines the 16-bit CRC (often called CRC-CCITT) polynomial. FIPS *| +|* PUB 78 says that the 32-bit FCS reduces otherwise undetected *| +|* errors by a factor of 10^-5 over 16-bit FCS. *| +\**********************************************************************/ + +/* Copyright (C) 1986 Gary S. Brown. You may use this program, or + code or tables extracted from it, as desired without restriction.*/ + +/* First, the polynomial itself and its table of feedback terms. The */ +/* polynomial is */ +/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ +/* Note that we take it "backwards" and put the highest-order term in */ +/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ +/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ +/* the MSB being 1. */ + +/* Note that the usual hardware shift register implementation, which */ +/* is what we're using (we're merely optimizing it by doing eight-bit */ +/* chunks at a time) shifts bits into the lowest-order term. In our */ +/* implementation, that means shifting towards the right. Why do we */ +/* do it this way? Because the calculated CRC must be transmitted in */ +/* order from highest-order term to lowest-order term. UARTs transmit */ +/* characters in order from LSB to MSB. By storing the CRC this way, */ +/* we hand it to the UART in the order low-byte to high-byte; the UART */ +/* sends each low-bit to hight-bit; and the result is transmission bit */ +/* by bit from highest- to lowest-order term without requiring any bit */ +/* shuffling on our part. Reception works similarly. */ + +/* The feedback terms table consists of 256, 32-bit entries. Notes: */ +/* */ +/* 1. The table can be generated at runtime if desired; code to do so */ +/* is shown later. It might not be obvious, but the feedback */ +/* terms simply represent the results of eight shift/xor opera- */ +/* tions for all combinations of data and CRC register values. */ +/* */ +/* 2. The CRC accumulation logic is the same for all CRC polynomials, */ +/* be they sixteen or thirty-two bits wide. You simply choose the */ +/* appropriate table. Alternatively, because the table can be */ +/* generated at runtime, you can start by generating the table for */ +/* the polynomial in question and use exactly the same "updcrc", */ +/* if your application needn't simultaneously handle two CRC */ +/* polynomials. (Note, however, that XMODEM is strange.) */ +/* */ +/* 3. For 16-bit CRCs, the table entries need be only 16 bits wide; */ +/* of course, 32-bit entries work OK if the high 16 bits are zero. */ +/* */ +/* 4. The values must be right-shifted by eight bits by the "updcrc" */ +/* logic; the shift must be unsigned (bring in zeroes). On some */ +/* hardware you could probably optimize the shift in assembler by */ +/* using byte-swap instructions. */ + +static uint32_t __crc32_table[] = { /* CRC polynomial 0xedb88320 */ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +inline uint32_t clib_crc32(unsigned char octet, uint32_t crc) +{ + return __crc32_table[(crc ^ (uint8_t) octet) & 0xff] ^ (crc >> 8); +} + +uint32_t memcpy32(void *__restrict __dst, const void *__restrict __src, + size_t __n) +{ + register uint32_t __crc = 0xFFFFFFFF; + + if (__dst == NULL) + for (; __n; --__n, ++__src) { + __crc = clib_crc32(*(uint8_t *) __src, __crc); + } else + for (; __n; --__n, ++__src, ++__dst) { + __crc = clib_crc32(*(uint8_t *) __src, __crc); + *(uint8_t *) __dst = *(uint8_t *) __src; + } + + return ~__crc; +} diff --git a/clib/src/crc32_main.c b/clib/src/crc32_main.c new file mode 100644 index 0000000..3bdf072 --- /dev/null +++ b/clib/src/crc32_main.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crc32.h" + +void usage(void) +{ + printf("Usage: crc32 infile\n"); +} + +int main(int argc, const char *argv[]) +{ + if (argc != 2) { + usage(); + return 1; + } + + int fd = open(argv[1], O_RDONLY); + if (fd < 0) { + printf("open %s: %s\n", argv[1], strerror(errno)); + return 1; + } + + struct stat stat; + int rc = fstat(fd, &stat); + if (rc < 0) { + printf("stat %s: %s\n", argv[1], strerror(errno)); + return 1; + } + + off_t filesize = stat.st_size; + + void *in_buf = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0); + if (in_buf == MAP_FAILED) { + /* FIXME could still try to use read().... */ + printf("mmap %d failed: %s\n", filesize, strerror(errno)); + return 1; + } + + rc = close(fd); + if (rc < 0) { + printf("close %s: %s\n", argv[1], strerror(errno)); + return 1; + } + + unsigned long crc32 = memcpy32(NULL, in_buf, filesize); + + printf("%#010lx", crc32), fflush(stdout); + + return 0; +} diff --git a/clib/src/db.c b/clib/src/db.c new file mode 100644 index 0000000..e61726c --- /dev/null +++ b/clib/src/db.c @@ -0,0 +1,400 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: db.c + * Author: Shaun Wetzstein + * Descr: SQlite wrapper + * Note: + * Date: 02/25/11 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "assert.h" +#include "err.h" + +#include "db.h" + +/* =======================================================================*/ + +int db_init(db_t * self, const char *path) +{ + assert(self != NULL); + + self->path = strdup(path); + if (self->path == NULL) { + ERRNO(errno); + return -1; + } + + self->db = NULL; + + return 0; +} + +int db_delete(db_t * self) +{ + if (self != NULL) { + db_close(self); + if (self->path != NULL) + free((char *)self->path); + memset(self, 0, sizeof(*self)); + } + + return 0; +} + +int db_execute(db_t * self, const char *fmt, ...) +{ + assert(self != NULL); + + va_list ap; + va_start(ap, fmt); + char *sql = NULL; + vasprintf(&sql, fmt, ap); + + assert(sql != NULL); + + int rc = sqlite3_exec(self->db, sql, NULL, NULL, NULL); + if (sql != NULL) + free(sql); + + if (rc != DB_OK) { + DBERR(self->db); + return -1; + } + + return 0; +} + +int db_open(db_t * self, int flags) +{ + assert(self != NULL); + + int rc = DB_OK; + + if (self->db != NULL) + db_close(self); + + rc = sqlite3_open_v2(self->path, &self->db, flags, NULL); + if (rc != DB_OK) { + DBERR(self->db); + return -1; + } + + return rc; +} + +int db_close(db_t * self) +{ + assert(self != NULL); + + int rc = DB_OK; + + if (self->db != NULL) { + /* finalize statements */ + rc = sqlite3_close(self->db); + } + + return rc; +} + +int db_register_function(db_t * self, const char *name, int argc, db_f func, + void *data) +{ + assert(self != NULL); + + int rc = DB_OK; + + rc = sqlite3_create_function(self->db, name, argc, SQLITE_ANY, data, + func, NULL, NULL); + if (rc != 0) { + DBERR(self->db); + return -1; + } + + return rc; +} + +int db_begin(db_t * self, transaction_type_t type) +{ + assert(self != NULL); + + const char *tran_type = NULL; + + switch (type) { + case tt_DEFERRED: + tran_type = "DEFERRED"; + break; + case tt_IMMEDIATE: + tran_type = "IMMEDIATE"; + break; + case tt_EXCLUSIVE: + tran_type = "EXCLUSIVE"; + break; + default: + UNEXPECTED("'%d' invalid transaction type", type); + return -1; + } + + return db_execute(self, "BEGIN %s TRANSACTION", tran_type); +} + +int db_commit(db_t * self) +{ + assert(self != NULL); + return db_execute(self, "COMMIT TRANSACTION"); +} + +int db_rollback(db_t * self) +{ + assert(self != NULL); + return db_execute(self, "ROLLBACK TRANSACTION"); +} + +/* =======================================================================*/ + +int statement_init(statement_t * self, db_t * db) +{ + assert(self != NULL); + assert(db != NULL); + + self->db = db; + self->stmt = NULL; + + return 0; +} + +int statement_delete(statement_t * self) +{ + assert(self != NULL); + + if (self->stmt != NULL) { + statement_finalize(self); + self->stmt = NULL; + } + + self->db = NULL; + free(self); + + return 0; +} + +int statement_prepare(statement_t * self, const char *sql) +{ + assert(self != NULL); + assert(sql != NULL); + + int rc = DB_OK; + + if (self->stmt != NULL) { + rc = sqlite3_finalize(self->stmt); + if (rc != DB_OK) { + SQLERR(self->db->db, self->stmt); + return -1; + } + } + + rc = sqlite3_prepare_v2(self->db->db, sql, -1, &self->stmt, NULL); + if (rc != DB_OK) { + SQLERR(self->db->db, self->stmt); + return -1; + } + + return rc; +} + +int statement_step(statement_t * self) +{ + assert(self != NULL); + + int rc = DB_OK; + + if (self->stmt != NULL) { + rc = sqlite3_step(self->stmt); + + switch (rc) { + case DB_DONE: + case DB_ROW: + break; + default: + SQLERR(self->db->db, self->stmt); + return -1; + } + } + + return rc; +} + +int statement_reset(statement_t * self) +{ + self = NULL; + return 0; +} + +int statement_finalize(statement_t * self) +{ + assert(self != NULL); + + int rc = DB_OK; + + if (self->stmt != NULL) { + rc = sqlite3_finalize(self->stmt); + if (rc != DB_OK) { + SQLERR(self->db->db, self->stmt); + return -1; + } + self->stmt = NULL; + } + + return rc; +} + +int statement_bind_int(statement_t * self, int pos, int val) +{ + assert(self != NULL); + + int rc = DB_OK; + + if (self->stmt != NULL) { + rc = sqlite3_bind_int(self->stmt, pos, val); + if (rc != DB_OK) { + SQLERR(self->db->db, self->stmt); + return -1; + } + } + + return rc; +} + +int statement_bind_int64(statement_t * self, int pos, int64_t val) +{ + assert(self != NULL); + + int rc = DB_OK; + + if (self->stmt != NULL) { + rc = sqlite3_bind_int64(self->stmt, pos, val); + if (rc != DB_OK) { + SQLERR(self->db->db, self->stmt); + return -1; + } + } + + return rc; +} + +int statement_bind_text(statement_t * self, int pos, const char *val) +{ + assert(self != NULL); + + int rc = DB_OK; + + if (self->stmt != NULL) { + rc = sqlite3_bind_text(self->stmt, pos, val, -1, + SQLITE_TRANSIENT); + if (rc != DB_OK) { + SQLERR(self->db->db, self->stmt); + return -1; + } + } + + return rc; +} + +int statement_bind_blob(statement_t * self, int pos, const void *val, int size) +{ + assert(self != NULL); + + int rc = DB_OK; + + if (self->stmt != NULL) { + rc = sqlite3_bind_blob(self->stmt, pos, val, size, + SQLITE_TRANSIENT); + if (rc != DB_OK) { + SQLERR(self->db->db, self->stmt); + return -1; + } + } + + return rc; +} + +int statement_column_int(statement_t * self, int pos) +{ + assert(self != NULL); + return sqlite3_column_int(self->stmt, pos); +} + +int64_t statement_column_int64(statement_t * self, int pos) +{ + assert(self != NULL); + return sqlite3_column_int64(self->stmt, pos); +} + +int statement_column_bytes(statement_t * self, int pos) +{ + assert(self != NULL); + return sqlite3_column_bytes(self->stmt, pos); +} + +const unsigned char *statement_column_text(statement_t * self, int pos) +{ + assert(self != NULL); + + const unsigned char *rc = NULL; + + if (self->stmt != NULL) { + rc = sqlite3_column_text(self->stmt, pos); + if (rc == NULL) { + SQLERR(self->db->db, self->stmt); + return NULL; + } + } + + return rc; +} + +const void *statement_column_blob(statement_t * self, int pos) +{ + assert(self != NULL); + + const void *rc = NULL; + + if (self->stmt != NULL) { + rc = sqlite3_column_blob(self->stmt, pos); + if (rc == NULL) { + SQLERR(self->db->db, self->stmt); + return NULL; + } + } + + return rc; +} + +/* =======================================================================*/ diff --git a/clib/src/dispatch.c b/clib/src/dispatch.c new file mode 100644 index 0000000..4630a70 --- /dev/null +++ b/clib/src/dispatch.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: dispatch.c + * Author: Shaun Wetzstein + * Descr: + * Note: + * Date: 10/03/10 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" +#include "nargs.h" +#include "dispatch.h" + +#define DISPATCH_PAGE_SIZE 64 +#define DISPATCH_EVENT_SIZE 10 + +/* ======================================================================= */ + +const char *__dispatch_msg[] = { + "dispatch: unexpected NULL self pointer", + "dispatch: unexpected NULL callback structure", +}; + +#define DISPATCH_NULL (__dispatch_msg[0]) +#define DISPATCH_CALLBACK_NULL (__dispatch_msg[1]) + +/* ======================================================================= */ + +void dispatch_init(dispatch_t * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(DISPATCH_NULL); + + if (self->fd != -1) + close(self->fd), self->fd = -1; + + self->fd = epoll_create1(EPOLL_CLOEXEC); + if (unlikely(self->fd == -1)) + throw_errno(errno); + + array_init(&self->events, sizeof(struct epoll_event), + DISPATCH_PAGE_SIZE); +} + +void dispatch_delete(dispatch_t * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(DISPATCH_NULL); + close(self->fd), self->fd = -1; + array_delete(&self->events); +} + +int dispatch_fileno(dispatch_t * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(DISPATCH_NULL); + return self->fd; +} + +int dispatch_add(dispatch_t * self, int fd, uint32_t events, + dispatch_callback_t * cb) +{ + if (unlikely(self == NULL)) + throw_unexpected(DISPATCH_NULL); + + array_status(&self->events, fd, true); + + struct epoll_event *ep; + ep = (struct epoll_event *)array_at(&self->events, fd); + ep->events = events; + ep->data.u64 = (uint64_t) fd << 32; + ep->data.ptr = (void *)cb; + + if (unlikely(epoll_ctl(self->fd, EPOLL_CTL_ADD, fd, ep))) + throw_errno(errno); + + return fd; +} + +void dispatch_remove(dispatch_t * self, int fd) +{ + if (unlikely(self == NULL)) + throw_unexpected(DISPATCH_NULL); + + if (unlikely(epoll_ctl(self->fd, EPOLL_CTL_DEL, fd, NULL))) + throw_errno(errno); + + array_status(&self->events, fd, false); +} + +void dispatch_wait1(dispatch_t * self) +{ + dispatch_wait2(self, -1); +} + +void dispatch_wait2(dispatch_t * self, int timeout) +{ + if (unlikely(self == NULL)) + throw_unexpected(DISPATCH_NULL); + + if (-1 < self->fd) { + struct epoll_event events[DISPATCH_EVENT_SIZE]; + + int rc = epoll_wait(self->fd, events, + DISPATCH_EVENT_SIZE, timeout); + + if (0 < rc) { + dump_memory(stdout, 0, events, sizeof(events)); + } + for (int n = 0; n < rc; n++) { + dispatch_callback_t *cb = + (dispatch_callback_t *) events[n].data.ptr; + if (cb != NULL) { + printf + ("cb[%p] fd[%d] data[%p] ep->data.u64[%lld]\n", + cb, cb->fd, cb->data, events[n].data.u64); + if (cb->func != NULL) { + // dispatch_event ev = {events[n].data.u64 >> 32, events[n].events}; + dispatch_event_t ev = + { cb->fd, events[n].events }; + cb->func(&ev, cb->data); + } + } + } + } +} + +/* ======================================================================= */ diff --git a/clib/src/ecc.c b/clib/src/ecc.c new file mode 100644 index 0000000..2103b00 --- /dev/null +++ b/clib/src/ecc.c @@ -0,0 +1,456 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: ecc.c + * Author: Shaun Wetzstein + * Descr: SFC ECC functions + * Note: + * Date: 08/02/12 + * Descr: Added New ECC function with correctable bit functionality. + * Date: 12/04/13 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ecc.h" +#include "builtin.h" +#include "attribute.h" + +#ifndef be64toh +#include +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define be64toh(x) __bswap_64(x) +#define htobe64(x) __bswap_64(x) +#else +#define be64toh(x) (x) +#define htobe64(x) (x) +#endif +#endif + +/* + * This is an alternative way to calculate the ECC byte taken + * from the SFC spec. + */ +uint8_t sfc_ecc2(uint8_t __data[8]) __unused__; +uint8_t sfc_ecc2(uint8_t __data[8]) +{ + uint8_t ecc = 0; + + for (int byte = 0; byte < 8; byte++) { + for (int bit = 0; bit < 8; bit++) { + static unsigned char m[] = + { 0xff, 0x00, 0x00, 0xe8, 0x42, 0x3c, 0x0f, 0x99 }; + + unsigned char x = + __data[byte] & m[(byte + bit + 1) & 7]; + x = x ^ (x >> 4); + x = x ^ (x >> 2); + x = x ^ (x >> 1); + + ecc ^= (x & 1) << bit; + } + } + + return ~ecc; +} + +uint8_t sfc_ecc(uint8_t __data[8]) +{ +/* each bit of the ECC data corresponds to a row in this matrix */ + static uint8_t __matrix[8][8] = { +/* 11111111 00000000 00000000 11101000 01000010 00111100 00001111 10011001 */ + {0xff, 0x00, 0x00, 0xe8, 0x42, 0x3c, 0x0f, 0x99}, +/* 10011001 11111111 00000000 00000000 11101000 01000010 00111100 00001111 */ + {0x99, 0xff, 0x00, 0x00, 0xe8, 0x42, 0x3c, 0x0f}, +/* 00001111 10011001 11111111 00000000 00000000 11101000 01000010 00111100 */ + {0x0f, 0x99, 0xff, 0x00, 0x00, 0xe8, 0x42, 0x3c}, +/* 00111100 00001111 10011001 11111111 00000000 00000000 11101000 01000010 */ + {0x3c, 0x0f, 0x99, 0xff, 0x00, 0x00, 0xe8, 0x42}, +/* 01000010 00111100 00001111 10011001 11111111 00000000 00000000 11101000 */ + {0x42, 0x3c, 0x0f, 0x99, 0xff, 0x00, 0x00, 0xe8}, +/* 11101000 01000010 00111100 00001111 10011001 11111111 00000000 00000000 */ + {0xe8, 0x42, 0x3c, 0x0f, 0x99, 0xff, 0x00, 0x00}, +/* 00000000 11101000 01000010 00111100 00001111 10011001 11111111 00000000 */ + {0x00, 0xe8, 0x42, 0x3c, 0x0f, 0x99, 0xff, 0x00}, +/* 00000000 00000000 11101000 01000010 00111100 00001111 10011001 11111111 */ + {0x00, 0x00, 0xe8, 0x42, 0x3c, 0x0f, 0x99, 0xff}, + }; + + static uint8_t __mask[] = { +/* 10000000 01000000 00100000 00010000 00001000 00000100 00000010 00000001 */ + 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 + }; + + uint8_t __and[8], __ecc = 0; + + for (uint32_t i = 0; i < sizeof(__matrix) / sizeof(*__matrix); i++) { + int __popcount = 0; + + for (uint32_t __byte = 0; __byte < 8; __byte++) { + /* compute the AND of the data and ECC matrix */ + __and[__byte] = __data[__byte] & __matrix[i][__byte]; + /* count the number of '1' bits in the result (parity) */ + __popcount += popcount(__and[__byte]); + } + + /* if the result is odd parity, turn on corresponding ECC bit */ + if ((__popcount) & 1) /* odd parity? */ + __ecc |= __mask[i]; + +#ifdef DEBUG + printf("\nmatrix["); + print_binary(NULL, __matrix[i], 8); + printf("]\n data["); + print_binary(NULL, __data, 8); + printf("]\n and["); + print_binary(NULL, __and, 8); + printf("]\n"); + printf("popcount: %d ecc %2.2x\n", __popcount, __ecc); +#endif + } + + /* the ECC data is inverted such that */ + /* 0xFFFFFFFFffffffff => 0xFF for erased NOR flash */ + return __ecc ^ 0xFF; +} + + +/* + * "aaaaaaaa [wwwwwwww_xxxxxxxx e yyyyyyyy_zzzzzzzz e] [........ ........]" + */ +static void __ecc_dump(FILE * __out, uint32_t __addr, + void *__restrict __buf, size_t __buf_sz, bool invert) +{ + if (__buf_sz <= 0 || __buf == NULL) + return; + if (__out == NULL) + __out = stdout; + + size_t hex_size = (16 + 2) * 2; // 16 bytes per doubleword plus ECC byte + size_t ascii_size = (8 + 1) * 2; // 8 bytes per doublewod plus ECC byte + + uint8_t hex[hex_size + 1], ascii[hex_size + 1]; + memset(hex, '.', hex_size), memset(ascii, '.', ascii_size); + + void print_line(void) { + const char *ansi_red = "\033[1;1m\033[1;31m"; + const char *ansi_norm = "\033[0m"; + + uint8_t c1 = sfc_ecc(ascii + 0), e1 = ascii[8]; + if (invert == true) + c1 = ~c1; + uint8_t c2 = sfc_ecc(ascii + 9), e2 = ascii[17]; + if (invert == true) + c2 = ~c2; + + for (size_t i = 0; i < ascii_size; i++) + ascii[i] = isprint(ascii[i]) ? ascii[i] : '.'; + + fprintf(__out, "%08x ", __addr); + + fprintf(__out, + "[%.8s_%.8s %.*s%.2s%.*s %.8s_%.8s %.*s%.2s%.*s] ", + hex + 0, hex + 8, (c1 != e1) ? strlen(ansi_red) : 0, + ansi_red, hex + 16, (c1 != e1) ? strlen(ansi_norm) : 0, + ansi_norm, hex + 18, hex + 26, + (c2 != e2) ? strlen(ansi_red) : 0, ansi_red, hex + 34, + (c2 != e2) ? strlen(ansi_norm) : 0, ansi_norm); + + fprintf(__out, "[%.8s %.8s]\n", ascii + 0, ascii + 9); + } + + size_t s = __addr % 16, i = 0; + __addr &= ~0xF; + + for (i = s; i < __buf_sz + s; i++) { + unsigned char c = ((unsigned char *)__buf)[i - s]; + + hex[((i << 1) + 0) % hex_size] = "0123456789abcdef"[c >> 4]; + hex[((i << 1) + 1) % hex_size] = "0123456789abcdef"[c & 0xf]; + + ascii[i % ascii_size] = c; + + if (i == 0) + continue; + + if ((i + 1) % ascii_size == 0) { + print_line(); + memset(hex, '.', hex_size), memset(ascii, '.', + ascii_size); + + __addr += ascii_size; + } + } + + if (i % ascii_size) + print_line(); + + return; +} + + + +/* ======================================== */ +static uint64_t ecc_matrix[] = { + //0000000000000000111010000100001000111100000011111001100111111111 + 0x0000e8423c0f99ff, + //0000000011101000010000100011110000001111100110011111111100000000 + 0x00e8423c0f99ff00, + //1110100001000010001111000000111110011001111111110000000000000000 + 0xe8423c0f99ff0000, + //0100001000111100000011111001100111111111000000000000000011101000 + 0x423c0f99ff0000e8, + //0011110000001111100110011111111100000000000000001110100001000010 + 0x3c0f99ff0000e842, + //0000111110011001111111110000000000000000111010000100001000111100 + 0x0f99ff0000e8423c, + //1001100111111111000000000000000011101000010000100011110000001111 + 0x99ff0000e8423c0f, + //1111111100000000000000001110100001000010001111000000111110011001 + 0xff0000e8423c0f99 +}; + +static uint8_t syndrome_matrix[] = { + GD, E7, E6, UE, E5, UE, UE, 47, E4, UE, UE, 37, UE, 35, 39, UE, + E3, UE, UE, 48, UE, 30, 29, UE, UE, 57, 27, UE, 31, UE, UE, UE, + E2, UE, UE, 17, UE, 18, 40, UE, UE, 58, 22, UE, 21, UE, UE, UE, + UE, 16, 49, UE, 19, UE, UE, UE, 23, UE, UE, UE, UE, 20, UE, UE, + E1, UE, UE, 51, UE, 46, 9, UE, UE, 34, 10, UE, 32, UE, UE, 36, + UE, 62, 50, UE, 14, UE, UE, UE, 13, UE, UE, UE, UE, UE, UE, UE, + UE, 61, 8, UE, 41, UE, UE, UE, 11, UE, UE, UE, UE, UE, UE, UE, + 15, UE, UE, UE, UE, UE, UE, UE, UE, UE, 12, UE, UE, UE, UE, UE, + E0, UE, UE, 55, UE, 45, 43, UE, UE, 56, 38, UE, 1, UE, UE, UE, + UE, 25, 26, UE, 2, UE, UE, UE, 24, UE, UE, UE, UE, UE, 28, UE, + UE, 59, 54, UE, 42, UE, UE, 44, 6, UE, UE, UE, UE, UE, UE, UE, + 5, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, + UE, 63, 53, UE, 0, UE, UE, UE, 33, UE, UE, UE, UE, UE, UE, UE, + 3, UE, UE, 52, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, + 7, UE, UE, UE, UE, UE, UE, UE, UE, 60, UE, UE, UE, UE, UE, UE, + UE, UE, UE, UE, 4, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, UE, +}; + + +static uint8_t generate_ecc(uint64_t i_data) +{ + uint8_t result = 0; + + for (int i = 0; i < 8; i++) + { + result |= __builtin_parityll(ecc_matrix[i] & i_data) << i; + } + return result; +} +static uint8_t verify_ecc(uint64_t i_data, uint8_t i_ecc) +{ + return syndrome_matrix[generate_ecc(i_data) ^ i_ecc ]; +} +static uint8_t correct_ecc(uint64_t *io_data, uint8_t *io_ecc) +{ + uint8_t bad_bit = verify_ecc(*io_data, *io_ecc); + + if ((bad_bit != GD) && (bad_bit != UE)) // Good is done, UE is hopeless. + { + // Determine if the ECC or data part is bad, do bit flip. + if (bad_bit >= E7) + { + *io_ecc ^= (1 << (bad_bit - E7)); + } + else + { + *io_data ^=(1ull << (63 - bad_bit)); + } + } + return bad_bit; +} + +static void inject_ecc(const uint8_t* i_src, size_t i_srcSz, + uint8_t* o_dst, bool invert) +{ + assert(0 == (i_srcSz % sizeof(uint64_t))); + + for(size_t i = 0, o = 0; + i < i_srcSz; + i += sizeof(uint64_t), o += sizeof(uint64_t) + sizeof(uint8_t)) + { + // Read data word, copy to destination. + uint64_t data = *(const uint64_t*)(&i_src[i]); + + *(uint64_t*)(&o_dst[o]) = data; + data = be64toh(data); + + // Calculate ECC, copy to destination. + uint8_t ecc = generate_ecc(data); + o_dst[o + sizeof(uint64_t)] = invert ? ~ecc : ecc; + } +} +static ecc_status_t remove_ecc(uint8_t* io_src, size_t i_srcSz, + uint8_t* o_dst, size_t i_dstSz, + bool invert) +{ + assert(0 == (i_dstSz % sizeof(uint64_t))); + + ecc_status_t rc = CLEAN; + + for(size_t i = 0, o = 0; + i < i_srcSz; + i += sizeof(uint64_t) + sizeof(uint8_t), o += sizeof(uint64_t)) + { + // Read data and ECC parts. + uint64_t data = *(uint64_t*)(&io_src[i]); + data = be64toh(data); + + uint8_t ecc = io_src[i + sizeof(uint64_t)]; + if(invert) + { + ecc = ~ecc; + } + // Calculate failing bit and fix data. + uint8_t bad_bit = correct_ecc(&data, &ecc); + + // Return data to big endian. + data = htobe64(data); + + // Perform correction and status update. + if (bad_bit == UE) + { + rc = UNCORRECTABLE; + } + else if (bad_bit != GD) + { + if (rc != UNCORRECTABLE) + { + rc = CORRECTED; + } + *(uint64_t*)(&io_src[i]) = data; + io_src[i + sizeof(uint64_t)] = invert ? ~ecc : ecc; + } + + // Copy fixed data to destination buffer. + *(uint64_t*)(&o_dst[o]) = data; + } + return rc; +} +/* ========================================= */ + +static ssize_t __ecc_inject(void *__restrict __dst, size_t __dst_sz, + const void *__restrict __src, size_t __src_sz, + bool invert) +{ + int __size = sizeof(uint64_t); + + errno = 0; + if (__src_sz & (__size - 1)) { + errno = EINVAL; + return -1; + } + if (__dst_sz < (__src_sz + (__src_sz / __size))) { + errno = ENOBUFS; + return -1; + } + + ssize_t rc=0; + ssize_t c_sz = __src_sz; + for (; c_sz; c_sz -= __size) { + rc += __size + 1; + } + + inject_ecc(__src, __src_sz, __dst, invert); + return (rc); +} + +static ssize_t __ecc_remove(void *__restrict __dst, size_t __dst_sz, + const void *__restrict __src, size_t __src_sz, + bool invert) +{ + int __size = sizeof(uint64_t); + + errno = 0; + if ((__src_sz % (__size + 1)) != 0) { + errno = EINVAL; + return -1; + } + if (__dst_sz < (__src_sz - (__src_sz / __size))) { + errno = ENOBUFS; + return -1; + } + + + int target_size = ((__src_sz / (sizeof(uint64_t) + 1))*sizeof(uint64_t)); + if( remove_ecc((uint8_t*)__src, __src_sz, __dst, __dst_sz, invert) != CLEAN) + { + target_size = 0; + } + return target_size; +} + +void sfc_ecc_dump(FILE * __out, uint32_t __addr, + void *__restrict __buf, size_t __buf_sz) +{ + return __ecc_dump(__out, __addr, __buf, __buf_sz, false); +} + +/* ========================================= */ +ssize_t sfc_ecc_inject(void *__restrict __dst, size_t __dst_sz, + const void *__restrict __src, size_t __src_sz) +{ + return __ecc_inject(__dst, __dst_sz, __src, __src_sz, true); +} +ssize_t sfc_ecc_remove(void *__restrict __dst, size_t __dst_sz, + const void *__restrict __src, size_t __src_sz) +{ + return __ecc_remove(__dst, __dst_sz, __src, __src_sz, true); + +} +ssize_t p8_ecc_remove_size (void *__restrict __dst, size_t __dst_sz, + void *__restrict __src, size_t __src_sz __unused__) +{ + return __ecc_remove(__dst, __dst_sz, __src, __src_sz, false); +} + +/* ========================================= */ +ssize_t p8_ecc_inject(void *__restrict __dst, size_t __dst_sz, + const void *__restrict __src, size_t __src_sz) +{ + return __ecc_inject(__dst, __dst_sz, __src, __src_sz, false); +} + +ecc_status_t p8_ecc_remove (void *__restrict __dst, size_t __dst_sz, + void *__restrict __src, size_t __src_sz __unused__) +{ + return remove_ecc(__src, __src_sz, __dst, __dst_sz, false); +} + +void p8_ecc_dump(FILE * __out, uint32_t __addr, + void *__restrict __buf, size_t __buf_sz) +{ + return __ecc_dump(__out, __addr, __buf, __buf_sz, true); +} + diff --git a/clib/src/err.c b/clib/src/err.c new file mode 100644 index 0000000..e16f453 --- /dev/null +++ b/clib/src/err.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: err.c + * Author: Shaun Wetzstein + * Descr: Error container + * Date: 04/04/12 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "attribute.h" +#include "min.h" + +#include "err.h" + +static pthread_key_t __err_key = 0; + +static const char *__err_type_name[] = { + [ERR_NONE] = "none", + [ERR_ERRNO] = "errno", + [ERR_UNEXPECTED] = "unexpected", + [ERR_VERSION] = "version", +}; + +/* =======================================================================*/ + +void err_delete(err_t * self) +{ + assert(self != NULL); + assert(self->magic == ERR_MAGIC); + + memset(self, 0, sizeof(*self)); + free(self); + + return; +} + +err_t *err_get(void) +{ + list_t *list = (list_t *) pthread_getspecific(__err_key); + + err_t *self = NULL; + + if (list != NULL) { + self = container_of(list_remove_tail(list), err_t, node); + + if (list_empty(list)) { + free(list), list = NULL; + assert(pthread_setspecific(__err_key, list) == 0); + } + } + + return self; +} + +void err_put(err_t * self) +{ + assert(self != NULL); + + list_t *list = pthread_getspecific(__err_key); + if (list == NULL) { + list = (list_t *) malloc(sizeof(*list)); + assert(list != NULL); + + list_init(list); + assert(pthread_setspecific(__err_key, list) == 0); + } + + list_add_head(list, &self->node); + + return; +} + +int err_type1(err_t * self) +{ + assert(self != NULL); + assert(self->magic == ERR_MAGIC); + + return self->type; +} + +void err_type2(err_t * self, int type) +{ + assert(self != NULL); + assert(self->magic == ERR_MAGIC); + + self->type = type; +} + +int err_code1(err_t * self) +{ + assert(self != NULL); + assert(self->magic == ERR_MAGIC); + + return self->code; +} + +void err_code2(err_t * self, int code) +{ + assert(self != NULL); + assert(self->magic == ERR_MAGIC); + + self->code = code; + + return; +} + +const char *err_file1(err_t * self) +{ + assert(self != NULL); + assert(self->magic == ERR_MAGIC); + + return self->file; +} + +void err_file2(err_t * self, const char *file) +{ + assert(self != NULL); + assert(self->magic == ERR_MAGIC); + + self->file = file; + + return; +} + +int err_line1(err_t * self) +{ + assert(self != NULL); + assert(self->magic == ERR_MAGIC); + + return self->line; +} + +void err_line2(err_t * self, int line) +{ + assert(self != NULL); + assert(self->magic == ERR_MAGIC); + + self->line = line; + + return; +} + +const void *err_data1(err_t * self) +{ + assert(self != NULL); + assert(self->magic == ERR_MAGIC); + + return self->data; +} + +void err_data2(err_t * self, int size, const void *data) +{ + assert(self != NULL); + assert(self->magic == ERR_MAGIC); + + self->size = min(size, ERR_DATA_SIZE); + memcpy(self->data, data, self->size); + + return; +} + +int err_size(err_t * self) +{ + assert(self != NULL); + assert(self->magic == ERR_MAGIC); + + return self->size; +} + +const char *err_type_name(err_t * self) +{ + assert(self != NULL); + assert(self->magic == ERR_MAGIC); + + return __err_type_name[self->type]; +} + +/* =======================================================================*/ + +__constructor static void __err__ctor__(void) +{ + assert(pthread_key_create(&__err_key, NULL) == 0); +} diff --git a/clib/src/exception.c b/clib/src/exception.c new file mode 100644 index 0000000..30b6dc6 --- /dev/null +++ b/clib/src/exception.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: exc.c + * Author: Shaun Wetzstein + * Descr: {set,long}jmp implementation of exceptions for C code. + * Note: using these macros will create an exception context + * in each thread. + * Date: 7/06/09 + */ + +#include +#include +#include +#include +#include +#include + +#include "exception.h" +#include "misc.h" + +/* =======================================================================*/ + +const char *exception_name[] = { + "assertion", + "unexpected", + "errno", +}; + +typedef struct exception_context { + pthread_once_t once; + pthread_key_t key; +} exception_context; + +/* =======================================================================*/ + +static exception_context __exc_ctx = { PTHREAD_ONCE_INIT, 0 }; + +static inline void __exc_key_create(void) +{ + if (pthread_key_create(&__exc_ctx.key, NULL)) + __exc_throw(ASSERTION, NULL, 0, __FILE__, __LINE__); +} + +/* =======================================================================*/ + +void __exc_init(void) +{ + if (pthread_once(&__exc_ctx.once, __exc_key_create)) + __exc_throw(ASSERTION, NULL, 0, __FILE__, __LINE__); +} + +exception_frame_t *__exc_get_frame(void) +{ + return (exception_frame_t *) (pthread_getspecific(__exc_ctx.key)); +} + +void __exc_set_frame(exception_frame_t * f) +{ + if (pthread_setspecific(__exc_ctx.key, f)) + __exc_throw(ASSERTION, NULL, 0, __FILE__, __LINE__); +} + +void __exc_backtrace(const char *fmt, ...) +{ + if (fmt != NULL) { + va_list va; + va_start(va, fmt); + vfprintf(stderr, fmt, va); + va_end(va); + } + + fprintf(stderr, "========== backtrace ==========\n"); + void *bt[1024]; + int nr = backtrace(bt, sizeof bt); + backtrace_symbols_fd(bt, nr, fileno(stderr)); + fprintf(stderr, "========== backtrace ==========\n"); +} + +int __exc_throw(int ex, void *data, int size, const char *file, int line) +{ + extern char *program_invocation_short_name; + + exception_frame_t *frame = __exc_get_frame(); + + if (frame == NULL) { + __exc_backtrace + ("*** UNHANDLED EXCEPTION *** -- %s: %s(%d) ex=%d\n\n", + program_invocation_short_name, file, line, ex); + abort(); + } + + if (frame->magic != EXC_MAGIC) { + __exc_backtrace + ("*** CORRUPTED EXCEPTION FRAME *** -- %s: %s(%d) ex=%d\n\n", + program_invocation_short_name, file, line, ex); + abort(); + } + + frame->exc.file = file; + frame->exc.line = line; + + frame->exc.data = data; + frame->exc.size = size; + + longjmp(frame->jmp, ex); + + return -1; +} + +int __exc_rethrow(int ex, void *data, int size, const char *file, int line) +{ + exception_frame_t *frame = __exc_get_frame(); + + if (frame != NULL) + __exc_set_frame(frame->prev); + + return __exc_throw(ex, data, size, file, line); +} + +const char *__exc_name(int exc) +{ + return (exc < 0 || EXC_LAST <= exc) ? NULL : exception_name[exc]; +} + +__constructor void __exc_ctor(void) +{ + __exc_init(); +} diff --git a/clib/src/heap.c b/clib/src/heap.c new file mode 100644 index 0000000..2b339a2 --- /dev/null +++ b/clib/src/heap.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "assert.h" +#include "misc.h" +#include "slab.h" +#include "heap.h" +#include "bb_trace.h" +#include "memory_leak_detection.h" + +/* ======================================================================= */ + +static const char *__heap_msg[] = { + "heap: unexpected NULL pointer" + "heap: unexpected cache and/or alloc size", + "heap: unexpected alloc size", +}; + +#define HEAP_NULL (__heap_msg[0]) +#define HEAP_SIZE (__heap_msg[1]) +#define HEAP_ALLOC (__heap_msg[2]) + +/* ======================================================================= */ + +void heap_dump(heap_t * self, FILE * out) +{ + if (self == NULL) + return; + + size_t i; + + fprintf(out, + "*******************************************************************\n"); + for (i = 0; i < self->slab_size; i++) + if (self->slab[i] != NULL) + slab_dump(self->slab[i], out); + fprintf(out, + "*******************************************************************\n"); +} + +heap_t *__heap_new(size_t alloc_size, size_t page_size, const char *file, + int line) +{ + alloc_size = max(__round_pow2(alloc_size), CACHE_ALLOC_MIN); + page_size = max(__round_pow2(page_size), CACHE_SIZE_MIN); + + if (alloc_size < CACHE_ALLOC_MIN || CACHE_ALLOC_MAX < alloc_size) + throw_unexpected(HEAP_SIZE); + if (page_size < CACHE_SIZE_MIN || CACHE_SIZE_MAX < page_size) + throw_unexpected(HEAP_SIZE); + + heap_t *self = (heap_t *) MALLOC(sizeof(*self) + + sizeof(*self->slab) * page_size / + alloc_size); + assert(self != NULL); + + self->page_size = page_size; + self->alloc_size = alloc_size; + self->slab_size = page_size / alloc_size; + + memset(self->slab, 0, self->slab_size * sizeof(*self->slab)); + + return self; +} + +void __heap_delete(heap_t * self, const char *file, int line) +{ + if (unlikely(self == NULL)) + return; + + size_t i; + for (i = 0; i < self->slab_size; i++) { + if (self->slab[i] != NULL) { + slab_delete(self->slab[i]); + self->slab[i] = NULL; + } + } + + memset(self, 0, sizeof *self); + FREE(self); +} + +void *__heap_alloc(heap_t * self, size_t size, const char *file, int line) +{ + if (unlikely(self == NULL)) + throw_unexpected(HEAP_NULL); + + size = max(align(size, self->alloc_size), self->alloc_size); + + size_t slab_pos = size / self->alloc_size - 1; + + if (unlikely(self->slab_size < slab_pos)) + throw_unexpected(HEAP_ALLOC); + + if (unlikely(self->slab[slab_pos] == NULL)) + self->slab[slab_pos] = slab_new(size, 0); + + return slab_alloc(self->slab[slab_pos]); +} + +void __heap_free(heap_t * self, void *ptr, const char *file, int line) +{ + if (unlikely(self == NULL)) + throw_unexpected(HEAP_NULL); + + void *data = (void *)((uint32_t) ptr & ~(self->page_size - 1)); + cache_t *c = (cache_t *) (data + self->page_size - sizeof(cache_t)); + cache_check(c); + slab_free(cache_slab(c), ptr); +} + +/* ======================================================================= */ + +static uint32_t page_size; + +void heap_ctor(void) __constructor; +void heap_ctor(void) +{ + page_size = (const uint32_t)sysconf(_SC_PAGESIZE); + assert(0 < page_size); +} + +void heap_dtor(void) __destructor; +void heap_dtor(void) +{ +} + +/* ======================================================================= */ diff --git a/clib/src/list.c b/clib/src/list.c new file mode 100644 index 0000000..3c72269 --- /dev/null +++ b/clib/src/list.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include "assert.h" +#include "misc.h" +#include "list.h" + +/* ======================================================================= */ + +void list_dump(list_t * self, FILE * out) +{ + assert(self != NULL); + + fprintf(out, + "===================================================================\n"); + fprintf(out, "head: %8x node: %8x\n", (uint32_t) self, + (uint32_t) & self->node); + + list_node_t *node = &self->node; + do { + fprintf(out, " node: %8x - prev: %8x - next: %8x\n", + (uint32_t) node, (uint32_t) node->prev, + (uint32_t) node->next); + node = node->next; + } while (node != &self->node); + + fprintf(out, + "===================================================================\n"); +} diff --git a/clib/src/list_iter.c b/clib/src/list_iter.c new file mode 100644 index 0000000..fb1b81e --- /dev/null +++ b/clib/src/list_iter.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: list_iter.c + * Author: Shaun Wetzstein + * Descr: List iterator + * Note: + * Date: 10/22/10 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libclib.h" +#include "list_iter.h" + +/* ======================================================================= */ + +static inline list_node_t *__list_iter_bwd(list_iter_t * self) +{ + assert(self != NULL); + + assert(self->list != NULL); + assert(self->node != NULL); + + if (self->node == &self->list->node) + self->node = NULL; + else + self->node = list_node_prev(self->node); + + return self->node; +} + +static inline list_node_t *__list_iter_fwd(list_iter_t * self) +{ + assert(self != NULL); + assert(self->list != NULL); + assert(self->node != NULL); + + if (self->node == &self->list->node) + self->node = NULL; + else + self->node = list_node_next(self->node); + + return self->node; +} + +void list_iter_init(list_iter_t * self, list_t * list, uint32_t flags) +{ + assert(self != NULL); + assert(list != NULL); + + self->flags = flags; + self->list = list; + + if (self->flags & LI_FLAG_BWD) + self->node = list_tail(self->list); + else + self->node = list_head(self->list); +} + +void list_iter_clear(list_iter_t * self) +{ + assert(self != NULL); + + if (self->flags & LI_FLAG_BWD) + self->node = list_tail(self->list); + else + self->node = list_head(self->list); +} + +list_node_t *list_iter_elem(list_iter_t * self) +{ + assert(self != NULL); + return self->node; +} + +list_node_t *list_iter_inc1(list_iter_t * self) +{ + return list_iter_inc2(self, 1); +} + +list_node_t *list_iter_inc2(list_iter_t * self, size_t count) +{ + assert(self != NULL); + + for (size_t i = 0; i < count && self->node != NULL; i++) { + if (self->flags & LI_FLAG_BWD) + __list_iter_bwd(self); + else + __list_iter_fwd(self); + } + + return self->node; +} + +list_node_t *list_iter_dec1(list_iter_t * self) +{ + return list_iter_dec2(self, 1); +} + +list_node_t *list_iter_dec2(list_iter_t * self, size_t count) +{ + assert(self != NULL); + + for (size_t i = 0; i < count && self->node != NULL; i++) { + if (self->flags & LI_FLAG_BWD) + __list_iter_fwd(self); + else + __list_iter_bwd(self); + } + + return self->node; +} + +/* ======================================================================= */ diff --git a/clib/src/memory_leak_detection.c b/clib/src/memory_leak_detection.c new file mode 100644 index 0000000..79dc6fb --- /dev/null +++ b/clib/src/memory_leak_detection.c @@ -0,0 +1,1277 @@ +/* + * Copyright (c) International Business Machines Corp., 2011 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: B. Rafanello + * + * Module: memory_leak_detection.c + * + * Implements a simple memory leak and buffer overwrite detection + * system. + * + * Version 0.0.0.1 + */ + +#define PROGRAM_VERSION "0.0.0.1" + +#include +#include +#include +#include +#include +#include +#include "bb_trace.h" + +/*-------------------------------------------------- + * Private Constants + --------------------------------------------------*/ + +#define LEAK_SIGNATURE 8283471 +#define SENTINEL_VALUE 0xD9B79573 + +/*-------------------------------------------------- + * Private Type definitions + --------------------------------------------------*/ + +typedef unsigned int Sentinel_t; + +struct _Memory_Leak_Data { + unsigned int Signature; /* Indentify this structure in memory */ + void *Mem_Address; /* The address actually returned by malloc. */ + unsigned int User_Size; /* The size of the allocation as requested by the user. */ + unsigned int Alignment; /* The alignment requested by the user. */ + const char *Module_Name; /* The name of the module containing the function which allocated this memory. */ + const char *Function_Name; /* The name of the function which allocated this memory. */ + unsigned int Line_Number; /* The line number of the MALLOC call in the function which allocated this memory. */ + struct _Memory_Leak_Data *Next; /* The next allocated block of memory (if any). */ + struct _Memory_Leak_Data *Previous; /* The previous allocated block of memory (if any). */ + Sentinel_t Starting_Sentinel; /* The starting sentinel used to detect buffer overrun/memory overwrite errors. */ +}; + +typedef struct _Memory_Leak_Data Memory_Leak_Data_t; + +/*-------------------------------------------------- + Private global variables. +--------------------------------------------------*/ + +static pthread_mutex_t Memory_Leak_Lock = PTHREAD_MUTEX_INITIALIZER; /* Mutex to serialize access to the chain of allocations. */ +static Memory_Leak_Data_t *Memory_Chain = NULL; /* The start of our chain of allocations. */ +static unsigned int Memory_Chain_Entries = 0; /* The number of items in our chain. */ + +/*-------------------------------------------------- + Local functions +--------------------------------------------------*/ + +void print_backtrace(void) +{ +#define BT_SIZE 20 + void *bt[BT_SIZE]; + int nr = backtrace(&bt[0], BT_SIZE); + + fprintf(stderr, "========== backtrace ==========\n"); + backtrace_symbols_fd(bt, nr, fileno(stderr)); + fprintf(stderr, "========== backtrace ==========\n"); + + return; +} + +/*********************************************************************/ +/* */ +/* Function Name: Check_Leak_List */ +/* */ +/* Descriptive Name: */ +/* */ +/* Input: */ +/* */ +/* Output: If Success : */ +/* */ +/* If Failure : */ +/* */ +/* Error Handling: */ +/* */ +/* Side Effects: */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +static int Check_Leak_List(const void *p) +{ + Memory_Leak_Data_t *Current_Item; /* Used to walk the memory allocation chain. */ + Memory_Leak_Data_t *Previous_Item; /* Used to check the memory allocation chain. */ + Sentinel_t *Ending_Sentinel; /* To allow access to the sentinel at the end of the allocated memory. */ + unsigned int Data_Start; /* The address of the first byte of the data area of the current allocation. */ + unsigned int Data_End; /* The first byte after the data area of the current allocation. */ + unsigned int Address_To_Test; /* The address we are looking for. */ + unsigned int Address_Found = 2; /* Used to track whether the address was found in the allocation list. + If 2, then the address was not found. If 0, then the address was found and + it is the start of the data area of an allocation. If 1, then the address + was found and it lies within the data area of an allocation. */ + int rc = 0; /* Holds the return code. Assume success. */ + unsigned int Count = 0; + + FUNCTION_ENTRY() + + PRINT_FUNCTION_PARAMETER("Parameters:\n") + PRINT_FUNCTION_PARAMETER(" p = %p\n", p) + + USER3_PRINT_LINE("Memory_Chain = %p", Memory_Chain) + Current_Item = Memory_Chain; + Previous_Item = NULL; + + if (p != NULL) { + USER3_PRINT_LINE + ("Request: Check the memory allocation list for p = %p.\n", + p); + } else { + USER3_PRINT_LINE + ("Request: Check the integrity of the memory allocation list.\n"); + USER3_PRINT_LINE(" Items expected in list: %d\n", + Memory_Chain_Entries); + } + + while (Current_Item != NULL) { + /* Perform some simple checks on the items in the memory allocation list. */ + if (Current_Item->Signature == LEAK_SIGNATURE) { + + if (Current_Item->Starting_Sentinel == SENTINEL_VALUE) { + + /* Check the ending sentinel. */ + Ending_Sentinel = + (Sentinel_t *) ((unsigned int)Current_Item + + sizeof(Memory_Leak_Data_t) + + Current_Item->User_Size); + if (*Ending_Sentinel == SENTINEL_VALUE) { + + /* Check the previous pointer. */ + if (Current_Item->Previous == + Previous_Item) { + + if (p != NULL) { + /* We have an address to check. Does this address lie within this memory allocation? */ + Data_End = + (unsigned long) + Ending_Sentinel; + Data_Start = + (unsigned + long)((unsigned + int) + Current_Item + + + sizeof + (Memory_Leak_Data_t)); + Address_To_Test = + (unsigned long)p; + + if (Address_To_Test == + Data_Start) { + /* This address was returned to the user by MALLOC_func. */ + Address_Found = + 0; + break; + } else { + + if ((Address_To_Test > Data_Start) && (Address_To_Test < Data_End)) { + /* The address lies within the data area of the current allocation. */ + Address_Found + = 1; + break; + } + + } + + } + + } else { + /* We have a pointer problem! The previous pointer field does not point to the previous entry in the list! */ + rc = 3; + fprintf(stderr, + "Link Error! The previous pointer of the current item does not point to the previous item in the allocation list!\n"); + fprintf(stderr, + "Current_Item->Previous = %p, Actual Previous Item is %p.\n", + Current_Item->Previous, + Previous_Item); + fprintf(stderr, + "Item position in list: %d\n", + Count); + print_backtrace(); + } + + } else { + rc = 3; + fprintf(stderr, + "Possible overrun error -- ending sentinel corrupted!\n" + " Header Address: %p\n" + " User Address: %p\n" + " Size: %d\n" + " Allocated in module %s, function %s, at line %d\n", + Current_Item, + (void *)((unsigned int) + Current_Item + + sizeof + (Memory_Leak_Data_t)), + Current_Item->User_Size, + Current_Item->Module_Name, + Current_Item->Function_Name, + Current_Item->Line_Number); + print_backtrace(); + } + + } else { + rc = 3; + fprintf(stderr, + "Memory Overwrite Error! Starting Sentinel is incorrect!\n Following data may be incorrect!\n" + " Header Address: %p\n" + " Address: %p\n" " Size: %d\n" + " Allocated in module %s, function %s, at line %d\n", + Current_Item, + (void *)((unsigned int)Current_Item + + sizeof(Memory_Leak_Data_t)), + Current_Item->User_Size, + Current_Item->Module_Name, + Current_Item->Function_Name, + Current_Item->Line_Number); + print_backtrace(); + } + + Count += 1; + + } else { + rc = 3; + fprintf(stderr, + "Memory Overwrite Error! Memory management data signature is incorrect! Data Dump:\n" + " Header Address: %p\n" + " Signature: Actual[%u, 0x%x], Expected[%u, 0x%x]\n" + " User Address: %p\n" + " Size: [%u, 0x%x]\n" + " Starting Sentinel: Actual[%u, 0x%x], Expected[%u, 0x%x]\n" + " Next: %p\n" " Previous: %p\n" + " Module: %p\n" " Function: %p\n" + " Line [%u, ox%x]\n", Current_Item, + Current_Item->Signature, + Current_Item->Signature, LEAK_SIGNATURE, + LEAK_SIGNATURE, + (void *)((unsigned int)Current_Item + + sizeof(Memory_Leak_Data_t)), + Current_Item->User_Size, + Current_Item->User_Size, + Current_Item->Starting_Sentinel, + Current_Item->Starting_Sentinel, SENTINEL_VALUE, + SENTINEL_VALUE, Current_Item->Next, + Current_Item->Previous, + Current_Item->Module_Name, + Current_Item->Function_Name, + Current_Item->Line_Number, + Current_Item->Line_Number); + fprintf(stderr, + "Memory_Chain = %p\nMemory_Chain_Entries = %u\nCurrent_Item = %p, Count = %u\n\n", + Memory_Chain, Memory_Chain_Entries, + Current_Item, Count); + print_backtrace(); + + break; + } + + Previous_Item = Current_Item; + Current_Item = Current_Item->Next; + } + + if (p != NULL) { + + if (rc == 0) { + rc = Address_Found; + } + + } else { + + if ((Count != Memory_Chain_Entries) && (rc == 0)) { + fprintf(stderr, + "The number of entries found in the allocation list [%u] != list count [%u]!!\n\n", + Count, Memory_Chain_Entries); + print_backtrace(); + rc = 3; + } + + } + + FUNCTION_RETURN("%i", rc) +} + +/*********************************************************************/ +/* */ +/* Function Name: Add_Memory_Allocation_To_Chain */ +/* */ +/* Descriptive Name: Adds a memory tracking object to the list of */ +/* memory tracking objects which represents all */ +/* memory allocations being tracked by this */ +/* module. */ +/* */ +/* Input: Memory_Leak_Data_t * Memory_Leak_Data - The memory */ +/* tracking object to add. */ +/* */ +/* Output: If Success : None. */ +/* */ +/* If Failure : None. */ +/* */ +/* Error Handling: None. */ +/* */ +/* Side Effects: Other memory tracking objects may be modified. */ +/* The count of objects being tracked will be */ +/* incremented. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +static void Add_Memory_Allocation_To_Chain(Memory_Leak_Data_t * + Memory_Leak_Data) +{ + + FUNCTION_ENTRY() + + PRINT_FUNCTION_PARAMETER("Parameters:\n") + PRINT_FUNCTION_PARAMETER(" Memory_Leak_Data = %p", + Memory_Leak_Data) + + if (Memory_Chain) { + /* Other allocations exist. Insert at the head of the chain. */ + Memory_Leak_Data->Next = Memory_Chain; + Memory_Chain->Previous = Memory_Leak_Data; + } else { + /* This is the first allocation. */ + Memory_Leak_Data->Next = NULL; + } + + /* Update the pointer to the start of the allocation list. */ + Memory_Chain = Memory_Leak_Data; + + /* Update the count of entries in the memory chain. */ + Memory_Chain_Entries += 1; + + FUNCTION_EXIT() +} + +/*********************************************************************/ +/* */ +/* Function Name: Remove_Memory_Allocation_From_Chain */ +/* */ +/* Descriptive Name: Removes a memory tracking object from */ +/* the linked list of memory tracking */ +/* objects representing current memory */ +/* allocations. */ +/* */ +/* Input: Memory_Leak_Data_t * Memory_Leak_Data - the memory */ +/* tracking object to remove. */ +/* */ +/* Output: If Success : None. */ +/* */ +/* If Failure : None. */ +/* */ +/* Error Handling: None. */ +/* */ +/* Side Effects: Other memory tracking objects may be modified. */ +/* The memory tracking object being removed will be */ +/* zeroed out and freed. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +static void Remove_Memory_Allocation_From_Chain(Memory_Leak_Data_t * + Memory_Leak_Data) +{ + Memory_Leak_Data_t *Temp; /* Used when adjusting the memory allocation chain. */ + Sentinel_t *Ending_Sentinel; /* To allow access to the sentinel at the end of the allocated memory. */ + + FUNCTION_ENTRY() + + /* Are we the first item in the chain? */ + if (Memory_Leak_Data->Previous == NULL) { + + /* We are at the head of the chain, which means that Memory_Chain points to us! + * Point Memory_Chain to the Memory_Leak_Data_t that follows us (if any). + */ + Memory_Chain = Memory_Leak_Data->Next; + + /* Is there another item in the chain? If so, update it. */ + if (Memory_Chain != NULL) + Memory_Chain->Previous = NULL; + + } else { + + /* Are we the last item in the chain? */ + if (Memory_Leak_Data->Next == NULL) { + + /* We are at the end of the memory chain with at least 1 entry before us. */ + Temp = Memory_Leak_Data->Previous; + Temp->Next = NULL; + } else { + /* We are in the middle of the chain. */ + Temp = Memory_Leak_Data->Previous; + Temp->Next = Memory_Leak_Data->Next; + Temp = Memory_Leak_Data->Next; + Temp->Previous = Memory_Leak_Data->Previous; + } + + } + + /* Adjust the count of entries in the memory chain. */ + Memory_Chain_Entries -= 1; + + /* Calculate the address of the ending sentinel so that we can access it. */ + Ending_Sentinel = + (Sentinel_t *) ((unsigned int)Memory_Leak_Data + + sizeof(Memory_Leak_Data_t) + + Memory_Leak_Data->User_Size); + + /* Clean out the memory leak data. */ + Memory_Leak_Data->Function_Name = NULL; + Memory_Leak_Data->Line_Number = 0; + Memory_Leak_Data->Module_Name = NULL; + Memory_Leak_Data->Signature = 0; + Memory_Leak_Data->Starting_Sentinel = 0; + Memory_Leak_Data->User_Size = 0; + Memory_Leak_Data->Next = NULL; + Memory_Leak_Data->Previous = NULL; + *Ending_Sentinel = 0; + + free(Memory_Leak_Data->Mem_Address); + + FUNCTION_EXIT() +} + +/*-------------------------------------------------- + API Functions +--------------------------------------------------*/ + +/*********************************************************************/ +/* */ +/* Function Name: Print_Leak_List */ +/* */ +/* Descriptive Name: This function walks the list of allocated */ +/* memory blocks and prints information about */ +/* each one. If this is done at program exit, */ +/* the resulting list of memory blocks most */ +/* likely represents leaked memory. */ +/* */ +/* Input: None. */ +/* */ +/* Output: If Success : If there are any memory blocks being */ +/* tracked by this module, information about */ +/* block still being tracked will be sent to */ +/* stderr. */ +/* */ +/* If Failure : Error messages may be sent to stderr. */ +/* */ +/* Error Handling: If errors are detected, then error messages are */ +/* output on stderr. */ +/* */ +/* Side Effects: The internal structures of this module are checked*/ +/* for errors with any errors being reported on */ +/* stderr. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +void Print_Leak_List(void) +{ + Memory_Leak_Data_t *Current_Item; /* Used to walk the memory allocation chain. */ + Sentinel_t *Ending_Sentinel; /* To allow access to the sentinel at the end of the allocated memory. */ + + API_FUNCTION_ENTRY() + + /* Get lock. */ + pthread_mutex_lock(&Memory_Leak_Lock); + + Current_Item = Memory_Chain; + + fprintf(stderr, + "\n\nMemory_Chain is %p, and Memory_Chain_Entries is %u.\n", + Memory_Chain, Memory_Chain_Entries); + fprintf(stderr, "\nCurrent Memory Allocation List\n\n"); + + while (Current_Item != NULL) { + /* Perform some simple checks on the data before we print it. */ + if (Current_Item->Signature == LEAK_SIGNATURE) { + + if (Current_Item->Starting_Sentinel == SENTINEL_VALUE) { + + /* Check the ending sentinel. */ + Ending_Sentinel = + (Sentinel_t *) ((unsigned int)Current_Item + + sizeof(Memory_Leak_Data_t) + + Current_Item->User_Size); + if (*Ending_Sentinel == SENTINEL_VALUE) { + fprintf(stderr, + "Memory Allocation Record\n"); + } else { + fprintf(stderr, + "\nPossible overrun error -- ending sentinel corrupted!\n"); + } + + fprintf(stderr, + " Allocation Block: %p\n" + " Header Address: %p\n" + " User Address: %p\n" + " Alignment: %u\n" + " Size: %d\n" + " Allocated in module %s, function %s, at line %d\n\n", + Current_Item->Mem_Address, + Current_Item, + (void *)((unsigned int)Current_Item + + sizeof(Memory_Leak_Data_t)), + Current_Item->Alignment, + Current_Item->User_Size, + Current_Item->Module_Name, + Current_Item->Function_Name, + Current_Item->Line_Number); + } else { + fprintf(stderr, + "\nMemory Overwrite Error! Starting Sentinel is incorrect!\n Following data may be incorrect!\n"); + fprintf(stderr, + " Allocation Block: %p\n" + " Header Address: %p\n" + " User Address: %p\n" + " Alignment: %u\n" " Size: %d\n" + " Allocated in module %s, function %s, at line %d\n\n", + Current_Item->Mem_Address, Current_Item, + (void *)((unsigned int)Current_Item + + sizeof(Memory_Leak_Data_t)), + Current_Item->Alignment, + Current_Item->User_Size, + Current_Item->Module_Name, + Current_Item->Function_Name, + Current_Item->Line_Number); + } + + } else { + fprintf(stderr, + "\n\nMemory Overwrite Error! Memory management data signature is incorrect! Data Dump:\n"); + fprintf(stderr, + " Allocation Block: %p\n" + " Header Address: %p\n" + " Signature: Actual[%u, 0x%x], Expected[%u, 0x%x]\n" + " Address: %p\n" " Alignment: %u\n" + " Size: [%u, 0x%x]\n" + " Starting Sentinel: Actual[%u, 0x%x], Expected[%u, 0x%x]\n" + " Next: %p\n" " Previous: %p\n" + " Module: %p\n" " Function: %p\n" + " Line [%u, ox%x]\n", + Current_Item->Mem_Address, Current_Item, + Current_Item->Signature, + Current_Item->Signature, LEAK_SIGNATURE, + LEAK_SIGNATURE, + (void *)((unsigned int)Current_Item + + sizeof(Memory_Leak_Data_t)), + Current_Item->Alignment, + Current_Item->User_Size, + Current_Item->User_Size, + Current_Item->Starting_Sentinel, + Current_Item->Starting_Sentinel, SENTINEL_VALUE, + SENTINEL_VALUE, Current_Item->Next, + Current_Item->Previous, + Current_Item->Module_Name, + Current_Item->Function_Name, + Current_Item->Line_Number, + Current_Item->Line_Number); + fprintf(stderr, + "Memory_Chain = %p\nMemory_Chain_Entries = %u\nCurrent_Item = %p\n\n", + Memory_Chain, Memory_Chain_Entries, + Current_Item); + break; + } + + Current_Item = Current_Item->Next; + } + + /* Release lock. */ + pthread_mutex_unlock(&Memory_Leak_Lock); + + API_FUNCTION_EXIT() + +} + +/*********************************************************************/ +/* */ +/* Function Name: MEMORY_func */ +/* */ +/* Descriptive Name: This function acts as a replacement for */ +/* malloc. It allocates memory (using malloc) */ +/* and enters that memory into a tracking */ +/* structure so that memory leaks, if any, may */ +/* be found. */ +/* */ +/* Input: size_t sz - The number of bytes to be allocated. */ +/* unsigned int Alignment - 0 for non-aligned (normal) */ +/* malloc, > 0 to return an */ +/* address aligned on a specific */ +/* memory boundary. If > 0, then */ +/* Alignment must be a power of 2 */ +/* and a multiple of sizeof(void *)*/ +/* void ** Memory_Location - The address of a variable which*/ +/* will hold the address of the */ +/* allocated by this function. */ +/* const char * mod_name - The name of the module from which*/ +/* this function was called. */ +/* const char * func - The name of the function from which */ +/* this function was called. */ +/* const int line - The line number of the code containing */ +/* the call to this function. */ +/* */ +/* Output: If Success : The function return value will be 0. */ +/* *Memory_Location will be set to the address*/ +/* of the first byte of the user portion of */ +/* any memory that was allocated. */ +/* */ +/* If Failure : The function return value will be EINVAL or*/ +/* ENOMEM. Errors may be reported on stderr. */ +/* *Memory_Location will be set to NULL. */ +/* */ +/* Error Handling: This function will abort if an error is */ +/* is detected. Errors could be lack of memory, or*/ +/* corruption of the internal structures used to */ +/* track allocated blocks of memory. */ +/* */ +/* Side Effects: Memory may be allocated, errors may be reported */ +/* on stderr. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +int MEMORY_func(size_t sz, + unsigned int Alignment, + void **Memory_Location, + const char *mod_name, const char *func, const int line) +{ + Memory_Leak_Data_t *Memory_Leak_Data; /* For accessing our memory tracking data. */ + void *ptr = NULL; /* To hold the address that will be returned to the caller. */ + size_t New_Size; /* Used to adjust the size of the memory allocation. */ + Sentinel_t *Ending_Sentinel; /* To allow access to the sentinel at the end of the allocated memory. */ + unsigned int Proposed_User_Address; /* Used to calculate the address to return to the caller. */ + unsigned int Shift_Amount; /* Used when adjusting the address returned to caller to ensure proper alignment. */ + int Error = 0; + unsigned int i = 0; /* Used when calculating powers of two to check the alignment. */ + unsigned int Power_Of_Two = 1; /* Used when calculating powers of two to check the alignment. */ + +#define MAX_POWER_OF_TWO 31 + + API_FUNCTION_ENTRY() + + PRINT_API_PARAMETER("Parameters:\n") + PRINT_API_PARAMETER(" sz = %d", sz) + PRINT_API_PARAMETER(" Alignment = %d", Alignment) + PRINT_API_PARAMETER(" Memory_Location = %p", Memory_Location) + PRINT_API_PARAMETER(" mod_name = %s", mod_name) + PRINT_API_PARAMETER(" func = %s", func) + PRINT_API_PARAMETER(" line = %i", line) + + /* Check the size of the request. */ + if (sz == 0) { + Error = EINVAL; + fprintf(stderr, + "MALLOC: request is invalid - size[%u] in module %s, function %s at line %d\n", + sz, mod_name, func, line); + print_backtrace(); + } + + /* Check the alignment, if one was specified. */ + if (Alignment > 0) { + if (Alignment % sizeof(void *)) { + /* Improper alignment! */ + Error = EINVAL; + } else { + do { + Power_Of_Two *= 2; + i++; + } while ((Power_Of_Two != Alignment) + && (i < MAX_POWER_OF_TWO)); + + if (Power_Of_Two != Alignment) { + Error = EINVAL; + } + + } + + if (Error) { + fprintf(stderr, + "MEMORY_func: request for aligned memory uses invalid alignment! size[%u], alignment [%u] in module %s, function %s at line %d\n", + sz, Alignment, mod_name, func, line); + } + + } + + if (Memory_Location == NULL) { + Error = EINVAL; + fprintf(stderr, + "MEMORY_func: Location to place address of allocated memory is NULL! size[%u], alignment [%u] in module %s, function %s at line %d\n", + sz, Alignment, mod_name, func, line); + } else + *Memory_Location = NULL; + + if (Error != 0) { + API_FUNCTION_RETURN("%i", Error); + } + + USER3_PRINT_LINE + ("MEMORY_func: processing request - size[%u], alignment[%u] from module %s, function %s at line %d\n", + sz, Alignment, mod_name, func, line); + + /* Check */ + + /* Get lock. */ + pthread_mutex_lock(&Memory_Leak_Lock); + + /* Check the memory allocation list. If the list is bad, then abort because we are already screwed ... */ + if (!Check_Leak_List(NULL)) { + + /* Adjust the size of the request to account for the additional memory we need to track this request. */ + New_Size = + sz + sizeof(Memory_Leak_Data_t) + sizeof(Sentinel_t) + + Alignment; + + /* Get the memory. */ + ptr = malloc(New_Size); + + /* Was the memory available? */ + if (ptr != NULL) { + /* Determine where to put our memory leak data. */ + if (Alignment == 0) { + /* We can place the memory leak data right at the start of the memory block we got from malloc. */ + Memory_Leak_Data = (Memory_Leak_Data_t *) ptr; + } else { + Proposed_User_Address = + (unsigned int)ptr + + sizeof(Memory_Leak_Data_t); + Shift_Amount = + Alignment - + (Proposed_User_Address % Alignment); + Memory_Leak_Data = + (Memory_Leak_Data_t *) ((unsigned int)ptr + + Shift_Amount); + } + + /* Save the address returned by malloc() for use with free(). */ + Memory_Leak_Data->Mem_Address = ptr; + + /* Create the address to return to the caller. This address should be the first byte after + our memory leak data. */ + ptr = + (void *)((unsigned int)Memory_Leak_Data + + sizeof(Memory_Leak_Data_t)); + + /* Calculate the address of the trailing sentinel. */ + Ending_Sentinel = + (Sentinel_t *) ((unsigned int)ptr + sz); + + /* Initialize our memory leak data. */ + Memory_Leak_Data->Signature = LEAK_SIGNATURE; + Memory_Leak_Data->User_Size = sz; + Memory_Leak_Data->Alignment = Alignment; + Memory_Leak_Data->Module_Name = mod_name; + Memory_Leak_Data->Function_Name = func; + Memory_Leak_Data->Line_Number = line; + Memory_Leak_Data->Previous = NULL; + Memory_Leak_Data->Next = NULL; + Memory_Leak_Data->Starting_Sentinel = SENTINEL_VALUE; + *Ending_Sentinel = SENTINEL_VALUE; + + USER3_PRINT_LINE + ("MALLOC: Allocated header at %p ( = user address %p) size[%u] in module %s, function %s at line %d\n", + Memory_Leak_Data, ptr, sz, mod_name, func, line); + Add_Memory_Allocation_To_Chain(Memory_Leak_Data); + USER3_PRINT_LINE + ("MALLOC: Memory_Chain is %p, and Memory_Chain_Entries is %u.\n", + Memory_Chain, Memory_Chain_Entries); + + /* Set up return value. */ + *Memory_Location = ptr; + } else { + + fprintf(stderr, + "MALLOC: Memory allocation failed - no more memory available at this time!\n"); + print_backtrace(); + + USER3_PRINT_LINE + ("MALLOC: request was issued from module %s, function %s at line %d\n", + mod_name, func, line); + + } + + } else { + fprintf(stderr, + "MALLOC: Found the memory leak data to be corrupted! Aborting!\n"); + print_backtrace(); + } + + /* Release lock. */ + pthread_mutex_unlock(&Memory_Leak_Lock); + + API_FUNCTION_RETURN("%i", Error) + +} + +/*********************************************************************/ +/* */ +/* Function Name: MALLOC_func */ +/* */ +/* Descriptive Name: This function acts as a replacement for */ +/* malloc. It allocates memory (using malloc) */ +/* and enters that memory into a tracking */ +/* structure so that memory leaks, if any, may */ +/* be found. */ +/* */ +/* Input: size_t sz - The number of bytes to be allocated. */ +/* const char * mod_name - The name of the module from which*/ +/* this function was called. */ +/* const char * func - The name of the function from which */ +/* this function was called. */ +/* const int line - The line number of the code containing */ +/* the call to this function. */ +/* */ +/* Output: If Success : The function return value will be non-NULL.*/ +/* */ +/* If Failure : The function return value will be NULL. */ +/* Errors may be reported on stderr. */ +/* */ +/* Error Handling: This function will abort if an error is */ +/* is detected. Errors could be lack of memory, or*/ +/* corruption of the internal structures used to */ +/* track allocated blocks of memory. */ +/* */ +/* Side Effects: Memory may be allocated, errors may be reported */ +/* on stderr. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +void *MALLOC_func(size_t sz, const char *mod_name, const char *func, + const int line) +{ + void *Return_Value; + int Error; + + Error = MEMORY_func(sz, 0, &Return_Value, mod_name, func, line); + + if (Error != 0) + return NULL; + else + return Return_Value; +} + +/*********************************************************************/ +/* */ +/* Function Name: FREE_func */ +/* */ +/* Descriptive Name: This function frees a block of memory being */ +/* tracked by this module and removes the block */ +/* from its tracking structures. */ +/* */ +/* Input: const void * p - The address of the block of memory to */ +/* be freed. */ +/* const char * mod_name - The name of the module requesting*/ +/* the block of memory be freed. */ +/* const char * func - The name of the function requesting */ +/* the block of memory be freed. */ +/* const int line - The line number of the line of code in */ +/* module calling this function. */ +/* */ +/* Output: If Success : None. */ +/* */ +/* If Failure : Errors may be reported to stderr. */ +/* */ +/* Error Handling: This function causes the internal structures */ +/* of this module to be checked as part of the */ +/* process of freeing the address p. This may */ +/* cause errors to be reported on stderr. If any */ +/* errors are found, then the address p may not be */ +/* freed. */ +/* */ +/* Side Effects: The block of memory associated with the address p */ +/* will be freed and available for reallocation. */ +/* Also, the memory tracking structures in this */ +/* module will undergo a series of checks. */ +/* */ +/* Notes: This function was not intended to be called directly but */ +/* rather through the macro FREE. */ +/* */ +/*********************************************************************/ +void FREE_func(const void *p, const char *mod_name, const char *func, + const int line) +{ + Memory_Leak_Data_t *Memory_Leak_Data; /* For accessing our memory tracking data. */ + + API_FUNCTION_ENTRY() + + PRINT_API_PARAMETER("Parameters:\n") + PRINT_API_PARAMETER(" p = %p", p) + PRINT_API_PARAMETER(" mod_name = %s", mod_name) + PRINT_API_PARAMETER(" func = %s", func) + PRINT_API_PARAMETER(" line = %i", line) + + /* Check the address passed to us. It must not be NULL, and must be > sizeof(Memory_Leak_Data_t) to + * prevent wrap around when we subtract sizeof(Memory_Leak_Data_t) to get the starting address of + * the memory leak data associated with p. + */ + if ((p == NULL) || ((unsigned int)p <= sizeof(Memory_Leak_Data_t))) { + fprintf(stderr, + "FREE: request has invalid user address [%p]. Request came from module %s, function %s, line %d\n", + p, mod_name, func, line); + print_backtrace(); + return; + } else { + USER3_PRINT_LINE + ("FREE: request to free [%p] received from module %s, function %s, line %d\n", + p, mod_name, func, line); + } + + /* Get lock. */ + pthread_mutex_lock(&Memory_Leak_Lock); + + /* Check the memory allocation list for errors. */ + if (!Check_Leak_List(NULL)) { + + /* Is the address given to us in the memory allocation list? */ + if (!Check_Leak_List(p)) { + /* Get access to the memory leak data. */ + Memory_Leak_Data = + (Memory_Leak_Data_t *) ((unsigned int)p - + sizeof(Memory_Leak_Data_t)); + + USER3_PRINT_LINE + ("FREE: Calling free on header address %p ( = user address %p) from module %s, function %s, line %d\n", + Memory_Leak_Data, p, mod_name, func, line); + Remove_Memory_Allocation_From_Chain(Memory_Leak_Data); + USER3_PRINT_LINE + ("FREE: Memory_Chain is %p, and Memory_Chain_Entries is %u.\n", + Memory_Chain, Memory_Chain_Entries); + + } else { + /* The address given to us was not in the memory allocation list! If we free this address, who knows what will happen! */ + fprintf(stderr, + "FREE: Invalid address! The address %p was not provided by MALLOC or has been passed to FREE more than once!\n", + p); + fprintf(stderr, + " Request came from module %s, function %s, line %d\n", + mod_name, func, line); + print_backtrace(); + } + + } else { + fprintf(stderr, + "FREE: Aborting due to errors in the memory leak data!\n"); + USER3_PRINT_LINE + ("FREE: request was submitted by module %s, function %s at line %d\n", + mod_name, func, line); + print_backtrace(); + + } + + pthread_mutex_unlock(&Memory_Leak_Lock); + + USER3_PRINT_LINE("FREE: request is complete.\n"); + + API_FUNCTION_EXIT() +} + +/*********************************************************************/ +/* */ +/* Function Name: Test_Address_Allocation */ +/* */ +/* Descriptive Name: This function tests the specified address to */ +/* to see if it lies within an allocated block */ +/* tracked by this module. */ +/* */ +/* Input: void * p - The address to be tested. */ +/* */ +/* Output: If Success : If the address p was found, then 0 will be */ +/* returned if the address is the start of */ +/* a block of allocated memory. If the */ +/* address p was found within an allocated */ +/* block of memory, then 1 is returned. */ +/* */ +/* If Failure : If the address p was NOT found, then 2 is */ +/* returned. If there was an error in the */ +/* memory tracking system then 3 will be */ +/* returned. */ +/* */ +/* Error Handling: This function relies on the error handling */ +/* built into the Check_Leak_List function and */ +/* has no error handling of its own. */ +/* */ +/* Side Effects: If the list of memory allocations contains errors */ +/* then those errors will be detected and reported */ +/* on stderr. */ +/* */ +/* Notes: If NULL is passed in as the address to test, then the */ +/* integrity of the internal tracking structures will be */ +/* checked, in which case a return value of 0 signifies */ +/* that the internal tracking structures have passed the */ +/* checks and a return value of 3 indicates that errors */ +/* were found. */ +/* */ +/*********************************************************************/ +unsigned int Test_Address_Allocation(void *p) +{ + unsigned int rc = 0; + + API_FUNCTION_ENTRY() + + PRINT_API_PARAMETER("Parameters:\n") + PRINT_API_PARAMETER(" p = %p", p) + + /* Get lock. */ + pthread_mutex_lock(&Memory_Leak_Lock); + + rc = Check_Leak_List(p); + + /* Release lock. */ + pthread_mutex_unlock(&Memory_Leak_Lock); + + API_FUNCTION_RETURN("%i", rc); +} + +/*********************************************************************/ +/* */ +/* Function Name: Duplicate_String */ +/* */ +/* Descriptive Name: This function duplicates a string. The memory*/ +/* allocated for the duplicate is allocated */ +/* using the MALLOC_func routine in this module */ +/* and is thus tracked by this module. */ +/* */ +/* Input: const char * Source - The string to be copied. */ +/* const char * mod_name - The name of the module containing*/ +/* the function which called this */ +/* function. */ +/* const char * func - The name of the function calling */ +/* this function. */ +/* const int line - The line number of the line of code in */ +/* module calling this function. */ +/* */ +/* Output: If Success : The function return value will be non-NULL */ +/* and will point to a duplicate of the */ +/* string given in Source. */ +/* */ +/* If Failure : The function return value will be NULL. */ +/* */ +/* Error Handling: Any errors detected by this function result in */ +/* a function return value of NULL. */ +/* */ +/* Side Effects: The memory tracking features of this module are */ +/* employed to allocate memory for the duplicate */ +/* string produced by this funciton. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +char *Duplicate_String(const char *Source, + const char *mod_name, const char *func, const int line) +{ + char *Result; + + Result = (char *)MALLOC_func(strlen(Source) + 1, mod_name, func, line); + if (Result != NULL) + strcpy(Result, Source); + + return Result; +} + +/*********************************************************************/ +/* */ +/* Function Name: Realloc_func */ +/* */ +/* Descriptive Name: This function performs the same function as */ +/* the realloc function in the ANSI C library. */ +/* */ +/* Input: const void * p - The address of the block of memory to */ +/* be reallocated. */ +/* size_t size - The size of the memory block to return. */ +/* const char * mod_name - The name of the module requesting*/ +/* the block of memory be freed. */ +/* const char * func - The name of the function requesting */ +/* the block of memory be freed. */ +/* const int line - The line number of the line of code in */ +/* module calling this function. */ +/* */ +/* Output: If Success : The function return value will be a pointer*/ +/* to the new block of memory. */ +/* */ +/* If Failure : NULL will be returned and errno will be set*/ +/* to a non-null error code. */ +/* */ +/* Error Handling: This function causes the internal structures */ +/* of this module to be checked. This may */ +/* cause errors to be reported on stderr. If any */ +/* errors are found, then the address p may not be */ +/* freed. */ +/* */ +/* Side Effects: A new block of memory of size bytes will be */ +/* allocated, the contents of the current block will */ +/* be copied to the new block (at least as much as */ +/* will fit, and the current block will be freed. */ +/* This will cause internal structures in this module*/ +/* to be modified accordingly. */ +/* */ +/* Notes: This function was not intended to be called directly but */ +/* rather through the macro REALLOC. */ +/* */ +/* If p is NULL, then this will cause this function to */ +/* behave like malloc. */ +/* */ +/* If size is 0, then this will cause this function to */ +/* behave like free. */ +/* */ +/*********************************************************************/ +void *Realloc_func(void *p, size_t size, const char *mod_name, const char *func, + const int line) +{ + Memory_Leak_Data_t *Memory_Leak_Data; /* For accessing our memory tracking data. */ + int Error = 0; + unsigned int Copy_Size = 0; + void *Return_Value = NULL; + + API_FUNCTION_ENTRY() + + PRINT_API_PARAMETER("Parameters:\n") + PRINT_API_PARAMETER(" p = %p", p) + PRINT_API_PARAMETER(" size = %u", size) + PRINT_API_PARAMETER(" mod_name = %s", mod_name) + PRINT_API_PARAMETER(" func = %s", func) + PRINT_API_PARAMETER(" line = %i", line) + + /* Check the address passed to us. If it is NULL and size > 0, then we are merely doing a malloc. */ + if ((p == NULL) && (size > 0)) { + USER3_PRINT_LINE + ("REALLOC: p was NULL and size > 0, acting as MALLOC.\n") + Return_Value = MALLOC_func(size, mod_name, func, line); + if (Return_Value == NULL) { + errno = ENOMEM; + } + } else { + /* If size is 0 and p is not NULL, then we are doing a free. */ + if ((p != NULL) && (size == 0)) { + USER3_PRINT_LINE + ("REALLOC: p was non-NULL but size is 0, acting as FREE.\n") + FREE_func(p, mod_name, func, line); + } else { + /* Do we have real work to do? */ + if ((p != NULL) && (size != 0)) { + USER3_PRINT_LINE + ("REALLOC: p was non-NULL and size > 0, we have actual work to do!.\n") + + /* Get lock. */ + pthread_mutex_lock(&Memory_Leak_Lock); + + /* Check the memory allocation list for errors. */ + if (!Check_Leak_List(NULL)) { + + /* Is the address given to us in the memory allocation list? */ + if (!Check_Leak_List(p)) { + /* Get access to the memory leak data. */ + Memory_Leak_Data = + (Memory_Leak_Data_t + *) ((unsigned int)p - + sizeof + (Memory_Leak_Data_t)); + + /* Release the lock so that MEMORY_func can get it. */ + pthread_mutex_unlock + (&Memory_Leak_Lock); + + /* Call MEMORY_func to get the memory we need and add it to the list of memory blocks we are tracking. */ + Error = + MEMORY_func(size, + Memory_Leak_Data-> + Alignment, + &Return_Value, + mod_name, func, + line); + if (Error != 0) { + Return_Value = NULL; + errno = ENOMEM; + } else { + + /* We have the replacement memory. Now lets copy what we can from the original block to the new block. */ + if (size > + Memory_Leak_Data-> + User_Size) { + Copy_Size = + Memory_Leak_Data-> + User_Size; + } else { + Copy_Size = + size; + } + + /* Copy the data to be preserved from the original memory block to the new memory block. */ + memcpy(Return_Value, p, + Copy_Size); + + /* Zero out the original memory block ... this can catch errors as users should not assume that + * realloc will leave their original memory block intact. + */ + memset(p, 0x0, + Memory_Leak_Data-> + User_Size); + + /* Get the lock again. */ + pthread_mutex_lock + (&Memory_Leak_Lock); + + USER3_PRINT_LINE + ("REALLOC: Calling free on header address %p ( = user address %p) from module %s, function %s, line %d\n", + Memory_Leak_Data, + p, mod_name, func, + line); + Remove_Memory_Allocation_From_Chain + (Memory_Leak_Data); + USER3_PRINT_LINE + ("REALLOC: Memory_Chain is %p, and Memory_Chain_Entries is %u.\n", + Memory_Chain, + Memory_Chain_Entries); + + } + + } else { + /* The address given to us was not in the memory allocation list! If we free this address, who knows what will happen! */ + fprintf(stderr, + "REALLOC: Invalid address! The address %p was not provided by MALLOC or has been passed to FREE already!\n", + p); + fprintf(stderr, + " Request came from module %s, function %s, line %d\n", + mod_name, func, line); + print_backtrace(); + } + + } else { + fprintf(stderr, + "REALLOC: Aborting due to errors in the memory leak data!\n"); + USER3_PRINT_LINE + ("REALLOC: request was submitted by module %s, function %s at line %d\n", + mod_name, func, line); + print_backtrace(); + + } + + pthread_mutex_unlock(&Memory_Leak_Lock); + + } + + } + + } + + USER3_PRINT_LINE("REALLOC: request is complete.\n"); + + API_FUNCTION_RETURN("%p", Return_Value) +} diff --git a/clib/src/misc.c b/clib/src/misc.c new file mode 100644 index 0000000..c79bd13 --- /dev/null +++ b/clib/src/misc.c @@ -0,0 +1,234 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: misc.c + * Author: Shaun Wetzstein + * Descr: + * Note: + * Date: 07/26/09 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" + +inline void prefetch(void *addr, size_t len, ...) +{ + va_list v; + va_start(v, len); + int w = va_arg(v, int); + int l = va_arg(v, int); + va_end(v); + + while (0 < len) { + if (w) { + switch (l) { + case 0: + __builtin_prefetch(addr, 1, 0); + break; + case 1: + __builtin_prefetch(addr, 1, 1); + break; + case 2: + __builtin_prefetch(addr, 1, 2); + break; + } + } else { + switch (l) { + case 0: + __builtin_prefetch(addr, 0, 0); + break; + case 1: + __builtin_prefetch(addr, 0, 1); + break; + case 2: + __builtin_prefetch(addr, 0, 2); + break; + } + } + len -= sizeof addr; + } +} + +unsigned long align(unsigned long size, unsigned long offset) +{ + --offset; + return (size + offset) & ~offset; +} + +void print_binary(FILE * __out, void *__data, size_t __len) +{ + static const char *__ascii[] = { + "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", + "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111", + }; + + if (__data == NULL) + return; + if (__out == NULL) + __out = stdout; + + size_t i; + for (i = 0; i < __len; i++) { + unsigned char c = *(unsigned char *)(__data + i); + + fprintf(__out, "%4.4s%4.4s", + __ascii[(c & 0xF0) >> 4], __ascii[c & 0x0F]); + } +} + +#if 0 +void dump_memory(FILE * file, unsigned long addr, const void *buf, size_t size) +{ + if (size <= 0 || buf == NULL) + return; + if (file == NULL) + file = stdout; + + unsigned long *ul = (unsigned long *)buf; + char hex[64] = { 0, }, ascii[32] = { + 0,}, c; + int hl = 0, al = 0; + + int cnt = size / sizeof(unsigned long); + + int i; + for (i = 0; i < cnt; i++) { + hl += snprintf(hex + hl, sizeof hex, "%08lx", ul[i]); + + c = (ul[i] & 0xFF000000) >> 24; + al += + snprintf(ascii + al, sizeof ascii, "%c", + (isprint(c) ? c : '.')); + + c = (ul[i] & 0x00FF0000) >> 16; + al += + snprintf(ascii + al, sizeof ascii, "%c", + (isprint(c) ? c : '.')); + + c = (ul[i] & 0x0000FF00) >> 8; + al += + snprintf(ascii + al, sizeof ascii, "%c", + (isprint(c) ? c : '.')); + + c = (ul[i] & 0x000000FF) >> 0; + al += + snprintf(ascii + al, sizeof ascii, "%c", + (isprint(c) ? c : '.')); + + if ((i + 1) % 4 == 0) { + fprintf(file, " %08lx [%s] [%s]\n", addr, hex, ascii); + + addr += 4 * sizeof(unsigned long); + hl = al = 0; + + memset(hex, 0, sizeof hex); + memset(ascii, 0, sizeof ascii); + } else { + hl += snprintf(hex + hl, sizeof hex, " "); + } + } + + int j; + for (j = 0; j < (i % 4); j++) { + hl += snprintf(hex + hl, sizeof hex, "%08lx ", 0ul); + al += snprintf(ascii + al, sizeof ascii, "%4.4s", "...."); + } + + if (i % 4 != 0) { + fprintf(file, " %08lx [%s] [%s]\n", addr, hex, ascii); + } + + return; +} +#endif + +/* + * "aaaaaaaa [wwwwwwww xxxxxxxx yyyyyyyy zzzzzzzz] [........ ........]" + */ +void dump_memory(FILE * __out, uint32_t __addr, const void *__restrict __buf, + size_t __buf_sz) +{ + if (__buf_sz <= 0 || __buf == NULL) + return; + if (__out == NULL) + __out = stdout; + + size_t hex_size = 16 * 2; // 16 bytes per doubleword + size_t ascii_size = 8 * 2; // 8 bytes per doubleword + + uint8_t hex[hex_size + 1], ascii[hex_size + 1]; + memset(hex, '.', hex_size), memset(ascii, '.', ascii_size); + + void print_line(void) { + fprintf(__out, "%08x ", __addr); + + fprintf(__out, "[%.8s %.8s %.8s %.8s] ", + hex + 0, hex + 8, hex + 16, hex + 24); + + fprintf(__out, "[%.8s %.8s]\n", ascii + 0, ascii + 8); + } + + size_t s = __addr % 16, i = 0; + __addr &= ~0xF; + + for (i = s; i < __buf_sz + s; i++) { + unsigned char c = ((unsigned char *)__buf)[i - s]; + + hex[((i << 1) + 0) % hex_size] = "0123456789abcdef"[c >> 4]; + hex[((i << 1) + 1) % hex_size] = "0123456789abcdef"[c & 0xf]; + + ascii[i % ascii_size] = isprint(c) ? c : '.'; + + if (i == 0) + continue; + + if ((i + 1) % ascii_size == 0) { + print_line(); + memset(hex, '.', hex_size), memset(ascii, '.', + ascii_size); + + __addr += ascii_size; + } + } + + if (i % ascii_size) + print_line(); + + return; +} + +int __round_pow2(int size) +{ + size--; + + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size |= size >> 8; + size |= size >> 16; + + return ++size; +} diff --git a/clib/src/mq.c b/clib/src/mq.c new file mode 100644 index 0000000..6faf108 --- /dev/null +++ b/clib/src/mq.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: mqueue.c + * Author: Shaun Wetzstein + * Descr: POSIX message queue wrapper + * Note: + * Date: 10/07/10 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libclib.h" +#include "mq.h" + +#define MQUEUE_ROOT "/dev/mqueue" + +/* ======================================================================= */ + +int mqueue_init(mqueue_t * self, const char *service) +{ + assert(self != NULL); + + self->service = strdup(service); + self->in = self->out = (mqd_t) - 1; + + return 0; +} + +int mqueue_create(mqueue_t * self, pid_t tid) +{ + assert(self != NULL); + + char path[pathconf(MQUEUE_ROOT, _PC_PATH_MAX)]; + + sprintf(path, "%s/%d->%s", MQUEUE_ROOT, tid, self->service); + + self->out = open(path, O_WRONLY | O_CREAT, S_IWUSR); + if (self->out == (mqd_t) - 1) { + ERRNO(errno); + return -1; + } + + sprintf(path, "%s/%d<-%s", MQUEUE_ROOT, tid, self->service); + + self->in = open(path, O_RDONLY | O_CREAT, S_IRUSR); + if (self->in == (mqd_t) - 1) { + ERRNO(errno); + return -1; + } + + return 0; +} + +int mqueue_open(mqueue_t * self, char *path) +{ + assert(self != NULL); + + if (path != NULL) { + char *endp = NULL; + (void)strtol(path + 1, &endp, 10); + + if (strncmp(endp, "->", 2) == 0) { + self->in = mq_open((char *)path, O_RDONLY, + S_IRWXU, NULL); + if (self->in == (mqd_t) - 1) { + ERRNO(errno); + return -1; + } + } else if (strncmp(endp, "<-", 2) == 0) { + self->out = mq_open((char *)path, O_WRONLY, + S_IRWXU, NULL); + if (self->out == (mqd_t) - 1) { + ERRNO(errno); + return -1; + } + } else { + UNEXPECTED("'%s' invalid service", path); + return -1; + } + } + + return 0; +} + +int mqueue_close(mqueue_t * self, char *path) +{ + assert(self != NULL); + + if (path != NULL) { + char *endp = NULL; + (void)strtol(path + 1, &endp, 10); + + if (strncmp(endp, "->", 2) == 0) { + if (self->in != (mqd_t) - 1) + mq_close(self->in), self->in = (mqd_t) - 1; + } else if (strncmp(endp, "<-", 2) == 0) { + if (self->out != (mqd_t) - 1) + mq_close(self->out), self->out = (mqd_t) - 1; + } else { + UNEXPECTED("'%s' invalid service", path); + return -1; + } + } + + return 0; +} + +mqueue_attr_t mqueue_getattr(mqueue_t * self) +{ + assert(self != NULL); + + mqueue_attr_t attr; + mq_getattr(self->in, &attr); + + return attr; +} + +int mqueue_delete(mqueue_t * self) +{ + assert(self != NULL); + + char path[pathconf(MQUEUE_ROOT, _PC_PATH_MAX)]; + if (self->in != (mqd_t) - 1) { + sprintf(path, "%s/%d->%s", + MQUEUE_ROOT, gettid(), self->service); + unlink(path); + if (mq_close(self->in) == (mqd_t) - 1) { + ERRNO(errno); + return -1; + } + self->in = (mqd_t) - 1; + } + + if (self->out != (mqd_t) - 1) { + sprintf(path, "%s/%d<-%s", + MQUEUE_ROOT, gettid(), self->service); + unlink(path); + if (mq_close(self->in) == (mqd_t) - 1) { + ERRNO(errno); + return -1; + } + self->out = (mqd_t) - 1; + } + + if (self->service) { + free((void *)self->service); + self->service = NULL; + } + + return 0; +} + +int mqueue_send(mqueue_t * self, void *ptr, size_t len) +{ + assert(self != NULL); + + int rc = mq_send(self->out, (char *)ptr, len, 0); + if (rc == -1) { + ERRNO(errno); + return -1; + } + + return rc; +} + +int mqueue_receive(mqueue_t * self, void *ptr, size_t len) +{ + assert(self != NULL); + + int rc = mq_receive(self->in, (char *)ptr, len, 0); + if (rc == -1) { + ERRNO(errno); + return -1; + } + + return rc; +} + +/* ======================================================================= */ diff --git a/clib/src/signal.c b/clib/src/signal.c new file mode 100644 index 0000000..ed75c05 --- /dev/null +++ b/clib/src/signal.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: signal.c + * Author: Shaun Wetzstein + * Descr: + * Note: + * Date: 10/03/10 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" +#include "signal.h" + +/* ======================================================================= */ + +const char *__signal_msg[] = { + "signal: unexpected NULL self pointer", + "signal: unexpected NULL callback structure", +}; + +#define SIGNAL_NULL (__signal_msg[0]) + +/* ======================================================================= */ + +void signal_init(signal_t * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(SIGNAL_NULL); + + if (self->fd != -1) + close(self->fd), self->fd = -1; + + sigemptyset(&self->mask); + self->flags = SFD_CLOEXEC; + + self->fd = signalfd(self->fd, &self->mask, self->flags); + if (unlikely(self->fd == -1)) + throw_errno(errno); +} + +void signal_delete(signal_t * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(SIGNAL_NULL); + + close(self->fd), self->fd = -1; + + if (sigprocmask(SIG_UNBLOCK, &self->mask, NULL) == -1) + throw_errno(errno); + + sigemptyset(&self->mask); + self->flags = 0; +} + +int signal_fileno(signal_t * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(SIGNAL_NULL); + return self->fd; +} + +int signal_setmask(signal_t * self, const sigset_t mask) +{ + if (unlikely(self == NULL)) + throw_unexpected(SIGNAL_NULL); + + self->mask = mask; + + if (sigprocmask(SIG_BLOCK, &self->mask, NULL) == -1) + throw_errno(errno); + + self->fd = signalfd(self->fd, &self->mask, self->flags); + if (unlikely(self->fd == -1)) + throw_errno(errno); + + return self->fd; +} + +#if 0 +void signal_wait1(signal_t * self) +{ + signal_wait2(self, -1); +} + +void signal_wait2(signal_t * self, int timeout) +{ + if (unlikely(self == NULL)) + throw_unexpected(SIGNAL_NULL); + + struct epoll_event events[signal_EVENT_SIZE]; + + int rc = epoll_wait(self->fd, events, + signal_EVENT_SIZE, timeout); + + for (int n = 0; n < rc; n++) { + signal_callback *cb = (signal_callback *) events[n].data.ptr; + if (cb != NULL) + if (cb->func != NULL) + cb->func((signal_event[1]) { { + events[n].data.u64 >> 32, + events[n].events} + } + , cb->data); + } +} +#endif + +/* ======================================================================= */ diff --git a/clib/src/slab.c b/clib/src/slab.c new file mode 100644 index 0000000..f9146af --- /dev/null +++ b/clib/src/slab.c @@ -0,0 +1,388 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: slab.c + * Author: Shaun Wetzstein + * Descr: Slab based allocator + * Note: + * Date: 06/05/10 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libclib.h" +#include "tree_iter.h" + +#include "slab.h" + +/* ======================================================================= */ + +/*! @cond */ +#define SLAB_NODE_MAGIC "SLND" + +#define SLAB_NODE_MAGIC_CHECK(m) ({ \ + bool rc = (((m)[0] != SLAB_NODE_MAGIC[0]) || \ + ((m)[1] != SLAB_NODE_MAGIC[1]) || \ + ((m)[2] != SLAB_NODE_MAGIC[2]) || \ + ((m)[3] != SLAB_NODE_MAGIC[3])); \ + rc; \ +}) + +typedef struct slab_node slab_node_t; + +struct slab_node { + uint8_t magic[4]; + + size_t free; + tree_node_t node; + + uint32_t bitmap[]; +}; + +#define SLAB_ALLOC_COUNT(s) ((s)->hdr.data_size / (s)->hdr.alloc_size) + +#define SLAB_PAGE_MAX UINT16_MAX +#define SLAB_PAGE_DIVISOR 32 +/*! @endcond */ + +/* ======================================================================= */ + +static slab_node_t *__slab_grow(slab_t * self) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, SLAB_MAGIC)); + + slab_node_t *node = NULL; + if (posix_memalign((void **)&node, self->hdr.align_size, + self->hdr.page_size) < 0) { + ERRNO(errno); + return NULL; + } + + memset(node, 0, self->hdr.page_size); + + node->magic[0] = SLAB_NODE_MAGIC[0]; + node->magic[1] = SLAB_NODE_MAGIC[1]; + node->magic[2] = SLAB_NODE_MAGIC[2]; + node->magic[3] = SLAB_NODE_MAGIC[3]; + + node->free = SLAB_ALLOC_COUNT(self); + + tree_node_init(&node->node, (const void *)int32_hash1((int32_t) node)); + splay_insert(&self->tree, &node->node); + + self->hdr.page_count++; + + return node; +} + +/* ======================================================================= */ + +int slab_init3(slab_t * self, const char *name, size_t alloc_size) +{ + size_t page_size = max(sysconf(_SC_PAGESIZE), + __round_pow2(alloc_size * SLAB_PAGE_DIVISOR)); + return slab_init5(self, name, alloc_size, page_size, page_size); +} + +int slab_init4(slab_t * self, const char *name, size_t alloc_size, + size_t page_size) +{ + size_t align_size = (size_t) sysconf(_SC_PAGESIZE); + return slab_init5(self, name, alloc_size, page_size, align_size); +} + +int slab_init5(slab_t * self, const char *name, size_t alloc_size, + size_t page_size, size_t align_size) +{ + assert(self != NULL); + + if (unlikely(MAGIC_CHECK(self->hdr.id, SLAB_MAGIC) == false)) + slab_delete(self); + + alloc_size = align(alloc_size, sizeof(void *)); + if (alloc_size < SLAB_ALLOC_MIN || SLAB_ALLOC_MAX < alloc_size) { + UNEXPECTED("alloc_size out of range [%d..%d]", + SLAB_ALLOC_MIN, SLAB_ALLOC_MAX); + return -1; + } + + page_size = __round_pow2(page_size); + if (page_size / alloc_size < SLAB_PAGE_DIVISOR) { + UNEXPECTED("page_size out of range [%d..%d]", + alloc_size * SLAB_PAGE_DIVISOR, SLAB_PAGE_MAX); + return -1; + } + + size_t __page_size = (size_t) sysconf(_SC_PAGESIZE); + + align_size = __round_pow2(align_size); + if (align_size % __page_size) { + UNEXPECTED("align_size must be 0x%x aligned", __page_size); + return -1; + } + + memset(self, 0, sizeof *self); + + self->hdr.id[IDENT_MAGIC_0] = SLAB_MAGIC[IDENT_MAGIC_0]; + self->hdr.id[IDENT_MAGIC_1] = SLAB_MAGIC[IDENT_MAGIC_1]; + self->hdr.id[IDENT_MAGIC_2] = SLAB_MAGIC[IDENT_MAGIC_2]; + self->hdr.id[IDENT_MAGIC_3] = SLAB_MAGIC[IDENT_MAGIC_3]; + + self->hdr.id[IDENT_MAJOR] = CLIB_MAJOR; + self->hdr.id[IDENT_MINOR] = CLIB_MINOR; + self->hdr.id[IDENT_PATCH] = CLIB_PATCH; + + if (__BYTE_ORDER == __LITTLE_ENDIAN) + self->hdr.id[IDENT_FLAGS] |= SLAB_FLAG_LSB; + if (__BYTE_ORDER == __BIG_ENDIAN) + self->hdr.id[IDENT_FLAGS] |= SLAB_FLAG_MSB; + + if (name != NULL && *name != '\0') + strncpy(self->hdr.name, name, sizeof(self->hdr.name)); + + self->hdr.page_size = page_size; + self->hdr.align_size = align_size; + self->hdr.alloc_size = alloc_size; + + self->hdr.bitmap_size = + align(page_size / alloc_size, CHAR_BIT * sizeof(uint32_t)); + self->hdr.bitmap_size /= CHAR_BIT; + + self->hdr.data_size = + self->hdr.page_size - sizeof(slab_node_t) - self->hdr.bitmap_size; + + tree_init(&self->tree, default_compare); + + return 0; +} + +int slab_delete(slab_t * self) +{ + if (unlikely(self == NULL)) + return 0; + + if (unlikely(MAGIC_CHECK(self->hdr.id, SLAB_MAGIC))) { + UNEXPECTED("'%s' invalid or corrupt slab object", + self->hdr.name); + return -1; + } + + tree_iter_t it; + tree_iter_init(&it, &self->tree, TI_FLAG_FWD); + + slab_node_t *node; + tree_for_each(&it, node, node) { + if (SLAB_NODE_MAGIC_CHECK(node->magic)) { + UNEXPECTED("'%s' invalid or corrupt slab_node object " + "=> '%.4s'", self->hdr.name, node->magic); + return -1; + } + + if (splay_remove(&self->tree, &node->node) < 0) + return -1; + memset(node, 0, sizeof(*node)); + + free(node); + } + + memset(self, 0, sizeof(*self)); + + return 0; +} + +void *slab_alloc(slab_t * self) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, SLAB_MAGIC)); + + tree_iter_t it; + tree_iter_init(&it, &self->tree, TI_FLAG_FWD); + + slab_node_t *node; + tree_for_each(&it, node, node) + if (0 < node->free) + break; + + if (unlikely(tree_iter_elem(&it) == NULL)) + node = __slab_grow(self); + + assert(node != NULL); + assert(0 < node->free); + + void *ptr = NULL; + + uint32_t map_pos; + for (map_pos = 0; map_pos < self->hdr.bitmap_size; map_pos++) + if (node->bitmap[map_pos] != UINT32_MAX) + break; + + if (unlikely(node->bitmap[map_pos] == UINT32_MAX)) { + UNEXPECTED("'%s' cache is corrupted", self->hdr.name); + return NULL; + } + if (unlikely(self->hdr.bitmap_size <= map_pos)) { + UNEXPECTED("'%s' cache is corrupted", self->hdr.name); + return NULL; + } + + uint32_t bit = clzl(~node->bitmap[map_pos]); + uint32_t mask = 0x80000000 >> bit; + + if (unlikely((node->bitmap[map_pos] & mask) == mask)) { + UNEXPECTED("'%s' cache is corrupted", self->hdr.name); + return NULL; + } + + node->bitmap[map_pos] |= mask; + node->free--; + + ptr = (void *)node->bitmap + self->hdr.bitmap_size + + (map_pos * INT32_BIT + bit) * self->hdr.alloc_size; + + return ptr; +} + +int slab_free(slab_t * self, void *ptr) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, SLAB_MAGIC)); + + if (unlikely(ptr == NULL)) + return 0; + + slab_node_t *node = (slab_node_t *) ((uintptr_t) ptr & + ~(self->hdr.page_size - 1)); + assert(node != NULL); + + if (unlikely(SLAB_NODE_MAGIC_CHECK(node->magic))) { + int32_t hash = int32_hash1((int32_t) node); + if (splay_find(&self->tree, (const void *)hash) == NULL) { + UNEXPECTED("'%s' invalid slab_node pointer, %p", + self->hdr.name, ptr); + return -1; + } + } + + void *data = (void *)node->bitmap + self->hdr.bitmap_size; + assert(data != NULL); + + if (unlikely(ptr < data)) { + UNEXPECTED("'%s' pointer out-of-range, %p", + self->hdr.name, ptr); + return -1; + } + + size_t slot = (ptr - data) / self->hdr.alloc_size; + uint32_t mask = 0x80000000 >> slot; + size_t map_pos = slot / INT32_BIT; + + if (unlikely((node->bitmap[map_pos] & mask) != mask)) { + UNEXPECTED("'%s' double free detected, %p", + self->hdr.name, ptr); + return -1; + } + + node->bitmap[map_pos] &= ~mask; + node->free++; + + if (SLAB_ALLOC_COUNT(self) - node->free <= 0) { + splay_remove(&self->tree, &node->node); + self->hdr.page_count = max(0UL, self->hdr.page_count - 1); + memset(node, 0, sizeof(*node)); + free(node); + } + + return 0; +} + +size_t slab_alloc_size(slab_t * self) +{ + assert(self != NULL); + return self->hdr.alloc_size; +} + +size_t slab_page_size(slab_t * self) +{ + assert(self != NULL); + return self->hdr.page_size; +} + +size_t slab_data_size(slab_t * self) +{ + assert(self != NULL); + return self->hdr.data_size; +} + +size_t slab_bitmap_size(slab_t * self) +{ + assert(self != NULL); + return self->hdr.bitmap_size; +} + +size_t slab_align_size(slab_t * self) +{ + assert(self != NULL); + return self->hdr.align_size; +} + +void slab_dump(slab_t * self, FILE * out) +{ + if (out == NULL) + out = stdout; + + if (self != NULL) { + if (unlikely(MAGIC_CHECK(self->hdr.id, SLAB_MAGIC))) { + UNEXPECTED("'%s' invalid or corrupt slab object", + self->hdr.name); + return; + } + + fprintf(out, "%s: page_size: %d bitmap_size: %d data_size: " + "%d alloc_size: %d -- page_count: %d\n", + self->hdr.name, self->hdr.page_size, + self->hdr.bitmap_size, self->hdr.data_size, + self->hdr.alloc_size, self->hdr.page_count); + + tree_iter_t it; + tree_iter_init(&it, &self->tree, TI_FLAG_FWD); + + slab_node_t *node; + tree_for_each(&it, node, node) { + fprintf(out, "magic[%.4s] node: %p bitmap: %p data: " + "%p -- alloc: %d free: %d\n", + node->magic, &node->node, node->bitmap, + (void *)node->bitmap + self->hdr.bitmap_size, + self->hdr.data_size / self->hdr.alloc_size - + node->free, node->free); + + dump_memory(out, (unsigned long)node, node, + self->hdr.page_size); + } + } +} + +/* ======================================================================= */ diff --git a/clib/src/table.c b/clib/src/table.c new file mode 100644 index 0000000..108763e --- /dev/null +++ b/clib/src/table.c @@ -0,0 +1,679 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: table.c + * Author: Shaun Wetzstein + * Descr: + * Note: + * Date: 08/21/10 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libclib.h" +#include "table.h" +#include "vector_iter.h" + +#define TABLE_PAGE_SIZE 4096 +#define TABLE_PAGE_DIVISOR 32 + +/* ======================================================================= */ +int table_init(table_t * self, const char *name, size_t col_nr) +{ + assert(self != NULL); + assert(name != NULL); + + if (col_nr == 0) { + UNEXPECTED("'%d' invalid column number", col_nr); + return -1; + } + + self->hdr.id[IDENT_MAGIC_0] = TABLE_MAGIC[IDENT_MAGIC_0]; + self->hdr.id[IDENT_MAGIC_1] = TABLE_MAGIC[IDENT_MAGIC_1]; + self->hdr.id[IDENT_MAGIC_2] = TABLE_MAGIC[IDENT_MAGIC_2]; + self->hdr.id[IDENT_MAGIC_3] = TABLE_MAGIC[IDENT_MAGIC_3]; + + self->hdr.id[IDENT_MAJOR] = CLIB_MAJOR; + self->hdr.id[IDENT_MINOR] = CLIB_MINOR; + self->hdr.id[IDENT_PATCH] = CLIB_PATCH; + + if (__BYTE_ORDER == __LITTLE_ENDIAN) + self->hdr.id[IDENT_FLAGS] |= TABLE_FLAG_LSB; + if (__BYTE_ORDER == __BIG_ENDIAN) + self->hdr.id[IDENT_FLAGS] |= TABLE_FLAG_MSB; + + /* FIX ME -- handle this more elegantly */ + //assert((col_nr * sizeof(value_t)) < TABLE_PAGE_SIZE); + + self->hdr.col_nr = col_nr; + + if (name == NULL || *name == '\0') + memset(self->hdr.name, 0, sizeof(self->hdr.name)); + else + strncpy(self->hdr.name, name, sizeof(self->hdr.name)); + + char name_vector[strlen(self->hdr.name) + 5]; + + size_t row_size = self->hdr.col_nr * sizeof(value_t); + + sprintf(name_vector, "%s.table", self->hdr.name); + vector_init(&self->table, name_vector, row_size, + __round_pow2(row_size * TABLE_PAGE_DIVISOR)); + + sprintf(name_vector, "%s.string", self->hdr.name); + vector_init(&self->string, name_vector, 1, TABLE_PAGE_SIZE); + + sprintf(name_vector, "%s.blob", self->hdr.name); + vector_init(&self->blob, name_vector, 1, TABLE_PAGE_SIZE); + + return 0; +} + +int table_delete(table_t * self) +{ + if (unlikely(self == NULL)) + return 0; + + if (MAGIC_CHECK(self->hdr.id, TABLE_MAGIC)) { + UNEXPECTED("'%2.2x%2.2x%2.2x%2.2x' invalid table magic " + "'%2.2x%2.2x%2.2x%2.2x", + self->hdr.id[IDENT_MAGIC_0], + self->hdr.id[IDENT_MAGIC_1], + self->hdr.id[IDENT_MAGIC_2], + self->hdr.id[IDENT_MAGIC_3], + TABLE_MAGIC[IDENT_MAGIC_0], + TABLE_MAGIC[IDENT_MAGIC_2], + TABLE_MAGIC[IDENT_MAGIC_3], + TABLE_MAGIC[IDENT_MAGIC_3]); + return -1; + } + + if (0 < vector_size(&self->table)) { + vector_iter_t it; + if (vector_iter_init(&it, &self->table, VI_FLAG_FWD) < 0) + return -1; + + value_t *v; + vector_for_each(&it, v) + for (size_t c = 0; c < self->hdr.col_nr; c++) + value_clear(v + c); + } + + if (vector_delete(&self->table) < 0) + return -1; + if (vector_delete(&self->string) < 0) + return -1; + if (vector_delete(&self->blob) < 0) + return -1; + + return 0; +} + +value_t *table_get(table_t * self, size_t row, size_t col) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC)); + + row++; /* hide the column names */ + + return (value_t *) vector_at(&self->table, + row * self->hdr.col_nr + col); +} + +value_t *table_row2(table_t * self, size_t row_nr) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC)); + + row_nr++; /* hide the column names */ + + return (value_t *) vector_at(&self->table, row_nr * self->hdr.col_nr); +} + +int table_row3(table_t * self, size_t row_nr, value_t * row) +{ + assert(self != NULL); + assert(row != NULL); + assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC)); + + row_nr++; /* hide the column names */ + + if (vector_size(&self->table) <= row_nr + 1) { + if (vector_size(&self->table, row_nr + 1) < 0) + return -1; + } else { + value_t *old; + old = (value_t *) vector_at(&self->table, row_nr); + assert(old != NULL); + + for (size_t col_nr = 0; col_nr < self->hdr.col_nr; col_nr++) + value_clear(old + col_nr); + } + + if (vector_put(&self->table, row_nr, row) < 0) + return -1; + + for (size_t col_nr = 0; col_nr < self->hdr.col_nr; col_nr++) + if (value_type(row + col_nr) == VT_STR || + value_type(row + col_nr) == VT_BLOB) + value_type(row + col_nr, VT_UNKNOWN); + + return 0; +} + +value_t *table_column(table_t * self, value_t * row, size_t col) +{ + assert(self != NULL); + assert(row != NULL); + assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC)); + assert(col < self->hdr.col_nr); + + return (void *)row + (vector_elem_size(&self->table) * col); +} + +int table_put(table_t * self, size_t row, size_t col, value_t * val) +{ + assert(self != NULL); + assert(val != NULL); + assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC)); + + row++; /* hide the column names */ + + size_t size = (row + 1) * self->hdr.col_nr; + + if (vector_size(&self->table) <= size) + if (vector_size(&self->table, size) < 0) + return -1; + + /* free existing pointer data */ + value_t *old = (value_t *) vector_at(&self->table, + row * self->hdr.col_nr + col); + assert(old != NULL); + value_clear(old); + + vector_put(&self->table, row * self->hdr.col_nr + col, (void *)val, 1); + + if ((value_type(val) == VT_STR) || (value_type(val) == VT_BLOB)) + value_type(val, VT_UNKNOWN); + + return 0; +} + +const char *table_name2(table_t * self, size_t col_nr) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC)); + assert(col_nr < self->hdr.col_nr); + + const char *rc = NULL; + + value_t *row = (value_t *) vector_at(&self->table, 0); + if (row != NULL) { + value_t *col = row + col_nr; + switch (value_type(col)) { + case VT_STR_OFF: + rc = vector_at(&self->string, col->u64); + break; + case VT_STR_INLINE: + rc = (const char *)col->data; + break; + case VT_STR_CONST: + case VT_STR: + rc = value_string(col); + break; + default: + rc = NULL; + } + } + + return rc; +} + +int _table_name3(table_t * self, size_t col_nr, const char *name) +{ + return _table_name4(self, col_nr, name, strlen(name)); +} + +int _table_name4(table_t * self, size_t col_nr, const char *name, size_t len) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC)); + assert(col_nr < self->hdr.col_nr); + + if (vector_size(&self->table) <= 0) + if (vector_size(&self->table, 1) < 0) + return -1; + + value_t *row = (value_t *) vector_at(&self->table, 0); + if (row != NULL) + value_string(row + col_nr, name, len); + + return 0; +} + +size_t table_rows(table_t * self) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC)); + return vector_size(&self->table) - 1; +} + +size_t table_columns(table_t * self) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC)); + return self->hdr.col_nr; +} + +int table_serialize(table_t * self) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC)); + + vector_iter_t it; + vector_iter_init(&it, &self->table, VI_FLAG_FWD); + + value_t *row; + vector_for_each(&it, row) { + for (size_t c = 0; c < self->hdr.col_nr; c++) { + value_t *col = row + c; + + value_type_t type = value_type(col); + size_t len = value_size(col); + + vector_t *vec = NULL; + if (type == VT_STR) + vec = &self->string, type = VT_STR_OFF, len++; + else if (type == VT_BLOB) + vec = &self->blob, type = VT_BLOB_OFF; + else + continue; + + uint64_t off = vector_size(vec); + + if (vector_size(vec, off + len) < 0) + return -1; + if (vector_put(vec, off, col->ptr, len) < 0) + return -1; + + free(col->ptr), col->ptr = NULL; + + col->u64 = off; + value_type(col, type); + } + } + + return 0; +} + +ssize_t table_save(table_t * self, FILE * out) +{ + assert(self != NULL); + assert(out != NULL); + assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC)); + + /* ============= */ + + int header_swap(table_header_t * hdr) { + assert(hdr != NULL); + + if (hdr->id[IDENT_FLAGS] & TABLE_FLAG_MSB) { + hdr->col_nr = htobe32(hdr->col_nr); + } else if (hdr->id[IDENT_FLAGS] & TABLE_FLAG_LSB) { + hdr->col_nr = htole32(hdr->col_nr); + } else { + UNEXPECTED("'%s' invalid or corrupt table object => " + "'%x'", hdr->name, hdr->id[IDENT_FLAGS]); + return -1; + } + + return 0; + } + + /* ============= */ + + ssize_t len = 0; + + table_header_t hdr = self->hdr; + if (header_swap(&hdr) < 0) + return -1; + + clearerr(out); + len = fwrite(&self->hdr, 1, sizeof(self->hdr), out); + if (len != sizeof(self->hdr)) { + if (ferror(out)) { + ERRNO(errno); + return -1; + } + } + + ssize_t rc = vector_save(&self->table, out); + if (rc < 0) + return -1; + len += rc; + + rc = vector_save(&self->string, out); + if (rc < 0) + return -1; + len += rc; + + rc = vector_save(&self->blob, out); + if (rc < 0) + return -1; + len += rc; + + return len; +} + +int table_deserialize(table_t * self) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC)); + + vector_iter_t it; + vector_iter_init(&it, &self->table, VI_FLAG_FWD); + + value_t *row; + vector_for_each(&it, row) { + for (size_t c = 0; c < self->hdr.col_nr; c++) { + value_t *col = row + c; + + value_type_t type = value_type(col); + size_t len = value_size(col); + + switch (type) { + uint64_t off; + case VT_STR_OFF: + len++; + value_type(col, VT_STR); + case VT_BLOB_OFF: + off = col->u64; + col->ptr = malloc(len); + + if (col->ptr == NULL) { + ERRNO(errno); + return -1; + } + + if (vector_get(&self->string, off, col->ptr, + len) < 0) + return -1; + + if (value_type(col) == VT_BLOB_OFF) + value_type(col, VT_BLOB); + break; + case VT_STR: + case VT_BLOB: + case VT_STR_CONST: + UNEXPECTED("'%s' invalid or corrupt type %d", + self->hdr.name, type); + return -1; + default: + ; + } + } + } + + if (vector_delete(&self->string) < 0) + return -1; + if (vector_delete(&self->blob) < 0) + return -1; + + return 0; +} + +ssize_t table_load(table_t * self, FILE * in) +{ + assert(self != NULL); + + /* ============= */ + + int header_swap(table_header_t * hdr) { + assert(hdr != NULL); + if (hdr->id[IDENT_FLAGS] & TABLE_FLAG_MSB) { + hdr->col_nr = be32toh(hdr->col_nr); + } else if (hdr->id[IDENT_FLAGS] & TABLE_FLAG_LSB) { + hdr->col_nr = le32toh(hdr->col_nr); + } else { + UNEXPECTED("'%s' invalid or corrupt table object => " + "'%x'", hdr->name, hdr->id[IDENT_FLAGS]); + return -1; + } + + return 0; + } + + int table_swap(table_t * tbl) { + vector_iter_t it; + if (vector_iter_init(&it, &tbl->table, VI_FLAG_FWD) < 0) + return -1; + + value_t *row; + vector_for_each(&it, row) { + for (size_t c = 0; c < self->hdr.col_nr; c++) { + value_t *col = row + c; + + value_type_t type = value_type(row); + +#define iBE(s) value_i##s(col, be##s##toh(value_i##s(col))) +#define uBE(s) value_u##s(col, be##s##toh(value_u##s(col))) +#define iLE(s) value_i##s(col, le##s##toh(value_i##s(col))) +#define uLE(s) value_u##s(col, le##s##toh(value_u##s(col))) + + if (tbl->hdr.id[IDENT_FLAGS] & TABLE_FLAG_MSB) { + switch (type) { + case VT_I16: + iBE(16); + break; + case VT_U16: + uBE(16); + break; + case VT_I32: + iBE(32); + break; + case VT_U32: + uBE(32); + break; + case VT_I64: + iBE(64); + break; + case VT_U64: + uBE(64); + break; + case VT_STR_OFF: + case VT_BLOB_OFF: + col->u64 = be64toh(col->u64); + break; + default: + ; + } + } else if (tbl->hdr.id[IDENT_FLAGS] & + TABLE_FLAG_LSB) { + switch (type) { + case VT_I16: + iLE(16); + break; + case VT_U16: + uLE(16); + break; + case VT_I32: + iLE(32); + break; + case VT_U32: + uLE(32); + break; + case VT_I64: + iLE(64); + break; + case VT_U64: + uLE(64); + break; + case VT_STR_OFF: + case VT_BLOB_OFF: + col->u64 = le64toh(col->u64); + break; + default: + ; + } + } else { + UNEXPECTED("'%s' invalid or corrupt " + "table object => '%x'", + tbl->hdr.name, + tbl->hdr.id[IDENT_FLAGS]); + return -1; + } + } + } + + return 0; + } + + /* ============= */ + + // zero'd table will cause a magic check + (void)table_delete(self); + + clearerr(in); + size_t len = fread(&self->hdr, 1, sizeof(self->hdr), in); + if (len != sizeof(self->hdr)) { + if (feof(in)) { + UNEXPECTED("'%s' end-of-file encountered", + self->hdr.name); + return -1; + } + if (ferror(in)) { + ERRNO(errno); + return -1; + } + } + + assert(!MAGIC_CHECK(self->hdr.id, TABLE_MAGIC)); + + if (header_swap(&self->hdr) < 0) + return -1; + + ssize_t rc = vector_load(&self->table, in); + if (rc < 0) + return -1; + len += rc; + + if (table_swap(self) < 0) + return -1; + + rc = vector_load(&self->string, in); + if (rc < 0) + return -1; + len += rc; + + rc = vector_load(&self->blob, in); + if (rc < 0) + return -1; + len += rc; + + return len; +} + +void table_print(table_t * self, FILE * out) +{ + if (self != NULL) { + if (unlikely(MAGIC_CHECK(self->hdr.id, TABLE_MAGIC))) { + UNEXPECTED("'%s' invalid or corrupt table object", + self->hdr.name); + return; + } + + vector_iter_t it; + vector_iter_init(&it, &self->table, VI_FLAG_FWD); + + value_t *row; + vector_for_each(&it, row) + for (size_t c = 0; c < self->hdr.col_nr; c++) + value_dump(row + c, out); + + vector_dump(&self->string, out); + } +} + +void table_dump(table_t * self, FILE * out) +{ + if (self != NULL) { + if (unlikely(MAGIC_CHECK(self->hdr.id, TABLE_MAGIC))) { + UNEXPECTED("'%s' invalid or corrupt table object", + self->hdr.name); + return; + } + + fprintf(out, + "table: [ size: %d cols: %d rows: %d name: '%s']\n", + sizeof(value_t), table_columns(self), table_rows(self), + self->hdr.name); + + dump_memory(out, (unsigned long)&self->hdr, &self->hdr, + sizeof(self->hdr)); + + vector_dump(&self->table, out); + vector_dump(&self->string, out); + vector_dump(&self->blob, out); + } +} + +#if 0 +void table_sort(table_t * self, compare_f cmp) +{ + /* The exchange function swaps two rows within the table + * exchange(table_t * self, size_t i, size_t j) + */ + + /* The partition method receives a list or sublist, and places the first element + * in its correct position within the list. It also ensures that all elements to + * the left of this are smaller, and all to the right are larger. + * + * partition(a[], p, r) + * i = p + * j = r + 1 + * pivot = a[p] + * do { + * do i = i + 1 while (a[i]pivot) + * if (i < j) exchange(a[i], a[j]) + * } while (i p then + * j = partition(a[], p, r) + * quicksort(a[], p, j-1) + * quicksort(a[], j+1, r) + * + */ +} +#endif + +/* ======================================================================= */ diff --git a/clib/src/table_iter.c b/clib/src/table_iter.c new file mode 100644 index 0000000..cb73573 --- /dev/null +++ b/clib/src/table_iter.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: table_iter.c + * Author: Shaun Wetzstein + * Descr: + * Note: + * Date: 10/22/10 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "assert.h" +#include "misc.h" + +#include "table_iter.h" + +/* ======================================================================= */ + +int table_iter_init(table_iter_t * self, table_t * table, uint32_t flags) +{ + assert(self != NULL); + assert(table != NULL); + + self->flags = flags; + self->table = table; + + if (self->flags & TI_FLAG_BWD) { + if (vector_iter_init(&self->it, &table->table, VI_FLAG_BWD) < 0) + return -1; + } else { + if (vector_iter_init(&self->it, &table->table, VI_FLAG_FWD) < 0) + return -1; + } + + return vector_iter_inc(&self->it) != NULL; +} + +int table_iter_clear(table_iter_t * self) +{ + assert(self != NULL); + + if (vector_iter_clear(&self->it) < 0) + return -1; + + self->table = NULL; + + return 0; +} + +const value_t *table_iter_elem(table_iter_t * self) +{ + assert(self != NULL); + return (value_t *) vector_iter_elem(&self->it); +} + +const value_t *table_iter_inc1(table_iter_t * self) +{ + return table_iter_inc2(self, 1); +} + +const value_t *table_iter_inc2(table_iter_t * self, size_t count) +{ + assert(self != NULL); + + const value_t *v = NULL; + + for (size_t i = 0; i < count; i++) { + if (self->flags & TI_FLAG_BWD) { + v = (value_t *) vector_iter_dec(&self->it); // columns + } else { + v = (value_t *) vector_iter_inc(&self->it); // columns + } + } + + return v; +} + +const value_t *table_iter_dec1(table_iter_t * self) +{ + return table_iter_dec2(self, 1); +} + +const value_t *table_iter_dec2(table_iter_t * self, size_t count) +{ + assert(self != NULL); + + const value_t *v = NULL; + + for (size_t i = 0; i < count; i++) { + if (self->flags & TI_FLAG_BWD) { + v = (value_t *) vector_iter_inc(&self->it); // columns + } else { + v = (value_t *) vector_iter_dec(&self->it); // columns + } + } + + return v; +} + +size_t table_iter_pos1(table_iter_t * self) +{ + assert(self != NULL); + return vector_iter_pos(&self->it) + 1; +} + +int table_iter_pos2(table_iter_t * self, size_t pos) +{ + assert(self != NULL); + assert(pos < table_rows(self->table)); + return vector_iter_pos(&self->it, pos + 1); +} + +/* ======================================================================= */ diff --git a/clib/src/timer.c b/clib/src/timer.c new file mode 100644 index 0000000..cb4d260 --- /dev/null +++ b/clib/src/timer.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: timer.c + * Author: Shaun Wetzstein + * Descr: + * Note: + * Date: 10/03/10 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" +#include "timer.h" + +#define TIMER_PAGE_SIZE 64 +#define TIMER_EVENT_SIZE 64 + +/* ======================================================================= */ + +const char *__timer_msg[] = { + "timer: unexpected NULL self pointer", + "timer: unexpected NULL callback structure", +}; + +#define TIMER_NULL (__timer_msg[0]) +#define TIMER_CALLBACK_NULL (__timer_msg[1]) + +/* ======================================================================= */ + +void timer_init(timer * self, int clock) +{ + if (unlikely(self == NULL)) + throw_unexpected(TIMER_NULL); + + if (self->fd != -1) + close(self->fd), self->fd = -1; + + self->fd = timerfd_create(clock, TFD_CLOEXEC); + if (unlikely(self->fd == -1)) + throw_errno(errno); + + vector_init(&self->callbacks, "callbacks", + sizeof(timer_callback), TIMER_PAGE_SIZE); +} + +void timer_delete(timer * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(TIMER_NULL); + close(self->fd), self->fd = -1; +} + +int timer_fileno(timer * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(TIMER_NULL); + return self->fd; +} + +uint32_t timer_add(timer * self, timer_callback * cb) +{ + if (unlikely(self == NULL)) + throw_unexpected(TIMER_NULL); + + if (access(path, F_OK) != 0) + throw_errno(errno); + + uint32_t wd = inotify_add_timer(self->fd, path, events); + if (unlikely((int)wd == -1)) + throw_errno(errno); + + if (cb != NULL) + vcetor_put(&self->callbacks, wd, cb); + + return wd; +} + +void timer_remove(timer * self, uint32_t wd) +{ + if (unlikely(self == NULL)) + throw_unexpected(TIMER_NULL); + + int rc = inotify_rm_timer(self->fd, wd); + if (unlikely(rc == -1)) + throw_errno(errno); + + array_status(&self->callbacks, wd, false); +} + +void timer_wait(timer * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(TIMER_NULL); + + /* FIX ME */ + + timer_event events[timer_EVENT_SIZE]; + + int n = read(self->fd, events, sizeof events); + printf("n[%d]\n", n); + + for (int i = 0; i < (n / sizeof *events); i++) + printf("%d: wd[%d] mask[%x] cookie[%x] name[%.*s]\n", + i, events[i].wd, events[i].mask, events[i].cookie, + events[i].len, events[i].name); +} + +/* ======================================================================= */ diff --git a/clib/src/trace_indent.c b/clib/src/trace_indent.c new file mode 100644 index 0000000..c1dd280 --- /dev/null +++ b/clib/src/trace_indent.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) International Business Machines Corp., 2011 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: B. Rafanello + * + * Module: trace_indent.c + * + * Implements indentation for the trace output from bb_trace + * + * Version 0.0.0.1 + */ + +#include "trace_indent.h" +#include "stdio.h" + +/*-------------------------------------------------- + * Private Constants + --------------------------------------------------*/ + +#define DEFAULT_INDENT_SIZE 5 +#define MAX_INDENT_SIZE 129 +#define DEFAULT_INDENT " " + +/*-------------------------------------------------- + * Global variables. + --------------------------------------------------*/ + +/* The current trace level. */ +unsigned char BB_TRACE_LEVEL = 0; +unsigned char BB_PREVIOUS_TRACE_LEVEL = 0; + +/*-------------------------------------------------- + * Private global variables. + --------------------------------------------------*/ + +static unsigned int Current_Indent_Level = 0; +static unsigned int Current_Indent_Size = DEFAULT_INDENT_SIZE; +static char Indent[MAX_INDENT_SIZE] = DEFAULT_INDENT; + +/*-------------------------------------------------- + * Public Functions + --------------------------------------------------*/ + +/*********************************************************************/ +/* */ +/* Function Name: Set_Indent_Size */ +/* */ +/* Descriptive Name: Sets the number of spaces to use per indent. */ +/* If the current indent level is 5 and this */ +/* function is called to set the indent size to */ +/* 10, then 50 spaces will be printed by the */ +/* trace macros before each line of trace */ +/* output at this indent level. */ +/* */ +/* Input: unsigned int Spaces_Per_Indent - A value less than 128. */ +/* */ +/* Output: If Success : The specified value will be used for the */ +/* size of an indent. */ +/* */ +/* If Failure : This should only happen if the value */ +/* specified is >= 128, in which case it is */ +/* ignored and the previous value is retained.*/ +/* */ +/* Error Handling: Bad values for Spaces_Per_Indent are ignored. */ +/* */ +/* Side Effects: The number of spaces per indent may be changed. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +void Set_Indent_Size(unsigned int Spaces_Per_Indent) +{ + unsigned int i; + + if (Spaces_Per_Indent <= MAX_INDENT_SIZE) { + Current_Indent_Size = Spaces_Per_Indent; + for (i = 0; i < Current_Indent_Size; i++) { + Indent[i] = ' '; + } + Indent[i] = 0x00; + } + + return; +} + +/*********************************************************************/ +/* */ +/* Function Name: Indent_Trace_Output */ +/* */ +/* Descriptive Name: This function increases the current indent */ +/* level by one. */ +/* */ +/* Input: None. */ +/* */ +/* Output: If Success : None */ +/* */ +/* If Failure : None */ +/* */ +/* Error Handling: None */ +/* */ +/* Side Effects: The current indent level for trace output will */ +/* be increased by 1. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +void Indent_Trace_Output(void) +{ + Current_Indent_Level += 1; + return; +} + +/*********************************************************************/ +/* */ +/* Function Name: Outdent_Trace_Output */ +/* */ +/* Descriptive Name: This function reduces the current indent */ +/* level if it is greater than 0. */ +/* */ +/* Input: None */ +/* */ +/* Output: If Success : None */ +/* */ +/* If Failure : None */ +/* */ +/* Error Handling: If the current indent level is zero, then this */ +/* function does nothing. */ +/* */ +/* Side Effects: The current indent level for trace output may be */ +/* decreased by 1. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +void Outdent_Trace_Output(void) +{ + if (Current_Indent_Level > 0) + Current_Indent_Level -= 1; + return; +} + +/*********************************************************************/ +/* */ +/* Function Name: Do_Indent */ +/* */ +/* Descriptive Name: This function prints to stderr the number of */ +/* spaces corresponding to the current indent */ +/* level. */ +/* */ +/* Input: None */ +/* */ +/* Output: If Success : None */ +/* */ +/* If Failure : None */ +/* */ +/* Error Handling: None */ +/* */ +/* Side Effects: Some number of space may be output to stderr. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +void Do_Indent(void) +{ + unsigned int i; + + for (i = 0; i < Current_Indent_Level; i++) { + fprintf(stderr, "%s", Indent); + } + +} diff --git a/clib/src/tree.c b/clib/src/tree.c new file mode 100644 index 0000000..820f288 --- /dev/null +++ b/clib/src/tree.c @@ -0,0 +1,630 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: tree.c + * Author: Shaun Wetzstein + * Descr: + * Note: + * Date: 08/21/10 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libclib.h" +#include "tree.h" +#include "slab.h" + +/* ======================================================================= */ + +tree_node_t *tree_node_prev(tree_node_t * self) +{ + assert(self != NULL); + + if (tree_node_left(self) != NULL) { + self = tree_node_left(self); + + while (tree_node_right(self) != NULL) + self = tree_node_right(self); + } else { + tree_node_t *parent = tree_node_parent(self); + + while (parent != NULL && self == tree_node_left(parent)) + self = parent, parent = tree_node_parent(parent); + + self = parent; + } + + return self; +} + +tree_node_t *tree_node_next(tree_node_t * self) +{ + assert(self != NULL); + + if (tree_node_right(self) != NULL) { + self = tree_node_right(self); + + while (self != NULL && tree_node_left(self) != NULL) + self = tree_node_left(self); + } else { + tree_node_t *parent = tree_node_parent(self); + + while (parent != NULL && self == tree_node_right(parent)) + self = parent, parent = tree_node_parent(parent); + + self = parent; + } + + return self; +} + +/* ======================================================================= */ + +int tree_init(tree_t * self, compare_f compare) +{ + assert(self != NULL); + assert(compare != NULL); + + self->root = NULL; + self->min = self->max = NULL; + self->compare = compare; + self->size = 0; + + return 0; +} + +static inline void __tree_new_min(tree_t * self, tree_node_t * node) +{ + assert(self != NULL); + assert(node != NULL); + + if (self->min == NULL) + self->min = node; + else if (self->compare(node->key, self->min->key) < 0) + self->min = node; +} + +static inline void __tree_new_max(tree_t * self, tree_node_t * node) +{ + assert(self != NULL); + assert(node != NULL); + + if (self->max == NULL) + self->max = node; + else if (self->compare(node->key, self->max->key) > 0) + self->max = node; +} + +static inline void +__tree_new_root(tree_t * self, tree_node_t * node, tree_node_t * left, + tree_node_t * right) +{ + assert(self != NULL); + assert(node != NULL); + + node->left = left; + node->right = right; + node->parent = NULL; + + if (unlikely(right != NULL)) + right->parent = node; + if (unlikely(left != NULL)) + left->parent = node; + + self->root = node; +} + +int tree_insert(tree_t * self, tree_node_t * node) +{ + assert(self != NULL); + assert(node != NULL); + + __tree_new_min(self, node); + __tree_new_max(self, node); + + if (unlikely(self->root == NULL)) { + __tree_new_root(self, node, NULL, NULL); + self->size++; + } else { + tree_node_t *root = self->root; + + while (root != NULL) { + int rc = self->compare(node->key, root->key); + + if (rc < 0) { + if (root->left == NULL) { + node->parent = root; + root->left = node; + self->size++; + return 0; + } else { + root = root->left; + } + } else if (0 < rc) { + if (root->right == NULL) { + node->parent = root; + root->right = node; + self->size++; + return 0; + } else { + root = root->right; + } + } else { + UNEXPECTED("duplicate key detected during " + "insert"); + return -1; + } + } + } + + return 0; +} + +static inline tree_node_t *__tree_find_min(tree_node_t * node) +{ + tree_node_t *min = node; + + if (node != NULL && node->right != NULL) { + min = node->right; + + while (min != NULL && min->left != NULL) + min = min->left; + } + + return min; +} + +static inline tree_node_t *__tree_find_max(tree_node_t * node) +{ + tree_node_t *max = node; + + if (node != NULL && node->left != NULL) { + max = node->left; + + while (max != NULL && max->right != NULL) + max = max->right; + } + + return max; +} + +int tree_remove(tree_t * self, tree_node_t * node) +{ + assert(self != NULL); + assert(node != NULL); + + if (unlikely(self->root == NULL) || unlikely(node == NULL)) + return 0; + + /* =========================== */ + + inline tree_node_t *__tree_find_min(tree_node_t * node) { + tree_node_t *min = node; + + if (node != NULL && node->right != NULL) { + min = node->right; + + while (min != NULL && min->left != NULL) + min = min->left; + } + + return min; + } + + inline tree_node_t *__tree_find_max(tree_node_t * node) { + tree_node_t *max = node; + + if (node != NULL && node->left != NULL) { + max = node->left; + + while (max != NULL && max->right != NULL) + max = max->right; + } + + return max; + } + + inline void remove_single_child(tree_node_t * node, bool left) { + tree_node_t *new_root = node->left; + + if (left == false) + new_root = node->right; + + if (node->parent != NULL) { + new_root->parent = node->parent; + + if (node->parent->left == node) // handle zig-zag + node->parent->left = new_root; + else if (node->parent->right == node) + node->parent->right = new_root; + } + + if (node == self->root) { + self->root = new_root; + self->root->parent = NULL; + } + } + + /* =========================== */ + + if (node == self->min) + self->min = tree_node_next(node); + if (node == self->max) + self->max = tree_node_prev(node); + + if (tree_node_leaf(node)) { + if (node->parent != NULL) { + if (node->parent->left == node) // left or right child? + node->parent->left = NULL; + else if (node->parent->right == node) + node->parent->right = NULL; + } + } else if (tree_node_internal(node)) { // two children! + tree_node_t *root = __tree_find_min(node); + assert(root != NULL); // must have a right child + + if (node->right == root) { // new 'root' is largest child + root->left = node->left; + if (node->left != NULL) + root->left->parent = root; + } else { // new 'root' is smallest grandchild + root->parent->left = root->right; + if (root->right != NULL) + root->right->parent = root->parent; + + root->right = node->right; + root->left = node->left; + + node->right->parent = root; + node->left->parent = root; + } + + root->parent = node->parent; + + if (self->root == node) { // find new parent + self->root = root; + } else { + if (node->parent->left == node) + node->parent->left = root; + else if (node->parent->right == node) + node->parent->right = root; + } + } else if (node->left != NULL) { // single left child + remove_single_child(node, true); + } else if (node->right != NULL) { // single right child + remove_single_child(node, false); + } + + node->right = node->left = node->parent = NULL; + + if (0 < self->size) + self->size--; + + if (self->size <= 0) + self->root = self->min = self->max = NULL; + + return 0; +} + +tree_node_t *tree_find(tree_t * self, const void *key) +{ + assert(self != NULL); + assert(key != NULL); + + tree_node_t *root = self->root; + + while (root != NULL) { + int rc = self->compare(key, root->key); + + if (rc < 0) + root = root->left; + else if (0 < rc) + root = root->right; + else + break; + } + + return root; +} + +int tree_walk(tree_t * self, tree_walk_f walk_func) +{ + assert(self != NULL); + assert(walk_func != NULL); + + int __tree_walk(tree_node_t * root) { + int rc = 0; + + if (likely(root != NULL)) { + __tree_walk(root->left); + rc = walk_func(root); + __tree_walk(root->right); + } + + return rc; + } + + return __tree_walk(self->root); +} + +void tree_dump(tree_t * self, FILE * out) +{ + assert(self != NULL); + + int __tree_node_dump(tree_node_t * node) { + if (node != NULL) + return fprintf(out, "node[%p] left[%p] right[%p] " + "parent[%p] -- key[%d]\n", + node, node->left, node->right, + node->parent, (intptr_t) node->key); + else + return 0; + } + + if (out == NULL) + out = stdout; + + fprintf(out, "root[%p] min[%p] max[%p] compare[%p] size[%d]\n", + self->root, self->min, self->max, self->compare, self->size); + + tree_walk(self, __tree_node_dump); +} + +void tree_node_dump(tree_node_t * node, FILE * out) +{ + if (node == NULL) + return; + + void __tree_node_dump(tree_node_t * root, int level) { + if (likely(root != NULL)) { + if (0 < level) { + for (int i = 0; i < level; i++) + fprintf(out, " "); + } + + fprintf(out, "node:[%p] left[%p] right[%p] parent[%p] " + "key[%d]\n", root, root->left, root->right, + root->parent, (intptr_t) node->key); + + level++; + __tree_node_dump(root->left, level); + __tree_node_dump(root->right, level); + level--; + } + } + + if (out == NULL) + out = stdout; + + __tree_node_dump(node, 0); +} + +/* ======================================================================= */ + +static tree_node_t *splay(tree_node_t * node, const void *key, + compare_f compare) +{ + if (node == NULL) + return node; + + tree_node_t N = { NULL, NULL, NULL, NULL } + , *l = &N, *r = &N, *y; + + for (;;) { + int rc = compare(key, node->key); + if (rc < 0) { + if (node->left == NULL) + break; + + /* rotate right */ + rc = compare(key, node->left->key); + if (rc < 0) { + y = node->left; + node->left = y->right; + if (y->right != NULL) + y->right->parent = node; + y->right = node; + node->parent = y; + y->parent = node->parent; + node = y; + + if (node->left == NULL) + break; + } + + /* link right */ + r->left = node; + node->parent = r; + + r = node; + node = node->left; + } else if (0 < rc) { + if (node->right == NULL) + break; + + /* rotate left */ + rc = compare(key, node->right->key); + if (0 < rc) { + y = node->right; + node->right = y->left; + if (y->left != NULL) + y->left->parent = node; + y->left = node; + node->parent = y; + y->parent = node->parent; + node = y; + + if (node->right == NULL) + break; + } + + /* link left */ + l->right = node; + node->parent = l; + + l = node; + node = node->right; + } else { + break; + } + } + + /* assemble */ + l->right = node->left; + if (node->left != NULL) + node->left->parent = l; + r->left = node->right; + if (node->right != NULL) + node->right->parent = r; + + node->left = N.right; + if (N.right != NULL) + N.right->parent = node; + + node->right = N.left; + if (N.left != NULL) + N.left->parent = node; + + node->parent = NULL; + + return node; +} + +int splay_insert(tree_t * self, tree_node_t * node) +{ + assert(self != NULL); + assert(node != NULL); + + __tree_new_min(self, node); + __tree_new_max(self, node); + + if (unlikely(self->root == NULL)) { + node->left = node->right = node->parent = NULL; + self->root = node; + self->size = 1; + return 0; + } + + self->root = splay(self->root, node->key, self->compare); + + int rc = self->compare(node->key, self->root->key); + + if (rc < 0) { + node->left = self->root->left; + if (node->left != NULL) + node->left->parent = node; + node->right = self->root; + self->root->left = NULL; + self->root->parent = node; + } else if (0 < rc) { + node->right = self->root->right; + if (node->right != NULL) + node->right->parent = node; + node->left = self->root; + self->root->right = NULL; + self->root->parent = node; + } else { + UNEXPECTED("duplicate key detected during insert"); + return -1; + } + + self->size++; + self->root = node; + + return 0; +} + +int splay_remove(tree_t * self, tree_node_t * node) +{ + assert(self != NULL); + assert(node != NULL); + + if (unlikely(self->root == NULL) || unlikely(node == NULL)) + return 0; + + if (node == self->min) + self->min = tree_node_next(node); + if (node == self->max) + self->max = tree_node_prev(node); + + self->root = splay(self->root, node->key, self->compare); + + if (node->key == self->root->key) { /* found it */ + tree_node_t *x; +#if SAVE + if (self->root->left == NULL) { + x = self->root->right; + } else { + x = splay(self->root->left, node->key, self->compare); + x->right = self->root->right; + } +#else + if (self->root->left != NULL && self->root->right != NULL) { + if (parity(int32_hash1((int32_t) self->root))) { + x = splay(self->root->left, node->key, + self->compare); + x->right = self->root->right; + } else { + x = splay(self->root->right, node->key, + self->compare); + x->left = self->root->left; + } + } else if (self->root->left == NULL) { + x = self->root->right; + } else { + x = splay(self->root->left, node->key, self->compare); + x->right = self->root->right; + } +#endif + + self->root->left = self->root->right = NULL; + self->root->parent = NULL; + self->root = x; + + if (0 < self->size) + self->size--; + if (0 < self->size) + self->root->parent = NULL; + } + + return 0; +} + +tree_node_t *splay_find(tree_t * self, const void *key) +{ + assert(self != NULL); + assert(key != NULL); + + self->root = splay(self->root, key, self->compare); + + if (self->root != NULL && self->compare(key, self->root->key)) + return NULL; + + return self->root; +} + +/* ======================================================================= */ diff --git a/clib/src/tree_iter.c b/clib/src/tree_iter.c new file mode 100644 index 0000000..3474b71 --- /dev/null +++ b/clib/src/tree_iter.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: tree_iter.c + * Author: Shaun Wetzstein + * Descr: + * Note: + * Date: 10/22/10 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libclib.h" +#include "vector.h" +#include "tree_iter.h" + +/* ======================================================================= */ + +int tree_iter_init(tree_iter_t * self, tree_t * tree, uint32_t flags) +{ + assert(self != NULL); + assert(tree != NULL); + + self->node = NULL; + self->safe = NULL; + + self->tree = tree; + self->flags = flags; + + return tree_iter_clear(self); +} + +int tree_iter_clear(tree_iter_t * self) +{ + assert(self != NULL); + + if (self->flags & TI_FLAG_FWD) { + self->node = tree_min(self->tree); + if (self->node != NULL) + self->safe = tree_node_next(self->node); + } else if (self->flags & TI_FLAG_BWD) { + self->node = tree_max(self->tree); + if (self->node != NULL) + self->safe = tree_node_prev(self->node); + } else { + UNEXPECTED("invalid tree_iter flags"); + return -1; + } + + return 0; +} + +tree_node_t *tree_iter_elem(tree_iter_t * self) +{ + assert(self != NULL); + return self->node; +} + +tree_node_t *tree_iter_inc1(tree_iter_t * self) +{ + return tree_iter_inc2(self, 1); +} + +tree_node_t *tree_iter_inc2(tree_iter_t * self, size_t count) +{ + assert(self != NULL); + + for (size_t i = 0; i < count && self->node != NULL; i++) { + if (self->flags & TI_FLAG_FWD) { + self->node = self->safe; + if (self->node != NULL) + self->safe = tree_node_next(self->node); + } else if (self->flags & TI_FLAG_BWD) { + self->node = self->safe; + if (self->node != NULL) + self->safe = tree_node_prev(self->node); + } else { + UNEXPECTED("invalid tree_iter flags"); + return NULL; + } + } + + return self->node; +} + +tree_node_t *tree_iter_dec1(tree_iter_t * self) +{ + return tree_iter_dec2(self, 1); +} + +tree_node_t *tree_iter_dec2(tree_iter_t * self, size_t count) +{ + assert(self != NULL); + + for (size_t i = 0; i < count && self->node != NULL; i++) { + if (self->flags & TI_FLAG_FWD) { + self->node = self->safe; + if (self->node != NULL) + self->safe = tree_node_prev(self->node); + } else if (self->flags & TI_FLAG_BWD) { + self->node = self->safe; + if (self->node != NULL) + self->safe = tree_node_next(self->node); + } else { + UNEXPECTED("invalid tree_iter flags"); + return NULL; + } + } + + return self->node; +} + +/* ======================================================================= */ diff --git a/clib/src/value.c b/clib/src/value.c new file mode 100644 index 0000000..ad90d81 --- /dev/null +++ b/clib/src/value.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: value.c + * Author: Shaun Wetzstein + * Descr: + * Note: + * Date: 08/23/10 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libclib.h" +#include "value.h" + +/* ======================================================================= */ + +void value_dump(const value_t * self, FILE * out) +{ + assert(self != NULL); + + static const char *type_name[] = { + "", + "int8_t", "int16_t", "int32_t", "int64_t", + "uint8_t", "uint16_t", "uint32_t", "uint64_t", + "float", "double", + "string", "string-inline", "string-offset", "string-const", + "blob", "blob-inline", "blob-offset", "blob-file", + }; + + switch (self->type) { + case VT_I8: + fprintf(out, "value: [ data: %d size: %d type: %s ] %p\n", + self->i8, self->size, type_name[self->type], self); + break; + case VT_I16: + fprintf(out, "value: [ data: %d size: %d type: %s ] %p\n", + self->i16, self->size, type_name[self->type], self); + break; + case VT_I32: + fprintf(out, "value: [ data: %d size: %d type: %s ] %p\n", + self->i32, self->size, type_name[self->type], self); + break; + case VT_I64: + fprintf(out, "value: [ data: %lld size: %d type: %s ] %p\n", + self->i64, self->size, type_name[self->type], self); + break; + case VT_U8: + fprintf(out, "value: [ data: %ud size: %d type: %s ] %p\n", + self->u8, self->size, type_name[self->type], self); + break; + case VT_U16: + fprintf(out, "value: [ data: %d size: %d type: %s ] %p\n", + self->u16, self->size, type_name[self->type], self); + break; + case VT_U32: + fprintf(out, "value: [ data: %d size: %d type: %s ] %p\n", + self->u32, self->size, type_name[self->type], self); + break; + case VT_U64: + fprintf(out, "value: [ data: %lld size: %d type: %s ] %p\n", + self->u64, self->size, type_name[self->type], self); + break; + case VT_REAL32: + fprintf(out, "value: [ data: %f size: %d type: %s ] %p\n", + self->r32, self->size, type_name[self->type], self); + break; + case VT_REAL64: + fprintf(out, "value: [ data: %g size: %d type: %s ] %p\n", + self->r64, self->size, type_name[self->type], self); + break; + case VT_STR: + fprintf(out, "value: [ data: '%s' size: %d type: %s ] %p\n", + (char *)self->ptr, self->size, type_name[self->type], + self); + break; + case VT_STR_INLINE: + fprintf(out, "value: [ data: '%s' size: %d type: %s ] %p\n", + (char *)self->data, self->size, type_name[self->type], + self); + break; + case VT_STR_OFF: + fprintf(out, "value: [ data: %llu size: %d type: %s ] %p\n", + self->u64, self->size, type_name[self->type], self); + break; + case VT_STR_CONST: + fprintf(out, "value: [ data: '%s' size: %d type: %s ] %p\n", + (const char *)self->ptr, self->size, + type_name[self->type], self); + break; + case VT_BLOB: + fprintf(out, "value: [ size: %d type: %s ] %p\n", + self->size, type_name[self->type], self); + dump_memory(out, (unsigned long)self->ptr, self->ptr, + self->size); + break; + case VT_BLOB_INLINE: + fprintf(out, "value: [ size: %d type: %s ] %p\n", + self->size, type_name[self->type], self); + dump_memory(out, (unsigned long)self->data, self->data, + self->size); + break; + case VT_BLOB_OFF: + fprintf(out, "value: [ data: %llu size: %d type: %s ] %p\n", + self->u64, self->size, type_name[self->type], self); + break; + case VT_BLOB_FILE: + fprintf(out, "value: [ data: '%s' size: %d type: %s ] %p\n", + (char *)self->data, self->size, type_name[self->type], + self); + break; + default: + // throw_unexpected(VALUE_TYPE); + fprintf(out, "value: [ size: %d type: %s ] %p\n", + self->size, type_name[self->type], self); + } +} + +/* ======================================================================= */ diff --git a/clib/src/vector.c b/clib/src/vector.c new file mode 100644 index 0000000..54f9981 --- /dev/null +++ b/clib/src/vector.c @@ -0,0 +1,662 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: vector.c + * Author: Shaun Wetzstein + * Descr: dynamic vector + * Note: + * Date: 08/29/10 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libclib.h" + +#include "vector.h" +#include "tree.h" +#include "tree_iter.h" +#include "mq.h" + +/* ======================================================================= */ + +/*! @cond */ +#define VECTOR_NODE_MAGIC "VCND" + +#define VECTOR_NODE_MAGIC_CHECK(m) ({ \ + bool rc = (((m)[0] != VECTOR_NODE_MAGIC[0]) || \ + ((m)[1] != VECTOR_NODE_MAGIC[1]) || \ + ((m)[2] != VECTOR_NODE_MAGIC[2]) || \ + ((m)[3] != VECTOR_NODE_MAGIC[3])); \ + rc; \ + }) + +typedef struct vector_node vector_node_t; + +struct vector_node { + uint8_t magic[4]; + + uint32_t address; + tree_node_t node; + + uint8_t data[]; +}; + +#define VECTOR_PAGE_MAX UINT16_MAX +#define VECTOR_PAGE_DIVISOR 32 + +#define __index_to_page(i,s) \ +({ \ + typeof(i) _p = ((i) / (s)); \ + _p; \ +}) + +#define __index_to_page_hashed(i,s) \ +({ \ + typeof(i) _h = int32_hash1(__index_to_page((i),(s))); \ + _h; \ +}) +/*! @endcond */ + +/* ======================================================================= */ + +static vector_node_t *__vector_find_page(vector_t * self, size_t idx) +{ + const void *hash; + hash = (const void *)__index_to_page_hashed(idx, self->hdr.elem_num); + + tree_node_t *node = tree_find(&self->tree, hash); + if (unlikely(node == NULL)) { + UNEXPECTED("'%d' index out of range", idx); + return NULL; + } + + return container_of(node, vector_node_t, node); +} + +static int __vector_shrink(vector_t * self) +{ + assert(self != NULL); + + vector_node_t *node = __vector_find_page(self, + vector_capacity(self) - 1); + assert(node != NULL); + + int rc = splay_remove(&self->tree, &node->node); + + free(node); + self->hdr.page_count--; + + return rc; +} + +static vector_node_t *__vector_grow(vector_t * self) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC)); + + vector_node_t *node = NULL; + int rc = posix_memalign((void **)&node, sizeof(void *), + self->hdr.page_size); + if (rc != 0) { + ERRNO(errno); + return NULL; + } + + memset(node, 0, self->hdr.page_size); + + node->magic[0] = VECTOR_NODE_MAGIC[0]; + node->magic[1] = VECTOR_NODE_MAGIC[1]; + node->magic[2] = VECTOR_NODE_MAGIC[2]; + node->magic[3] = VECTOR_NODE_MAGIC[3]; + + node->address = (uint32_t) node; + + size_t hash = __index_to_page_hashed(vector_capacity(self), + self->hdr.elem_num); + + tree_node_init(&node->node, (const void *)hash); + if (splay_insert(&self->tree, &node->node) < 0) { + free(node); + return NULL; + } + self->hdr.page_count++; + + return node; +} + +static int __vector_compare(const int i1, const int i2) +{ + return i1 - i2; +} + +/* ======================================================================= */ + +int vector_init3(vector_t * self, const char *name, size_t elem_size) +{ + size_t page_size = max(sysconf(_SC_PAGESIZE), + __round_pow2(elem_size * VECTOR_PAGE_DIVISOR)); + return vector_init4(self, name, elem_size, page_size); +} + +int vector_init4(vector_t * self, const char *name, size_t elem_size, + size_t page_size) +{ + assert(self != NULL); + + if (unlikely(MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC)) == false) + vector_delete(self); + + if (elem_size < VECTOR_ELEM_MIN || VECTOR_ELEM_MAX < elem_size) { + UNEXPECTED("'%d' elem_size out of range [%d..%d]", + elem_size, VECTOR_ELEM_MIN, VECTOR_ELEM_MAX); + return -1; + } + + page_size = __round_pow2(page_size); + if (page_size / elem_size < VECTOR_PAGE_DIVISOR) { + UNEXPECTED("'%d' page_size out of range [%d..%d]", + page_size, elem_size * VECTOR_PAGE_DIVISOR, + VECTOR_PAGE_MAX); + return -1; + } + + memset(self, 0, sizeof *self); + + self->hdr.id[IDENT_MAGIC_0] = VECTOR_MAGIC[IDENT_MAGIC_0]; + self->hdr.id[IDENT_MAGIC_1] = VECTOR_MAGIC[IDENT_MAGIC_1]; + self->hdr.id[IDENT_MAGIC_2] = VECTOR_MAGIC[IDENT_MAGIC_2]; + self->hdr.id[IDENT_MAGIC_3] = VECTOR_MAGIC[IDENT_MAGIC_3]; + + self->hdr.id[IDENT_MAJOR] = CLIB_MAJOR; + self->hdr.id[IDENT_MINOR] = CLIB_MINOR; + self->hdr.id[IDENT_PATCH] = CLIB_PATCH; + + if (__BYTE_ORDER == __LITTLE_ENDIAN) + self->hdr.id[IDENT_FLAGS] |= VECTOR_FLAG_LSB; + if (__BYTE_ORDER == __BIG_ENDIAN) + self->hdr.id[IDENT_FLAGS] |= VECTOR_FLAG_MSB; + + self->hdr.page_size = page_size; + self->hdr.elem_size = elem_size; + self->hdr.elem_num = (self->hdr.page_size - sizeof(vector_node_t)) / + self->hdr.elem_size; + + if (name != NULL && *name != '\0') + strncpy(self->hdr.name, name, sizeof(self->hdr.name)); + + tree_init(&self->tree, (compare_f) __vector_compare); + + return 0; +} + +int vector_delete(vector_t * self) +{ + if (unlikely(self == NULL)) + return 0; + + assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC)); + + tree_iter_t it; + tree_iter_init(&it, &self->tree, TI_FLAG_FWD); + + vector_node_t *node; + tree_for_each(&it, node, node) { + if (VECTOR_NODE_MAGIC_CHECK(node->magic)) { + UNEXPECTED("'%s' invalid or corrupt vector_node" + "object => '%.4s'", self->hdr.name, + node->magic); + return -1; + } + + if (splay_remove(&self->tree, &node->node) < 0) + return -1; + + memset(node, 0, sizeof(*node)); + free(node); + } + + self->hdr.page_count = self->hdr.size = 0; + + return 0; +} + +const void *vector_at(vector_t * self, size_t idx) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC)); + assert(idx < self->hdr.size); + + vector_node_t *node = __vector_find_page(self, idx); + return node->data + (self->hdr.elem_size * (idx % self->hdr.elem_num)); +} + +int vector_get3(vector_t * self, size_t idx, void *ptr) +{ + return vector_get4(self, idx, ptr, 1); +} + +int vector_get4(vector_t * self, size_t idx, void *ptr, size_t count) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC)); + + while (0 < count) { + memcpy(ptr, vector_at(self, idx), self->hdr.elem_size); + + idx++; + count--; + + ptr += self->hdr.elem_size; + } + + return 0; +} + +static inline int __vector_put(vector_t * self, size_t idx, const void *ptr) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC)); + + vector_node_t *node = __vector_find_page(self, idx); + assert(node != NULL); + + if (VECTOR_NODE_MAGIC_CHECK(node->magic)) { + UNEXPECTED("'%s' invalid or corrupt vector_node object => " + "'%.4s'", self->hdr.name, node->magic); + return -1; + } + + memcpy(node->data + (self->hdr.elem_size * (idx % self->hdr.elem_num)), + ptr, self->hdr.elem_size); + + return 0; +} + +int vector_put3(vector_t * self, size_t idx, const void *ptr) +{ + return vector_put4(self, idx, ptr, 1); +} + +int vector_put4(vector_t * self, size_t idx, const void *ptr, size_t count) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC)); + assert(idx < self->hdr.size); + + while (0 < count) { + if (__vector_put(self, idx, ptr) < 0) + return -1; + + idx++; + count--; + + ptr += self->hdr.elem_size; + } + + return 0; +} + +size_t vector_size1(vector_t * self) +{ + assert(self != NULL); + return self->hdr.size; +} + +int vector_size2(vector_t * self, size_t size) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC)); + + size_t pages = __index_to_page(size, self->hdr.elem_num) + 1; + + if (vector_pages(self) < pages) { + while (vector_pages(self) < pages) + (void)__vector_grow(self); + } else if (pages < vector_pages(self)) { + if (size <= 0) + vector_delete(self); + else + while (pages < vector_pages(self)) + if (__vector_shrink(self) < 0) + return -1; + } + + return self->hdr.size = size; +} + +size_t vector_pages(vector_t * self) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC)); + return self->hdr.page_count; +} + +size_t vector_capacity(vector_t * self) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC)); + return self->hdr.page_count * self->hdr.elem_num; +} + +size_t vector_elem_size(vector_t * self) +{ + assert(self != NULL); + assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC)); + return self->hdr.elem_size; +} + +ssize_t vector_save(vector_t * self, FILE * out) +{ + assert(self != NULL); + assert(out != NULL); + assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC)); + + int header_swap(vector_header_t * hdr) { + assert(hdr != NULL); + + if (hdr->id[IDENT_FLAGS] & VECTOR_FLAG_MSB) { + hdr->page_size = htobe32(hdr->page_size); + hdr->elem_size = htobe16(hdr->elem_size); + hdr->elem_num = htobe16(hdr->elem_num); + hdr->size = htobe32(hdr->size); + hdr->page_count = htobe32(hdr->page_count); + } else if (hdr->id[IDENT_FLAGS] & VECTOR_FLAG_LSB) { + hdr->page_size = htole32(hdr->page_size); + hdr->elem_size = htole16(hdr->elem_size); + hdr->elem_num = htole16(hdr->elem_num); + hdr->size = htole32(hdr->size); + hdr->page_count = htole32(hdr->page_count); + } else { + UNEXPECTED("'%s' invalid or corrupt flash object => " + "'%x'", hdr->name, hdr->id[IDENT_FLAGS]); + return -1; + } + + return 0; + } + + ssize_t save(vector_t * self, FILE * out) { + tree_iter_t it; + tree_iter_init(&it, &self->tree, TI_FLAG_FWD); + + ssize_t len = 0; + + vector_node_t *node; + tree_for_each(&it, node, node) { + if (VECTOR_NODE_MAGIC_CHECK(node->magic)) { + UNEXPECTED("'%s' invalid or corrupt vector_node" + "object => '%.4s'", self->hdr.name, + node->magic); + return -1; + } + + size_t rc; + + vector_node_t copy = *node; + + copy.address = 0; + copy.node.left = copy.node.right = NULL; + copy.node.parent = NULL; + + rc = fwrite((char *)©, 1, sizeof(copy), out); + if (rc != sizeof(copy)) { + if (ferror(out)) { + ERRNO(errno); + return -1; + } + } + len += rc; + + rc = fwrite((char *)node->data, 1, + self->hdr.page_size - sizeof(*node), out); + if (rc != self->hdr.page_size - sizeof(*node)) { + if (ferror(out)) { + ERRNO(errno); + return -1; + } + } + len += rc; + } + + return len; + } + + ssize_t total = 0; + + vector_header_t hdr = self->hdr; + if (header_swap(&hdr) < 0) + return -1; + + clearerr(out); + total = fwrite(&hdr, 1, sizeof(hdr), out); + if (total != sizeof(hdr)) { + if (ferror(out)) { + ERRNO(errno); + return -1; + } + } + + total += save(self, out); + + return total; +} + +ssize_t vector_load(vector_t * self, FILE * in) +{ + assert(self != NULL); + assert(in != NULL); + assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC)); + + int header_swap(vector_header_t * hdr) { + assert(hdr != NULL); + + if (hdr->id[IDENT_FLAGS] & VECTOR_FLAG_MSB) { + hdr->page_size = be32toh(hdr->page_size); + hdr->elem_size = be16toh(hdr->elem_size); + hdr->elem_num = be16toh(hdr->elem_num); + hdr->size = be32toh(hdr->size); + hdr->page_count = be32toh(hdr->page_count); + } else if (hdr->id[IDENT_FLAGS] & VECTOR_FLAG_LSB) { + hdr->page_size = le32toh(hdr->page_size); + hdr->elem_size = le16toh(hdr->elem_size); + hdr->elem_num = le16toh(hdr->elem_num); + hdr->size = le32toh(hdr->size); + hdr->page_count = le32toh(hdr->page_count); + } else { + UNEXPECTED("'%s' invalid or corrupt flash object => " + "'%x'", hdr->name, hdr->id[IDENT_FLAGS]); + return -1; + } + + return 0; + } + + vector_delete(self); + + clearerr(in); + ssize_t len = fread(&self->hdr, 1, sizeof(self->hdr), in); + if (len != sizeof(self->hdr)) { + if (feof(in)) { + UNEXPECTED("'%s' end-of-file encountered", + self->hdr.name); + return -1; + } + if (ferror(in)) { + ERRNO(errno); + return -1; + } + } + + if (header_swap(&self->hdr) < 0) + return -1; + + assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC)); + + tree_init(&self->tree, (compare_f) __vector_compare); + vector_node_t *node = NULL; + + for (size_t i = 0; i < vector_pages(self); i++) { + size_t rc = posix_memalign((void **)&node, sizeof(void *), + self->hdr.page_size); + if (rc != 0) { + ERRNO(errno); + return -1; + } + memset(node, 0, self->hdr.page_size); + + rc = fread((void *)node, 1, self->hdr.page_size, in); + if (rc != self->hdr.page_size) { + if (feof(in)) { + UNEXPECTED("'%s' end-of-file encountered", + self->hdr.name); + return -1; + } + if (ferror(in)) { + ERRNO(errno); + return -1; + } + } + + len += rc; + + if (VECTOR_NODE_MAGIC_CHECK(node->magic)) { + UNEXPECTED("'%s' invalid or corrupt vector_node " + "object => '%.4s'", self->hdr.name, + node->magic); + return -1; + } + + node->address = (uint32_t) node; + tree_node_init(&node->node, node->node.key); + splay_insert(&self->tree, &node->node); + + node = NULL; + } + + return len; +} + +int vector_send(vector_t * self, mqueue_t * mq) +{ + assert(self != NULL); + assert(mq != NULL); + assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC)); + + mqueue_send(mq, (char *)self, sizeof(*self)); + + tree_iter_t it; + tree_iter_init(&it, &self->tree, TI_FLAG_FWD); + + vector_node_t *node; + tree_for_each(&it, node, node) { + assert(!VECTOR_NODE_MAGIC_CHECK(node->magic)); + mqueue_send(mq, (char *)node, self->hdr.page_size); + } + + return 0; +} + +int vector_receive(vector_t * self, mqueue_t * mq) +{ + assert(self != NULL); + assert(mq != NULL); + assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC)); + + vector_delete(self); + + mqueue_attr_t attr = mqueue_getattr(mq); + + vector_node_t *node = NULL; + size_t rc = posix_memalign((void **)&node, attr.mq_msgsize, + attr.mq_msgsize); + if (rc != 0) { + ERRNO(errno); + return -1; + } + + ssize_t len = mqueue_receive(mq, (void *)node, attr.mq_msgsize); + assert(0 < len); + + assert(!MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC)); + + memcpy(self, (void *)node, sizeof(*self)); + tree_init(&self->tree, (compare_f) __vector_compare); + + for (size_t i = 0; i < vector_pages(self); i++) { + rc = posix_memalign((void **)&node, attr.mq_msgsize, + attr.mq_msgsize); + if (rc != 0) { + ERRNO(errno); + return -1; + } + + len = mqueue_receive(mq, (void *)node, attr.mq_msgsize); + assert(0 < len); + + assert(!VECTOR_NODE_MAGIC_CHECK(node->magic)); + + node->address = (uint32_t) node; + tree_node_init(&node->node, node->node.key); + splay_insert(&self->tree, &node->node); + + node = NULL; + } + + return 0; +} + +void vector_dump(vector_t * self, FILE * out) +{ + if (out == NULL) + out = stdout; + + if (self != NULL) { + assert(!unlikely(MAGIC_CHECK(self->hdr.id, VECTOR_MAGIC))); + + fprintf(out, "%s: page_size: %d elem_size: %d elem_num: %d -- " + "size: %d capacity: %d -- page_count: %d\n", + self->hdr.name, self->hdr.page_size, + self->hdr.elem_size, self->hdr.elem_num, + vector_size(self), vector_capacity(self), + self->hdr.page_count); + + tree_iter_t it; + tree_iter_init(&it, &self->tree, TI_FLAG_FWD); + + vector_node_t *node; + tree_for_each(&it, node, node) { + fprintf(out, "magic[%.4s] node: %p data: %p -- " + "address: %x\n", node->magic, &node->node, + node->data, node->address); + + dump_memory(out, (unsigned long)node, node, + self->hdr.page_size); + } + } +} + +/* ======================================================================= */ diff --git a/clib/src/vector_iter.c b/clib/src/vector_iter.c new file mode 100644 index 0000000..4c95fd8 --- /dev/null +++ b/clib/src/vector_iter.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: vector_iter.c + * Author: Shaun Wetzstein + * Descr: dynamic array + * Note: + * Date: 10/22/10 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libclib.h" +#include "vector_iter.h" + +/* ======================================================================= */ + +static inline const void *__vector_iter_bwd(vector_iter_t * self) +{ + size_t low = 0; + const void *ret = NULL; + + if (low < self->idx) + self->idx--; + + if (low < self->idx) + ret = vector_at(self->vector, self->idx); + + return ret; +} + +static inline const void *__vector_iter_fwd(vector_iter_t * self) +{ + size_t high = vector_size(self->vector); + const void *ret = NULL; + + if (self->idx < high) + self->idx++; + + if (self->idx < high) + ret = vector_at(self->vector, self->idx); + + return ret; +} + +int vector_iter_init(vector_iter_t * self, vector_t * vector, uint32_t flags) +{ + assert(self != NULL); + assert(vector != NULL); + + self->flags = flags; + self->vector = vector; + + if (self->flags & VI_FLAG_BWD) { + self->idx = vector_size(self->vector); + __vector_iter_bwd(self); + } else { + self->idx = 0; + } + + return 0; +} + +int vector_iter_clear(vector_iter_t * self) +{ + assert(self != NULL); + + if (self->flags & VI_FLAG_BWD) + self->idx = vector_size(self->vector); + else + self->idx = 0; + + self->vector = NULL; + return 0; +} + +const void *vector_iter_elem(vector_iter_t * self) +{ + assert(self != NULL); + + if (vector_capacity(self->vector) <= self->idx) { + UNEXPECTED("'%d' index out-of-range", self->idx); + return NULL; + } + + if (vector_size(self->vector) <= self->idx) + return NULL; + + return vector_at(self->vector, self->idx); +} + +const void *vector_iter_inc1(vector_iter_t * self) +{ + return vector_iter_inc2(self, 1); +} + +const void *vector_iter_inc2(vector_iter_t * self, size_t count) +{ + assert(self != NULL); + + const void *ret = NULL; + + for (size_t i = 0; i < count; i++) { + if (self->flags & VI_FLAG_BWD) + ret = __vector_iter_bwd(self); + else + ret = __vector_iter_fwd(self); + } + + return ret; +} + +const void *vector_iter_dec1(vector_iter_t * self) +{ + return vector_iter_dec2(self, 1); +} + +const void *vector_iter_dec2(vector_iter_t * self, size_t count) +{ + assert(self != NULL); + + const void *ret = NULL; + + for (size_t i = 0; i < count; i++) { + if (self->flags & VI_FLAG_BWD) + ret = __vector_iter_fwd(self); + else + ret = __vector_iter_bwd(self); + } + + return ret; +} + +size_t vector_iter_pos1(vector_iter_t * self) +{ + assert(self != NULL); + return self->idx; +} + +int vector_iter_pos2(vector_iter_t * self, size_t pos) +{ + assert(self != NULL); + + if (vector_size(self->vector) <= pos) { + UNEXPECTED("'%d' index out-of-range", pos); + return -1; + } + + self->idx = pos; + return 0; +} + +/* ======================================================================= */ diff --git a/clib/src/watch.c b/clib/src/watch.c new file mode 100644 index 0000000..6b78533 --- /dev/null +++ b/clib/src/watch.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: watch.c + * Author: Shaun Wetzstein + * Descr: + * Note: + * Date: 10/03/10 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" +#include "nargs.h" +#include "watch.h" + +#define WATCH_PAGE_SIZE 64 +#define WATCH_EVENT_SIZE 64 + +/* ======================================================================= */ + +const char *__watch_msg[] = { + "watch: unexpected NULL self pointer", + "watch: unexpected NULL callback structure", +}; + +#define WATCH_NULL (__watch_msg[0]) +#define WATCH_CALLBACK_NULL (__watch_msg[1]) + +/* ======================================================================= */ + +void watch_init(watch_t * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(WATCH_NULL); + + if (self->fd != -1) + close(self->fd), self->fd = -1; + + self->fd = inotify_init1(IN_CLOEXEC); + if (unlikely(self->fd == -1)) + throw_errno(errno); + + array_init(&self->callbacks, sizeof(watch_callback_t), WATCH_PAGE_SIZE); +} + +void watch_delete(watch_t * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(WATCH_NULL); + close(self->fd), self->fd = -1; + array_delete(&self->callbacks); +} + +int watch_fileno(watch_t * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(WATCH_NULL); + return self->fd; +} + +uint32_t watch_add(watch_t * self, const char *path, uint32_t events, + watch_callback_t * cb) +{ + if (unlikely(self == NULL)) + throw_unexpected(WATCH_NULL); + + if (access(path, F_OK) != 0) + throw_errno(errno); + + uint32_t wd = inotify_add_watch(self->fd, path, events); + if (unlikely((int)wd == -1)) + throw_errno(errno); + + if (cb != NULL) + array_put(&self->callbacks, wd, cb, 1); + + return wd; +} + +void watch_remove(watch_t * self, uint32_t wd) +{ + if (unlikely(self == NULL)) + throw_unexpected(WATCH_NULL); + + int rc = inotify_rm_watch(self->fd, wd); + if (unlikely(rc == -1)) + throw_errno(errno); + + array_status(&self->callbacks, wd, false); +} + +void watch_wait(watch_t * self) +{ + if (unlikely(self == NULL)) + throw_unexpected(WATCH_NULL); + + /* FIX ME */ + + watch_event_t events[WATCH_EVENT_SIZE]; + + ssize_t n = read(self->fd, events, sizeof events); + printf("n[%d]\n", n); + + for (ssize_t i = 0; i < (ssize_t) (n / sizeof *events); i++) + printf("%d: wd[%d] mask[%x] cookie[%x] name[%.*s]\n", + i, events[i].wd, events[i].mask, events[i].cookie, + events[i].len, events[i].name); +} + +/* ======================================================================= */ diff --git a/clib/stack.h b/clib/stack.h new file mode 100644 index 0000000..521d55b --- /dev/null +++ b/clib/stack.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! + * @file stack.h + * @brief stack container + * @details + * A stack is a data structure (container) used for collecting a sequence of elements. + * stack allow for efficient insertion, removal and retreival of elements. + * + * @details For example, + * @code + * #include + * #include + * + * int main(const int argc, const char * argv[]) { + * typedef struct { + * stack_node_t node; + * int i; + * float f; + * } data_t; + * + * slab_t s; + * slab_init(&s, sizeof(data_t), 0); + * + * stack_t a; + * stack_init(&a); + * + * int i; + * for (i=0; i<10; i++) { + * data_t * d = (data_t *)slab_alloc(&s); + * + * d->i = i; + * d->f = (float)i; + * + * stack_add_tail(&l, &d->node); + * } + * + * data_t * d; + * stack_for_each(&l, d, node) { + * printf("i: %d f: %f\n", d->i, d->f); + * } + * + * stack_dump(&l, stdout); + * slab_delete(&s); + * + * return 0; + * } + * @endcode + * @author Shaun Wetzstein + * @date 2010-2011 + */ + +#ifndef __STACK_H__ +#define __STACK_H__ + +#include "list.h" +#include "type.h" + +typedef list stack; +typedef list_node stack_node; + +#define stack_init(s) list_init((list *)(s)) +#define stack_push(s,n) list_add_tail((list *)(s),(n)) +#define stack_pop(s) list_remove_tail((list *)(s)) +#define stack_empty(s) list_empty((list *)(s)) +#define stack_dump(s,o) list_dump((list *)(s),(o)) + +#define stack_entry(n, t, m) list_entry((n),(t),(m)) +#define stack_top(s) list_head((list *)(s)) +#define stack_bottom(s) list_tail((list *)(s)) + +#define stack_for_each(s, i, m) \ + for (i = container_of_var(s->node.next, i, m); \ + &i->m != &(s)->node; \ + i = container_of_var(i->m.next, i, m)) + +#define stack_for_each_safe(s, i, n, m) \ + for (i = container_of_var((s)->node.next, i, m), \ + n = container_of_var(i->m.next, i, m); \ + &i->m != &(s)->node; \ + i = n, n = container_of_var(i->m.next, i, m)) + +#endif /* __STACK_H__ */ diff --git a/clib/table.h b/clib/table.h new file mode 100644 index 0000000..b2190b5 --- /dev/null +++ b/clib/table.h @@ -0,0 +1,272 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! + * @file table.h + * @brief Table Container + * @details For example, + * @code + * #include + * #include + * + * int main(const int argc, const char * argv[]) { + * table t; + * table_init(&t, COLS); + * + * const char * str = "column0 is a really long string"; + * + * table_name(&t, "table name"); + * table_name(&t, 0, str); + * table_name(&t, 1, "column1"); + * table_name(&t, 2, "column2"); + * table_name(&t, 3, "column3"); + * table_name(&t, 4, "column4"); + * + * printf("%s\n", table_name(&t, 0)); + * + * size_t r, c; + * value v; + * + * for (r=0; r %d\n", r, c, r * COLS + c); + * } + * } + * + * table_iter it; + * table_iter_init(&it, &t); + * + * value * val; + * table_for_each(&it, val) + * value_dump(val, stdout); + * + * table_delete(&t); + * + * return 0; + * } + * @endcode + * @author Shaun Wetzstein + * @date 2010-2011 + */ + +#ifndef __TABLE_H__ +#define __TABLE_H__ + +#include +#include + +#include "compare.h" +#include "vector.h" +#include "value.h" +#include "mqueue.h" + +/* ======================================================================= */ + +#define TABLE_NAME_SIZE 52 //!< Maximum table name size (in bytes) + +#define TABLE_MAGIC "TBLE" //!< Table magic number + +#define TABLE_FLAG_LSB 0x01 //!< little-endian header data +#define TABLE_FLAG_MSB 0x02 //!< big-endian header data + +#define INIT_TABLE_HEADER {INIT_IDENT,{0,},0} +#define INIT_TABLE {INIT_TABLE_HEADER,INIT_VECTOR,INIT_VECTOR,INIT_VECTOR} + +/*! + * @brief table container header + */ +struct table_header { + ident_t id; //!< Identification + char name[TABLE_NAME_SIZE]; //!< Table name + + size_t col_nr; //!< Number of columns +}; +typedef struct table_header table_header_t; //!< Alias for the @em table_header class + +/*! + * @brief table container + */ +struct table { //!< The Table class + table_header_t hdr; //!< Table metadata + + vector_t table; //!< @private + vector_t string; //!< @private + vector_t blob; //!< @private +}; +typedef struct table table_t; //!< Alias for the @em table class + +/* ==================================================================== */ + +/*! + * @brief Constructs a @em table object + * @memberof table + * @param self [in] table object @em self pointer + * @param name [in] table object @em name string + * @param col_nr [in] Number of columns + * @return None + */ +extern int table_init(table_t *, const char *, size_t) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Destructs an @em table container object + * @details Deallocate all backing storage associated with this \em table object + * @memberof table + * @param self [in] table object @em self pointer + * @return None + */ +extern int table_delete(table_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Copy content from the @em table + * @memberof table + * @param self [in] table object @em self pointer + * @param row [in] Row number + * @param col [out] Column number + * @return Reference to @em value on success, NULL otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern value_t *table_get(table_t *, size_t, size_t) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @fn const void * table_row(table_iter_t * self, size_t row_nr, + * [ value_t * row ]) + */ +/*! @cond */ +#define table_row(...) STRCAT(table_row, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern value_t *table_row2(table_t *, size_t) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern int table_row3(table_t *, size_t, value_t *) +/*! @cond */ +__nonnull((1, 3)) /*! @endcond */ ; +/* !endcond */ + +extern value_t *table_column(table_t *, value_t *, size_t) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Assign new content to the @em table + * @memberof table + * @param self [in] table object @em self pointer + * @param row [in] Row number + * @param col [in] Column number + * @param value [in] Reference to source @em value + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int table_put(table_t *, size_t, size_t, value_t *) +/*! @cond */ +__nonnull((1, 4)) /*! @endcond */ ; + +/*! + * @fn const char * table_name(table_t * self, size_t col, const char * name, + * size_t len) + * @brief Set or return the name of a column within a @em table + * @note If the fourth parameter is omitted, the @em name is assumed to contain + * a trailing NULL-byte + * @note If the third parameter is omitted, the column's current name is + * returned + * @memberof table + * @param self [in] table object @em self pointer + * @param col [in] Column number + * @param name [in] New column name string (optional) + * @param len [in] New column name string length (optional) + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ + +/*! @cond */ +#define table_name(...) STRCAT(table_name, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern const char *table_name2(table_t *, size_t) __nonnull((1)); + +#define table_name3(t,c,s) \ + choose_expr(const_expr(s), \ + _table_name3((t),(c),(const char *)(s)), \ + _table_name3((t),(c),(const char *)(s))) + +extern int _table_name3(table_t *, size_t, const char *) __nonnull((1, 3)); + +#define table_name4(t,c,s,l) \ + choose_expr(const_expr(s), \ + _table_name4((t),(c),(const char *)(s),(l)), \ + _table_name4((t),(c),(const char *)(s),(l))) + +extern int _table_name4(table_t *, size_t, const char *, size_t) +__nonnull((1, 3)); +/*! @endcond */ + +/*! + * @brief Return the number of rows in the @em table + * @memberof table + * @param self [in] table object @em self pointer + * @return None + */ +extern size_t table_rows(table_t *) /*! @cond */ __nonnull((1)) /*! @endcond */ +; + +/*! + * @brief Return the number of column in the @em table + * @memberof table + * @param self [in] table object @em self pointer + * @return None + */ +extern size_t table_columns(table_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +extern int table_serialize(table_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +extern int table_deserialize(table_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +extern ssize_t table_save(table_t *, FILE *) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +extern ssize_t table_load(table_t *, FILE *) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +extern void table_print(table_t *, FILE *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +extern void table_dump(table_t *, FILE *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +extern int table_sort(table_t *, compare_f) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/* ==================================================================== */ + +#endif /* __TABLE_H__ */ diff --git a/clib/table_iter.h b/clib/table_iter.h new file mode 100644 index 0000000..1b01e0d --- /dev/null +++ b/clib/table_iter.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! + * @file table_iter.h + * @brief Table Iterator + * @code + * ... + * table_iter_t it; + * table_iter_init(&it, &t); + * + * value_t * val; + * table_for_each(&it, val) { + * if (value_type(val) != VT_UNKNOWN) + * value_dump(val, stdout); + * } + * ... + * @endcode + * @author Shaun Wetzstein + * @date 2010-2011 + */ + +#ifndef __TABLE_ITER_H__ +#define __TABLE_ITER_H__ + +#include +#include + +#include "attribute.h" + +#include "value.h" +#include "table.h" +#include "vector_iter.h" + +/* ======================================================================= */ + +typedef struct table_iter table_iter_t; //!< Alias for the @em table class + +/*! + * @brief Table iterator + */ +struct table_iter { + table_t *table; //!< Reference to the target table object + vector_iter_t it; //!< Current position of the table + uint32_t flags; //!< Iterator configuration flags +}; + +/* ==================================================================== */ + +#define TI_FLAG_NONE 0x00000000 //!< No flag mask +#define TI_FLAG_FWD 0x00000001 //!< Forward (FWD) flag mask +#define TI_FLAG_BWD 0x00000002 //!< Backwards (BWD) flag mask +#define TI_FLAG_MASK 0x00000003 //!< All flag mask + +/*! + * @brief Initializes an @em table_iter iterator object + * @memberof table_iter + * @param self [in] table_iter object @em self pointer + * @param table [in] table container object to iterate + * @param flags [in] iterator configuration flags + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + * @throws UNEXPECTED if @em table pointer is NULL + */ +extern int table_iter_init(table_iter_t *, table_t *, uint32_t) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Resets an @em table iterator object + * @memberof table_iter + * @param self [in] table_iter object @em self pointer + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int table_iter_clear(table_iter_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return a pointer to @em value element at the current iterator position + * @memberof table_iter + * @param self [in] table_iter object @em self pointer + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern const value_t *table_iter_elem(table_iter_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Increment the position of an @em table iterator + * @memberof table_iter + * @param self [in] table_iter object @em self pointer + * @param count [in] Number of positions to increment + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +#define table_iter_inc(...) STRCAT(table_iter_inc, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern const value_t *table_iter_inc1(table_iter_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern const value_t *table_iter_inc2(table_iter_t *, size_t) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Decrement the position of an @em table iterator + * @memberof table_iter + * @param self [in] table_iter object @em self pointer + * @param count [in] Number of positions to decrement + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +#define table_iter_dec(...) STRCAT(table_iter_dec, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern const value_t *table_iter_dec1(table_iter_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern const value_t *table_iter_dec2(table_iter_t *, size_t) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @fn const void * table_iter_pos(table_iter_t * self, size_t pos) + * @brief Return or set the iterator position witin the @em table + * @details If the second (2nd) parameter is omitted, this functions returns + * the current position. + * @memberof table_iter + * @param self [in] table_iter object @em self pointer + * @param pos [in] new iterator position (optional) + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define table_iter_pos(...) STRCAT(table_iter_pos, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern size_t table_iter_pos1(table_iter_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern int table_iter_pos2(table_iter_t *, size_t) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @def table_for_each_row(it, i) + * @hideinitializer + * @brief Table for-each-row element algorithm + * @param it [in] Table iterator object + * @param r [in] Value element variable (row) + */ +#define table_for_each(it,r) \ + for (r = (typeof(r))table_iter_elem((it)); \ + r != NULL; \ + r = (typeof(r))table_iter_inc((it))) + +/* ==================================================================== */ + +#endif /* __TABLE_ITER_H__ */ diff --git a/clib/test/array.c b/clib/test/array.c new file mode 100644 index 0000000..825db06 --- /dev/null +++ b/clib/test/array.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include + +#include +#include + +#define SIZE 2550 + +int main(const int argc, const char * argv[]) { + array_t a; + array_init(&a, 4, 1024); + +printf("size[%d]\n", array_size(&a)); +printf("pages[%d]\n", array_pages(&a)); +printf("capacity[%d]\n", array_capacity(&a)); +printf("low[%u] high[%u]\n", array_low(&a), array_high(&a)); +exit(1); + + array_dump(&a, stdout); + + array_put(&a, 52, (uint32_t[]){52}); + array_put(&a, 53, (uint32_t[]){53}); + array_put(&a, 167, (uint32_t[]){167}); + array_put(&a, 223, (uint32_t[]){223}); + array_put(&a, 78, (uint32_t[]){78}); + array_put(&a, 205, (uint32_t[]){205}); + array_put(&a, 183, (uint32_t[]){183}); + array_put(&a, 150, (uint32_t[]){150}); + array_put(&a, 90, (uint32_t[]){90}); + array_put(&a, 66, (uint32_t[]){66}); + array_put(&a, 91, (uint32_t[]){91}); + array_put(&a, 45, (uint32_t[]){45}); + array_put(&a, 211, (uint32_t[]){211}); + + uint32_t arr[] = {985,986,987,988,990,991,992,993,994}; + array_put(&a, 985, arr, 9); + + array_iter_t it; + array_iter_init(&it, &a, AI_FLAG_FWD); + + uint32_t * j; + array_for_each(&it, j) { + printf("arr[%d]\n", *j); + } + + array_dump(&a, stdout); + array_delete(&a); + + return 0; +} diff --git a/clib/test/checksum.c b/clib/test/checksum.c new file mode 100644 index 0000000..c615cca --- /dev/null +++ b/clib/test/checksum.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include +#include + +int main(void) +{ + unsigned int i; + uint8_t *buf = malloc(32); + uint8_t align[8] = { 0x01, 0x02, 0x04, 0x08, + 0x10, 0x20, 0x40, 0x80 }; + uint8_t unaligned1[9] = { 0x11, 0x11, 0x11, 0x11, + 0x22, 0x22, 0x22, 0x22, 0x33 }; + uint8_t unaligned2[10] = { 0x11, 0x11, 0x11, 0x11, + 0x22, 0x22, 0x22, 0x22, 0x33, 0x33 }; + uint8_t unaligned3[11] = { 0x11, 0x11, 0x11, 0x11, + 0x22, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33 }; + + memcpy(buf, align, sizeof(align)); + uint32_t csum = memcpy_checksum(NULL, buf, sizeof(align)); + if (csum != 0x11224488) { + printf("fail %d a:%08x e:%08x\n", __LINE__, csum, 0x11224488); + return 1; + } + + memcpy(buf, unaligned1, sizeof(unaligned1)); + csum = memcpy_checksum(NULL, buf, sizeof(unaligned1)); + if (csum != 0x00333333) { + printf("fail %d a:%08x e:%08x\n", __LINE__, csum, 0x00333333); + return 1; + } + + memcpy(buf, unaligned2, sizeof(unaligned2)); + csum = memcpy_checksum(NULL, buf, sizeof(unaligned2)); + if (csum != 0x00003333) { + printf("fail %d a:%08x e:%08x\n", __LINE__, csum, 0x00003333); + return 1; + } + + memcpy(buf, unaligned3, sizeof(unaligned3)); + csum = memcpy_checksum(NULL, buf, sizeof(unaligned3)); + if (csum != 0x00000033) { + printf("fail %d a:%08x e:%08x\n", __LINE__, csum, 0x00000033); + return 1; + } + + char dst[16]; + csum = memcpy_checksum(dst, align, sizeof(align)); + if (csum != 0x11224488) { + printf("fail %d a:%08x e:%08x\n", __LINE__, csum, 0x11224488); + return 1; + } + + csum = memcpy_checksum(dst, unaligned1, sizeof(unaligned1)); + for (i = 0; i < sizeof(unaligned1); i++) { + if (dst[i] != unaligned1[i]) + printf("fail %d byte %i a:%08x e:%08x\n", + __LINE__, i, dst[i], unaligned1[i]); + } + if (csum != 0x00333333) { + printf("fail %d a:%08x e:%08x\n", __LINE__, csum, 0x00333333); + return 1; + } + + csum = memcpy_checksum(dst, unaligned2, sizeof(unaligned2)); + for (i = 0; i < sizeof(unaligned2); i++) { + if (dst[i] != unaligned2[i]) + printf("fail %d byte %i a:%08x e:%08x\n", + __LINE__, i, dst[i], unaligned2[i]); + } + if (csum != 0x00003333) { + printf("fail %d a:%08x e:%08x\n", __LINE__, csum, 0x00003333); + return 1; + } + + csum = memcpy_checksum(dst, unaligned3, sizeof(unaligned3)); + for (i = 0; i < sizeof(unaligned3); i++) { + if (dst[i] != unaligned3[i]) + printf("fail %d byte %i a:%08x e:%08x\n", + __LINE__, i, dst[i], unaligned3[i]); + } + if (csum != 0x00000033) { + printf("fail %d a:%08x e:%08x\n", __LINE__, csum, 0x00000033); + return 1; + } + +#if 0 + exception_t ex; + try { + (void)memcpy_checksum(NULL, &align[1], sizeof(align) - 1); + } else (ex) { + printf("Hey, what's this in the weeds? It's a baby, awesome!\n"); + } end_try; + +#endif + + return 0; +} diff --git a/clib/test/dispatch.c b/clib/test/dispatch.c new file mode 100644 index 0000000..8851c55 --- /dev/null +++ b/clib/test/dispatch.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include +#include + +int callback(dispatch_event_t * ev, void * ptr) { + char buf[256]; + int rc; + + rc = read(ev->fd, buf, sizeof buf); + printf("rc[%d]\n", rc); + + printf("fd[%x]\n", ev->fd); + printf("events[%x]\n", ev->events); + + return 0; +} + +int main(const int argc, const char * argv[]) { + watch_t w; + watch_init(&w); + + watch_callback_t wc = { + .data = NULL, + .func = NULL, + }; + + watch_add(&w, "/dev/mqueue", IN_CREATE | IN_ATTRIB, &wc); + + dispatch_t d; + dispatch_init(&d); + + dispatch_callback_t dc = { + .data = NULL, + .func = callback, + }; + + dispatch_add(&d, watch_fileno(&w), EPOLLIN, &dc); + dispatch_wait(&d); + + return 0; +} diff --git a/clib/test/ecc.c b/clib/test/ecc.c new file mode 100644 index 0000000..90760d7 --- /dev/null +++ b/clib/test/ecc.c @@ -0,0 +1,220 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include +#include + +#include +int test_sfc_ecc() +{ + uint64_t in[] = { 0x3131313131313131, 0x3232323232323232, + 0x3333333333333333, 0x3434343434343434, + 0x3535353535353535, 0x3636363636363636, + 0x3737373737373737, 0x3838383838383838, + 0x3132333435363738, 0x3938373635343332, + }; + printf("Function: test_sfc_ecc\n"); + printf("IN [\n"); + for (uint i=0; i +#include +#include +#include +#include +#include + +#include +#include +#include + +extern char * program_invocation_short_name; + +#define FOO_MAJOR 0x01 +#define FOO_MINOR 0x00 +#define FOO_PATCH 0x00 +#define FOO_VER VER(FOO_MAJOR, FOO_MINOR, FOO_PATCH) + +#define FOO_UNEXPECTED(f, ...) ({ \ + UNEXPECTED(f, ##__VA_ARGS__); \ + VERSION(FOO_VER, "%s", program_invocation_short_name); \ + }) + + +int main (int argc, char * argv[]) { + ERRNO(EINVAL); + FOO_UNEXPECTED("cannot frob the ka-knob"); + + goto error; + + if (false) { + err_t * err = NULL; +error: + while ((err = err_get()) != NULL) { + switch (err_type(err)) { + case ERR_VERSION: + fprintf(stderr, "%s: %s : %s(%d) : v%d.%02d.%04d %.*s\n", + basename((char *)argv[0]), + err_type_name(err), basename(err_file(err)), err_line(err), + VER_TO_MAJOR(err_code(err)), VER_TO_MINOR(err_code(err)), + VER_TO_PATCH(err_code(err)), + err_size(err), (char *)err_data(err)); + break; + default: + fprintf(stderr, "%s: %s : %s(%d) : (code=%d) %.*s\n", + basename((char *)argv[0]), + err_type_name(err), basename(err_file(err)), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + } + } + } + + return 0; +} + diff --git a/clib/test/exc_throw.c b/clib/test/exc_throw.c new file mode 100644 index 0000000..de06241 --- /dev/null +++ b/clib/test/exc_throw.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include + +#include + +#define EXCEPTION_FOO 10 + +void foo() { + throw(EXCEPTION_FOO, ex.data, ex.size); +} + +int main() { + exception_t ex; + + try { + printf("try block: BEFORE foo\n"); + foo(); + printf("try block: AFTER foo <-- should not get here\n"); + } catch (EXCEPTION_FOO, ex) { + printf("main: CAUGHT %s(%d) EXCEPTION_FOO data[%d]\n", + ex.file, ex.line, *(int *)ex.data); + } else (ex) { + printf("main: CAUGHT %s(%d) data[%d]\n", + ex.file, ex.line, *(int *)ex.data); + } end + + return 0; +} + diff --git a/clib/test/exception.c b/clib/test/exception.c new file mode 100644 index 0000000..8a8e16f --- /dev/null +++ b/clib/test/exception.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include + +#include +#include + +#define EXCEPTION_FOO 1 +#define EXCEPTION_BAR 2 +#define EXCEPTION_TAZ 3 + +int bar(int i) { + int * data = MALLOC(sizeof *data); + *data = i; + + throw_bytes(EXCEPTION_BAR, data, sizeof *data); + + return 0; +} + +int foo(int i) { + + exception_t ex; + try { + printf("%s: %d\n", __func__, __LINE__); + bar(i); + } catch (EXCEPTION_BAR, ex) { + printf("foo: CAUGHT %s(%d) EXCEPTION_BAR data[%d]\n", + ex.file, ex.line, *(int *)ex.data); + throw_bytes(EXCEPTION_FOO, ex.data, ex.size); + } end_try + + throw_bytes(EXCEPTION_FOO, "this is a test", 14); + + printf("%s: %d\n", __func__, __LINE__); + + return 0; +} + +int main(void) { + exception_t ex; + + try { + printf("%s: %d\n", __func__, __LINE__); + foo(1); + printf("try block: AFTER foo <-- should not get here\n"); + } catch (EXCEPTION_FOO, ex) { + printf("main: CAUGHT %s(%d) EXCEPTION_FOO data[%d]\n", + ex.file, ex.line, *(int *)ex.data); + } else (ex) { + printf("main: CAUGHT %s(%d) data[%d]\n", + ex.file, ex.line, *(int *)ex.data); + } end_try + + return 0; +} diff --git a/clib/test/heap.c b/clib/test/heap.c new file mode 100644 index 0000000..db7008e --- /dev/null +++ b/clib/test/heap.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include + +int main(const int argc, const char * argv[]) { + heap_t h; + heap_init(h, 0, 0); + + void * p5 = heap_alloc(&h, 5); + void * p9 = heap_alloc(&h, 9); + void * p13 = heap_alloc(&h, 13); + + heap_free(&h, p13); + heap_free(&h, p5); + heap_free(&h, p9); + + return 0; +} diff --git a/clib/test/list.c b/clib/test/list.c new file mode 100644 index 0000000..a93f6a7 --- /dev/null +++ b/clib/test/list.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include +#include +#include + +struct data { + list_node_t node; + int i; + float f; +}; +typedef struct data data_t; + +int main (void) { +// slab_t s = INIT_SLAB; +// slab_init(&s, "my_slab", sizeof(data_t), 0); + + list_t l = INIT_LIST; + list_init(&l); + + int i; + for (i=0; i<10; i++) { +// data_t * d = (data_t *)slab_alloc(&s); + data_t * d = (data_t *)malloc(sizeof(*d)); + + d->i = i; + d->f = (float)i; + + list_add_tail(&l, &d->node); + } + + list_iter_t it; + list_iter_init(&it, &l, LI_FLAG_FWD); + + data_t * d; + list_for_each(&it, d, node) { + printf("i: %d f: %f\n", d->i, d->f); + } + + while (list_empty(&l) == false) { + data_t * d = container_of(list_remove_tail(&l), data_t, node); + printf("i: %d f: %f\n", d->i, d->f); + } + +// slab_dump(&s, stdout); +// slab_delete(&s); + + return 0; +} + diff --git a/clib/test/map.c b/clib/test/map.c new file mode 100644 index 0000000..2501a32 --- /dev/null +++ b/clib/test/map.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include +#include +#include + +struct data { + map_node_t node; + int i; + float f; +}; +typedef struct data data_t; + +int main (void) { + slab_t s = INIT_SLAB; + slab_init(&s, "my_slab", sizeof(data_t), 0); + + int compare(const int i1, const int i2) { + return i1 - i2; + } + + map_t m; + map_init(&m, (compare_f)compare); + + int i; + for (i=0; i<100; i++) { + data_t * d = (data_t *)slab_alloc(&s); + + printf("i[%d]\n", i); + + d->i = i; + d->f = (float)i; + + map_node_init(&d->node, (const void *)(d->i)); + map_insert(&m, &d->node); + } + +#if 1 + i = 6; + map_node_t * n = map_find(&m, (const void *)(i)); + map_remove(&m, n); + n = map_find(&m, (const void *)i); + + i = 2; + map_find(&m, (const void *)i); + i = 8; + map_find(&m, (const void *)i); +#endif + + map_dump(&m, stdout); + slab_delete(&s); + + return 0; +} + diff --git a/clib/test/mq.c b/clib/test/mq.c new file mode 100644 index 0000000..aaa9eab --- /dev/null +++ b/clib/test/mq.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define SIZE 2550 + +int main(void) { + vector_t a = INIT_VECTOR; + vector_init(&a, "my_vector", 4, 1024); + + vector_put(&a, 52, (uint32_t[]){52}); + vector_put(&a, 53, (uint32_t[]){53}); + vector_put(&a, 167, (uint32_t[]){167}); + vector_put(&a, 223, (uint32_t[]){223}); + vector_put(&a, 78, (uint32_t[]){78}); + vector_put(&a, 205, (uint32_t[]){205}); + vector_put(&a, 183, (uint32_t[]){183}); + vector_put(&a, 150, (uint32_t[]){150}); + vector_put(&a, 90, (uint32_t[]){90}); + vector_put(&a, 66, (uint32_t[]){66}); + vector_put(&a, 91, (uint32_t[]){91}); + vector_put(&a, 45, (uint32_t[]){45}); + vector_put(&a, 211, (uint32_t[]){211}); + uint32_t arr[] = {55,56,57,58,59,60,61,62,63}; + vector_put(&a, 985, arr, 9); + + vector_iter_t it; + vector_iter_init(&it, &a, VI_FLAG_FWD); + + uint32_t * j; + vector_for_each(&it, j) { + printf("XXX i[%d]\n", *j); + } + + vector_dump(&a, stdout); + + mqueue_t mq = INIT_MQUEUE; + mqueue_init(&mq, "dbs"); + mqueue_create(&mq, gettid()); + + vector_send(&a, &mq); + vector_delete(&a); + + sleep(1); + + mqueue_delete(&mq); + + return 0; +} diff --git a/clib/test/slab.c b/clib/test/slab.c new file mode 100644 index 0000000..721c98f --- /dev/null +++ b/clib/test/slab.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include + +#define SIZE 6500 +#define ALLOC 16 +#define PAGE 4096 + +int main(void) { + slab_t s = INIT_SLAB; + slab_init(&s, "my_slab", ALLOC, PAGE); + + void * ptr[SIZE] = {NULL,}; + + int i; + for (i=0; i +#include +#include + +#include +#include +#include +#include + +struct data { + tree_node_t node; + int i; + float f; +}; +typedef struct data data_t; + +static inline int hash(int key) { + key = ~key + (key << 15); + key = key ^ (key >> 12); + key = key + (key << 2); + key = key ^ (key >> 4); + key = key * 2057; + key = key ^ (key >> 16); + + return key; +} + +int main (void) { + slab_t s = INIT_SLAB; + slab_init(&s, "my_slab", sizeof(data_t), 4096); + + int compare(const int i1, const int i2) { + return i1 - i2; + } + + tree_t l = INIT_TREE; + tree_init(&l, (compare_f)compare); + + int i; + for (i=0; i<10000; i++) { + data_t * d = (data_t *)slab_alloc(&s); + + d->i = hash(i); + d->f = (float)i; + + tree_node_init(&d->node, (const void *)d->i); + splay_insert(&l, &d->node); + } + + tree_dump(&l, stdout); + + i = hash(6); + tree_node_t * n = tree_find(&l, (const void *)i); + printf("n[%p]\n", n); + + data_t * d = container_of(n, data_t, node); + printf("d->i[%d]\n", d->i); + + splay_remove(&l, n); + n = tree_find(&l, (const void *)i); + printf("n[%p]\n", n); + + i = 2; + splay_find(&l, (const void *)i); + i = 8; + splay_find(&l, (const void *)i); + +#if 0 + slab_dump(&s, stdout); + tree_delete(&l, d, node); +#endif + + tree_dump(&l, stdout); + slab_delete(&s); + + return 0; +} + diff --git a/clib/test/table.c b/clib/test/table.c new file mode 100644 index 0000000..62253df --- /dev/null +++ b/clib/test/table.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include + +#define COLS 5 + +int main(void) { + table_t t = INIT_TABLE; + table_init(&t, "table", COLS); + + const char * str = "column4 is a really long assed string"; + + table_name(&t, 0, "column0"); + table_name(&t, 1, "column1"); + table_name(&t, 2, "column2"); + table_name(&t, 3, "column3"); + table_name(&t, 4, str); + + printf("%s\n", table_name(&t, 4)); + printf("cols: %d\n", table_columns(&t)); + printf("rows: %d\n", table_rows(&t)); + + table_dump(&t, stdout); + + table_iter_t it; + table_iter_init(&it, &t, TI_FLAG_FWD); + + size_t r, c; + + value_t * val; + table_for_each(&it, val) { + for (c=0; c %d\n", r, c, r * COLS + c); + value_i32(v+c, r * COLS + c); + value_dump(v+c, stdout); + } + table_row(&t, r, v); + } + table_dump(&t, stdout); + printf("yyy =================\n"); + + table_for_each(&it, val) { + for (c=0; c +#include +#include + +#include +#include +#include + +#include +#include + +struct data { + tree_node_t node; + int i; + float f; +}; +typedef struct data data_t; + +int main (void) { + slab_t s = INIT_SLAB; + slab_init(&s, "my_slab", sizeof(data_t), 4096); + + tree_t t = INIT_TREE; + tree_init(&t, default_compare); + + int i; + for (i=0; i<25; i++) { + data_t * d = (data_t *)slab_alloc(&s); + + printf("insert i[%d] --> %p\n", i, d); + d->i = i; + d->f = (float)i; /* key */ + + tree_node_init(&d->node, (const void *)(d->i)); + tree_insert(&t, &d->node); + } + +#if 1 + i = 6; + tree_node_t * n = tree_find(&t, (const void *)(i)); + tree_remove(&t, n); + n = tree_find(&t, (const void *)i); + + i = 2; + tree_find(&t, (const void *)i); + i = 8; + tree_find(&t, (const void *)i); +#endif + + tree_dump(&t, stdout); + + data_t * d; + + tree_iter_t it; + tree_iter_init(&it, &t, TI_FLAG_FWD); + + tree_for_each(&it, d, node) { + printf("depth first (FWD) i[%d] f[%f]\n", d->i, d->f); + } + +// tree_dump(&t, stdout); + slab_delete(&s); + + return 0; +} + diff --git a/clib/test/vector.c b/clib/test/vector.c new file mode 100644 index 0000000..2925fbe --- /dev/null +++ b/clib/test/vector.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include + +#include +#include + +int main(void) { + vector_t a = INIT_VECTOR; + vector_init(&a, "foo", 4, 1024); + + vector_size(&a, 1000); + + vector_put(&a, 0, (uint32_t[]){0xffffffff}); + vector_put(&a, 1, (uint32_t[]){0xffffffff}); + vector_put(&a, 52, (uint32_t[]){52}); + vector_put(&a, 53, (uint32_t[]){53}); + vector_put(&a, 167, (uint32_t[]){167}); + vector_put(&a, 223, (uint32_t[]){223}); + vector_put(&a, 78, (uint32_t[]){78}); + vector_put(&a, 205, (uint32_t[]){205}); + vector_put(&a, 183, (uint32_t[]){183}); + vector_put(&a, 150, (uint32_t[]){150}); + vector_put(&a, 90, (uint32_t[]){90}); + vector_put(&a, 66, (uint32_t[]){66}); + vector_put(&a, 91, (uint32_t[]){91}); + vector_put(&a, 45, (uint32_t[]){45}); + vector_put(&a, 211, (uint32_t[]){211}); + + uint32_t arr[] = {985,986,987,988,990,991,992,993,994}; + vector_put(&a, 985, arr, 9); + + vector_dump(&a, stdout); + + vector_size(&a, 200); + + vector_dump(&a, stdout); + + FILE *f = fopen("vector.bin", "w+"); + vector_save(&a, f); + fclose(f); + + vector_delete(&a); + + f = fopen("vector.bin", "r"); + vector_load(&a, f); + fclose(f); + +#if 1 + vector_iter_t it; + vector_iter_init(&it, &a, VI_FLAG_FWD); + + uint32_t * j; + vector_for_each(&it, j) { + printf("arr[%d] = %d\n", it.idx, *j); + } +#endif + + vector_dump(&a, stdout); + + vector_delete(&a); + + return 0; +} diff --git a/clib/test/watch.c b/clib/test/watch.c new file mode 100644 index 0000000..07e68cf --- /dev/null +++ b/clib/test/watch.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include + +int callback(watch_event_t * we) { + printf("hello\n"); + return 0; +} + +int main(const int argc, const char * argv[]) { + watch_t w; + watch_init(&w); + + watch_callback_t cb = { + .data = NULL, + .func = callback, + }; + + watch_add(&w, "/dev/mqueue", IN_CREATE | IN_ATTRIB, &cb); + watch_wait(&w); + + return 0; +} diff --git a/clib/timer.h b/clib/timer.h new file mode 100644 index 0000000..48dc748 --- /dev/null +++ b/clib/timer.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: timer.h + * Author: Shaun Wetzstein + * Descr: Timer + * Note: + * Date: 10/03/10 + */ + +#ifndef __TIMER_H__ +#define __TIMER_H__ + +#include +#include +#include + +#include + +#include "attribute.h" +#include "builtin.h" +#include "assert.h" +#include "vector.h" + +/* ==================================================================== */ + +typedef struct timer_struct timer_t; +typedef struct timer_callback_struct timer_callback_t; +typedef struct timer_event_struct timer_event_t; + +typedef int (*timer_f) (timer_event *); + +struct timer_struct { + int fd; + vector_t callbacks; +}; + +struct timer_callback_struct { + void *data; + timer_f func; +}; + +struct timer_event_struct { +}; + +/* ======================================================================= */ + +extern void timer_init(timer *, int) __nonnull((1)); +extern void timer_delete(timer *) __nonnull((1)); + +extern int timer_fileno(timer *) __nonnull((1)); + +extern uint32_t timer_add(timer *, const char *, uint32_t, + timer_callback *) __nonnull((1, 2)); +extern void timer_remove(timer *, uint32_t) __nonnull((1)); + +extern void timer_wait(timer *) __nonnull((1)); + +/* ======================================================================= */ + +#endif /* __timer_H__ */ diff --git a/clib/trace_indent.h b/clib/trace_indent.h new file mode 100644 index 0000000..94ccb7e --- /dev/null +++ b/clib/trace_indent.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) International Business Machines Corp., 2011 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: B. Rafanello + * + * Module: trace_indent.h + * + * Implements indentation for the trace output from bb_trace + * + * Version 0.0.0.1 + */ + +#ifndef TRACE_INDENT_H + +#define TRACE_INDENT_H + +/*********************************************************************/ +/* */ +/* Function Name: Set_Indent_Size */ +/* */ +/* Descriptive Name: Sets the number of spaces to use per indent. */ +/* If the current indent level is 5 and this */ +/* function is called to set the indent size to */ +/* 10, then 50 spaces will be printed by the */ +/* trace macros before each line of trace */ +/* output at this indent level. */ +/* */ +/* Input: unsigned int Spaces_Per_Indent - A value less than 128. */ +/* */ +/* Output: If Success : The specified value will be used for the */ +/* size of an indent. */ +/* */ +/* If Failure : This should only happen if the value */ +/* specified is >= 128, in which case it is */ +/* ignored and the previous value is retained.*/ +/* */ +/* Error Handling: Bad values for Spaces_Per_Indent are ignored. */ +/* */ +/* Side Effects: The number of spaces per indent may be changed. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +void Set_Indent_Size(unsigned int Spaces_Per_Indent); + +/*********************************************************************/ +/* */ +/* Function Name: Indent_Trace_Output */ +/* */ +/* Descriptive Name: This function increases the current indent */ +/* level by one. */ +/* */ +/* Input: None. */ +/* */ +/* Output: If Success : None */ +/* */ +/* If Failure : None */ +/* */ +/* Error Handling: None */ +/* */ +/* Side Effects: The current indent level for trace output will */ +/* be increased by 1. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +void Indent_Trace_Output(void); + +/*********************************************************************/ +/* */ +/* Function Name: Outdent_Trace_Output */ +/* */ +/* Descriptive Name: This function reduces the current indent */ +/* level if it is greater than 0. */ +/* */ +/* Input: None */ +/* */ +/* Output: If Success : None */ +/* */ +/* If Failure : None */ +/* */ +/* Error Handling: If the current indent level is zero, then this */ +/* function does nothing. */ +/* */ +/* Side Effects: The current indent level for trace output may be */ +/* decreased by 1. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +void Outdent_Trace_Output(void); + +/*********************************************************************/ +/* */ +/* Function Name: Do_Indent */ +/* */ +/* Descriptive Name: This function prints to stderr the number of */ +/* spaces corresponding to the current indent */ +/* level. */ +/* */ +/* Input: None */ +/* */ +/* Output: If Success : None */ +/* */ +/* If Failure : None */ +/* */ +/* Error Handling: None */ +/* */ +/* Side Effects: Some number of space may be output to stderr. */ +/* */ +/* Notes: */ +/* */ +/*********************************************************************/ +void Do_Indent(void); + +#endif diff --git a/clib/tree.h b/clib/tree.h new file mode 100644 index 0000000..747ec3b --- /dev/null +++ b/clib/tree.h @@ -0,0 +1,455 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! + * @file tree.h + * @brief Binary Tree Container + * @details Trees are a kind of associative container that stores elements formed + * by the conbination of a @em key and a @em tree_node + * @details For example, + * @code + * ... + * int main (int argc, char *argv[]) { + * typedef struct { + * int i; + * float f; + * tree_node_t node; + * } data_t; + * + * slab_t s; + * slab_init(&s, "my_slab", sizeof(data_t), 4096); + * + * tree_t t; + * tree_init(&t, default_compare); + * + * int i; + * for (i=0; i<25; i++) { + * data_t * d = (data_t *)slab_alloc(&s); + * + * d->i = i; + * d->f = (float)i // key + * + * tree_node_init(&d->node, (const void *)(d->i)); + * tree_insert(&t, &d->node); + * + * printf("insert i[%d] --> %p\n", i, d); + * } + * @endcode + * @author Shaun Wetzstein + * @date 2010-2011 + */ + +#ifndef __TREE_H__ +#define __TREE_H__ + +#include +#include +#include +#include + +#include "exception.h" +#include "builtin.h" +#include "compare.h" +#include "type.h" + +#define INIT_TREE_NODE {NULL,} +#define INIT_TREE {NULL,NULL,NULL,NULL,0} + +/* ==================================================================== */ + +typedef struct tree_node tree_node_t; //!< Alias for the @em tree_node class + +typedef int (*tree_walk_f) (tree_node_t *); //!< Tree walk callback function + +/*! + * @brief tree node + * @details Primitive types cannot be stored in the @em tree container, instead the user must + * embed a @em tree_node object within the stored object. + */ +struct tree_node { + tree_node_t *left; //!< Reference the left tree_node (a.k.a. left sub-tree) + tree_node_t *right; //!< Reference the right tree_node (a.k.a. right sub-tree) + tree_node_t *parent; //!< Reference the parent tree_node + + const void *key; //!< Reference to the key bytes for this tree_node +}; + +/*! + * @brief tree container + * @details Primitive types cannot be stored in the @em tree container, instead the user must + * embed a @em tree_node object within the stored object. + */ +struct tree { + tree_node_t *root; //!< Reference to the root tree_node of the @em tree + + tree_node_t *min; //!< Reference to the node with smallest 'key' in the tree + tree_node_t *max; //!< reference to the node with largest 'key' in the tree + + compare_f compare; //!< Reference to the function used to distinguish tree_nodes + size_t size; //!< Cache of the number of tree_node's contained in the @em tree +}; +typedef struct tree tree_t; //!< Alias for the @em tree class + +/* ==================================================================== */ + +/*! + * @brief Constructs a @em tree_node object + * @memberof tree_node + * @param self [in] tree_node object @em self pointer + * @param key [in] pointer to key bytes + * @return Reference to an initialized tree_node object on SUCCESS + */ +static inline tree_node_t *tree_node_init(tree_node_t * self, const void *key) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Check if the @em tree_node object is a leaf node + * @details A leaf node is one where both @em .left and @em .right are NULL + * @memberof tree_node + * @param self [in] tree_node object @em self pointer + * @return True if the @em tree_node is a leaf node, false otherwise + */ +static inline bool tree_node_leaf(tree_node_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Check if the @em tree_node object is an internal node + * @details An internal node is one where either (or both) @em .left and @em .right are non-NULL + * @memberof tree_node + * @param self [in] tree_node object @em self pointer + * @return True if the @em tree_node is an internal node, false otherwise + */ +static inline bool tree_node_internal(tree_node_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the left sub-tree of a @em tree_node + * @memberof tree_node + * @param self [in] tree_node object @em self pointer + * @return Reference to a @em tree_node if @em self is non-NULL, NULL otherwise + */ +static inline tree_node_t *tree_node_left(tree_node_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the right sub-tree of a @em tree_node + * @memberof tree_node + * @param self [in] tree_node object @em self pointer + * @return Reference to a @em tree_node if @em self is non-NULL, NULL otherwise + */ +static inline tree_node_t *tree_node_right(tree_node_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the parent tree_node of a @em tree_node + * @memberof tree_node + * @param self [in] tree_node object @em self pointer + * @return Reference to a @em tree_node if @em self is non-NULL, NULL otherwise + */ +static inline tree_node_t *tree_node_parent(tree_node_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the key pointer of a @em tree_node + * @memberof tree_node + * @param self [in] tree_node object @em self pointer + * @return Reference to the key bytes if @em self is non-NULL, NULL otherwise + */ +static inline const void *tree_node_key(tree_node_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the prev tree_node of a @em tree_node + * @memberof tree_node + * @param self [in] tree_node object @em self pointer + * @return Reference to a @em tree_node if @em self is non-NULL, NULL otherwise + */ +extern tree_node_t *tree_node_prev(tree_node_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the next tree_node of a @em tree_node + * @memberof tree_node + * @param self [in] tree_node object @em self pointer + * @return Reference to a @em tree_node if @em self is non-NULL, NULL otherwise + */ +extern tree_node_t *tree_node_next(tree_node_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Dump the contents of a @em tree to @em out output stream + * @memberof tree_node + * @param self [in] tree_node object @em self pointer + * @param out [in] Reference to the @em out output stream + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern void tree_node_dump(tree_node_t * self, FILE * out) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/* ======================================================== */ + +static inline tree_node_t *tree_node_init(tree_node_t * self, const void *key) +{ + self->left = self->right = NULL; + self->parent = NULL; + self->key = key; + return self; +} + +static inline bool tree_node_leaf(tree_node_t * self) +{ + return self ? self->left == NULL && self->right == NULL : false; +} + +static inline bool tree_node_internal(tree_node_t * self) +{ + return self ? self->left != NULL && self->right != NULL : false; +} + +static inline tree_node_t *tree_node_left(tree_node_t * self) +{ + return self ? self->left : NULL; +} + +static inline tree_node_t *tree_node_right(tree_node_t * self) +{ + return self ? self->right : NULL; +} + +static inline tree_node_t *tree_node_parent(tree_node_t * self) +{ + return self ? self->parent : NULL; +} + +static inline const void *tree_node_key(tree_node_t * self) +{ + return self ? self->key : NULL; +} + +/* ======================================================== */ + +/*! + * @brief Constructs a @em tree object + * @memberof tree + * @param self [in] tree_node object @em self pointer + * @param compare [in] Reference to the @em tree_node compare function + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int tree_init(tree_t * self, compare_f compare) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Insert a new @em tree_node into the @em tree container + * @memberof tree + * @param self [in] tree object @em self pointer + * @param node [in] Reference to the @em tree_node to insert + * @return @em true if the @em tree_node was inserted, @em false otherwise + * @throws UNEXPECTED if @em self pointer is NULL + * @throws UNEXPECTED if @em tree_node.key points to a duplicate key + */ +extern int tree_insert(tree_t * self, tree_node_t * node) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Removes a @em tree_node from the @em tree container + * @memberof tree + * @param self [in] tree object @em self pointer + * @param node [in] Reference to the @em tree_node to remove + * @return @em true if the @em tree_node was removed, @em false otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int tree_remove(tree_t * self, tree_node_t * node) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Find a @em tree_node within the @em tree container + * @memberof tree + * @param self [in] tree object @em self pointer + * @param key [in] Reference to the @em key to find + * @return Reference to a @em tree_node on SUCCESS, false otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern tree_node_t *tree_find(tree_t * self, const void *key) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Walk the @em tree and call walk_func for each node + * @memberof tree + * @param self [in] tree object @em self pointer + * @param walk_func [in] Reference to the walk function callback + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int tree_walk(tree_t * self, tree_walk_f walk_func) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Hexdump the contents of a @em tree to @em out output stream + * @memberof tree + * @param self [in] tree object @em self pointer + * @param out [in] Reference to the @em out output stream + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern void tree_dump(tree_t * self, FILE * out) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Return the root @em tree_node of a @em tree container + * @memberof tree + * @param self [in] tree object @em self pointer + * @return Reference to a @em tree_node if @em self is non-NULL, NULL otherwise + */ +static inline tree_node_t *tree_root(tree_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return whether a @em tree container is empty + * @memberof tree + * @param self [in] tree object @em self pointer + * @return @em true if @em tree is empty, false otherwise + */ +static inline bool tree_empty(tree_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the node count of a @em tree container + * @memberof tree + * @param self [in] tree object @em self pointer + * @return @em 0 if @em tree is empty, @em non-0 otherwise + */ +static inline size_t tree_size(tree_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the minimum @em tree_node of a @em tree container + * @memberof tree + * @param self [in] tree object @em self pointer + * @return Reference to a @em tree_node if @em self is non-NULL, NULL otherwise + */ +static inline tree_node_t *tree_min(tree_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return the maximum @em tree_node of a @em tree container + * @memberof tree + * @param self [in] tree object @em self pointer + * @return Reference to a @em tree_node if @em self is non-NULL, NULL otherwise + */ +static inline tree_node_t *tree_max(tree_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/* ======================================================== */ + +static inline tree_node_t *tree_root(tree_t * self) +{ + return self ? self->root : NULL; +} + +static inline bool tree_empty(tree_t * self) +{ + return self ? self->root == NULL : true; +} + +static inline size_t tree_size(tree_t * self) +{ + return self ? self->size : 0; +} + +static inline tree_node_t *tree_min(tree_t * self) +{ + return self ? self->min : NULL; +} + +static inline tree_node_t *tree_max(tree_t * self) +{ + return self ? self->max : NULL; +} + +/* ======================================================== */ + +/*! + * @brief Insert a new @em tree_node into the @em tree container + * @details @em splay_insert is similar to @em tree_insert except + * it will self-adjust using the splay algorithm + * @memberof tree + * @param self [in] splay object @em self pointer + * @param node [in] Reference to the @em tree_node to insert + * @return @em true if the @em tree_node was inserted, @em false otherwise + * @throws UNEXPECTED if @em self pointer is NULL + * @throws UNEXPECTED if @em tree_node.key points to a duplicate key + */ +extern int splay_insert(tree_t * self, tree_node_t * node) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Removes a @em tree_node from the splay @em tree container + * @details @em splay_remove is similar to @em tree_remove except + * it will self-adjust using the splay algorithm + * @memberof tree + * @param self [in] splay object @em self pointer + * @param node [in] Reference to the @em tree_node to remove + * @return @em true if the @em tree_node was removed, @em false otherwise + * @throws UNEXPECTED if @em self pointer is NULL + * @throws UNEXPECTED if @em tree_node.key points to a duplicate key + */ +extern int splay_remove(tree_t * self, tree_node_t * node) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Find a @em tree_node within the splay @em tree container + * @details @em splay_find is similar to @em tree_find except it will + * self-adjust using the splay algorithm + * @memberof tree + * @param self [in] splay object @em self pointer + * @param key [in] Reference to the @em key to find + * @return Reference to a @em tree_node on SUCCESS, false otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern tree_node_t *splay_find(tree_t * self, const void *key) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/* ======================================================== */ + +#endif /* __TREE_H__ */ diff --git a/clib/tree_iter.h b/clib/tree_iter.h new file mode 100644 index 0000000..2f410ca --- /dev/null +++ b/clib/tree_iter.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! @file tree_iter.h + * @brief Binary Tree Iterator + * @details For example, + * @code + * ... + * data_t * d; + * ... + * tree_iter_t it; + * tree_iter_init(&it, &t, TI_FLAG_DFT_FWD); + * ... + * tree_for_each(&it, d, node) { + * printf("depth first (FWD) i[%d] f[%f]\n", d->i, d->f); + * } + * ... + * @endcode + * @author Shaun Wetzstein + * @date 2010-2012 + */ + +#ifndef __TREE_ITER_H__ +#define __TREE_ITER_H__ + +#include +#include + +#include "builtin.h" +#include "compare.h" +#include "type.h" + +#include "tree.h" + +/* ======================================================== */ + +typedef struct tree_iter tree_iter_t; //!< Alias for the @em tree_iter class + +/*! + * @brief tree iterator + * @details Binary tree container iterator + */ +struct tree_iter { + tree_t *tree; //!< Tree container to iterate + + tree_node_t *node; //!< Current position in the iteration + tree_node_t *safe; + + uint32_t flags; //!< Iterator configuration flags +}; + +/* ======================================================== */ + +#define TI_FLAG_NONE 0x00000000 //!< All flag mask +#define TI_FLAG_FWD 0x00000001 //!< Depth-first-traversal Forward (FWD) flag mask +#define TI_FLAG_BWD 0x00000002 //!< Depth-first-traversal Backward (BWD) flag mask +#define TI_FLAG_MASK 0x00000003 //!< Depth-first-traversal All flag mask + +/*! + * @brief Initializes an @em tree_iter iterator object + * @memberof tree_iter + * @param self [in] tree_iter object @em self pointer + * @param tree [in] tree container object to iterate + * @param flags [in] iterator configuration flags + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + * @throws UNEXPECTED if @em tree pointer is NULL + */ +extern int tree_iter_init(tree_iter_t * self, tree_t * tree, uint32_t flags) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Resets an @em tree iterator object + * @memberof tree_iter + * @param self [in] tree_iter object @em self pointer + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int tree_iter_clear(tree_iter_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return a pointer to a @em tree_node element at the current + * iterator position + * @memberof tree_iter + * @param self [in] tree_iter object @em self pointer + * @return non-NULL on success, NULL otherwise + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern tree_node_t *tree_iter_elem(tree_iter_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @fn tree_node_t * tree_iter_inc(tree_iter_t * self, size_t count = 1) + * @brief Increment the position of an @em tree iterator + * @details If the second (2nd) parameter is omitted, the iterator is + * incremented by one (1) position. + * @details For example, + * @code + * ... + * tree_t l; + * tree_init(&l); + * ... + * tree_iter_t it; + * tree_iter_init(&it, &l, LI_FLAG_FWD); + * ... + * tree_iter_inc(&it); + * ... + * @endcode + * @memberof tree_iter + * @param self [in] tree_iter object @em self pointer + * @param count [in] Number of positions to increment (optional) + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +#define tree_iter_inc(...) STRCAT(tree_iter_inc, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern tree_node_t *tree_iter_inc1(tree_iter_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern tree_node_t *tree_iter_inc2(tree_iter_t * self, size_t count) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @fn tree_node_td * tree_iter_dec(tree_iter_t * self, size_t count = 1) + * @brief decrement the position of an @em list iterator + * @note If the second (2nd) parameter is omitted, the iterator is decremented + * by one (1) position. + * @details For example, + * @code + * ... + * tree_t l; + * tree_init(&l); + * ... + * tree_iter_t it; + * tree_iter_init(&it, &l, LI_FLAG_FWD); + * ... + * tree_iter_dec(&it, 3); + * ... + * @endcode + * @memberof tree_iter + * @param self [in] tree_iter object @em self pointer + * @param count [in] Number of positions to decrement (optional) + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +#define tree_iter_dec(...) STRCAT(tree_iter_dec, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern tree_node_t *tree_iter_dec1(tree_iter_t * self) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern tree_node_t *tree_iter_dec2(tree_iter_t * self, size_t count) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @def tree_for_each(it, i, m) + * @hideinitializer + * @brief Tree for-each algorithm + * @param it [in] Tree iterator object + * @param i [in] Tree element variable + * @param m [in] Member name + */ +#define tree_for_each(it, i, m) \ + for (tree_iter_clear(it), i = container_of_var(tree_iter_elem(it), i, m); \ + tree_iter_elem(it) != NULL; \ + i = container_of_var(tree_iter_inc(it), i, m)) + +/* ======================================================== */ + +#endif /* __TREE_ITER_H__ */ diff --git a/clib/type.h b/clib/type.h new file mode 100644 index 0000000..1e259c0 --- /dev/null +++ b/clib/type.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! @file type.h + * @brief Type helpers + * @author Shaun Wetzstein + * @date 2010-2011 + */ + +#ifndef __TYPE_H__ +#define __TYPE_H__ + +#include "align.h" + +/*! + * @def cast_type(e,T) + * @hideinitializer + * @brief Casts @em p to type @em T + * @param e [in] Expression + * @param T [in] Type name + */ +#define cast_type(e,T) \ + ((T)(e)) + +/*! + * @def check_type(e,T) + * @hideinitializer + * @brief Check the type of expression @em e is equal to type @em T + * @param e [in] Expression + * @param T [in] Type name + */ +#define check_type(e,T) \ + ((typeof(e) *)0 != (T *)0) + +/*! + * @def check_types_match(e,T) + * @hideinitializer + * @brief Check the types of expressions @em e1 and @em e2 are equal + * @param e1 [in] Expression + * @param e2 [in] Expression + */ +#define check_types_match(e1,e2) \ + ((typeof(e1) *) 0 != (typeof(e2) *)0) + +/*! + * @def container_of(member_ptr, containing_type, member) + * @hideinitializer + * @brief Returns a pointer to the containing structure of a member + * @param p [in] Pointer to a strucure member + * @param T [in] Type of the containing structure + * @param m [in] Name of the member @em p references + */ +#define container_of(p,T,m) \ + ((T *)((char *)(p) - offsetof(T,m)) - check_types_match(*(p),((T*)0)->m)) + +/*! + * @def container_of_var(member_ptr, var, member) + * @hideinitializer + * @brief Returns a pointer to the containing structure of a member + * @param p [in] Pointer to a strucure member + * @param v [in] Pointer to a variable with the same of type as the containing structure + * @param m [in] Name of the member @em p references + */ +#define container_of_var(p, v, m) \ + container_of(p, typeof(*v), m) + +#endif /* __TYPE_H__ */ diff --git a/clib/value.h b/clib/value.h new file mode 100644 index 0000000..255ff0b --- /dev/null +++ b/clib/value.h @@ -0,0 +1,558 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! + * @file value.h + * @brief Value Container + * @details Values are a kind of variant structure that can contain + * values of different types + * @author Shaun Wetzstein + * @date 2010-2011 + */ + +#ifndef __VALUE_H__ +#define __VALUE_H__ + +#include +#include + +#include "libclib.h" + +/* ======================================================================= */ + +typedef enum value_type_enum value_type_t; //!< Alias for the @em value_type_enum enum +typedef struct value value_t; //!< Alias for the @em value class + +/*! + * @brief Value types + * @details Supported value type + */ +enum value_type_enum { + VT_UNKNOWN = 0, //!< Uninitialized or unknown type + VT_I8, //!< 8-bit signed integer type + VT_I16, //!< 16-bit signed integer type + VT_I32, //!< 32-bit signed integer type + VT_I64, //!< 64-bit signed integer type + VT_U8, //!< 8-bit unsigned integer type + VT_U16, //!< 16-bit unsigned integer type + VT_U32, //!< 32-bit unsigned integer type + VT_U64, //!< 64-bit unsigned integer type + VT_REAL32, //!< 32-bit float type + VT_REAL64, //!< 64-bit float type + VT_STR, //!< Character string type + VT_STR_INLINE, //!< Character string inline type + VT_STR_OFF, //!< Character string offset type + VT_STR_CONST, //!< Constant character string type + VT_BLOB, //!< Blob type + VT_BLOB_INLINE, //!< Blob inline type + VT_BLOB_OFF, //!< Blob offset type + VT_BLOB_FILE, //!< Blob file type +} /*! @cond */ __packed /*! @endcond */ ; + +/*! @cond */ +#ifndef VALUE_PAD_DEFAULT +#define VALUE_PAD_DEFAULT 12 +#endif +/*! @endcond */ ; + +/*! + * @brief value container + */ +struct value { + uint32_t type:8, //!< Value type + size:24; //!< Value size (in bytes) + + union { + int8_t i8; //!< 8-bit signed integer + int16_t i16; //!< 16-bit signed integer + int32_t i32; //!< 32-bit signed integer + int64_t i64; //!< 64-bit signed integer + + uint8_t u8; //!< 8-bit unsigned integer + uint16_t u16; //!< 16-bit unsigned integer + uint32_t u32; //!< 32-bit unsigned integer + uint64_t u64; //!< 64-bit unsigned integer + + float r32; //!< 32-bit float + double r64; //!< 64-bit float + + void *ptr; //!< pointer + + uint8_t data[VALUE_PAD_DEFAULT]; + }; //!< Anonymous variant record +}; + +/* ==================================================================== */ + +/*! @cond */ +#define value_type(...) STRCAT(value_type, NARGS(__VA_ARGS__))(__VA_ARGS__) +static inline value_type_t value_type1(const value_t * self) +{ + return self ? self->type : VT_UNKNOWN; +} + +static inline void value_type2(value_t * self, value_type_t type) +{ + if (self != NULL) + self->type = type; +} + +/*! @endcond */ + +/*! @cond */ +#define value_size(...) STRCAT(value_size, NARGS(__VA_ARGS__))(__VA_ARGS__) +static inline uint32_t value_size1(const value_t * self) +{ + return self ? self->size : 0; +} + +static inline void value_size2(value_t * self, uint32_t size) +{ + if (self != NULL) + self->size = size; +} + +/*! @endcond */ + +/*! + * @brief Clear the contents of a @em value + * @memberof value + * @param self [in] value object @em self pointer + */ +static inline void value_clear(value_t * self) +{ + if (self != NULL) { + if (self->type == VT_BLOB || self->type == VT_STR) + if (self->ptr != NULL) + free(self->ptr); + memset(self, 0, sizeof *self); + } +} + +#define value_i8(...) STRCAT(value_i8_, NARGS(__VA_ARGS__))(__VA_ARGS__) +static inline int8_t value_i8_1(const value_t * v) +{ + assert(value_type(v) == VT_I8); + return v->i8; +} + +static inline value_t *value_i8_2(value_t * v, const int8_t i8) +{ + value_clear(v), v->type = VT_I8, v->i8 = i8, v->size = sizeof(i8); + return v; +} + +#define value_i16(...) STRCAT(value_i16_, NARGS(__VA_ARGS__))(__VA_ARGS__) +static inline int16_t value_i16_1(const value_t * v) +{ + assert(value_type(v) == VT_I16); + return v->i16; +} + +static inline value_t *value_i16_2(value_t * v, const int16_t i16) +{ + value_clear(v), v->type = VT_I16, v->i16 = i16, v->size = sizeof(i16); + return v; +} + +#define value_i32(...) STRCAT(value_i32_, NARGS(__VA_ARGS__))(__VA_ARGS__) +static inline int32_t value_i32_1(const value_t * v) +{ + assert(value_type(v) == VT_I32); + return v->i32; +} + +static inline value_t *value_i32_2(value_t * v, const int32_t i32) +{ + value_clear(v), v->type = VT_I32, v->i32 = i32, v->size = sizeof(i32); + return v; +} + +#define value_i64(...) STRCAT(value_i64_, NARGS(__VA_ARGS__))(__VA_ARGS__) +static inline int64_t value_i64_1(const value_t * v) +{ + assert(value_type(v) == VT_I64); + return v->i64; +} + +static inline value_t *value_i64_2(value_t * v, const int64_t i64) +{ + value_clear(v), v->type = VT_I64, v->i64 = i64, v->size = sizeof(i64); + return v; +} + +#define value_u8(...) STRCAT(value_u8_, NARGS(__VA_ARGS__))(__VA_ARGS__) +static inline uint8_t value_u8_1(const value_t * v) +{ + assert(value_type(v) == VT_U8); + return v->u8; +} + +static inline value_t *value_u8_2(value_t * v, const uint8_t u8) +{ + value_clear(v), v->type = VT_U8, v->u8 = u8, v->size = sizeof(u8); + return v; +} + +#define value_u16(...) STRCAT(value_u16_, NARGS(__VA_ARGS__))(__VA_ARGS__) +static inline uint16_t value_u16_1(const value_t * v) +{ + assert(value_type(v) == VT_U16); + return v->u16; +} + +static inline value_t *value_u16_2(value_t * v, const uint16_t u16) +{ + value_clear(v), v->type = VT_U16, v->u16 = u16, v->size = sizeof(u16); + return v; +} + +#define value_u32(...) STRCAT(value_u32_, NARGS(__VA_ARGS__))(__VA_ARGS__) +static inline uint32_t value_u32_1(const value_t * v) +{ + assert(value_type(v) == VT_U32); + return v->u32; +} + +static inline value_t *value_u32_2(value_t * v, const uint32_t u32) +{ + value_clear(v), v->type = VT_U32, v->u32 = u32, v->size = sizeof(u32); + return v; +} + +#define value_u64(...) STRCAT(value_u64_, NARGS(__VA_ARGS__))(__VA_ARGS__) +static inline uint64_t value_u64_1(const value_t * v) +{ + assert(value_type(v) == VT_U64); + return v->u64; +} + +static inline value_t *value_u64_2(value_t * v, const uint64_t u64) +{ + value_clear(v), v->type = VT_U64, v->u64 = u64, v->size = sizeof(u64); + return v; +} + +#define value_float(...) STRCAT(value_float_, NARGS(__VA_ARGS__))(__VA_ARGS__) +static inline float value_float1(const value_t * v) +{ + assert(value_type(v) == VT_REAL32); + return v->r32; +} + +static inline value_t *value_float2(value_t * v, const float r32) +{ + value_clear(v), v->type = VT_REAL32, v->r32 = r32, v->size = + sizeof(r32); + return v; +} + +#define value_double(...) STRCAT(value_double, NARGS(__VA_ARGS__))(__VA_ARGS__) +static inline double value_double1(const value_t * v) +{ + assert(value_type(v) == VT_REAL64); + return v->r64; +} + +static inline value_t *value_double2(value_t * v, const double r64) +{ + value_clear(v), v->type = VT_REAL64, v->r64 = r64, v->size = + sizeof(r64); + return v; +} + +#define value_blob(...) STRCAT(value_blob, NARGS(__VA_ARGS__))(__VA_ARGS__) +/*! @cond */ +static inline void *value_blob1(const value_t * v) +{ + if (value_type(v) == VT_BLOB) + return v->ptr; + else if (value_type(v) == VT_BLOB_INLINE) + return (void *)v->data; + else if (value_type(v) == VT_BLOB_OFF) + return NULL; + else if (value_type(v) == VT_BLOB_FILE) ; + else { + UNEXPECTED("invalid value_t blob type '%d'", value_type(v)); + } + + return NULL; +} + +static inline value_t *value_blob4(value_t * self, const void *blob, size_t len, + size_t pad) +{ + value_clear(self); + + if (pad <= len) { + self->ptr = malloc(len + 1); + self->type = VT_BLOB; + + memcpy(self->ptr, blob, len); + memset(self->ptr + len, 0, 1); + } else { + self->type = VT_BLOB_INLINE; + + memcpy(self->data, blob, len); + self->data[len] = '\0'; + } + + self->size = len; + + return self; +} + +static inline value_t *value_blob3(value_t * self, const void *blob, size_t len) +{ + return value_blob4(self, blob, len, VALUE_PAD_DEFAULT); +} + +static inline value_t *value_blob2(value_t * self, const char *path) +{ + value_t *rc = value_blob4(self, path, strlen(path), VALUE_PAD_DEFAULT); + if (rc != NULL) + value_type(rc, VT_BLOB_FILE); + return rc; +} + +/*! @endcond */ + +#define value_string(...) STRCAT(value_string, NARGS(__VA_ARGS__))(__VA_ARGS__) +/*! @cond */ +static inline char *value_string1(const value_t * v) +{ + if (value_type(v) == VT_STR) + return (char *)v->ptr; + else if (value_type(v) == VT_STR_INLINE) + return (char *)v->data; + else if (value_type(v) == VT_STR_OFF) ; + else { + UNEXPECTED("invalid value string type '%d'", value_type(v)); + } + + return NULL; +} + +static inline value_t *value_string4(value_t * self, const char *str, + size_t len, size_t pad) +{ + value_blob(self, (const char *)str, len, pad); + + if (self->type == VT_BLOB) + self->type = VT_STR; + else if (self->type == VT_BLOB_INLINE) + self->type = VT_STR_INLINE; + + return self; +} + +static inline value_t *value_string3(value_t * self, const char *str, + size_t len) +{ + return value_string4(self, str, len, VALUE_PAD_DEFAULT); +} + +static inline value_t *value_string2(value_t * self, const char *str) +{ + return value_string4(self, str, strlen(str), VALUE_PAD_DEFAULT); +} + +static inline value_t *value_set_string_const(value_t * self, const char *str, + size_t len) +{ + value_clear(self); + + self->type = VT_STR_CONST; + self->ptr = (void *)str; + self->size = len; + + return self; +} + +static inline value_t *value_set_string_offset(value_t * self, size_t off, + size_t len) +{ + value_clear(self); + + self->type = VT_STR_OFF; + self->u32 = off; + self->size = len; + + return self; +} + +/*! @endcond */ + +/*! + * @brief Pretty print the contents of a @em value to stdout + * @memberof value + * @param self [in] value object @em self pointer + * @param out [in] output stream + */ +extern void value_dump(const value_t * self, FILE * out) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! @cond */ +#define __choose__ __builtin_choose_expr +#define __compat__ __builtin_types_compatible_p +#define __const__ __builtin_constant_p +/*! @endcond */ + +/*! + * @fn T value_get(const value * self, T) + * @brief Return a @em value as primitive type @em T + * @note @em T can be one fo the following types: + * - @em char, @em int8_t + * - @em short, @em int16_t + * - @em int, @em long @em int32_t + * - @em long @em long, @em int64_t + * - @em unsigned @em char, @em uint8_t + * - @em unsigned @em short, @em uint16_t + * - @em unsigned @em int, @em long @em uint32_t + * - @em unsigned @em long @em long, @em uint64_t + * - @em float, @em double + * - @em char @em *, @em unsigned @em char @em * + * @memberof value + * @param self [in] value object @em self pointer + * @param T [in] Type to cast + * @throws ASSERTION if T is not equal to the object's type + */ + +#if DELETE +/*! @cond */ +#define value_get(v, T) ({ \ + __choose__(__compat__(T, char), \ + value_get_int8((v)), \ + __choose__(__compat__(T, int8_t), \ + value_get_int8((v)), \ + __choose__(__compat__(T, short), \ + value_get_int16((v)), \ + __choose__(__compat__(T, int16_t), \ + value_get_int16((v)), \ + __choose__(__compat__(T, int), \ + value_get_int32((v)), \ + __choose__(__compat__(T, long), \ + value_get_int32((v)), \ + __choose__(__compat__(T, int32_t), \ + value_get_int32((v)), \ + __choose__(__compat__(T, long long), \ + value_get_int64((v)), \ + __choose__(__compat__(T, int64_t), \ + value_get_int64((v)), \ + __choose__(__compat__(T, unsigned char), \ + value_get_uint8((v)), \ + __choose__(__compat__(T, uint8_t), \ + value_get_uint8((v)), \ + __choose__(__compat__(T, unsigned short), \ + value_get_uint16((v)), \ + __choose__(__compat__(T, uint16_t), \ + value_get_uint16((v)), \ + __choose__(__compat__(T, unsigned int), \ + value_get_uint32((v)), \ + __choose__(__compat__(T, unsigned long), \ + value_get_uint32((v)), \ + __choose__(__compat__(T, uint32_t), \ + value_get_uint32((v)), \ + __choose__(__compat__(T, unsigned long long), \ + value_get_uint64((v)), \ + __choose__(__compat__(T, uint64_t), \ + value_get_uint64((v)), \ + __choose__(__compat__(T, float), \ + value_get_float((v)), \ + __choose__(__compat__(T, double), \ + value_get_double((v)), \ + __choose__(__compat__(T, char *), \ + value_get_string((v)), \ + __choose__(__compat__(T, unsigned char *), \ + value_get_string((v)), \ + 0xDEADBEEF)))))))))))))))))))))); \ +}) +/*! @endcond */ +#endif +/*! + * @fn void value_set(const value * self, T v) + * @brief Assign a @em value to @em T v + * @memberof value + * @param self [in] value object @em self pointer + * @param v [in] value to assign + */ + +#if 0 +/*! @cond */ +#define value_set(v, x) ({ \ + __choose__(__compat__(typeof (x), char), \ + value_set_int8((v),(int8_t)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), int8_t), \ + value_set_int8((v),(int8_t)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), short), \ + value_set_int16((v),(int16_t)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), int16_t), \ + value_set_int16((v),(int16_t)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), int), \ + value_set_int32((v),(int32_t)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), long), \ + value_set_int32((v),(int32_t)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), int32_t), \ + value_set_int32((v),(int32_t)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), long long), \ + value_set_int64((v),(int64_t)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), int64_t), \ + value_set_int64((v),(int64_t)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), unsigned char), \ + value_set_uint8((v),(uint8_t)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), uint8_t), \ + value_set_uint8((v),(uint8_t)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), unsigned short), \ + value_set_uint16((v),(uint16_t)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), uint16_t), \ + value_set_uint16((v),(uint16_t)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), unsigned int), \ + value_set_uint32((v),(uint32_t)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), unsigned long), \ + value_set_uint32((v),(uint32_t)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), uint32_t), \ + value_set_uint32((v),(uint32_t)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), unsigned long long), \ + value_set_uint64((v),(uint64_t)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), uint64_t), \ + value_set_uint64((v),(uint64_t)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), float), \ + value_set_float((v),(float)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), double), \ + value_set_double((v),(double)(intptr_t)(x)), \ + __choose__(__compat__(typeof (x), char *), \ + __choose__(__const__((x)), \ + value_set_string_const((v),(const char *)(intptr_t)(x), \ + strlen((const char *)(intptr_t)(x))),\ + value_set_string((v),(char *)(intptr_t)(x), \ + strlen((const char *)(intptr_t)(x)))), \ + __choose__(__compat__(typeof (x), unsigned char *), \ + __choose__(__const__((x)), \ + value_set_string_const((v),(const char *)(intptr_t)(x), \ + strlen((const char *)(intptr_t)(x))),\ + value_set_string((v),(const char *)(intptr_t)(x), \ + strlen((const char *)(intptr_t)(x)))), \ + ((void)0))))))))))))))))))))))); \ +}) +/*! @endcond */ +#endif + +/* ==================================================================== */ + +#endif /* __VALUE_H__ */ diff --git a/clib/vector.h b/clib/vector.h new file mode 100644 index 0000000..c67c155 --- /dev/null +++ b/clib/vector.h @@ -0,0 +1,432 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! + * @file vector.h + * @brief Vector container + * @details Vectors are a container whose elements are in sequential order + * @details For example, + * @code + * #include + * #include + * + * int main(const int argc, const char * argv[]) { + * vector_t a; + * vector_init(&a, 4, 1024); + * + * vector_size(&a, 10); + * + * int i; + * for (i=0; i<10; i++) + * vector_put(&a, i, &i); + * vector_put(&a, 223, (uint32_t[]){223}); + * + * vector_iter_t it; + * vector_iter_init(&it, &a, VI_FLAG_FWD); + * + * uint32_t * j; + * vector_for_each(&it, j) { + * printf("vec[%d]\n", *j); + * } + * + * vector_dump(&a, stdout); + * vector_delete(&a); + * + * return 0; + * } + * @endcode + * @author Shaun Wetzstein + * @date 2010-2011 + */ + +#ifndef __VECTOR_H__ +#define __VECTOR_H__ + +#include + +#include +#include + +#include "version.h" +#include "nargs.h" +#include "ident.h" +#include "mq.h" +#include "tree.h" + +/* ======================================================================= */ + +#define VECTOR_MAGIC "VCTR" + +#define VECTOR_NAME_SIZE 40 //!< Maximum vector name size (in bytes) + +#define VECTOR_FLAG_LSB 0x01 //!< Little-endian header data +#define VECTOR_FLAG_MSB 0x02 //!< Big-endian header data + +#define VECTOR_ELEM_MIN 1 //!< Minimum element size (in bytes) +#define VECTOR_ELEM_MAX 8192 //!< Maximum element size (in bytes) + +#define INIT_VECTOR_HEADER {INIT_IDENT,{0,},0,0,0,0,0} +#define INIT_VECTOR {INIT_VECTOR_HEADER, INIT_TREE} + +/*! + * @brief vector container header + */ +struct vector_header { + ident_t id; //!< identification + char name[VECTOR_NAME_SIZE]; //!< vector name + + size_t page_size; //!< data page size (in bytes) + size_t page_count; //!< number of data pages allocated (currently) + + size_t elem_size; //!< element size (in bytes) + size_t elem_num; //!< element count (per page) + + size_t size; //!< number of initialized elements +}; +typedef struct vector_header vector_header_t; //!< Alias for the @em vector_header class + +/*! + * @brief vector container + */ +struct vector { //! The vector class + vector_header_t hdr; //!< Table metadata + + tree_t tree; //!< @private +}; +typedef struct vector vector_t; //!< Alias for the @em vector class + +/* ======================================================================= */ + +/*! + * @fn void vector_init(vector_t * self, const char * name, size_t elem_size [, size_t page_size]) + * @brief Constructs an @em vector container object + * @details For example, + * @code + * ... + * vector_t ar; + * vector_init(&ar, 4, 1024); + * ... + * @endcode + * @memberof vector + * @param self [in] vector object @em self pointer + * @param name [in] vector object @em name string + * @param elem_size [in] vector element size, in bytes + * @param page_size [in] size of page, in bytes + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define vector_init(...) STRCAT(vector_init, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern int vector_init3(vector_t *, const char *, size_t) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern int vector_init4(vector_t *, const char *, size_t, size_t) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @brief Destructs an @em vector container object + * @details Deallocate all backing storage associated with this \em vector object + * @details For example, + * @code + * ... + * vector_init(&ar, 4, 1024); + * vector_put(&ar, 524, &count); + * vector_delete(&ar); + * ... + * @endcode + * @memberof vector + * @param self [in] vector object @em self pointer + * @return None + */ +extern int vector_delete(vector_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Returns a reference to the element at position @em idx in the @em vector + * @details For example, + * @code + * ... + * vector_init(&ar, 4, 1024); + * vector_put(&ar, 524, &count); + * printf("ar[524] = %d\n", *(int *)vector_at(&ar, 524)); + * vector_delete(&ar); + * ... + * @endcode + * @memberof vector + * @param self [in] vector object @em self pointer + * @param idx [in] vector element index + * @return Reference to vector element at @em idx on SUCCESS + * @throws UNEXPECTED if @em self pointer is NULL + * @throws UNEXPECTED if vector element at @em idx is uninitialized + */ +extern const void *vector_at(vector_t *, size_t) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @fn void vector_get(vector_t * self, size_t idx, const void * ptr, size_t count=1) + * @brief Copy content from the @em vector + * @details Copies @em elem_num element(s) starting at position @em elem_off in the source @em vector to destination pointer @em ptr + * @note If the fourth parameter is omitted, it defaults to 1 + * @details For example, + * @code + * ... + * vector_init(&ar, 4, 1024); + * vector_put(&ar, 524, &count); + * vector_get(&ar, 524, &count); + * vector_delete(&ar); + * ... + * @endcode + * @memberof vector + * @param self [in] vector object @em self pointer + * @param idx [in] vector element index + * @param ptr [out] Destination storage pointer + * @param count [in] Desgination element count (optional) + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define vector_get(...) STRCAT(vector_get, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern int vector_get3(vector_t *, size_t, void *) +/*! @cond */ +__nonnull((1, 3)) /*! @endcond */ ; +extern int vector_get4(vector_t *, size_t, void *, size_t) +/*! @cond */ +__nonnull((1, 3)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @fn void vector_put(vector_t * self, size_t idx, const void * ptr, size_t count=1) + * @brief Assign new content to the @em vector + * @details Copies @em elem_num element(s) from source pointer @em ptr to the destination @em vector starting at position @em elem_off + * @note If the fourth parameter is omitted, it defaults to 1 + * @details For example, + * @code + * ... + * vector_init(&ar, 4, 1024); + * vector_put(&ar, 524, &count); + * vector_get(&ar, 524, &count); + * vector_delete(&ar); + * ... + * @endcode + * @memberof vector + * @param self [in] vector object @em self pointer + * @param idx [in] vector element index + * @param ptr [in] Source storage pointer + * @param count [in] Source element count (optional) + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define vector_put(...) STRCAT(vector_put, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern int vector_put3(vector_t *, size_t, const void *) +/*! @cond */ +__nonnull((1, 3)) /*! @endcond */ ; +extern int vector_put4(vector_t *, size_t, const void *, size_t) +/*! @cond */ +__nonnull((1, 3)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @fn size_t vector_size(vector_t * self, size_t size = 1) + * @brief Return or set the size of the @em vector + * @details Return or set the number of allocated elements in the @em vector + * @details For example, + * @code + * ... + * vector_init(&ar, 4, 1024); + * vector_size(&ar, 2040); + * vector_delete(&ar); + * ... + * @endcode + * @memberof vector + * @param self [in] vector object @em self pointer + * @param size [in] New vector size + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define vector_size(...) STRCAT(vector_size, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern size_t vector_size1(vector_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern int vector_size2(vector_t *, size_t) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @brief Return pages of the @em vector container + * @details Return the number of pages in the @em vector container + * @details For example, + * @code + * ... + * vector_init(&ar, 4, 1024); + * vector_size(&ar, 2040); + * printf("pages = %d\n", vector_pages(&ar)); + * vector_delete(&ar); + * ... + * @endcode + * @memberof vector + * @param self [in] vector object @em self pointer + * @return The number of pages that conform the vector's content + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern size_t vector_pages(vector_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return capacity of the @em vector + * @details Return the number of allocated and unallocated elements in the @em vector container + * @details For example, + * @code + * ... + * vector_init(&ar, 4, 1024); + * vector_size(&ar, 2040); + * printf("capacity = %d\n", vector_capacity(&ar)); + * vector_delete(&ar); + * ... + * @endcode + * @memberof vector + * @param self [in] vector object @em self pointer + * @return The number of total elements that conform the vector's content + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern size_t vector_capacity(vector_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +extern size_t vector_elem_size(vector_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Save (write) an @em vector object to a stream + * @details For example, + * @code + * ... + * vector_init(&ar, 4, 1024); + * vector_put(&ar, 7, &count); + * vector_put(&ar, 7000, &count); + * ... + * FILE * f = fopen("...", "w"); + * ... + * vector_save(&ar, f); + * vector_delete(&ar); + * ... + * @endcode + * @memberof vector + * @param self [in] vector object @em self pointer + * @param out [in] save destination stream + * @return non-0 on success + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern ssize_t vector_save(vector_t *, FILE *) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Load (read) a @em vector object from a stream + * @details For example, + * @code + * ... + * vector ar; + * ... + * FILE * f = fopen("...", "r"); + * ... + * vector_load(&ar, f); + * vector_dump(&ar); + * ... + * @endcode + * @memberof vector + * @param self [in] vector object @em self pointer + * @param in [in] load source stream + * @return non-0 on success + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern ssize_t vector_load(vector_t *, FILE *) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Send (write) an @em vector object to a message queue + * @details For example, + * @code + * ... + * vector_init(&ar, 4, 1024); + * vector_put(&ar, 7, &count); + * vector_put(&ar, 7000, &count); + * ... + * mqueue mq; + * mqueue_init(&mq, "my_server"); + * mqueue_create(&mq, gettid()); + * ... + * vector_send(&ar, &mq); + * vector_delete(&ar); + * ... + * @endcode + * @memberof vector + * @param self [in] vector object @em self pointer + * @return non-0 on success + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int vector_send(vector_t *, mqueue_t *) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief receive (read) an @em vector object from a message queue + * @details For example, + * @code + * ... + * vector ar; + * ... + * mqueue mq; + * mqueue_open(&mq, path); + * ... + * vector_receive(&ar, &mq); + * vector_dump(&ar); + * ... + * @endcode + * @memberof vector + * @param self [in] vector object @em self pointer + * @return non-0 on success + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int vector_receive(vector_t *, mqueue_t *) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Pretty print the contents of an @em vector to stdout + * @memberof vector + * @param self [in] vector object @em self pointer + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern void vector_dump(vector_t *, FILE *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/* ======================================================================= */ + +#endif /* __VECTOR_H__ */ diff --git a/clib/vector_iter.h b/clib/vector_iter.h new file mode 100644 index 0000000..37708d0 --- /dev/null +++ b/clib/vector_iter.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! @file vector_iter.h + * @brief Vector Iterator + * @details Vectors are a kind of sequence container. As such, their elements + * are ordered following a strict linear sequence. + * @code + * ... + * vector_t vec; + * vector_init(&vec, sizeof(uint32_t), 1024); + * ... + * vector_iter_t it; + * vector_iter_init(&it, &vec, VI_FLAG_FWD); + * + * uint32_t * j; + * vector_for_each(&it, j) { + * printf("vec[%d] = %d\n", it.idx, *j); + * } + * ... + * @endcode + * @author Shaun Wetzstein + * @date 2010-2011 + */ + +#ifndef __VECTOR_ITER_H__ +#define __VECTOR_ITER_H__ + +#include +#include + +#include "builtin.h" +#include "vector.h" + +/* ======================================================================= */ + +typedef struct vector_iter vector_iter_t; //!< Alias for the @em vector_iter class + +/*! + * @brief vector iterator + * @details Vector iterator class + */ +struct vector_iter { + vector_t *vector; //!< Reference to the target vector object + size_t idx; //!< Current position of the iterator + uint32_t flags; //!< Iterator configuration flags +}; + +/* ======================================================================= */ + +#define VI_FLAG_NONE 0x00000000 //!< No flag mask +#define VI_FLAG_FWD 0x00000000 //!< Forward (FWD) flag mask +#define VI_FLAG_BWD 0x00000002 //!< Backward (BWD) flag mask +#define VI_FLAG_MASK 0x00000003 //!< All flags mask + +/*! + * @brief Initializes an @em vector_iter iterator object + * @memberof vector_iter + * @param self [in] vector_iter object @em self pointer + * @param vector [in] vector container object to iterate + * @param flags [in] iterator configuration flags + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + * @throws UNEXPECTED if @em array pointer is NULL + */ +extern int vector_iter_init(vector_iter_t *, vector_t *, uint32_t) +/*! @cond */ +__nonnull((1, 2)) /*! @endcond */ ; + +/*! + * @brief Resets an @em vector iterator object + * @memberof vector_iter + * @param self [in] vector_iter object @em self pointer + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern int vector_iter_clear(vector_iter_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @brief Return a pointer to @em vector element bytes at the current iterator position + * @memberof vector_iter + * @param self [in] vector_iter object @em self pointer + * @throws UNEXPECTED if @em self pointer is NULL + */ +extern const void *vector_iter_elem(vector_iter_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; + +/*! + * @fn const void * vector_iter_inc(vector_iter_t * self, size_t count) + * @brief Increment the position of an @em vector iterator + * @details If the second (2nd) parameter is omitted, the iterator is + * incremented by one (1) position. + * @memberof vector_iter + * @param self [in] vector_iter object @em self pointer + * @param count [in] Number of positions to increment (optional) + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define vector_iter_inc(...) STRCAT(vector_iter_inc, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern const void *vector_iter_inc1(vector_iter_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern const void *vector_iter_inc2(vector_iter_t *, size_t) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @fn const void * vector_iter_dec(vector_iter_t * self, size_t count) + * @brief decrement the position of an @em vector iterator + * @note If the second (2nd) parameter is omitted, the iterator is decremented by one (1) position. + * @memberof vector_iter + * @param self [in] vector_iter object @em self pointer + * @param count [in] Number of positions to decrement (optional) + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define vector_iter_dec(...) STRCAT(vector_iter_dec, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern const void *vector_iter_dec1(vector_iter_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern const void *vector_iter_dec2(vector_iter_t *, size_t) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @fn const void * vector_iter_pos(vector_iter_t * self, size_t pos) + * @brief Return or set the iterator position witin the @em vector + * @details If the second (2nd) parameter is omitted, this functions returns the current position. + * @memberof vector_iter + * @param self [in] vector_iter object @em self pointer + * @param pos [in] new iterator position (optional) + * @return None + * @throws UNEXPECTED if @em self pointer is NULL + */ +/*! @cond */ +#define vector_iter_pos(...) STRCAT(vector_iter_pos, NARGS(__VA_ARGS__))(__VA_ARGS__) +extern size_t vector_iter_pos1(vector_iter_t *) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +extern int vector_iter_pos2(vector_iter_t *, size_t) +/*! @cond */ +__nonnull((1)) /*! @endcond */ ; +/*! @endcond */ + +/*! + * @def vector_for_each(it, i) + * @hideinitializer + * @brief Vector for-each algorithm + * @param it [in] Tree iterator object + * @param i [in] Tree element variable + */ +#define vector_for_each(it, i) \ + for (i = (typeof(i))vector_iter_elem(it); \ + i != NULL; \ + i = (typeof(i))vector_iter_inc(it)) + +/* ======================================================================= */ + +#endif /* __VECTOR_ITER_H__ */ diff --git a/clib/version.h b/clib/version.h new file mode 100644 index 0000000..1c990b2 --- /dev/null +++ b/clib/version.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __VERSION__H__ +#define __VERSION__H__ + +#define VER_TO_MAJOR(x) (((x) & 0xFF000000) >> 24) +#define VER_TO_MINOR(x) (((x) & 0x00FF0000) >> 16) +#define VER_TO_PATCH(x) (((x) & 0x0000FF00) >> 8) + +#define MAJOR_TO_VER(x) ((0xFF & (x)) << 24) +#define MINOR_TO_VER(x) ((0xFF & (x)) << 16) +#define PATCH_TO_VER(x) ((0xFF & (x)) << 8) + +#define VER(x,y,z) (MAJOR_TO_VER(x) | MINOR_TO_VER(y) | PATCH_TO_VER(z)) + +#endif /* __VERSION__H__ */ diff --git a/clib/watch.h b/clib/watch.h new file mode 100644 index 0000000..7f09f3e --- /dev/null +++ b/clib/watch.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: watch.h + * Author: Shaun Wetzstein + * Descr: Directory activity callback monitor + * Note: + * Date: 10/03/10 + */ + +#ifndef __WATCH_H__ +#define __WATCH_H__ + +#include +#include + +#include + +#include "attribute.h" +#include "builtin.h" +#include "assert.h" + +#include "array.h" + +/* ==================================================================== */ + +#define WATCH_ACCESS IN_ACCESS +#define WATCH_ATTRIB IN_ATTRIB +#define WATCH_CLOSE_WRITE IN_CLOSE_WRITE +#define WATCH_CLOSE_NOWRITE IN_CLOSE_NOWRITE +#define WATCH_CREATE IN_CREATE +#define WATCH_DELETE IN_DELETE +#define WATCH_DELETE_SELF IN_DELETE_SELF +#define WATCH_MODIFY IN_MODIFY +#define WATCH_MOVE_SELF IN_MOVE_SELF +#define WATCH_MOVE_FROM IN_MOVE_FROM +#define WATCH_MOVE_TO IN_MOVE_TO +#define WATCH_OPEN IN_OPEN + +typedef struct watch watch_t; +typedef struct watch_callback watch_callback_t; +typedef struct inotify_event watch_event_t; + +typedef int (*watch_f) (watch_event_t *); + +struct watch { + int fd; + array_t callbacks; +}; + +struct watch_callback { + void *data; + watch_f func; +}; + +/* ======================================================================= */ + +extern void watch_init(watch_t * self) __nonnull((1)); +extern void watch_delete(watch_t * self) __nonnull((1)); + +extern int watch_fileno(watch_t * self) __nonnull((1)); + +extern uint32_t watch_add(watch_t * self, const char *path, uint32_t events, + watch_callback_t * cb) __nonnull((1, 2)); +extern void watch_remove(watch_t * self, uint32_t wd) __nonnull((1)); + +extern void watch_wait(watch_t * self) __nonnull((1)); + +/* ======================================================================= */ + +#endif /* __watch_H__ */ diff --git a/clib/x86/Makefile b/clib/x86/Makefile new file mode 100644 index 0000000..af7b947 --- /dev/null +++ b/clib/x86/Makefile @@ -0,0 +1,5 @@ +DEPTH = .. + +CLIB_INSTALL = $(INST_USR_X86) + +include ../Rules.mk diff --git a/clib/x86/cunit/Makefile b/clib/x86/cunit/Makefile new file mode 100644 index 0000000..5748fb1 --- /dev/null +++ b/clib/x86/cunit/Makefile @@ -0,0 +1,7 @@ +DEPTH = ../../../.. +include $(DEPTH)/integration/Rules.mk +include $(DEPTH)/integration/Rules.x86.mk + +CLIB_INSTALL = $(INST_TESTS_X86)/clib + +include ../../Rules.cunit.mk diff --git a/clib/x86/test/Makefile b/clib/x86/test/Makefile new file mode 100644 index 0000000..e410eee --- /dev/null +++ b/clib/x86/test/Makefile @@ -0,0 +1,7 @@ +DEPTH = ../../../.. +include $(DEPTH)/integration/Rules.mk +include $(DEPTH)/integration/Rules.x86.mk + +CLIB_INSTALL = $(INST_TESTS_X86)/clib + +include ../../Rules.test.mk diff --git a/ecc/.gitignore b/ecc/.gitignore new file mode 100644 index 0000000..b879775 --- /dev/null +++ b/ecc/.gitignore @@ -0,0 +1 @@ +*/ecc diff --git a/ecc/Makefile b/ecc/Makefile new file mode 100644 index 0000000..e08925f --- /dev/null +++ b/ecc/Makefile @@ -0,0 +1,10 @@ +SUBDIRS=x86 + +.PHONY: subdirs $(SUBDIRS) + +subdirs: $(SUBDIRS) + +$(SUBDIRS):: + $(MAKE) -C $@ $(MAKECMDGOALS) + +all clean install: $(SUBDIRS) diff --git a/ecc/Rules.mk b/ecc/Rules.mk new file mode 100644 index 0000000..a2e6caf --- /dev/null +++ b/ecc/Rules.mk @@ -0,0 +1,20 @@ +CFLAGS += -m32 -D_GNU_SOURCE -std=gnu99 -D_FILE_OFFSET_BITS=64 -I$(DEPTH)/.. -iquote.. +LDFLAGS += -L. -L$(DEPTH)/../clib/x86 -m32 + +OBJS=main.o + +TARGETS=ecc + +vpath %.c ../src +vpath %.h .. + +all: $(TARGETS) + +ecc: main.o $(DEPTH)/../clib/x86/libclib.a + $(CC) $(LDFLAGS) -o $@ $^ -lpthread + +install: $(TARGETS) + $(INSTALL) ecc $(ECC_INSTALL)/bin + +clean distclean: + $(RM) -f $(TARGETS) $(OBJS) diff --git a/ecc/src/main.c b/ecc/src/main.c new file mode 100644 index 0000000..dc6899c --- /dev/null +++ b/ecc/src/main.c @@ -0,0 +1,625 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: main.c + * Author: Shaun Wetzstein + * Descr: cmdline tool for ECC + * Date: 08/31/2012 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "main.h" + +#define ECC_SIZE 8 + +args_t args; + +static const char copyright[] __unused__ = + "Licensed Internal Code - Property of IBM\n" + "IBM Support Processor Licensed Internal Code\n" + "(c) Copyright IBM Corp 2012 All Rights Reserved\n" + "US Government Users Restricted Rights - Use, duplication\n" + "or disclosure restricted by GSA ADP Schedule Contract\n" "with IBM Corp."; + +static void usage(const char *short_name, bool verbose) +{ + const char *n = short_name; + FILE *e = stderr; + + fprintf(e, "FSP NOR ECC Tool v%d.%d.%d -- Authors: \n" + "(c) Copyright IBM Corp 2012 All Rights Reserved\n", + ECC_MAJOR, ECC_MINOR, ECC_PATCH); + + fprintf(e, "\nUsage:\n"); + fprintf(e, " %s ...\n", n); + + fprintf(e, "\nExamples:\n"); + fprintf(e, " %s --inject sample.nor --output sample.nor.ecc\n", n); + fprintf(e, " %s --remove sample.nor.ecc --output sample.nor\n", n); + fprintf(e, " %s --hexdump sample.nor.ecc\n", n); + + fprintf(e, "\nCommands:\n"); + fprintf(e, " -I, --inject [options]\n"); + if (verbose) + fprintf(e, + "\n Make a copy of the input file and inject an ECC syndrom byte for\n" + " every 8 bytes of input data.\n\n"); + + fprintf(e, " -R, --remove [options]\n"); + if (verbose) + fprintf(e, + "\n Make a copy of the input file and remove the ECC syndrom byte for\n" + " for every 9 byte of input data.\n\n"); + + fprintf(e, " -H, --hexdump [options]\n"); + if (verbose) + fprintf(e, + "\n Hex dump the contents of file to stdout.\n\n"); + + fprintf(e, "\nOptions:\n"); + fprintf(e, " -o, --output \n"); + if (verbose) + fprintf(e, "\n Specifies the output file path name.\n\n"); + + fprintf(e, " -h, --help\n"); + if (verbose) + fprintf(e, "\n Write this help text to stderr and exit\n"); + + fprintf(e, "\nFlags:\n"); + fprintf(e, " -p, --p8\n"); + if (verbose) + fprintf(e, + "\n Invert the ECC bits for the P8 ECC engine.\n\n"); + + fprintf(e, "\n"); + + fprintf(e, + "Report bugs to (vendor='MCP for FSP*')\n"); +} + +static int process_argument(args_t * args, int opt, const char *optarg) +{ + assert(args != NULL); + + switch (opt) { + case c_INJECT: /* inject */ + case c_REMOVE: /* remove */ + case c_HEXDUMP: /* hexdump */ + if (args->cmd != c_ERROR) { + UNEXPECTED("commands '%c' and '%c' are mutually " + "exclusive", args->cmd, opt); + return -1; + } + + args->cmd = (cmd_t) opt; + args->path = strdup(optarg); + + break; + case o_OUTPUT: /* offset */ + args->file = strdup(optarg); + break; + case f_FORCE: /* force */ + args->force = (flag_t) opt; + break; + case f_VERBOSE: /* verbose */ + args->verbose = (flag_t) opt; + break; + case f_P8: /* p8 */ + args->p8 = (flag_t) opt; + break; + case f_HELP: /* help */ + usage(args->short_name, true); + exit(EXIT_SUCCESS); + break; + case '?': /* error */ + default: + usage(args->short_name, false); + UNEXPECTED("unknown option '%c', please see " + "--help for details\n", opt); + return -1; + } + + return 0; +} + +static int process_option(args_t * args, const char *opt) +{ + assert(args != NULL); + assert(opt != NULL); + + if (args->opt_sz <= args->opt_nr) { + args->opt_sz += 5; + args->opt = (const char **)realloc(args->opt, + sizeof(*args->opt) * + args->opt_sz); + memset(args->opt + args->opt_nr, 0, + sizeof(*args->opt) * (args->opt_sz - args->opt_nr)); + } + + args->opt[args->opt_nr] = strdup(opt); + args->opt_nr++; + + return 0; +} + +bool check_extension(const char *path, const char *ext) +{ + int len = strlen(path), ext_len = strlen(ext); + return (ext_len < len) + && (strncasecmp(path + len - ext_len, ext, ext_len) == 0); +} + +static int validate_args(args_t * args) +{ + assert(args != NULL); + + if (args->cmd == c_ERROR) { + usage(args->short_name, false); + UNEXPECTED("no command specified, please see --help " + "for details\n"); + return -1; + } + + if (args->path == NULL) { + UNEXPECTED("no path specified, please see --help " + "for details"); + return -1; + } + + #define REQUIRED(name,cmd) ({ \ + if (args->name == NULL) { \ + UNEXPECTED("--%s is required for the --%s command", \ + #name, #cmd); \ + return -1; \ + } \ + }) + + #define UNSUPPORTED(name,cmd) ({ \ + if (args->name != NULL) { \ + UNEXPECTED("--%s is unsupported for the --%s command", \ + #name, #cmd); \ + return -1; \ + } \ + }) + + + if (args->cmd == c_INJECT) { + if (check_extension(args->path, ECC_EXT)) { + UNEXPECTED("'%s' extension '%s' already exists " + "-- ignored", args->path, ECC_EXT); + return -1; + } + + if (args->file == NULL) { + args->file = (char *)malloc(strlen(args->path) + 8); + assert(args->file != NULL); + sprintf((char *)args->file, "%s%s", args->path, + ECC_EXT); + + if (args->force != f_FORCE) + fprintf(stderr, "%s: --output missing, " + "writing output to '%s'\n", + args->short_name, args->file); + } + } else if (args->cmd == c_REMOVE) { + if (!check_extension(args->path, ECC_EXT)) { + UNEXPECTED("'%s' unknown extension, must be '%s' -- " + "ignored", args->path, ECC_EXT); + return -1; + } + + if (args->file == NULL) { + args->file = strdup(args->path); + assert(args->file != NULL); + sprintf((char *)args->file, "%*s", + strlen(args->path) - strlen(ECC_EXT), + args->path); + + fprintf(stderr, "%s: --output missing, writing " + "output to '%s'\n", + args->short_name, args->file); + } + } else if (args->cmd == c_HEXDUMP) { + if (!check_extension(args->path, ECC_EXT)) { + UNEXPECTED("'%s' unknown extension, must be '%s' -- " + "ignored", args->path, ECC_EXT); + return -1; + } + } else { + UNEXPECTED("'%c' invalid command", args->cmd); + return -1; + } + + return 0; +} + +static int command_inject(args_t * args) +{ + assert(args != NULL); + + struct stat st; + if (stat(args->path, &st) != 0) { + ERRNO(errno); + return -1; + } + + if (!S_ISREG(st.st_mode)) { + ERRNO(errno); + return -1; + } + + FILE *i = fopen(args->path, "r"); + if (i == NULL) { + ERRNO(errno); + return-1; + } + + FILE *o = fopen(args->file, "w"); + if (o == NULL) { + ERRNO(errno); + return -1; + } + +#define INPUT_SIZE (4096 - (4096 / ECC_SIZE)) + char input[INPUT_SIZE]; // 4KB less 512 ECC bytes +#undef INPUT_SIZE + + size_t count = 0; + while (count < st.st_size) { + clearerr(i); + + size_t rc = fread(input, 1, sizeof input, i); + if (rc == 0) { + int err = ferror(i); + if (err) { + ERRNO(errno); + return -1; + } else + break; + } + + count += rc; + +#define OUTPUT_SIZE 4096 + char output[OUTPUT_SIZE]; +#undef OUTPUT_SIZE + + memset(output + sizeof input, 0, sizeof output - sizeof input); + + rc = (rc + 7) & ~7; // 8-byte alignment + + ssize_t injected_size = 0; + if (args->p8 == f_P8) + injected_size = + p8_ecc_inject(output, sizeof output, input, rc); + else + injected_size = + sfc_ecc_inject(output, sizeof output, input, rc); + if (injected_size < 0) { + ERRNO(errno); + return -1; + } + + clearerr(o); + rc = fwrite(output, 1, injected_size, o); + if (rc == 0) { + int err = ferror(o); + if (err) { + ERRNO(errno); + return -1; + } + } + } + + if (fclose(i) == EOF) { + ERRNO(errno); + return -1; + } + + if (fclose(o) == EOF) { + ERRNO(errno); + return -1; + } + + return 0; +} + +static int command_remove(args_t * args) +{ + assert(args != NULL); + + struct stat st; + if (stat(args->path, &st) != 0) { + ERRNO(errno); + return -1; + } + + if (!S_ISREG(st.st_mode)) { + ERRNO(errno); + return -1; + } + + FILE *i = fopen(args->path, "r"); + if (i == NULL) { + ERRNO(errno); + return -1; + } + + FILE *o = fopen(args->file, "w"); + if (o == NULL) { + ERRNO(errno); + return -1; + } + +#define INPUT_SIZE 4086 + char input[INPUT_SIZE]; // multiple of 9-bytes +#undef INPUT_SIZE + + size_t count = 0; + while (count < st.st_size) { + clearerr(i); + + size_t rc = fread(input, 1, sizeof input, i); + if (rc == 0) { + int err = ferror(i); + if (err) { + ERRNO(errno); + return -1; + } else + break; + } + + count += rc; + + char output[(((sizeof input)/(ECC_SIZE+1))*ECC_SIZE) ]; + + ssize_t removed_size; + if (args->p8 == f_P8) + removed_size = + p8_ecc_remove_size(output, sizeof output, input, rc); + else + removed_size = + sfc_ecc_remove(output, sizeof output, input, rc); + if (removed_size < 0) { + ERRNO(errno); + return -1; + } + + clearerr(o); + rc = fwrite(output, 1, removed_size, o); + if (rc == 0) { + int err = ferror(o); + if (err) { + ERRNO(errno); + return -1; + } + } + } + + if (fclose(i) == EOF) { + ERRNO(errno); + return -1; + } + + if (fclose(o) == EOF) { + ERRNO(errno); + return -1; + } + + return 0; +} + +static int command_hexdump(args_t * args) +{ + assert(args != NULL); + + struct stat st; + if (stat(args->path, &st) != 0) { + ERRNO(errno); + return -1; + } + + if (!S_ISREG(st.st_mode)) { + ERRNO(errno); + return -1; + } + + FILE *i = fopen(args->path, "r"); + if (i == NULL) { + ERRNO(errno); + return -1; + } + + FILE *o = stdout; + if (args->file != NULL) { + o = fopen(args->file, "w"); + if (o == NULL) { + ERRNO(errno); + return -1; + } + } +#define INPUT_SIZE 4086 + char input[INPUT_SIZE]; // multiple of 9-bytes +#undef INPUT_SIZE + + if (setvbuf(i, NULL, _IOFBF, __round_pow2(sizeof input)) != 0) { + ERRNO(errno); + return -1; + } + + size_t count = 0; + while (count < st.st_size) { + clearerr(i); + + size_t rc = fread(input, 1, sizeof input, i); + if (rc == 0) { + int err = ferror(i); + if (err) { + ERRNO(errno); + return -1; + } else + break; + } + + if (args->p8 == f_P8) + p8_ecc_dump(o, count, input, rc); + else + sfc_ecc_dump(o, count, input, rc); + + count += rc; + } + + if (fclose(i) == EOF) { + ERRNO(errno); + return -1; + } + + if (o != stdout) { + if (fclose(o) == EOF) { + ERRNO(errno); + return -1; + } + } + + return 0; +} + +static int process_args(args_t * args) +{ + assert(args != NULL); + + switch (args->cmd) { + case c_INJECT: + command_inject(args); + break; + case c_REMOVE: + command_remove(args); + break; + case c_HEXDUMP: + command_hexdump(args); + break; + default: + UNEXPECTED("NOT IMPLEMENTED YET => '%c'", args->cmd); + return -1; + } + + return 0; +} + +static void args_dump(args_t * args) +{ + assert(args != NULL); + + printf("short_name[%s]\n", args->short_name); + printf("path[%s]\n", args->path); + printf("cmd[%d]\n", args->cmd); + printf("output[%s]\n", args->file); + printf("force[%d]\n", args->force); + printf("p8[%d]\n", args->p8); + printf("verbose[%d]\n", args->force); +} + +int main(int argc, char *argv[]) +{ + static const struct option long_opts[] = { + /* commands */ + {"inject", required_argument, NULL, c_INJECT}, + {"remove", required_argument, NULL, c_REMOVE}, + {"hexdump", required_argument, NULL, c_HEXDUMP}, + /* options */ + {"output", required_argument, NULL, o_OUTPUT}, + /* flags */ + {"force", no_argument, NULL, f_FORCE}, + {"p8", no_argument, NULL, f_P8}, + {"verbose", no_argument, NULL, f_VERBOSE}, + {"help", no_argument, NULL, f_HELP}, + {0, 0, 0, 0} + }; + + static const char *short_opts = "I:R:H:o:fpvh"; + + int rc = EXIT_FAILURE; + + if (argc == 1) + usage(args.short_name, false), exit(rc); + + int opt = 0, idx = 0; + while ((opt = getopt_long(argc, argv, short_opts, long_opts, + &idx)) != -1) + if (process_argument(&args, opt, optarg) < 0) + goto error; + + /* getopt_long doesn't know what to do with orphans, */ + /* so we'll scoop them up here, and deal with them later */ + + while (optind < argc) + if (process_option(&args, argv[optind++]) < 0) + goto error; + + if (args.verbose == f_VERBOSE) + args_dump(&args); + + if (validate_args(&args) < 0) + goto error; + if (process_args(&args) < 0) + goto error; + + rc = EXIT_SUCCESS; + + if (false) { + err_t *err; +error: + err = err_get(); + assert(err != NULL); + + fprintf(stderr, "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + } + + return rc; +} + +static void __ctor__(void) __constructor; +static void __ctor__(void) +{ + /* early initialization before main() is called the crt0 */ + args.short_name = program_invocation_short_name; +} diff --git a/ecc/src/main.h b/ecc/src/main.h new file mode 100644 index 0000000..c4f04a0 --- /dev/null +++ b/ecc/src/main.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: main.h + * Author: Shaun Wetzstein + * Descr: cmdline tool for ECC + * Date: 08/30/2012 + */ + +#ifndef __MAIN_H__ +#define __MAIN_H__ + +#include + +typedef enum { + c_ERROR = 0, + c_INJECT = 'I', + c_REMOVE = 'R', + c_HEXDUMP = 'H', +} cmd_t; + +typedef enum { + o_ERROR = 0, + o_OUTPUT = 'o', +} option_t; + +typedef enum { + f_ERROR = 0, + f_FORCE = 'f', + f_P8 = 'p', + f_VERBOSE = 'v', + f_HELP = 'h', +} flag_t; + +typedef struct { + const char * short_name; + + /* target */ + const char * path; + + /* command */ + cmd_t cmd; + + /* options */ + const char * file; + + /* flags */ + flag_t force, p8, verbose; + + const char ** opt; + int opt_sz, opt_nr; +} args_t; + +extern args_t args; + +#define ECC_MAJOR 0x02 +#define ECC_MINOR 0x00 +#define ECC_PATCH 0x00 + +#define ECC_EXT ".ecc" + +#endif /* __MAIN_H__ */ diff --git a/ecc/x86/Makefile b/ecc/x86/Makefile new file mode 100644 index 0000000..e111ae8 --- /dev/null +++ b/ecc/x86/Makefile @@ -0,0 +1,6 @@ +DEPTH = .. + +ARCH=x86 +ECC_INSTALL = $(INST_USR_X86) + +include ../Rules.mk diff --git a/fcp/.gitignore b/fcp/.gitignore new file mode 100644 index 0000000..3a1f0a1 --- /dev/null +++ b/fcp/.gitignore @@ -0,0 +1,3 @@ +*/ffs +*/fcp +test/test_libffs diff --git a/fcp/Makefile b/fcp/Makefile new file mode 100644 index 0000000..e08925f --- /dev/null +++ b/fcp/Makefile @@ -0,0 +1,10 @@ +SUBDIRS=x86 + +.PHONY: subdirs $(SUBDIRS) + +subdirs: $(SUBDIRS) + +$(SUBDIRS):: + $(MAKE) -C $@ $(MAKECMDGOALS) + +all clean install: $(SUBDIRS) diff --git a/fcp/Rules.mk b/fcp/Rules.mk new file mode 100644 index 0000000..490afd4 --- /dev/null +++ b/fcp/Rules.mk @@ -0,0 +1,30 @@ +CFLAGS += -m32 -D_GNU_SOURCE -std=gnu99 -D_FILE_OFFSET_BITS=64 +CFLAGS += -I$(DEPTH)/.. -iquote.. + +LDFLAGS += -L. -m32 + +NAME=fcp + +CLIB=$(DEPTH)/../clib/x86/libclib.a +FFS=$(DEPTH)/../ffs/x86/libffs.a + +OBJS=cmd_list.o cmd_read.o cmd_write.o cmd_erase.o \ + cmd_copy.o cmd_trunc.o cmd_user.o misc.o main.o + +TARGETS=$(NAME) + +vpath %.c ../src +vpath %.h .. + +all: $(TARGETS) + +$(NAME): $(OBJS) $(FFS) $(CLIB) + $(CC) $(LDFLAGS) -Wl,-no-whole-archive \ + -o $@ $^ -lpthread -ldl -lrt + +install: $(TARGETS) + $(INSTALL) fcp $(FCP_INSTALL)/bin + $(INSTALL) ../fcp.sh $(FCP_INSTALL_TEST) + +clean distclean: + $(RM) -f $(TARGETS) $(OBJS) diff --git a/fcp/fcp.sh b/fcp/fcp.sh new file mode 100755 index 0000000..6fa56a1 --- /dev/null +++ b/fcp/fcp.sh @@ -0,0 +1,313 @@ +#!/bin/bash +# =================================================================== +# Copyright (c) International Business Machines Corp., 2012 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +# the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# File: ffs cmdline test script +# Author: Shaun wetzstein +# Date: 02/06/13 +# +# Todo: +# 1) function to check syntax of each command + +CP=cp +RM=rm +FFS=ffs +FCP=fcp +MKDIR=mkdir +GREP=grep +HEAD=head +HEX=hexdump +CRC=crc32 +DD=dd +DIFF=diff +CAT=cat +CP=cp +TRUNC=truncate + +TARGET=test.nor +COPY=copy.nor +TMP=/tmp/fcp.$$ +URANDOM=/dev/urandom +POFFSET="0x00000,0x10000" + +FAIL=1 +PASS=0 + +KB=$((1*1024)) +MB=$(($KB*1024)) + +function expect() +{ + local expect=${1} + shift + local command=${*} + eval $command + local actual=${?} + + if [[ ${expect} -eq ${actual} ]]; then + echo "[PASSED] rc: '${command}' ===> expect=${expect}, actual=${actual}" >&2 + else + echo "[FAILED] rc: '${command}' ===> expect=${expect}, actual=${actual}" >&2 + exit 1 + fi +} + +function pass() +{ + expect ${PASS} ${*} +} + +function fail() +{ + expect ${FAIL} ${*} +} + +function size() +{ + local size=$(stat -L -c %s ${2}) + if [[ ${1} == ${size} ]]; then + echo "[PASSED] size: '${2}' ===> expect=${1}, actual=${size}" + else + echo "[FAILED] size: '${2}' ===> expect=${1}, actual=${size}" + exit 1 + fi +} + +function crc() +{ + local crc=$(${CRC} ${2}) + if [[ ${1} == ${crc} ]]; then + echo "[PASSED] crc: '${2}' ===> expect=${1}, actual=${crc}" + else + echo "[FAILED] crc: '${2}' ===> expect=${1}, actual=${crc}" + exit 1 + fi +} + +function setup() +{ + local target=${TMP}/${TARGET} + local output=${TMP}/${TARGET}.txt + + pass ${RM} -rf ${TMP} + pass ${MKDIR} -p ${TMP} + pass ${RM} -f ${target} + + pass ${FFS} -t ${target} -s 64M -b 64K -p 0x3F0000 -C + pass ${FFS} -t ${target} -s 64M -b 64K -p 0x7F0000 -C + + for ((j=0; j<2; j++)); do + local name="logical${j}" + local base=$((${j}*$MB*4)) + + pass ${FFS} -t ${target} -n ${name} -g 0 -l -A + pass ${FFS} -t ${target} -n ${name} -L > ${output} + + pass ${GREP} ${name} ${output} > /dev/null + pass ${GREP} "l-----" ${output} > /dev/null + + for ((i=0; i<4; i++)); do + local full="${name}/entry${i}" + local offset=$((${base}+${i}*$MB)) + + if [[ ${i} -eq 3 ]]; then + local size=$(($MB-64*${KB})) + else + local size=$MB + fi + + pass ${FFS} -t ${target} -o ${offset} -s ${size} \ + -g 0 -n ${full} -a ${i} -A + + pass ${FFS} -t ${target} -n ${full} -L > ${output} + pass ${GREP} ${full} ${output} > /dev/null + pass ${GREP} "d-----" ${output} > /dev/null + + local range=$(printf "%.8x-%.8x" ${offset} \ + $((${offset}+${size}-1))) + + pass ${GREP} ${range} ${output} > /dev/null + pass ${GREP} $(printf "%x" ${size}) ${output} > \ + /dev/null + pass ${RM} -f ${output} + done + done +} + +function cleanup() +{ + pass ${RM} -rf ${TMP} +} + +function erase() +{ + local target=${TMP}/${TARGET} + local name="logical" + + for ((i=0; i<4; i++)); do + local output=${TMP}/entry${i}.hex + local full=${name}0/entry${i} + + pass ${FCP} ${target}:${full} -E ${i} + pass ${FCP} -T ${target}:${full} + pass ${FCP} -R ${target}:${full} - | ${HEX} -C > ${output} + + local p=$(printf "%2.2x %2.2x %2.2x %2.2x" $i $i $i $i) + pass ${GREP} \"${p} ${p}\" ${output} + + pass ${RM} -f ${output} + done + + pass ${FCP} ${target}:${name}0 -E 0x00 + pass ${FCP} ${target}:${name}1 -E 0x00 +} + +function write() +{ + local target=${TMP}/${TARGET} + local output=${TMP}/${TARGET}.txt + + local name="logical" + local input=${TMP}/write.in + local output=${TMP}/write.out + + if [[ -z "${1}" ]]; then + local block=$((64*KB)) + else + local block=${1} + fi + + for ((i=0; i<4; i++)); do + if [[ ${i} -eq 3 ]]; then + local size=$(($MB-64*${KB})) + else + local size=$MB + fi + + local count=$((${size}/$block)) + + for ((c=1; c<=${count}; c++)); do + pass ${DD} if=${URANDOM} of=${input} bs=${block} \ + count=${c} 2> /dev/null + + pass ${CRC} ${input} > ${output} + local crc=$(${CAT} ${output}) + local full=${name}0/entry${i} + + pass ${FCP} ${target}:${full} -E 0x00 + pass ${FCP} -W ${input} ${target}:${full} + pass ${FCP} ${target}:${full} -U 0=${crc} + pass ${FCP} ${target}:${full} -R - -f | cat > ${output} + + size $((${c}*${block})) ${output} + crc ${crc} ${output} + + local crc=$(printf "%x" ${crc}) + pass "${FCP} ${target}:${full} -o 0x3F0000 -U 0 | \ + ${GREP} ${crc}" > /dev/null + pass "${FCP} ${target}:${full} -o 0x7F0000 -U 0 | \ + ${GREP} ${crc}" > /dev/null + + pass ${RM} -f ${input} ${output} + done + done +} + +function copy() +{ + local src=${TMP}/${TARGET} + local dst=${TMP}/${TARGET}.copy + + local input=${TMP}/copy.in + local output=${TMP}/copy.out + + if [[ -z "${1}" ]]; then + local block=$((64*KB)) + else + local block=${1} + fi + + pass ${TRUNC} -s $(stat -L -c %s ${src}) ${dst} + + for ((i=0; i<4; i++)); do + if [[ ${i} -eq 3 ]]; then + local size=$(($MB-64*${KB})) + else + local size=$MB + fi + + local count=$((${size}/$block)) + + for ((c=1; c<=${count}; c++)); do + pass ${DD} if=${URANDOM} of=${input} bs=${block} count=${c} 2> /dev/null + local crc=$(${CRC} ${input}) + + local name="logical0/entry${i}" + + pass ${FCP} ${src}:${name} -E 0x00 + pass ${FCP} ${input} ${src}:${name} -W + pass ${FCP} ${src}:${name} ${output}.src -f -R + pass ${FCP} ${src}:${name} -U 0 0=${crc} + + pass ${FCP} ${src}:${name} ${dst}:${name} -C -v -f + pass ${FCP} ${src}:${name} ${dst}:${name} -M -v + + pass ${FCP} ${dst}:${name} ${output}.dst -R -f + size $((${c}*${block})) ${output}.src + size $((${c}*${block})) ${output}.dst + crc ${crc} ${output}.src + crc ${crc} ${output}.dst + pass ${DIFF} ${output}.src ${output}.dst + + local crc=$(printf "%x" ${crc}) + pass "${FCP} ${dst}:${name} -o 0x3F0000 -U 0 | \ + ${GREP} ${crc}" > /dev/null + pass "${FCP} ${dst}:${name} -o 0x7F0000 -U 0 | \ + ${GREP} ${crc}" > /dev/null + + pass ${RM} -f ${input}* ${output}* + done + done + + pass ${FCP} ${src}":logical0" ${src}":logical1" -C -v # logical mirror + pass ${FCP} ${src}":logical1" ${dst}":logical1" -C -v # logical copy + + expect 2 ${DIFF} ${src} ${dst} +} + +function main() +{ + erase + write $((21*$KB)) + write $((64*$KB)) + copy $((21*$KB)) + copy $((64*$KB)) +} + +setup +if [[ -z "${1:-}" ]]; then + main +else + case "$1" in + erase ) erase ;; + write ) write ;; + copy ) copy ;; + * ) echo "$1 not implemented"; exit 1 ;; + esac + exit 0; +fi +cleanup diff --git a/fcp/ppc/Makefile b/fcp/ppc/Makefile new file mode 100644 index 0000000..65df0dc --- /dev/null +++ b/fcp/ppc/Makefile @@ -0,0 +1,9 @@ +DEPTH = ../../.. +include $(DEPTH)/integration/Rules.mk +include $(DEPTH)/integration/Rules.ppc.mk + +ARCH=ppc +FCP_INSTALL = $(INST_USR) +FCP_INSTALL_TEST = $(INST_TESTS) + +include ../Rules.mk diff --git a/fcp/src/cmd_copy.c b/fcp/src/cmd_copy.c new file mode 100644 index 0000000..4b496b6 --- /dev/null +++ b/fcp/src/cmd_copy.c @@ -0,0 +1,582 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: cmd_copy.c + * Author: Shaun Wetzstein + * Descr: copy & compare implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" +#include "main.h" + +static int validate_files(ffs_t * src, ffs_t * dst) +{ + assert(src != NULL); + assert(dst != NULL); + + size_t s, d; + + if (__ffs_info(src, FFS_INFO_BLOCK_SIZE, &s) < 0) + return -1; + if (__ffs_info(dst, FFS_INFO_BLOCK_SIZE, &d) < 0) + return -1; + if (s != d) { + UNEXPECTED("source '%s' and destination '%s' block " + "size differs, use --force to overwrite\n", + src->path, dst->path); + return -1; + } + + if (__ffs_info(src, FFS_INFO_BLOCK_COUNT, &s) < 0) + return -1; + if (__ffs_info(dst, FFS_INFO_BLOCK_COUNT, &d) < 0) + return -1; + if (s != d) { + UNEXPECTED("source '%s' and destination '%s' block " + "count differs, use --force to overwrite\n", + src->path, dst->path); + return -1; + } + + if (__ffs_info(src, FFS_INFO_ENTRY_SIZE, &s) < 0) + return -1; + if (__ffs_info(dst, FFS_INFO_ENTRY_SIZE, &d) < 0) + return -1; + if (s != d) { + UNEXPECTED("source '%s' and destination '%s' entry " + "size differs, use --force to overwrite\n", + src->path, dst->path); + return -1; + } + + if (__ffs_info(src, FFS_INFO_VERSION, &s) < 0) + return -1; + if (__ffs_info(dst, FFS_INFO_VERSION, &d) < 0) + return -1; + if (s != d) { + UNEXPECTED("source '%s' and destination '%s' version " + "differs, use --force to overwrite\n", + src->path, dst->path); + return -1; + } + + return 0; +} + +static int __copy_entry(args_t * args, + ffs_t * src_ffs, ffs_entry_t * src_entry, + ffs_t * dst_ffs, ffs_entry_t * dst_entry, + entry_list_t * done_list) +{ + char full_src_name[page_size]; + if (__ffs_entry_name(src_ffs, src_entry, full_src_name, + sizeof full_src_name) < 0) { + return -1; + } + + char full_dst_name[page_size]; + if (__ffs_entry_name(src_ffs, dst_entry, full_dst_name, + sizeof full_dst_name) < 0) { + return -1; + } + + if (dst_entry->type == FFS_TYPE_LOGICAL) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: logical partition (skip)\n", + dst_ffs->offset, full_dst_name); + return 0; + } + + if (src_entry->type == FFS_TYPE_LOGICAL) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: logical partition (skip)\n", + src_ffs->offset, full_src_name); + return 0; + } + + if (dst_entry->type == FFS_TYPE_PARTITION) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: partition table (skip)\n", + dst_ffs->offset, full_dst_name); + return 0; + } + + if (src_entry->type == FFS_TYPE_PARTITION) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: partition table (skip)\n", + src_ffs->offset, full_src_name); + return 0; + } + + if (args->protected != f_PROTECTED) { + if (dst_entry->flags & FFS_FLAGS_PROTECTED) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: protected " + "partition (skip)\n", dst_ffs->offset, + full_dst_name); + return 0; + } + if (src_entry->flags & FFS_FLAGS_PROTECTED) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: protected " + "partition (skip)\n", src_ffs->offset, + full_src_name); + return 0; + } + } + + if (src_entry->size != dst_entry->size) { + UNEXPECTED("%8llx: %s: source partition '%s' size mismatch " + "'%x', use --force to overwrite\n", dst_ffs->offset, + full_dst_name, full_src_name, dst_entry->size); + return -1; + } + + if (src_entry->type != dst_entry->type) { + UNEXPECTED("%8llx: %s: source partition '%s' type mismatch " + "'%x', use --force to overwrite\n", dst_ffs->offset, + full_dst_name, full_src_name, dst_entry->size); + return -1; + } + + if (__ffs_entry_truncate(dst_ffs, full_dst_name, + src_entry->actual) < 0) { + ERRNO(errno); + return -1; + } + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: trunc size '%x' (done)\n", + dst_ffs->offset, full_dst_name, src_entry->actual); + + uint32_t src_val, dst_val; + for (uint32_t i=0; iforce != f_FORCE && i == USER_DATA_VOL) + continue; + + if (src_val != dst_val) { + if (__ffs_entry_user_put(dst_ffs, full_dst_name, + i, src_val) < 0) + return -1; + } + } + if (args->verbose == f_VERBOSE) + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: copy user[] from '%s' " + "(done)\n", dst_ffs->offset, full_dst_name, + src_ffs->path); + + if (entry_list_exists(done_list, src_entry) == 1) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: copy from '%s' (skip)\n", + dst_ffs->offset, full_dst_name, src_ffs->path); + return 0; + } + + if (entry_list_add(done_list, src_entry) < 0) + return -1; + + if (fcp_copy_entry(src_ffs, full_src_name, dst_ffs, full_dst_name) < 0) + return -1; + + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: copy from '%s' (done)\n", + dst_ffs->offset, full_dst_name, src_ffs->path); + + return 0; +} + +static int __compare_entry(args_t * args, + ffs_t * src_ffs, ffs_entry_t * src_entry, + ffs_t * dst_ffs, ffs_entry_t * dst_entry, + entry_list_t * done_list) +{ + char full_src_name[page_size]; + if (__ffs_entry_name(src_ffs, src_entry, full_src_name, + sizeof full_src_name) < 0) { + return -1; + } + + char full_dst_name[page_size]; + if (__ffs_entry_name(src_ffs, dst_entry, full_dst_name, + sizeof full_dst_name) < 0) { + return -1; + } + + if (dst_entry->type == FFS_TYPE_LOGICAL) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: logical partition (skip)\n", + dst_ffs->offset, full_dst_name); + return 0; + } + + if (src_entry->type == FFS_TYPE_LOGICAL) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: logical partition (skip)\n", + src_ffs->offset, full_src_name); + return 0; + } + + if (args->protected != f_PROTECTED) { + if (dst_entry->flags & FFS_FLAGS_PROTECTED) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: protected " + "partition (skip)\n", dst_ffs->offset, + full_dst_name); + return 0; + } + if (src_entry->flags & FFS_FLAGS_PROTECTED) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: protected " + "partition (skip)\n", src_ffs->offset, + full_src_name); + return 0; + } + } + + if (src_entry->size != dst_entry->size) { + UNEXPECTED("%8llx: %s: source partition '%s' size mismatch " + "'%x', use --force to overwrite\n", dst_ffs->offset, + full_dst_name, full_src_name, dst_entry->size); + return -1; + } + + if (src_entry->type != dst_entry->type) { + UNEXPECTED("%8llx: %s: source partition '%s' type mismatch " + "'%x', use --force to overwrite\n", dst_ffs->offset, + full_dst_name, full_src_name, dst_entry->size); + return -1; + } + + if (entry_list_exists(done_list, src_entry) == 1) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: compare from '%s' (skip)\n", + dst_ffs->offset, full_dst_name, src_ffs->path); + return 0; + } + + if (entry_list_add(done_list, src_entry) < 0) + return -1; + + if (fcp_compare_entry(src_ffs, full_src_name, + dst_ffs, full_dst_name) < 0) + return -1; + + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: compare from '%s' (done)\n", + dst_ffs->offset, full_dst_name, src_ffs->path); + + return 0; +} + +static int __force_part(ffs_t * src, FILE * dst) +{ + assert(src != NULL); + assert(dst != NULL); + + size_t block_size; + if (__ffs_info(src, FFS_INFO_BLOCK_SIZE, &block_size) < 0) + return -1; + + char part[block_size]; + memset(part, 0, block_size); + + if (__ffs_entry_read(src, FFS_PARTITION_NAME, part, + 0, block_size) < 0) + return -1; + + uint32_t offset; + if (__ffs_info(src, FFS_INFO_OFFSET, &offset) < 0) + return -1; + + if (fseek(dst, offset, SEEK_SET) < 0) { + ERRNO(errno); + return -1; + } + + if (fwrite(part, 1, block_size, dst) != block_size) { + if (ferror(dst)) { + ERRNO(errno); + return -1; + } + } + + if (fflush(dst) == EOF) { + ERRNO(errno); + return -1; + } + + return 0; +} + +static int __copy_compare(args_t * args, off_t offset, entry_list_t * done_list) +{ + assert(args != NULL); + assert(done_list != NULL); + + char * src_type = args->src_type; + char * src_target = args->src_target; + char * src_name = args->src_name; + + char * dst_type = args->dst_type; + char * dst_target = args->dst_target; + char * dst_name = src_name; + + if (args->dst_name != NULL && *args->dst_name != '\0') + dst_name = args->dst_name; + + if (src_name == NULL) + src_name = "*"; + if (dst_name == NULL) + dst_name = "*"; + + RAII(FILE*, src_file, __fopen(src_type, src_target, "r", debug), + fclose); + if (src_file == NULL) + return -1; + if (check_file(src_target, src_file, offset) < 0) + return -1; + RAII(ffs_t*, src_ffs, __ffs_fopen(src_file, offset), __ffs_fclose); + if (src_ffs == NULL) + return -1; + + src_ffs->path = basename(src_target); + done_list->ffs = src_ffs; + + RAII(FILE*, dst_file, __fopen(dst_type, dst_target, "r+", debug), + fclose); + if (dst_file == NULL) + return -1; + + if (args->force == f_FORCE && args->cmd == c_COPY) { + if (__force_part(src_ffs, dst_file) < 0) + return -1; + + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: partition table '%s' => '%s' " + "(done)\n", offset, src_target, dst_target); + } + + if (check_file(dst_target, dst_file, offset) < 0) + return -1; + + RAII(ffs_t*, dst_ffs, __ffs_fopen(dst_file, offset), __ffs_fclose); + if (dst_ffs == NULL) + return -1; + + dst_ffs->path = basename(dst_target); + + if (validate_files(src_ffs, dst_ffs) < 0) + return -1; + + if (src_ffs->count <= 0) // fix me + return 0; + + size_t block_size; + if (__ffs_info(src_ffs, FFS_INFO_BLOCK_SIZE, &block_size) < 0) + return -1; + + if (args->buffer != NULL) { + size_t buffer; + if (parse_size(args->buffer, &buffer) < 0) + return -1; + if (__ffs_buffer(src_ffs, buffer) < 0) + return -1; + if (__ffs_buffer(dst_ffs, buffer) < 0) + return -1; + } + + ffs_entry_t src_parent; + ffs_entry_t dst_parent; + + if (strcmp(src_name, "*") == 0 && strcmp(dst_name, "*") == 0) { + src_parent.type = FFS_TYPE_LOGICAL; + src_parent.id = -1; + dst_parent.type = FFS_TYPE_LOGICAL; + dst_parent.id = -1; + } else if ((src_name != NULL) && (dst_name != NULL)) { + if (__ffs_entry_find(src_ffs, src_name, &src_parent) == false) { + UNEXPECTED("%8llx: partition entry '%s' not found in " + "'%s'\n", src_ffs->offset, src_name, + src_target); + return -1; + } + + if (__ffs_entry_find(dst_ffs, dst_name, &dst_parent) == false) { + UNEXPECTED("%8llx: partition entry '%s' not found in " + "'%s'\n", dst_ffs->offset, dst_name, + dst_target); + return -1; + } + } + + if (src_parent.type == FFS_TYPE_DATA && + dst_parent.type == FFS_TYPE_DATA) { + if (args->cmd == c_COPY) + return __copy_entry(args, src_ffs, &src_parent, + dst_ffs, &dst_parent, done_list); + else + return __compare_entry(args, src_ffs, &src_parent, + dst_ffs, &dst_parent, done_list); + } else if (src_parent.type == FFS_TYPE_LOGICAL && + dst_parent.type == FFS_TYPE_LOGICAL) { + + RAII(entry_list_t*, src_list, entry_list_create(src_ffs), + entry_list_delete); + if (src_list == NULL) + return -1; + if (entry_list_add_child(src_list, &src_parent) < 0) + return -1; + + RAII(entry_list_t*, dst_list, entry_list_create(dst_ffs), + entry_list_delete); + if (dst_list == NULL) + return -1; + if (entry_list_add_child(dst_list, &dst_parent) < 0) + return -1; + + while (list_empty(&src_list->list) == 0) { + RAII(entry_node_t*, src_node, + container_of(list_head(&src_list->list), + entry_node_t, node), free); + + entry_list_remove(src_list, src_node); + ffs_entry_t * src_entry = &src_node->entry; + + char full_src_name[page_size]; + if (__ffs_entry_name(src_ffs, src_entry, full_src_name, + sizeof full_src_name) < 0) { + free(src_node); + return -1; + } + + if (list_empty(&dst_list->list)) { + UNEXPECTED("source entry '%s' not a child of " + "destination entry '%s'\n", + full_src_name, dst_name); + return -1; + } + + RAII(entry_node_t*, dst_node, + container_of(list_head(&dst_list->list), + entry_node_t, node), free); + + entry_list_remove(dst_list, dst_node); + ffs_entry_t * dst_entry = &dst_node->entry; + + char full_dst_name[page_size]; + if (__ffs_entry_name(dst_ffs, dst_entry, full_dst_name, + sizeof full_dst_name) < 0) + return -1; + + if (src_entry->type == FFS_TYPE_LOGICAL) { + if (entry_list_add_child(src_list, + src_entry) < 0) + return -1; + } + + if (dst_entry->type == FFS_TYPE_LOGICAL) { + if (entry_list_add_child(dst_list, + dst_entry) < 0) + return -1; + } + + if (args->cmd == c_COPY) { + if (__copy_entry(args, src_ffs, src_entry, + dst_ffs, dst_entry, + done_list) < 0) + return -1; + } else if (args->cmd == c_COMPARE) { + if (__compare_entry(args, src_ffs, src_entry, + dst_ffs, dst_entry, + done_list) < 0) { + return -1; + } + } + } + } + + return 0; +} + +int command_copy_compare(args_t * args) +{ + assert(args != NULL); + + int rc = 0; + + RAII(entry_list_t*, done_list, entry_list_create(NULL), + entry_list_delete); + if (done_list == NULL) + return -1; + + char * end = (char *)args->offset; + while (rc == 0 && end != NULL && *end != '\0') { + errno = 0; + off_t offset = strtoull(end, &end, 0); + if (end == NULL || errno != 0) { + UNEXPECTED("invalid --offset specified '%s'", + args->offset); + return -1; + } + + if (*end != ',' && *end != ':' && *end != '\0') { + UNEXPECTED("invalid --offset separator " + "character '%c'", *end); + return -1; + } + + rc = __copy_compare(args, offset, done_list); + if (rc < 0) + break; + + if (*end == '\0') + break; + end++; + } + + return rc; +} diff --git a/fcp/src/cmd_erase.c b/fcp/src/cmd_erase.c new file mode 100644 index 0000000..972f0be --- /dev/null +++ b/fcp/src/cmd_erase.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: cmd_erase.c + * Author: Shaun Wetzstein + * Descr: erase implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" +#include "main.h" + +static int __erase(args_t * args, off_t offset, entry_list_t * done_list) +{ + assert(args != NULL); + + char * type = args->dst_type; + char * target = args->dst_target; + char * name = args->dst_name; + + size_t fill; + if (args->opt_nr == 1) { + fill = 0xFF; + } else if (args->opt_nr == 2) { + if (parse_number(args->opt[1], &fill) < 0) + return -1; + } + + RAII(FILE*, file, __fopen(type, target, "r+", debug), fclose); + if (file == NULL) + return -1; + if (check_file(target, file, offset) < 0) + return -1; + RAII(ffs_t*, ffs, __ffs_fopen(file, offset), __ffs_fclose); + if (ffs == NULL) + return -1; + + ffs->path = basename(target); + done_list->ffs = ffs; + + if (ffs->count <= 0) + return 0; + + size_t block_size; + if (__ffs_info(ffs, FFS_INFO_BLOCK_SIZE, &block_size) < 0) + return -1; + + if (args->buffer != NULL) { + size_t buffer; + if (parse_size(args->buffer, &buffer) < 0) + return -1; + if (__ffs_buffer(ffs, buffer) < 0) + return -1; + } + + ffs_entry_t parent; + if (__ffs_entry_find(ffs, name, &parent) == false) { + UNEXPECTED("partition entry '%s' not found\n", name); + return -1; + } + + RAII(entry_list_t*, entry_list, entry_list_create(ffs), + entry_list_delete); + if (entry_list == NULL) + return -1; + if (entry_list_add(entry_list, &parent) < 0) + return -1; + + while (list_empty(&entry_list->list) == 0) { + RAII(entry_node_t*, entry_node, + container_of(list_head(&entry_list->list), + entry_node_t, node), free); + + entry_list_remove(entry_list, entry_node); + ffs_entry_t * entry = &entry_node->entry; + + if (entry->type == FFS_TYPE_LOGICAL) { + if (entry_list_add_child(entry_list, entry) < 0) + return -1; + continue; + } + + char full_name[page_size]; + if (__ffs_entry_name(ffs, entry, full_name, + sizeof full_name) < 0) + return -1; + + if (args->protected != f_PROTECTED) { + if (entry->flags & FFS_FLAGS_PROTECTED) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: protected " + "(skip)\n", offset, full_name); + continue; + } + } + + if (__ffs_entry_truncate(ffs, full_name, 0ULL) < 0) { + ERRNO(errno); + return -1; + } + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: trunc size '%x' (done)\n", + offset, full_name, 0); + + if (entry_list_exists(done_list, entry) == 1) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: erase partition " + "(skip)\n", offset, full_name); + continue; + } + + if (entry_list_add(done_list, entry) < 0) + return -1; + + if (fcp_erase_entry(ffs, full_name, (char)fill) < 0) + return -1; + + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: erase partition (done)\n", + offset, full_name); + } + + return 0; +} + +int command_erase(args_t * args) +{ + assert(args != NULL); + + int rc = 0; + + RAII(entry_list_t*, done_list, entry_list_create(NULL), + entry_list_delete); + if (done_list == NULL) + return -1; + + char * end = (char *)args->offset; + while (rc == 0 && end != NULL && *end != '\0') { + errno = 0; + off_t offset = strtoull(end, &end, 0); + if (end == NULL || errno != 0) { + UNEXPECTED("invalid --offset specified '%s'", + args->offset); + return -1; + } + + if (*end != ',' && *end != ':' && *end != '\0') { + UNEXPECTED("invalid --offset separator " + "character '%c'", *end); + return -1; + } + + rc = __erase(args, offset, done_list); + if (rc < 0) + break; + + if (*end == '\0') + break; + end++; + } + + return rc; +} diff --git a/fcp/src/cmd_list.c b/fcp/src/cmd_list.c new file mode 100644 index 0000000..069407a --- /dev/null +++ b/fcp/src/cmd_list.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: cmd_list.c + * Author: Shaun Wetzstein + * Descr: list implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" +#include "main.h" + +static int list(args_t * args, off_t offset) +{ + assert(args != NULL); + + char * type = args->dst_type; + char * target = args->dst_target; + char * name = args->dst_name; + + RAII(FILE*, file, __fopen(type, target, "r", debug), fclose); + if (file == NULL) + return -1; + if (check_file(target, file, offset) < 0) + return -1; + RAII(ffs_t*, ffs, __ffs_fopen(file, offset), __ffs_fclose); + if (ffs == NULL) + return -1; + + if (ffs->count <= 0) + return 0; + + fprintf(stdout, "========================[ PARTITION TABLE" + " 0x%llx ]=======================\n", ffs->offset); + fprintf(stdout, "vers:%04x size:%04x * blk:%06x blk(s):" + "%06x * entsz:%06x ent(s):%06x\n", + ffs->hdr->version, ffs->hdr->size, ffs->hdr->block_size, + ffs->hdr->block_count, ffs->hdr->entry_size, + ffs->hdr->entry_count); + fprintf(stdout, "----------------------------------" + "----------------------------------" + "-------\n"); + + RAII(entry_list_t*, entry_list, entry_list_create_by_regex(ffs, name), + entry_list_delete); + if (entry_list == NULL) + return -1; + + list_iter_t it; + entry_node_t * entry_node; + + list_iter_init(&it, &entry_list->list, LI_FLAG_FWD); + list_for_each(&it, entry_node, node) { + ffs_entry_t * entry = &entry_node->entry; + char full_name[page_size]; + + if (__ffs_entry_name(ffs, entry, full_name, + sizeof full_name) < 0) + return -1; + + size_t offset = entry->base * ffs->hdr->block_size; + size_t size = entry->size * ffs->hdr->block_size; + + char type; + if (entry->type == FFS_TYPE_LOGICAL) + type ='l'; + else if (entry->type == FFS_TYPE_DATA) + type ='d'; + else if (entry->type == FFS_TYPE_PARTITION) + type ='p'; + fprintf(stdout, "%3d [%08x-%08x] [%8x:%8x] ", + entry->id, offset, offset+size-1, size, entry->actual); + + fprintf(stdout, "[%c%c%c%c%c%c] %s\n", + type, '-', '-', '-', + entry->flags & FFS_FLAGS_U_BOOT_ENV ? 'b' : '-', + entry->flags & FFS_FLAGS_PROTECTED ? 'p' : '-', + full_name); + + if (args->verbose == f_VERBOSE) { + for (int i=0; iuser.data[i]); + if ((i+1) % 4 == 0) + fprintf(stdout, "\n"); + } + } + } + fprintf(stdout, "\n"); + + return 0; +} + +int command_list(args_t * args) +{ + assert(args != NULL); + + int rc = 0; + + char * end = (char *)args->offset; + while (rc == 0 && end != NULL && *end != '\0') { + errno = 0; + off_t offset = strtoull(end, &end, 0); + if (end == NULL || errno != 0) { + UNEXPECTED("invalid --offset specified '%s'", + args->offset); + return -1; + } + + if (*end != ',' && *end != ':' && *end != '\0') { + UNEXPECTED("invalid --offset separator " + "character '%c'", *end); + return -1; + } + + rc = list(args, offset); + if (rc < 0) + break; + + if (*end == '\0') + break; + end++; + } + + return rc; +} diff --git a/fcp/src/cmd_probe.c b/fcp/src/cmd_probe.c new file mode 100644 index 0000000..b0e55a8 --- /dev/null +++ b/fcp/src/cmd_probe.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: cmd_probe.c + * Author: Shaun Wetzstein + * Descr: probe implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "misc.h" +#include "main.h" + +static int aa_probe(int verbose) +{ + int size = 16; + + uint16_t ports[size]; + uint32_t unique_ids[size]; + + int rc = aa_find_devices_ext(size, ports, size, unique_ids); + + fprintf(stdout, "%d aardvark(s) found:\n", rc); + + for (int i = 0; i < min(rc, 16); i++) { + if (ports[i] & AA_PORT_NOT_FREE) { + fprintf(stdout, " %d: [%04d-%06d] ===> BUSY", + ports[i] & ~AA_PORT_NOT_FREE, + unique_ids[i] / 1000000, + unique_ids[i] % 1000000); + + continue; + } + + spinor_t * nor = spinor_open(ports[i]); + if (nor == NULL) { + debug("unable to open aaflash on port '%d'\n", ports[i]); + continue; + } + + if (spinor_configure(nor) < 0) { + debug("unable to configure Aardvark on port '%d'\n", + ports[i]); + continue; + } + + fprintf(stdout, " %d: [%04d-%06d] => ", + ports[i] & ~AA_PORT_NOT_FREE, + unique_ids[i] / 1000000, unique_ids[i] % 1000000); + + spinor_id_t id; + if (spinor_read_id(nor, id) < 0) { + debug("unable to read device id\n"); + continue; + } + + fprintf(stdout, "%s %s %s", + spinor_id_mfg(id), spinor_id_type(id), + spinor_id_capacity(id)); + + fprintf(stdout, " [%2.2x %2.2x %2.2x]\n", + id[0], id[1], id[2]); + + if (spinor_close(nor) < 0) { + debug("unable to read device id\n"); + continue; + } + + nor = NULL; + } + + fprintf(stdout, "\n"); + + return 0; +} + +static int rw_probe(const char * host, int verbose) +{ + assert(host != NULL); + + rwflash_cookie_t * cookie = rwflash_cookie_open(host, verbose); + if (cookie == NULL) { + debug("unable to open rwflash on host '%s'\n", host); + return -1; + } + + if (rwflash_cookie_init(cookie) < 0) { + debug("unable to init rwflash on host '%s'\n", host); + return -1; + } + + if (rwflash_cookie_probe(cookie) < 0) { + debug("unable to probe rwflash on host '%s'\n", host); + return -1; + } + + uint8_t * id = spinor_id(rwflash_cookie_spinor(cookie)); + if (id == NULL) { + debug("invalid id rwflash on host '%s'\n", host); + return -1; + } + + fprintf(stdout, " %s ===> %s %s %s", + host, + spinor_id_mfg(id), spinor_id_type(id), + spinor_id_capacity(id)); + + fprintf(stdout, " [%2.2x %2.2x %2.2x]\n", + id[0], id[1], id[2]); + + return 0; +} + +int command_probe(args_t * args) +{ + assert(args != NULL); + + int rc = 0; + + char * type = args->dst_type; + char * target = args->dst_target; + + int verbose = args->verbose == f_VERBOSE; + + if (strcasecmp(type, TYPE_AA) == 0) { + rc = aa_probe(verbose); + } else if (strcasecmp(type, TYPE_RW) == 0) { + rc = rw_probe(target, verbose); + } else { + UNEXPECTED("NOT IMPLEMENTED YET!"); + rc = -1; + } + + return rc; +} diff --git a/fcp/src/cmd_read.c b/fcp/src/cmd_read.c new file mode 100644 index 0000000..7923b46 --- /dev/null +++ b/fcp/src/cmd_read.c @@ -0,0 +1,187 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: cmd_read.c + * Author: Shaun Wetzstein + * Descr: read implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" +#include "main.h" + +static int __read(args_t * args, off_t offset, entry_list_t * done_list) +{ + assert(args != NULL); + + char * type = args->src_type; + char * target = args->src_target; + char * name = args->src_name; + + char * out_path = args->dst_target; + + RAII(FILE*, file, __fopen(type, target, "r", debug), fclose); + if (file == NULL) + return -1; + if (check_file(target, file, offset) < 0) + return -1; + RAII(ffs_t*, ffs, __ffs_fopen(file, offset), __ffs_fclose); + if (ffs == NULL) + return -1; + + done_list->ffs = ffs; + + if (ffs->count <= 0) + return 0; + + size_t block_size; + if (__ffs_info(ffs, FFS_INFO_BLOCK_SIZE, &block_size) < 0) + return -1; + + if (args->buffer != NULL) { + size_t buffer; + if (parse_size(args->buffer, &buffer) < 0) + return -1; + if (__ffs_buffer(ffs, buffer) < 0) + return -1; + } + + ffs_entry_t entry; + if (__ffs_entry_find(ffs, name, &entry) == false) { + UNEXPECTED("partition entry '%s' not found\n", name); + return -1; + } + + if (entry.actual == 0) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: is empty (skip)\n", + offset, name); + return 0; + } + + char full_name[page_size]; + if (__ffs_entry_name(ffs, &entry, full_name, + sizeof full_name) < 0) + return -1; + + if (entry.type == FFS_TYPE_LOGICAL) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: logical (skip)\n", + offset, name); + return 0; + } + + if (entry_list_exists(done_list, &entry) == 1) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: write to '%s' (skip)\n", + offset, full_name, out_path); + return 0; + } + + if (entry_list_add(done_list, &entry) < 0) + return -1; + + if (strcmp(out_path, "-") == 0) { + if (fcp_read_entry(ffs, full_name, stdout) < 0) + return -1; + } else { + if (access(out_path, F_OK) == 0 && args->force != f_FORCE) { + UNEXPECTED("output file '%s' already exists, " + "use --force to overwrite\n", out_path); + return -1; + } + + RAII(FILE*, out, fopen(out_path, "w+"), fclose); + if (out == NULL) { + ERRNO(errno); + return -1; + } + + if (fcp_read_entry(ffs, full_name, out) < 0) + return -1; + + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: write to '%s' (done)\n", + offset, full_name, out_path); + } + + return 0; +} + +int command_read(args_t * args) +{ + assert(args != NULL); + + int rc = 0; + + RAII(entry_list_t*, done_list, entry_list_create(NULL), + entry_list_delete); + if (done_list == NULL) + return -1; + + char * end = (char *)args->offset; + while (rc == 0 && end != NULL && *end != '\0') { + errno = 0; + off_t offset = strtoull(end, &end, 0); + if (end == NULL || errno != 0) { + UNEXPECTED("invalid --offset specified '%s'", + args->offset); + return -1; + } + + if (*end != ',' && *end != ':' && *end != '\0') { + UNEXPECTED("invalid --offset separator " + "character '%c'", *end); + return -1; + } + + rc = __read(args, offset, done_list); + if (rc < 0) + break; + + if (*end == '\0') + break; + end++; + } + + return rc; +} diff --git a/fcp/src/cmd_trunc.c b/fcp/src/cmd_trunc.c new file mode 100644 index 0000000..cf65788 --- /dev/null +++ b/fcp/src/cmd_trunc.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: cmd_trunc.c + * Author: Shaun Wetzstein + * Descr: trunc implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" +#include "main.h" + +static int __trunc(args_t * args, off_t offset) +{ + assert(args != NULL); + + char * type = args->dst_type; + char * target = args->dst_target; + char * name = args->dst_name; + + RAII(FILE*, file, __fopen(type, target, "r+", debug), fclose); + if (file == NULL) + return -1; + if (check_file(target, file, offset) < 0) + return -1; + RAII(ffs_t*, ffs, __ffs_fopen(file, offset), __ffs_fclose); + if (ffs == NULL) + return -1; + + if (ffs->count <= 0) + return 0; + + size_t block_size; + if (__ffs_info(ffs, FFS_INFO_BLOCK_SIZE, &block_size) < 0) + return -1; + + if (args->buffer != NULL) { + size_t buffer; + if (parse_size(args->buffer, &buffer) < 0) + return -1; + if (__ffs_buffer(ffs, buffer) < 0) + return -1; + } + + ffs_entry_t entry; + if (__ffs_entry_find(ffs, name, &entry) == false) { + UNEXPECTED("partition entry '%s' not found\n", name); + return -1; + } + + char full_name[page_size]; + if (__ffs_entry_name(ffs, &entry, full_name, + sizeof full_name) < 0) + return -1; + + if (entry.type == FFS_TYPE_LOGICAL) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: logical (skip)\n", + offset, full_name); + return 0; + } + + if (args->protected != f_PROTECTED && + entry.flags && FFS_FLAGS_PROTECTED) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: protected (skip)\n", + offset, full_name); + return 0; + } + + size_t size = 0; + if (args->opt_nr == 1) { + size = entry.size * block_size; + } else if (args->opt_nr == 2) { + if (parse_number(args->opt[1], &size) < 0) + return -1; + } + + if (__ffs_entry_truncate(ffs, full_name, size) < 0) { + ERRNO(errno); + return -1; + } + + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: truncate '%x' (done)\n", + offset, full_name, size); + + return 0; +} + +int command_trunc(args_t * args) +{ + assert(args != NULL); + + int rc = 0; + + char * end = (char *)args->offset; + while (rc == 0 && end != NULL && *end != '\0') { + errno = 0; + off_t offset = strtoull(end, &end, 0); + if (end == NULL || errno != 0) { + UNEXPECTED("invalid --offset specified '%s'", + args->offset); + return -1; + } + + if (*end != ',' && *end != ':' && *end != '\0') { + UNEXPECTED("invalid --offset separator " + "character '%c'", *end); + return -1; + } + + rc = __trunc(args, offset); + if (rc < 0) + break; + + if (*end == '\0') + break; + end++; + } + + return rc; +} diff --git a/fcp/src/cmd_user.c b/fcp/src/cmd_user.c new file mode 100644 index 0000000..46637ce --- /dev/null +++ b/fcp/src/cmd_user.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: cmd_user.c + * Author: Shaun Wetzstein + * Descr: user implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" +#include "main.h" + +#define DELIM '=' + +static int __user(args_t * args, off_t offset) +{ + assert(args != NULL); + + char * type = args->dst_type; + char * target = args->dst_target; + char * name = args->dst_name; + + RAII(FILE*, file, __fopen(type, target, "r+", debug), fclose); + if (file == NULL) + return -1; + if (check_file(target, file, offset) < 0) + return -1; + RAII(ffs_t*, ffs, __ffs_fopen(file, offset), __ffs_fclose); + if (ffs == NULL) + return -1; + + if (ffs->count <= 0) + return 0; + + size_t block_size; + if (__ffs_info(ffs, FFS_INFO_BLOCK_SIZE, &block_size) < 0) + return -1; + + if (args->buffer != NULL) { + size_t buffer; + if (parse_size(args->buffer, &buffer) < 0) + return -1; + if (__ffs_buffer(ffs, buffer) < 0) + return -1; + } + + ffs_entry_t entry; + if (__ffs_entry_find(ffs, name, &entry) == false) { + UNEXPECTED("partition entry '%s' not found\n", name); + return -1; + } + + char full_name[page_size]; + if (__ffs_entry_name(ffs, &entry, full_name, + sizeof full_name) < 0) + return -1; + + if (args->protected != f_PROTECTED && + entry.flags && FFS_FLAGS_PROTECTED) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: protected (skip)\n", + offset, full_name); + return 0; + } + + // parse [=] + + if (args->opt_nr <= 1) { + for (uint32_t word=0; wordopt_nr; i++) { + if (args->opt[i] == '\0') { + UNEXPECTED("invalid user '%s', use form " + "'[=]'\n", + args->opt[i]); + return -1; + } + + char * __value = strrchr(args->opt[i], DELIM); + if (__value != NULL) + *__value = '\0'; + + size_t word = 0; + if (parse_number(args->opt[i], &word) < 0) + return -1; + + uint32_t value = 0; + if (__value != NULL) { // write + if (parse_number(__value+1, &value) < 0) + return -1; + + *__value = DELIM; + + if (__ffs_entry_user_put(ffs, full_name, + word, value) < 0) + return -1; + + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: [%02d] = " + "%08x\n", offset, full_name, + word, value); + } else { // read + if (__ffs_entry_user_get(ffs, full_name, + word, &value) < 0) + return -1; + + if (isatty(fileno(stdout))) + fprintf(stdout, "%8llx: %s: [%02d] = " + "%08x\n", offset, full_name, + word, value); + else + fprintf(stdout, "%x", value); + } + } + } + + return 0; +} + +int command_user(args_t * args) +{ + assert(args != NULL); + + int rc = 0; + + char * end = (char *)args->offset; + while (rc == 0 && end != NULL && *end != '\0') { + errno = 0; + off_t offset = strtoull(end, &end, 0); + if (end == NULL || errno != 0) { + UNEXPECTED("invalid --offset specified '%s'", + args->offset); + return -1; + } + + if (*end != ',' && *end != ':' && *end != '\0') { + UNEXPECTED("invalid --offset separator " + "character '%c'", *end); + return -1; + } + + rc = __user(args, offset); + if (rc < 0) + break; + + if (*end == '\0') + break; + end++; + } + + return rc; +} diff --git a/fcp/src/cmd_write.c b/fcp/src/cmd_write.c new file mode 100644 index 0000000..5152532 --- /dev/null +++ b/fcp/src/cmd_write.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: cmd_write.c + * Author: Shaun Wetzstein + * Descr: write implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" +#include "main.h" + +static int __write(args_t * args, off_t offset, entry_list_t * done_list) +{ + assert(args != NULL); + + char * in_path = args->src_target; + + char * type = args->dst_type; + char * target = args->dst_target; + char * name = args->dst_name; + + RAII(FILE*, file, __fopen(type, target, "r+", debug), fclose); + if (file == NULL) + return -1; + if (check_file(target, file, offset) < 0) + return -1; + RAII(ffs_t*, ffs, __ffs_fopen(file, offset), __ffs_fclose); + if (ffs == NULL) + return -1; + + ffs->path = basename(target); + done_list->ffs = ffs; + + if (ffs->count <= 0) + return 0; + + size_t block_size; + if (__ffs_info(ffs, FFS_INFO_BLOCK_SIZE, &block_size) < 0) + return -1; + + if (args->buffer != NULL) { + size_t buffer; + if (parse_size(args->buffer, &buffer) < 0) + return -1; + if (__ffs_buffer(ffs, buffer) < 0) + return -1; + } + + ffs_entry_t entry; + if (__ffs_entry_find(ffs, name, &entry) == false) { + UNEXPECTED("partition entry '%s' not found\n", name); + return -1; + } + + char full_name[page_size]; + if (__ffs_entry_name(ffs, &entry, full_name, + sizeof full_name) < 0) + return -1; + + if (entry.type == FFS_TYPE_LOGICAL) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: logical (skip)\n", + offset, full_name); + return 0; + } + + if (args->protected != f_PROTECTED && + entry.flags & FFS_FLAGS_PROTECTED) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: protected (skip)\n", + offset, full_name); + return 0; + } + + struct stat st; + if (stat(in_path, &st) < 0) { + ERRNO(errno); + return -1; + } + + if (entry.actual < st.st_size) { + if (__ffs_entry_truncate(ffs, full_name, + st.st_size) < 0) { + ERRNO(errno); + return -1; + } + + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: trunc size '%llx' (done)\n", + offset, full_name, st.st_size); + } + + if (entry_list_exists(done_list, &entry) == 1) { + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: read from '%s' (skip)\n", + offset, full_name, in_path); + return 0; + } + + if (entry_list_add(done_list, &entry) < 0) + return -1; + + if (strcmp(in_path, "-") == 0) { + if (fcp_write_entry(ffs, full_name, stdin) < 0) + return -1; + } else { + RAII(FILE*, in, fopen(in_path, "r"), fclose); + if (in == NULL) { + ERRNO(errno); + return -1; + } + + if (fcp_write_entry(ffs, full_name, in) < 0) + return -1; + + if (args->verbose == f_VERBOSE) + fprintf(stderr, "%8llx: %s: read from '%s' (done)\n", + offset, full_name, in_path); + } + + return 0; +} + +int command_write(args_t * args) +{ + assert(args != NULL); + + int rc = 0; + + RAII(entry_list_t*, done_list, entry_list_create(NULL), + entry_list_delete); + if (done_list == NULL) + return -1; + + char * end = (char *)args->offset; + while (rc == 0 && end != NULL && *end != '\0') { + errno = 0; + off_t offset = strtoull(end, &end, 0); + if (end == NULL || errno != 0) { + UNEXPECTED("invalid --offset specified '%s'", + args->offset); + return -1; + } + + if (*end != ',' && *end != ':' && *end != '\0') { + UNEXPECTED("invalid --offset separator " + "character '%c'", *end); + return -1; + } + + rc = __write(args, offset, done_list); + if (rc < 0) + break; + + if (*end == '\0') + break; + end++; + } + + return rc; +} diff --git a/fcp/src/main.c b/fcp/src/main.c new file mode 100644 index 0000000..c122c3f --- /dev/null +++ b/fcp/src/main.c @@ -0,0 +1,709 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: fcp_main.c + * Author: Shaun Wetzstein + * Descr: cp for FFS files + * Date: 04/25/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" +#include "main.h" + +args_t args; +size_t page_size; + +int verbose; +int debug; + +static const char copyright[] __unused__ = + "Licensed Internal Code - Property of IBM\n" + "IBM Support Processor Licensed Internal Code\n" + "(c) Copyright IBM Corp 2013 All Rights Reserved\n" + "US Government Users Restricted Rights - Use, duplication\n" + "or disclosure restricted by GSA ADP Schedule Contract\n" + "with IBM Corp."; + +static void usage(bool verbose) +{ + FILE *e = stderr; + + fprintf(e, + "fcp - FFS copy v%d.%d.%d -- Authors: " + "\n (c) Copyright IBM Corp 2013 All Rights " + "Reserved\n", FCP_MAJOR, FCP_MINOR, FCP_PATCH); + + fprintf(e, "\n"); + fprintf(e, "Usage:\n"); + fprintf(e," fcp [:][:] -PLETU" + "\n [-b ] [-o ] [-fpvdh]\n"); + fprintf(e," fcp [:][:] " + "[:][:] -RWCM" + "\n [-b ] [-o ] [-fpvdh]\n"); + fprintf(e, "\n"); + fprintf(e, " \n"); + fprintf(e, " 'aa' : Aardvark USB probe\n"); + fprintf(e, " 'rw' : RISCWatch Ethernet probe\n"); + fprintf(e, " 'sfc' : FSP SFC character device\n"); + fprintf(e, " 'file' : UNIX regular file\n"); + fprintf(e, " \n"); + fprintf(e, " 'aa' : USB device number [0..9]\n"); + fprintf(e, " 'rw' : @ RISCwatch probe\n"); + fprintf(e, " 'sfc' : to SFC char device\n"); + fprintf(e, " 'file' : to UNIX regular file\n"); + fprintf(e, " \n"); + fprintf(e, " : FFS name, e.g. bank0/bootenv/card\n"); + fprintf(e, "\n"); + + if (verbose) { + fprintf(e, "Examples:\n"); + fprintf(e, " fcp -P aa:0\n"); + fprintf(e, " fcp -P rw:riscwatch.ibm.com\n"); + fprintf(e, "\n"); + fprintf(e, " fcp -L aa:0\n"); + fprintf(e, " fcp -L rw:riscwatch.ibm.com\n"); + fprintf(e, " fcp -L nor.mif\n"); + fprintf(e, "\n"); + fprintf(e, " fcp -E 0 nor.mif:bank0\n"); + fprintf(e, " fcp -E 0xff nor.mif:bank0/spl\n"); + fprintf(e, "\n"); + fprintf(e, " fcp -W ipl.bin file:nor:bank0/ipl\n"); + fprintf(e, " fcp -R file:nor:bank0/ipl ipl.bin\n"); + fprintf(e, "\n"); + fprintf(e, " cat ipl.bin | fcp -R file:nor:bank0/ipl -\n"); + fprintf(e, " fcp -W file:nor:bank0/ipl - | cat > ipl.bin\n"); + fprintf(e, "\n"); + fprintf(e, " fcp -C file:nor:bank0 rw:host.ibm.com:bank0\n"); + fprintf(e, " fcp -C file:nor rw:host.ibm.com@6470\n"); + fprintf(e, "\n"); + fprintf(e, " fcp -M file:nor:bank0 rw:host.ibm.com:bank0\n"); + fprintf(e, " fcp -M file:nor:* rw:host.ibm.com@6470:*\n"); + fprintf(e, "\n"); + fprintf(e, " fcp -C nor.mif:bank0 nor.mif:bank1\n"); + fprintf(e, " fcp -C rw:host:bank0/ipl aa:0:bank1/ipl\n"); + fprintf(e, "\n"); + fprintf(e, " fcp -M nor.mif:bank0 nor.mif:bank1\n"); + fprintf(e, " fcp -M rw:host:bank0/ipl aa:0:bank1/ipl\n"); + fprintf(e, "\n"); + fprintf(e, " fcp -T nor.mif:bank0/spl\n"); + fprintf(e, " fcp -T 0 nor.mif:bank0/spl\n"); + fprintf(e, "\n"); + fprintf(e, " fcp -U nor.mif:bank0/spl\n"); + fprintf(e, " fcp -U 0 1 2 nor.mif:bank0/spl\n"); + fprintf(e, " fcp -U 0=0xffffffff 1=0 nor.mif:bank0/spl\n"); + fprintf(e, "\n"); + } + + /* =============================== */ + + fprintf(e, "Commands:\n"); + + fprintf(e, " -P, --probe\n"); + if (verbose) + fprintf(e, + "\n Read the SPI NOR device Id\n\n"); + + fprintf(e, " -L, --list\n"); + if (verbose) + fprintf(e, + "\n List the contents of a partition table\n\n"); + + fprintf(e, " -R, --read\n"); + if (verbose) + fprintf(e, + "\n Read the contents of a partition and write " + "the data to an output file (use\n '-' for stdout)." + "\n\n"); + + fprintf(e, " -W, --write\n"); + if (verbose) + fprintf(e, + "\n Read the contents of an input file (use '-' for " + "stdin) and write the data\n to a partition.\n\n"); + + fprintf(e, " -E, --erase \n"); + if (verbose) + fprintf(e, + "\n Fill the contents of a partition with . " + " is a decimal (or hex)\n number, default is " + "0xFF.\n\n"); + + fprintf(e, " -C, --copy\n"); + if (verbose) + fprintf(e, + "\n Copy source partition(s) to destination " + "partition(s). Both source and\n destination name(s) " "can specify either 'data' or 'logical' partitions." + "\n\n"); + + fprintf(e, " -T, --trunc \n"); + if (verbose) + fprintf(e, + "\n Truncate the actual size of partition(s) to " + " size bytes. is\n a decimal (or hex) " + "number, default is the partition entry size\n\n"); + + fprintf(e, " -M, --compare\n"); + if (verbose) + fprintf(e, + "\n Compare source partition(s) to destination " + "partition(s). Both source and\n destination name(s) " + "can specify either 'data' or 'logical' partitions." + "\n\n"); + + fprintf(e, " -U, --user [[=] ...]\n"); + if (verbose) + fprintf(e, + "\n Get or set a user word. and are " + "decimal (or hex) numbers.\n"); + + fprintf(e, "\n"); + + fprintf(e, "Options:\n"); + fprintf(e, " -o, --offset \n"); + if (verbose) + fprintf(e, "\n Specifies a comma (,) separated list of" + " partition table offsets, in bytes\n from the start" + " of the target file (or device).\n\n"); + + fprintf(e, " -b, --buffer \n"); + if (verbose) + fprintf(e, + "\n Specifies the buffer size used by --read, --write," + " --copy and --compare\n commands is a " + "decimal (or hex) number, default is buffer size.\n\n"); + fprintf(e, "\n"); + + /* =============================== */ + + fprintf(e, "Flags:\n"); + + fprintf(e, " -f, --force\n"); + if (verbose) + fprintf(e, "\n Override command safe guards\n\n"); + + fprintf(e, " -p, --protected\n"); + if (verbose) + fprintf(e, "\n Do not ignore protected partition " + "entries\n\n"); + + fprintf(e, " -v, --verbose\n"); + if (verbose) + fprintf(e, "\n Write progress messages to stdout\n\n"); + + fprintf(e, " -d, --debug\n"); + if (verbose) + fprintf(e, "\n Write debug messages to stdout\n\n"); + + fprintf(e, " -h, --help\n"); + if (verbose) + fprintf(e, "\n Write this help text to stdout and exit\n"); + + fprintf(e, "\n"); + + fprintf(e, "Report bugs to " + " (vendor='MCP for FSP*')\n"); +} + +static int process_argument(args_t * args, int opt, const char *optarg) +{ + assert(args != NULL); + + switch (opt) { + case c_PROBE: /* probe */ + case c_LIST: /* list */ + case c_READ: /* read */ + case c_WRITE: /* write */ + case c_ERASE: /* erase */ + case c_COPY: /* copy */ + case c_TRUNC: /* trunc */ + case c_COMPARE: /* compare */ + case c_USER: /* user */ + if (args->cmd != c_ERROR) { + UNEXPECTED("commands '%c' and '%c' are mutually " + "exclusive", args->cmd, opt); + return -1; + } + args->cmd = (cmd_t) opt; + break; + case o_OFFSET: /* offset */ + args->offset = strdup(optarg); + break; + case o_BUFFER: /* buffer */ + args->buffer = strdup(optarg); + break; + case f_FORCE: /* force */ + args->force = (flag_t) opt; + break; + case f_PROTECTED: /* protected */ + args->protected = (flag_t) opt; + break; + case f_VERBOSE: /* verbose */ + verbose = 1; + args->verbose = (flag_t) opt; + break; + case f_DEBUG: /* debug */ + debug = 1; + args->debug = (flag_t) opt; + break; + case f_HELP: /* help */ + usage(true); + exit(EXIT_SUCCESS); + break; + case '?': /* error */ + default: + UNEXPECTED("unknown option '%c', please see --help for " + "details\n", opt); + return -1; + } + return 0; +} + +static int process_option(args_t * args, const char *opt) +{ + assert(args != NULL); + assert(opt != NULL); + + if (args->opt_sz <= args->opt_nr) { + size_t size = sizeof(*args->opt); + + args->opt_sz += 5; + args->opt = (const char **)realloc(args->opt, + size * args->opt_sz); + if (args->opt == NULL) { + ERRNO(errno); + return -1; + } + + memset(args->opt + args->opt_nr, 0, + size * (args->opt_sz - args->opt_nr)); + } + + args->opt[args->opt_nr] = strdup(opt); + args->opt_nr++; + + return 0; +} + +/* + * probe: + * fcp [:][:] -P + * list: + * fcp -L + * fcp [:][:] -L + * erase: + * fcp [:][:] -E + * trunc: + * fcp [:][:] -T + * user: + * fcp [:][:] -U [=] ... + * write: + * fcp [:]: -W + * read: + * fcp [:]: -R + * copy: + * fcp :]: [:]: -C + * fcp [:]: [:]: -C + * compare: + * fcp :]: [:]: -M + * fcp [:]: [:]: -M + */ +static int validate_args(args_t * args) +{ + assert(args != NULL); + + if (args->offset == NULL) { + UNEXPECTED("--offset is required for all '%s' " + "commands", args->short_name); + return -1; + } + + switch (args->cmd) { + case c_PROBE: + case c_LIST: + case c_ERASE: + case c_TRUNC: + case c_USER: + if (args->opt_nr < 1) { + UNEXPECTED("invalid options, please see --help for " + "details"); + return -1; + } + + if (parse_path(args->opt[0], &args->dst_type, + &args->dst_target, &args->dst_name) < 0) + return -1; + + break; + case c_READ: + case c_WRITE: + case c_COPY: + case c_COMPARE: + if (args->opt_nr < 2) { + UNEXPECTED("invalid options, please see --help for " + "details"); + return -1; + } + + if (parse_path(args->opt[0], &args->src_type, + &args->src_target, &args->src_name) < 0) + return -1; + if (parse_path(args->opt[1], &args->dst_type, + &args->dst_target, &args->dst_name) < 0) + return -1; + + break; + case 0: + UNEXPECTED("specify a command, or please see --help " + "for details"); + return -1; + default: + UNEXPECTED("invalid command '%c', please see --help for " + "details", args->cmd); + return -1; + + } + + debug("cmd[%c]\n", args->cmd); + + debug(" src_type: '%s'\n", args->src_type); + debug("src_target: '%s'\n", args->src_target); + debug(" src_name: '%s'\n", args->src_name); + + debug(" dst_type: '%s'\n", args->dst_type); + debug("dst_target: '%s'\n", args->dst_target); + debug(" dst_name: '%s'\n", args->dst_name); + + #define REQ_OPT(name,cmd) ({ \ + if (args->name == 0) { \ + UNEXPECTED("--%s is required for the --%s command", \ + #name, #cmd); \ + syntax(); \ + return -1; \ + } \ + }) + + #define REQ_FIELD(name,cmd) ({ \ + if (args->name == 0) { \ + UNEXPECTED("<%s> is required for the --%s command", \ + #name, #cmd); \ + syntax(); \ + return -1; \ + } \ + }) + + #define UNSUP_OPT(name,cmd) ({ \ + if (args->name != 0) { \ + UNEXPECTED("--%s is unsupported for the --%s command", \ + #name, #cmd); \ + syntax(); \ + return -1; \ + } \ + }) + + if (args->cmd == c_PROBE) { + void syntax(void) { + fprintf(stderr, "Syntax: %s [:]" + "[:] --probe [--verbose]\n", + args->short_name); + } + if (args->opt_nr != 1) { + syntax(); + UNEXPECTED("syntax error"); + return -1; + } + + UNSUP_OPT(buffer, probe); + } else if (args->cmd == c_LIST) { + void syntax(void) { + fprintf(stderr, "Syntax: %s [:]" + "[:] --list [--verbose]\n", + args->short_name); + } + if (args->opt_nr != 1) { + syntax(); + UNEXPECTED("syntax error"); + return -1; + } + + UNSUP_OPT(buffer, list); + } else if (args->cmd == c_READ) { + void syntax(void) { + fprintf(stderr, "Syntax: %s [:]" + ": --read [--verbose] " + "[--force] [--protected] [--buffer ]\n", + args->short_name); + } + if (args->opt_nr != 2) { + syntax(); + UNEXPECTED("syntax error"); + return -1; + } + + REQ_FIELD(src_name, read); + } else if (args->cmd == c_WRITE) { + void syntax(void) { + fprintf(stderr, "Syntax: %s [:]" + ": --write [--verbose] " + "[--force] [--protected] [--buffer ]\n", + args->short_name); + } + if (args->opt_nr != 2) { + syntax(); + UNEXPECTED("syntax error"); + return -1; + } + + + REQ_FIELD(dst_name, write); + } else if (args->cmd == c_ERASE) { + void syntax(void) { + fprintf(stderr, "Syntax: %s [:]" + ": --erase [--verbose] " + "[--force] [--protected] [--buffer ]\n", + args->short_name); + } + if (args->opt_nr != 2) { + syntax(); + UNEXPECTED("syntax error"); + return -1; + } + + REQ_FIELD(dst_name, erase); + } else if (args->cmd == c_TRUNC) { + void syntax(void) { + fprintf(stderr, "Syntax: %s [:]" + ": --trunc [--verbose] " + "[--force] [--protected]\n", args->short_name); + } + if (2 < args->opt_nr) { + syntax(); + UNEXPECTED("syntax error"); + return -1; + } + + REQ_FIELD(dst_name, trunc); + + UNSUP_OPT(buffer, trunc); + } else if (args->cmd == c_USER) { + void syntax(void) { + fprintf(stderr, "Syntax: %s [:]" + ": --user [=short_name); + } + + REQ_FIELD(dst_name, user); + + UNSUP_OPT(buffer, user); + } else if (args->cmd == c_COPY) { + void syntax(void) { + fprintf(stderr, "Syntax: %s [:]" + "[:] [:]" + "[:] --copy [--verbose] [--force] " + "[--protected] [--buffer ]\n", + args->short_name); + } + if (args->opt_nr != 2) { + syntax(); + UNEXPECTED("syntax error"); + return -1; + } + } else if (args->cmd == c_COMPARE) { + void syntax(void) { + fprintf(stderr, "Syntax: %s [:]" + "[:] [:]" + "[:] --compare [--verbose] [--force] " + "[--protected] [--buffer ]\n", + args->short_name); + } + if (args->opt_nr != 2) { + syntax(); + UNEXPECTED("syntax error"); + return -1; + } + } else { + UNEXPECTED("invalid command '%c'", args->cmd); + return -1; + } + + return 0; +} + +static int process_args(args_t * args) +{ + assert(args != NULL); + int rc = 0; + + switch (args->cmd) { + case c_PROBE: + //rc = command_probe(args); + break; + case c_LIST: + rc = command_list(args); + break; + case c_READ: + rc = command_read(args); + break; + case c_WRITE: + rc = command_write(args); + break; + case c_ERASE: + rc = command_erase(args); + break; + case c_TRUNC: + rc = command_trunc(args); + break; + case c_USER: + rc = command_user(args); + break; + case c_COPY: + case c_COMPARE: + rc = command_copy_compare(args); + break; + default: + UNEXPECTED("NOT IMPLEMENTED YET => '%c'", args->cmd); + rc = -1; + } + + return rc; +} + +static void args_dump(args_t * args) +{ + assert(args != NULL); + + if (args->short_name != NULL) + printf("short_name[%s]\n", args->short_name); + printf("cmd[%c]\n", args->cmd); + if (args->offset != NULL) + printf("offset[%s]\n", args->offset); + if (args->buffer != NULL) + printf("buffer[%s]\n", args->buffer); + if (args->force != 0) + printf("force[%c]\n", args->force); + if (args->protected != 0) + printf("protected[%c]\n", args->protected); + if (args->verbose != 0) + printf("verbose[%c]\n", args->verbose); + if (args->debug != 0) + printf("debug[%c]\n", args->debug); + + for (int i = 0; i < args->opt_nr; i++) { + if (args->opt[i] != NULL) + printf("opt%d[%s]\n", i, args->opt[i]); + } +} + +int main(int argc, char *argv[]) +{ + static const struct option long_opt[] = { + /* commands */ + {"probe", no_argument, NULL, c_PROBE}, + {"list", no_argument, NULL, c_LIST}, + {"read", no_argument, NULL, c_READ}, + {"write", no_argument, NULL, c_WRITE}, + {"erase", no_argument, NULL, c_ERASE}, + {"copy", no_argument, NULL, c_COPY}, + {"trunc", no_argument, NULL, c_TRUNC}, + {"compare", no_argument, NULL, c_COMPARE}, + {"user", no_argument, NULL, c_USER}, + /* options */ + {"offset", required_argument, NULL, o_OFFSET}, + {"buffer", required_argument, NULL, o_BUFFER}, + /* flags */ + {"force", no_argument, NULL, f_FORCE}, + {"protected", no_argument, NULL, f_PROTECTED}, + {"verbose", no_argument, NULL, f_VERBOSE}, + {"debug", no_argument, NULL, f_DEBUG}, + {"help", no_argument, NULL, f_HELP}, + {0, 0, 0, 0} + }; + + static const char *short_opt; + short_opt = "PLRWECTMUo:b:fpvdh"; + + int rc = EXIT_FAILURE; + + setlinebuf(stdout); + + if (argc == 1) + usage(false), exit(rc); + + int opt = 0, idx = 0; + while ((opt = getopt_long(argc, argv, short_opt, long_opt, &idx)) != -1) + if (process_argument(&args, opt, optarg) < 0) + goto error; + + /* getopt_long doesn't know what to do with orphans, */ + /* so we'll scoop them up here, and deal with them later */ + + while (optind < argc) + if (process_option(&args, argv[optind++]) < 0) + goto error; + + if (args.debug == f_DEBUG) + args_dump(&args); + + if (validate_args(&args) < 0) + goto error; + if (process_args(&args) < 0) + goto error; + + rc = EXIT_SUCCESS; + + if (false) { +error: + dump_errors(args.short_name, stderr); + } + + return rc; +} + +static void __ctor__(void) __constructor; +static void __ctor__(void) +{ + page_size = sysconf(_SC_PAGESIZE); + + args.short_name = program_invocation_short_name; + args.offset = "0x3F0000,0x7F0000"; +} diff --git a/fcp/src/main.h b/fcp/src/main.h new file mode 100644 index 0000000..93d8f59 --- /dev/null +++ b/fcp/src/main.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: main.h + * Author: Shaun Wetzstein + * Descr: cmdline tool for libffs.so + * Date: 05/12/2012 + */ + +#ifndef __FCP_H__ +#define __FCP_H__ + +#include + +#include + +#include + +#define FCP_MAJOR 0x01 +#define FCP_MINOR 0x00 +#define FCP_PATCH 0x00 + +#define TYPE_FILE "file" +#define TYPE_RW "rw" +#define TYPE_AA "aa" +#define TYPE_SFC "sfc" + +#define verbose(fmt, args...) \ + ({if (verbose) printf("%s: " fmt, __func__, ##args); }) +#define debug(fmt, args...) \ + ({if (debug) printf("%s: " fmt, __func__, ##args); }) + +typedef enum { + c_ERROR = 0, + c_PROBE = 'P', + c_LIST = 'L', + c_READ = 'R', + c_WRITE = 'W', + c_ERASE = 'E', + c_COPY = 'C', + c_TRUNC = 'T', + c_COMPARE = 'M', + c_USER = 'U', +} cmd_t; + +typedef enum { + o_ERROR = 0, + o_OFFSET = 'o', + o_BUFFER = 'b', +} option_t; + +typedef enum { + f_ERROR = 0, + f_FORCE = 'f', + f_PROTECTED = 'p', + f_VERBOSE = 'v', + f_DEBUG = 'd', + f_HELP = 'h', +} flag_t; + +typedef struct { + const char *short_name; + + cmd_t cmd; + char *src_type, *src_target, *src_name; + char *dst_type, *dst_target, *dst_name; + + /* options */ + const char *offset; + const char *buffer; + + /* flags */ + flag_t force; + flag_t protected; + flag_t verbose; + flag_t debug; + + const char **opt; + int opt_sz, opt_nr; +} args_t; + +extern args_t args; +extern size_t page_size; + +extern int verbose; +extern int debug; + +extern int fcp_read_entry(ffs_t *, const char *, FILE *); +extern int fcp_write_entry(ffs_t *, const char *, FILE *); +extern int fcp_erase_entry(ffs_t *, const char *, char); +extern int fcp_copy_entry(ffs_t *, const char *, ffs_t *, const char *); +extern int fcp_compare_entry(ffs_t *, const char *, ffs_t *, const char *); + +extern int command_probe(args_t *); +extern int command_list(args_t *); +extern int command_read(args_t *); +extern int command_write(args_t *); +extern int command_erase(args_t *); +extern int command_copy_compare(args_t *); +extern int command_trunc(args_t *); +extern int command_compare(args_t *); +extern int command_user(args_t *); + +#endif /* __FCP_H__ */ diff --git a/fcp/src/misc.c b/fcp/src/misc.c new file mode 100644 index 0000000..5fdfbfd --- /dev/null +++ b/fcp/src/misc.c @@ -0,0 +1,937 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: fcp_misc.c + * Author: Shaun Wetzstein + * Descr: misc helpers + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" +#include "main.h" + +#define COMPARE_SIZE 256UL + +static regex_t * regex_create(const char * str) +{ + assert(str != NULL); + + regex_t * self = (regex_t *)malloc(sizeof(*self)); + if (self == NULL) { + ERRNO(errno); + return self; + } + + if (regcomp(self, str, REG_ICASE | REG_NOSUB) != 0) { + free(self); + ERRNO(errno); + return NULL; + } + + return self; +} + +static int regex_free(regex_t * self) +{ + if (self == NULL) + return 0; + + regfree(self); + free(self); + + return 0; +} + +int parse_offset(const char *str, off_t *offset) +{ + assert(offset != NULL); + + if (str == NULL) { + *offset = 0; + return 0; + } + + char *end = NULL; + + errno = 0; + *offset = strtoull(str, &end, 0); + if (errno != 0) { + ERRNO(errno); + return -1; + } + + if (*end != '\0') { + if (!strcmp(end, "KiB") || + !strcasecmp(end, "KB") || + !strcasecmp(end, "K")) + *offset <<= 10; + else if (!strcmp(end, "MiB") || + !strcasecmp(end, "MB") || + !strcasecmp(end, "M")) + *offset <<= 20; + else if (!strcmp(end, "GiB") || + !strcasecmp(end, "GB") || + !strcasecmp(end, "G")) + *offset <<= 30; + else { + UNEXPECTED("invalid offset specified '%s'", end); + return -1; + } + } + + return 0; +} + +int parse_size(const char *str, size_t *size) +{ + assert(size != NULL); + + if (str == NULL) { + *size = 0; + return 0; + } + + char *end = NULL; + + errno = 0; + *size = strtoul(str, &end, 0); + if (errno != 0) { + ERRNO(errno); + return -1; + } + + if (*end != '\0') { + if (!strcmp(end, "KiB") || !strcasecmp(end, "K") || + !strcasecmp(end, "KB")) + *size <<= 10; + else if (!strcmp(end, "MiB") || !strcasecmp(end, "M") || + !strcasecmp(end, "MB")) + *size <<= 20; + else if (!strcmp(end, "GiB") || !strcasecmp(end, "G") || + !strcasecmp(end, "GB")) + *size <<= 30; + else { + UNEXPECTED("invalid size specified '%s'", end); + return -1; + } + } + + return 0; +} + +int parse_number(const char *str, size_t *num) +{ + assert(num != NULL); + + if (str == NULL) { + *num = 0; + return 0; + } + + char *end = NULL; + + errno = 0; + *num = strtoul(str, &end, 0); + if (errno != 0) { + ERRNO(errno); + return -1; + } + + if (*end != '\0') { + UNEXPECTED("invalid number specified '%s'", end); + return -1; + } + + return 0; +} + +int parse_path(const char * path, char ** type, char ** target, char ** name) +{ + assert(path != NULL); + + *type = *target = *name = NULL; + + char * delim1 = strchr(path, ':'); + char * delim2 = strrchr(path, ':'); + + if (delim1 == NULL && delim2 == NULL) { // + if (asprintf(target, "%s", path) < 0) { + ERRNO(errno); + return -1; + } + } else if (delim1 == delim2) { // : + if (asprintf(target, "%.*s", delim1 - path, path) < 0) { + ERRNO(errno); + return -1; + } + delim1++; + if (asprintf(name, "%s", delim1) < 0) { + ERRNO(errno); + return -1; + } + + if (valid_type(*target) == true) { + *type = *target; + *target = *name; + *name = NULL; + } + } else if (delim1 != delim2) { // :: + if (asprintf(type, "%.*s", delim1 - path, path) < 0) { + ERRNO(errno); + return -1; + } + delim1++; + if (asprintf(target, "%.*s", delim2 - delim1, delim1) < 0) { + ERRNO(errno); + return -1; + } + delim2++; + if (asprintf(name, "%s", delim2) < 0) { + ERRNO(errno); + return -1; + } + } + + return 0; +} + +int dump_errors(const char * name, FILE * out) +{ + assert(name != NULL); + + if (out == NULL) + out = stderr; + + err_t * err = NULL; + + while ((err = err_get()) != NULL) { + switch (err_type(err)) { + case ERR_VERSION: + fprintf(out, "%s: %s : %s(%d) : v%d.%02d.%04d %.*s\n", + basename((char *)name), err_type_name(err), + basename(err_file(err)), err_line(err), + VER_TO_MAJOR(err_code(err)), + VER_TO_MINOR(err_code(err)), + VER_TO_PATCH(err_code(err)), + err_size(err), (char *)err_data(err)); + break; + default: + fprintf(out, "%s: %s : %s(%d) : (code=%d) %.*s\n", + basename((char *)name), err_type_name(err), + basename(err_file(err)), err_line(err), + err_code(err), err_size(err), + (char *)err_data(err)); + } + } + + return 0; +} + +int check_file(const char * path, FILE * file, off_t offset) { + assert(file != NULL); + + switch (__ffs_fcheck(file, offset)) { + case 0: + return 0; + case FFS_CHECK_HEADER_MAGIC: + UNEXPECTED("'%s' no partition table found at offset '%llx'\n", + path, offset); + return -1; + case FFS_CHECK_HEADER_CHECKSUM: + UNEXPECTED("'%s' partition table at offset '%llx', is " + "corrupted\n", path, offset); + return -1; + case FFS_CHECK_ENTRY_CHECKSUM: + UNEXPECTED("'%s' partition table at offset '%llx', has " + "corrupted entries\n", path, offset); + return -1; + default: + return -1; + } +} + +entry_list_t * entry_list_create(ffs_t * ffs) +{ + entry_list_t * self = (entry_list_t *)malloc(sizeof(*self)); + if (self == NULL) { + ERRNO(errno); + return NULL; + } + + list_init(&self->list); + + self->ffs = ffs; + + return self; +} + +entry_list_t * entry_list_create_by_regex(ffs_t * ffs, const char * name) +{ + assert(ffs != NULL); + + if (name == NULL) + name = ".*"; + + RAII(regex_t*, rx, regex_create(name), regex_free); + if (rx == NULL) + return NULL; + + entry_list_t * self = entry_list_create(ffs); + if (self == NULL) + return NULL; + + int entry_list(ffs_entry_t * entry) + { + assert(entry != NULL); + + char full_name[page_size]; + if (__ffs_entry_name(ffs, entry, full_name, + sizeof full_name) < 0) + return -1; + if (regexec(rx, full_name, 0, NULL, 0) == REG_NOMATCH) + return 0; + + entry_node_t * entry_node; + entry_node = (entry_node_t *)malloc(sizeof(*entry_node)); + if (entry_node == NULL) { + ERRNO(errno); + return -1; + } + + memcpy(&entry_node->entry, entry, sizeof(*entry)); + list_add_tail(&self->list, &entry_node->node); + + return 0; + } + + if (__ffs_iterate_entries(ffs, entry_list) < 0) { + entry_list_delete(self); + return NULL; + } + + return self; +} + +int entry_list_add(entry_list_t * self, ffs_entry_t * entry) +{ + assert(self != NULL); + assert(entry != NULL); + + entry_node_t * entry_node; + entry_node = (entry_node_t *)malloc(sizeof(*entry_node)); + if (entry_node == NULL) { + ERRNO(errno); + return -1; + } + + memcpy(&entry_node->entry, entry, sizeof(entry_node->entry)); + list_add_tail(&self->list, &entry_node->node); + + return 0; +} + +int entry_list_add_child(entry_list_t * self, ffs_entry_t * parent) +{ + assert(self != NULL); + assert(parent != NULL); + + int child_entry_list(ffs_entry_t * child) + { + assert(child != NULL); + + if (child->pid != parent->id) + return 0; + + entry_node_t * entry_node; + entry_node = (entry_node_t *)malloc(sizeof(*entry_node)); + if (entry_node == NULL) { + ERRNO(errno); + return -1; + } + + memcpy(&entry_node->entry, child, sizeof(*child)); + list_add_tail(&self->list, &entry_node->node); + + return 0; + } + + if (__ffs_iterate_entries(self->ffs, child_entry_list) < 0) + return -1; + + return 0; +} + +int entry_list_remove(entry_list_t * self, entry_node_t * node) +{ + assert(self != NULL); + assert(node != NULL); + + list_remove_node(&self->list, &node->node); + + return 0; +} + +int entry_list_delete(entry_list_t * self) +{ + if (self == NULL) + return 0; + + while (!list_empty(&self->list)) { + free(container_of(list_remove_head(&self->list), entry_node_t, + node)); + } + + return 0; +} + +int entry_list_exists(entry_list_t * self, ffs_entry_t * entry) +{ + assert(self != NULL); + assert(entry != NULL); + + list_iter_t it; + entry_node_t * entry_node; + + list_iter_init(&it, &self->list, LI_FLAG_FWD); + + list_for_each(&it, entry_node, node) { + ffs_entry_t * __entry = &entry_node->entry; + + if (__entry->base == entry->base && + __entry->size == entry->size) + return 1; + } + + return 0; +} + +ffs_entry_t * entry_list_find(entry_list_t * self, const char * name) +{ + assert(self != NULL); + assert(name != NULL); + + list_iter_t it; + entry_node_t * entry_node = NULL; + + list_iter_init(&it, &self->list, LI_FLAG_FWD); + + list_for_each(&it, entry_node, node) { + ffs_entry_t * entry = &entry_node->entry; + + if (strcmp(entry->name, name) == 0) + return entry; + } + + return NULL; +} + +int entry_list_dump(entry_list_t * self, FILE * out) +{ + assert(self != NULL); + if (out == NULL) + out = stdout; + + list_iter_t it; + entry_node_t * entry_node = NULL; + + list_iter_init(&it, &self->list, LI_FLAG_FWD); + + list_for_each(&it, entry_node, node) { + ffs_entry_t * entry = &entry_node->entry; + + fprintf(stderr, "id[%d] pid[%d] name[%s]\n", + entry->id, entry->pid, entry->name); + } + + return 0; +} + +FILE *__fopen(const char * type, const char * target, const char * mode, + int debug) +{ + assert(target != NULL); + assert(mode != NULL); + + FILE *file = NULL; + size_t port = 0; + + if (type == NULL) + type = TYPE_FILE; + + if (strcasecmp(type, TYPE_AA) == 0) { + if (parse_number(target, &port) < 0) + return NULL; + UNEXPECTED("Removed support"); + //file = fopen_aaflash(port, mode, debug); + } else if (strcasecmp(type, TYPE_RW) == 0) { + UNEXPECTED("Removed support"); + //file = fopen_rwflash(target, mode, debug); + } else if (strcasecmp(type, TYPE_SFC) == 0) { + UNEXPECTED("FIX ME"); + return NULL; + } else if (strcasecmp(type, TYPE_FILE) == 0) { + file = fopen(target, mode); + if (file == NULL) + ERRNO(errno); + } else { + errno = EINVAL; + ERRNO(errno); + } + + return file; +} + +int is_file(const char * type, const char * target, const char * name) +{ + return type == NULL && target != NULL && name == NULL; +} + +int valid_type(const char * type) +{ + return type == NULL ? 0 : + strcasecmp(type, TYPE_FILE) == 0 || + strcasecmp(type, TYPE_RW) == 0 || + strcasecmp(type, TYPE_AA) == 0 || + strcasecmp(type, TYPE_SFC) == 0; +} + +int fcp_read_entry(ffs_t * src, const char * name, FILE * out) +{ + assert(src != NULL); + assert(name != NULL); + + size_t block_size; + if (__ffs_info(src, FFS_INFO_BLOCK_SIZE, &block_size) < 0) + return -1; + + size_t buffer_count; + if (__ffs_info(src, FFS_INFO_BUFFER_COUNT, &buffer_count) < 0) + return -1; + + size_t buffer_size = block_size * buffer_count; + RAII(void*, buffer, malloc(buffer_size), free); + if (buffer == NULL) { + ERRNO(errno); + return -1; + } + + ffs_entry_t entry; + if (__ffs_entry_find(src, name, &entry) == false) { + UNEXPECTED("'%s' partition not found => %s", + src->path, name); + return -1; + } + + uint32_t poffset; + if (__ffs_info(src, FFS_INFO_OFFSET, &poffset) < 0) + return -1; + + size_t total = 0; + size_t size = entry.actual; + off_t offset = 0; + + if (isatty(fileno(stderr))) { + fprintf(stderr, "%8x: %s: read partition %8x/%8x", + poffset, name, entry.actual, total); + } + + while (0 < size) { + size_t count = min(buffer_size, size); + + ssize_t rc; + rc = __ffs_entry_read(src, name, buffer, offset, count); + + + rc = fwrite(buffer, 1, rc, out); + if (rc <= 0 && ferror(out)) { + ERRNO(errno); + return -1; + } + + size -= rc; + total += rc; + offset += rc; + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); + fprintf(stderr, "%8x/%8x", entry.actual, total); + } + } + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\n"); + } + + return total; +} + +int fcp_write_entry(ffs_t * dst, const char * name, FILE * in) +{ + assert(dst != NULL); + assert(name != NULL); + + size_t block_size; + if (__ffs_info(dst, FFS_INFO_BLOCK_SIZE, &block_size) < 0) + return -1; + + size_t buffer_count; + if (__ffs_info(dst, FFS_INFO_BUFFER_COUNT, &buffer_count) < 0) + return -1; + + size_t buffer_size = block_size * buffer_count; + RAII(void*, buffer, malloc(buffer_size), free); + if (buffer == NULL) { + ERRNO(errno); + return -1; + } + + ffs_entry_t entry; + if (__ffs_entry_find(dst, name, &entry) == false) { + UNEXPECTED("'%s' partition not found => %s", + dst->path, name); + return -1; + } + + uint32_t poffset; + if (__ffs_info(dst, FFS_INFO_OFFSET, &poffset) < 0) + return -1; + + size_t total = 0; + size_t size = entry.actual; + off_t offset = 0; + + if (isatty(fileno(stderr))) { + fprintf(stderr, "%8x: %s: write partition %8x/%8x", + poffset, name, entry.actual, total); + } + + while (0 < size) { + size_t count = min(buffer_size, size); + + ssize_t rc; + rc = fread(buffer, 1, count, in); + if (rc <= 0) { + if (feof(in)) { + break; + } else if (ferror(in)) { + ERRNO(errno); + return -1; + } + } + + rc = __ffs_entry_write(dst, name, buffer, offset, rc); + + if (__ffs_fsync(dst) < 0) + return -1; + + size -= rc; + total += rc; + offset += rc; + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); + fprintf(stderr, "%8x/%8x", entry.actual, total); + } + } + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\n"); + } + + return total; +} + +int fcp_erase_entry(ffs_t * dst, const char * name, char fill) +{ + assert(dst != NULL); + assert(name != NULL); + + size_t block_size; + if (__ffs_info(dst, FFS_INFO_BLOCK_SIZE, &block_size) < 0) + return -1; + + size_t buffer_count; + if (__ffs_info(dst, FFS_INFO_BUFFER_COUNT, &buffer_count) < 0) + return -1; + + size_t buffer_size = block_size * buffer_count; + RAII(void*, buffer, malloc(buffer_size), free); + if (buffer == NULL) { + ERRNO(errno); + return -1; + } + + memset(buffer, fill, buffer_size); + + ffs_entry_t entry; + if (__ffs_entry_find(dst, name, &entry) == false) { + UNEXPECTED("'%s' partition not found => %s", + dst->path, name); + return -1; + } + + uint32_t poffset; + if (__ffs_info(dst, FFS_INFO_OFFSET, &poffset) < 0) + return -1; + + size_t total = 0; + size_t size = entry.size * block_size; + off_t offset = 0; + + if (isatty(fileno(stderr))) { + fprintf(stderr, "%8x: %s: erase partition %8x/%8x", + poffset, name, entry.actual, total); + } + + while (0 < size) { + size_t count = min(buffer_size, size); + + ssize_t rc; + rc = __ffs_entry_write(dst, name, buffer, offset, count); + + if (__ffs_fsync(dst) < 0) + return -1; + + size -= rc; + total += rc; + offset += rc; + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); + fprintf(stderr, "%8x/%8x", entry.size * block_size, + total); + } + } + + if (__ffs_entry_truncate(dst, name, 0ULL) < 0) { + ERRNO(errno); + return -1; + } + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\n"); + } + + return total; +} + +int fcp_copy_entry(ffs_t * src, const char * src_name, + ffs_t * dst, const char * dst_name) +{ + assert(src != NULL); + assert(src_name != NULL); + assert(dst != NULL); + assert(dst_name != NULL); + + size_t block_size; + if (__ffs_info(src, FFS_INFO_BLOCK_SIZE, &block_size) < 0) + return -1; + + size_t buffer_count; + if (__ffs_info(dst, FFS_INFO_BUFFER_COUNT, &buffer_count) < 0) + return -1; + + size_t buffer_size = block_size * buffer_count; + RAII(void*, buffer, malloc(buffer_size), free); + if (buffer == NULL) { + ERRNO(errno); + return -1; + } + + ffs_entry_t src_entry; + if (__ffs_entry_find(src, src_name, &src_entry) == false) { + UNEXPECTED("'%s' partition not found => %s", + src->path, src_name); + return -1; + } + + ffs_entry_t dst_entry; + if (__ffs_entry_find(dst, dst_name, &dst_entry) == false) { + UNEXPECTED("'%s' partition not found => %s", + dst->path, dst_name); + return -1; + } + + size_t total = 0; + size_t size = src_entry.actual; + off_t offset = 0; + + if (isatty(fileno(stderr))) { + fprintf(stderr, "%8llx: %s: copy partition %8x/%8x", + src->offset, dst_name, src_entry.actual, total); + } + + while (0 < size) { + size_t count = min(buffer_size, size); + + ssize_t rc; + rc = __ffs_entry_read(src, src_name, buffer, offset, count); + if (rc < 0) + return -1; + rc = __ffs_entry_write(dst, dst_name, buffer, offset, rc); + if (rc < 0) + return -1; + + if (__ffs_fsync(dst) < 0) + return -1; + + size -= rc; + total += rc; + offset += rc; + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); + fprintf(stderr, "%8x/%8x", src_entry.actual, total); + } + } + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\n"); + } + + return total; +} + +int fcp_compare_entry(ffs_t * src, const char * src_name, + ffs_t * dst, const char * dst_name) +{ + assert(src != NULL); + assert(src_name != NULL); + assert(dst != NULL); + assert(dst_name != NULL); + + size_t block_size; + if (__ffs_info(src, FFS_INFO_BLOCK_SIZE, &block_size) < 0) + return -1; + + size_t buffer_count; + if (__ffs_info(dst, FFS_INFO_BUFFER_COUNT, &buffer_count) < 0) + return -1; + + size_t buffer_size = block_size * buffer_count; + + RAII(void*, src_buffer, malloc(buffer_size), free); + if (src_buffer == NULL) { + ERRNO(errno); + return -1; + } + RAII(void*, dst_buffer, malloc(buffer_size), free); + if (dst_buffer == NULL) { + ERRNO(errno); + return -1; + } + + ffs_entry_t src_entry; + if (__ffs_entry_find(src, src_name, &src_entry) == false) { + UNEXPECTED("'%s' partition not found => %s", + src->path, src_name); + return -1; + } + + ffs_entry_t dst_entry; + if (__ffs_entry_find(dst, dst_name, &dst_entry) == false) { + UNEXPECTED("'%s' partition not found => %s", + dst->path, dst_name); + return -1; + } + + size_t total = 0; + size_t size = src_entry.actual; + off_t offset = 0; + + if (isatty(fileno(stderr))) { + fprintf(stderr, "%8llx: %s: compare partition %8x/%8x", + src->offset, dst_name, src_entry.actual, total); + } + + while (0 < size) { + size_t count = min(buffer_size, size); + + ssize_t rc; + rc = __ffs_entry_read(src, src_name, src_buffer, offset, count); + if (rc < 0) + return -1; + rc = __ffs_entry_read(dst, dst_name, dst_buffer, offset, rc); + if (rc < 0) + return -1; + + char * src_ptr = src_buffer; + char * dst_ptr = dst_buffer; + size_t cnt = 0; + + while (cnt < count) { + size_t cmp_sz = min(count - cnt, COMPARE_SIZE); + + if (memcmp(src_ptr, dst_ptr, cmp_sz) != 0) { + UNEXPECTED("MISCOMPARE! '%s' != '%s' at " + "offset '%llx'\n", src_name, + dst_name, offset + cnt); + + if (isatty(fileno(stderr))) + fprintf(stderr, " <== [ERROR]\n"); + + return -1; + } + + src_ptr += cmp_sz; + dst_ptr += cmp_sz; + cnt += cmp_sz; + } + + size -= rc; + total += rc; + offset += rc; + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); + fprintf(stderr, "%8x/%8x", src_entry.actual, total); + } + } + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\n"); + } + + return total; +} diff --git a/fcp/src/misc.h b/fcp/src/misc.h new file mode 100644 index 0000000..c96eb11 --- /dev/null +++ b/fcp/src/misc.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: fcp_misc.h + * Author: Shaun Wetzstein + * Descr: Misc. helpers + * Date: 05/12/2012 + */ + +#ifndef __MISC__H__ +#define __MISC__H__ + +#include + +#include +#include + +#include + +#include + +typedef struct entry_list entry_list_t; +struct entry_list { + list_t list; + ffs_t * ffs; +}; + +typedef struct entry_node entry_node_t; +struct entry_node { + list_node_t node; + ffs_entry_t entry; +}; + +extern entry_list_t * entry_list_create(ffs_t *); +extern entry_list_t * entry_list_create_by_regex(ffs_t *, const char *); +extern int entry_list_add(entry_list_t *, ffs_entry_t *); +extern int entry_list_add_child(entry_list_t *, ffs_entry_t *); +extern int entry_list_remove(entry_list_t *, entry_node_t *); +extern int entry_list_delete(entry_list_t *); +extern int entry_list_exists(entry_list_t *, ffs_entry_t *); +extern ffs_entry_t * entry_list_find(entry_list_t *, const char *); +extern int entry_list_dump(entry_list_t *, FILE *); + +extern int parse_offset(const char *, off_t *); +extern int parse_size(const char *, size_t *); +extern int parse_number(const char *, size_t *); +extern int parse_path(const char *, char **, char **, char **); + +extern int dump_errors(const char *, FILE *); +extern int check_file(const char *, FILE *, off_t); +extern int is_file(const char *, const char *, const char *); +extern int valid_type(const char *); + +extern FILE *__fopen(const char *, const char *, const char *, int); + +#endif /* __MISC__H__ */ diff --git a/fcp/x86/Makefile b/fcp/x86/Makefile new file mode 100644 index 0000000..2a5a240 --- /dev/null +++ b/fcp/x86/Makefile @@ -0,0 +1,7 @@ +DEPTH = .. + +ARCH=x86 +FCP_INSTALL = $(INST_USR_X86) +FCP_INSTALL_TEST = $(INST_TESTS_X86) + +include ../Rules.mk diff --git a/ffs/.gitignore b/ffs/.gitignore new file mode 100644 index 0000000..3a1f0a1 --- /dev/null +++ b/ffs/.gitignore @@ -0,0 +1,3 @@ +*/ffs +*/fcp +test/test_libffs diff --git a/ffs/Makefile b/ffs/Makefile new file mode 100644 index 0000000..e08925f --- /dev/null +++ b/ffs/Makefile @@ -0,0 +1,10 @@ +SUBDIRS=x86 + +.PHONY: subdirs $(SUBDIRS) + +subdirs: $(SUBDIRS) + +$(SUBDIRS):: + $(MAKE) -C $@ $(MAKECMDGOALS) + +all clean install: $(SUBDIRS) diff --git a/ffs/Rules.mk b/ffs/Rules.mk new file mode 100644 index 0000000..eb8b736 --- /dev/null +++ b/ffs/Rules.mk @@ -0,0 +1,30 @@ +CFLAGS += -m32 -D_GNU_SOURCE -std=gnu99 -D_FILE_OFFSET_BITS=64 +CFLAGS += -I$(DEPTH)/.. -I$(DEPTH)/boot/fsp2_ipl -iquote.. + +LDFLAGS += -L. -m32 + +NAME=libffs + +CLIB=$(DEPTH)/../clib/x86/libclib.a + +OBJS=$(NAME).o $(NAME)2.o + +TARGETS=$(NAME).so $(NAME).a + +vpath %.c ../src +vpath %.h .. + +all: $(TARGETS) + +$(NAME).so: $(OBJS) + $(CC) $(LDFLAGS) -fPIC -shared -Wl,-soname,$@ -o $@ $^ + +$(NAME).a: $(OBJS) + $(AR) -r $@ $^ + +install: $(TARGETS) + $(INSTALL) libffs.so libffs.a $(FFS_INSTALL)/lib + $(INSTALL) ../ffs.h ../libffs.h ../libffs2.h $(FFS_INSTALL)/include + +clean distclean: + $(RM) -f $(TARGETS) $(OBJS) diff --git a/ffs/Rules.test.mk b/ffs/Rules.test.mk new file mode 100644 index 0000000..9b63533 --- /dev/null +++ b/ffs/Rules.test.mk @@ -0,0 +1,25 @@ +CFLAGS += -m32 -D_GNU_SOURCE -std=gnu99 -D_FILE_OFFSET_BITS=64 -I$(DEPTH)/apps -iquote.. +LDFLAGS += -L. -L$(DEPTH)/apps/clib/$(ARCH_DEP_DIR) + +OBJS=test_libffs.o + +TARGETS=test_libffs + +vpath %.c ../test +vpath %.h .. + +all: $(TARGETS) + +#libffs.a: $(OBJS) +# $(AR) -r $@ $^ + +test_libffs: test_libffs.o ../ppc/libffs.a $(DEPTH)/apps/clib/$(ARCH_DEP_DIR)/libclib.a + $(CC) $(LDFLAGS) -o $@ $^ -lpthread + +install: $(TARGETS) + $(INSTALL) ../test/test_libffs $(TEST_LIBFFS_INSTALL) + $(INSTALL) ../test/libffs_test.sh $(TEST_LIBFFS_INSTALL) + $(INSTALL) ../test/ffs_tool_test.sh $(TEST_LIBFFS_INSTALL) + +clean distclean: + $(RM) -f $(TARGETS) $(OBJS) diff --git a/ffs/ffs.h b/ffs/ffs.h new file mode 100644 index 0000000..3c113bc --- /dev/null +++ b/ffs/ffs.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * FSP Flash Structure + * + * This header defines the layout for the FSP Flash Structure. + */ + +#ifndef __FFS_H__ +#define __FFS_H__ + +/* Pull in the correct header depending on what is being built */ +#if defined(__KERNEL__) +#include +#else +#include +#endif + +/* The version of this partition implementation */ +#define FFS_VERSION_1 1 + +/* Magic number for the partition header (ASCII 'PART') */ +#define FFS_MAGIC 0x50415254 + +/* The maximum length of the partition name */ +#define PART_NAME_MAX 15 + +/* + * Sizes of the data structures + */ +#define FFS_HDR_SIZE sizeof(struct ffs_hdr) +#define FFS_ENTRY_SIZE sizeof(struct ffs_entry) + +/* + * Sizes of the data structures w/o checksum + */ +#define FFS_HDR_SIZE_CSUM (FFS_HDR_SIZE - sizeof(uint32_t)) +#define FFS_ENTRY_SIZE_CSUM (FFS_ENTRY_SIZE - sizeof(uint32_t)) + +/* pid of logical partitions/containers */ +#define FFS_PID_TOPLEVEL 0xFFFFFFFF + +/* + * Type of image contained w/in partition + */ +enum type { + FFS_TYPE_DATA = 1, + FFS_TYPE_LOGICAL = 2, + FFS_TYPE_PARTITION = 3, +}; + +/* + * Flag bit definitions + */ +#define FFS_FLAGS_PROTECTED 0x0001 +#define FFS_FLAGS_U_BOOT_ENV 0x0002 + +/* + * Number of user data words + */ +#define FFS_USER_WORDS 16 + +/* + * Define layout of user.data in struct ffs_entry + */ +enum user_data { + USER_DATA_VOL = 0, + USER_DATA_SIZE = 1, + USER_DATA_CRC = 2, +}; + +/** + * struct ffs_entry - Partition entry + * + * @name: Opaque null terminated string + * @base: Starting offset of partition in flash (in hdr.block_size) + * @size: Partition size (in hdr.block_size) + * @pid: Parent partition entry (FFS_PID_TOPLEVEL for toplevel) + * @id: Partition entry ID [1..65536] + * @type: Describe type of partition + * @flags: Partition attributes (optional) + * @actual: Actual partition size (in bytes) + * @resvd: Reserved words for future use + * @user: User data (optional) + * @checksum: Partition entry checksum (includes all above) + */ +struct ffs_entry { + char name[PART_NAME_MAX + 1]; + uint32_t base; + uint32_t size; + uint32_t pid; + uint32_t id; + uint32_t type; + uint32_t flags; + uint32_t actual; + uint32_t resvd[4]; + struct { + uint32_t data[FFS_USER_WORDS]; + } user; + uint32_t checksum; +} __attribute__ ((packed)); + +/** + * struct ffs_hdr - FSP Flash Structure header + * + * @magic: Eye catcher/corruption detector + * @version: Version of the structure + * @size: Size of partition table (in block_size) + * @entry_size: Size of struct ffs_entry element (in bytes) + * @entry_count: Number of struct ffs_entry elements in @entries array + * @block_size: Size of block on device (in bytes) + * @block_count: Number of blocks on device + * @resvd: Reserved words for future use + * @checksum: Header checksum + * @entries: Pointer to array of partition entries + */ +struct ffs_hdr { + uint32_t magic; + uint32_t version; + uint32_t size; + uint32_t entry_size; + uint32_t entry_count; + uint32_t block_size; + uint32_t block_count; + uint32_t resvd[4]; + uint32_t checksum; + struct ffs_entry entries[]; +} __attribute__ ((packed)); + +#endif /* __FFS_H__ */ diff --git a/ffs/libffs.h b/ffs/libffs.h new file mode 100644 index 0000000..9772cac --- /dev/null +++ b/ffs/libffs.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __LIBFFS_H__ +#define __LIBFFS_H__ + +#include +#include + +#include + +#include "ffs.h" + +typedef struct ffs_entry ffs_entry_t; +typedef struct ffs_hdr ffs_hdr_t; +typedef enum type ffs_type_t; + +#define FFS_EXCEPTION_DATA 1024 + +/* ============================================================ */ + +/*! + * @brief ffs I/O interface + */ +struct ffs { + ffs_hdr_t * hdr; + + char * path; + off_t offset; + + FILE * file; + uint32_t count; + + void * buf; // FILE* buffer + uint32_t buf_count; // multiple of hdr->block_size + + bool dirty; +}; + +typedef struct ffs ffs_t; + +struct ffs_exception { + int rc; + char data[FFS_EXCEPTION_DATA]; +}; + +typedef struct ffs_exception ffs_exception_t; + +#define FFS_PARTITION_NAME "part" + +#define FFS_INFO_ERROR 0 +#define FFS_INFO_MAGIC 1 +#define FFS_INFO_VERSION 2 +#define FFS_INFO_ENTRY_SIZE 3 +#define FFS_INFO_ENTRY_COUNT 4 +#define FFS_INFO_BLOCK_SIZE 5 +#define FFS_INFO_BLOCK_COUNT 6 +#define FFS_INFO_BUFFER_COUNT 7 +#define FFS_INFO_OFFSET 8 + +#define FFS_CHECK_PATH -3 +#define FFS_CHECK_HEADER_MAGIC -4 +#define FFS_CHECK_HEADER_CHECKSUM -5 +#define FFS_CHECK_ENTRY_CHECKSUM -6 + +#ifdef __cplusplus +extern "C" { +#endif + +extern int __ffs_fcheck(FILE *, off_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern int __ffs_check(const char *, off_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern ffs_t * __ffs_fcreate(FILE *, off_t, uint32_t, uint32_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern ffs_t * __ffs_create(const char *, off_t, uint32_t, uint32_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern ffs_t * __ffs_fopen(FILE *, off_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern ffs_t * __ffs_open(const char *, off_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern int __ffs_info(ffs_t *, int, uint32_t *) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern int __ffs_buffer(ffs_t *, size_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern int __ffs_close(ffs_t *) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern int __ffs_fclose(ffs_t *) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern int __ffs_fsync(ffs_t *) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern int __ffs_list_entries(ffs_t *, const char *, bool, FILE *) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +extern int __ffs_iterate_entries(ffs_t *, int (*)(ffs_entry_t*)) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +extern int __ffs_entry_find(ffs_t *, const char *, ffs_entry_t *) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +extern int __ffs_entry_find_parent(ffs_t *, const char *, ffs_entry_t *) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +extern int __ffs_entry_name(ffs_t *, ffs_entry_t *, char *, size_t) +/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ; + +extern int __ffs_entry_add(ffs_t *, const char *, off_t, + size_t, ffs_type_t, uint32_t) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +extern int __ffs_entry_delete(ffs_t *, const char *) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +extern int __ffs_entry_user_get(ffs_t *, const char *, uint32_t, uint32_t *) +/*! @cond */ __nonnull ((1,2,4)) /*! @endcond */ ; + +extern int __ffs_entry_user_put(ffs_t *, const char *, uint32_t, uint32_t) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +extern ssize_t __ffs_entry_hexdump(ffs_t *, const char *, FILE *) +/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ; + +extern ssize_t __ffs_entry_truncate(ffs_t *, const char *, size_t) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +extern ssize_t __ffs_entry_read(ffs_t *, const char *, void *, off_t, size_t) +/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ; + +extern ssize_t __ffs_entry_write(ffs_t *, const char *, const void *, + off_t, size_t) +/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ; + +extern ssize_t __ffs_entry_copy(ffs_t *, ffs_t *, const char *) +/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ; + +extern ssize_t __ffs_entry_compare(ffs_t *, ffs_t *, const char *) +/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ; + +extern int __ffs_entry_list(ffs_t *, ffs_entry_t ** list) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +#ifdef __cplusplus +} +#endif + +/* ============================================================ */ + +#endif /* __LIBFFS_H__ */ diff --git a/ffs/libffs2.h b/ffs/libffs2.h new file mode 100644 index 0000000..79bc856 --- /dev/null +++ b/ffs/libffs2.h @@ -0,0 +1,332 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! + * @file libffs2.h + * @brief FSP Flash File Structure API + * @details This library provides an API to the FFS partitioning scheme. + * @author Shaun Wetzstein + * @date 2010-2012 + */ + +#ifndef __LIBFFS2_H__ +#define __LIBFFS2_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "libffs.h" + +/* FFS_OPEN_* are deprecated, use FFS_CHECK_* instead */ +#define FFS_OPEN_INVALID_PATH FFS_CHECK_PATH +#define FFS_OPEN_FOPEN_FAILURE -1 +#define FFS_OPEN_MAGIC_CHECK FFS_CHECK_HEADER_MAGIC +#define FFS_OPEN_CRC_CHECK FFS_CHECK_HEADER_CHECKSUM + +/*! + * @brief Clears the (global) FFS error indicator + * @memberof ffs + * @return None + */ +extern void ffs_errclr(void); + +/*! + * @brief Return the error number of an FFS error + * @memberof ffs + * @return non-0 on success, '0' if no pending error + */ +extern int ffs_errnum(void); + +/*! + * @brief Return the error string of an FFS error + * @memberof ffs + * @return non-NULL on success, 'NULL' if no pending error + */ +extern const char * ffs_errstr(void); + +/*! + * @brief Open the file named 'path' and check the @em FFS partition table + * at 'offset' bytes from the beginning of the file (or device). + * @memberof ffs + * @param path [in] Path of target file or device + * @param offset [in] Byte offset, from the beginning of the file (or device), + * of the ffs_hdr_t structure + * @return 0 on success, non-0 otherwise + * FFS_CHECK_PATH if path is NULL or "" + * FFS_CHECK_HEADER_MAGIC for header magic corruption + * FFS_CHECK_HEADER_CHECKSUM for header data corruption + * FFS_CHECK_ENTRY_CHECKSUM for [an] entry data corruption + */ +extern int ffs_check(const char *, off_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +/*! + * @brief Create an empty @em FFS partition table and store it at 'offset' + * bytes from the beginning of the file (or device) named 'path' + * @memberof ffs + * @param path [in] Path of target file or device + * @param offset [in] Byte offset, from beginning of file (or device), + * of the ffs_hdr_t structure + * @param block_size [in] Block size in bytes + * @param block_count [in] Number of blocks in the device size + * @return Pointer to ffs_t (allocated on the heap) on success, + * NULL otherwise + */ +extern ffs_t * ffs_create(const char *, off_t, uint32_t, uint32_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +/*! + * @brief Open the file name 'path' and read the @em FFS partition table + * at 'offset' bytes from the beggining of the file (or device). + * @memberof ffs + * @param path [in] Path of target file or device + * @param offset [in] Byte offset, from beginning of file (or device), + * of the ffs_hdr_t structure + * @return Pointer to ffs_t (allocated on the heap) on success, + * NULL otherwise + */ +extern ffs_t * ffs_open(const char *, off_t) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +/*! + * @brief Query a @em FFS object for header metadata. + * @memberof ffs + * @param self [in] Pointer to ffs object + * @param name [in] Field number, see ffs.h for details + * FFS_INFO_MAGIC - ffs_hdr::magic + * FFS_INFO_VERSION - ffs_hdr::version + * FFS_INFO_ENTRY_SIZE - ffs_hdr::entry_size + * FFS_INFO_ENTRY_COUNT - ffs_hdr::entry_count + * FFS_INFO_BLOCK_SIZE - ffs_hdr::block_size + * FFS_INFO_BLOCK_COUNT - ffs_hdr::block_count + * @param value [out] Pointer to output data + * @return '0' on success, non-0 otherwise + */ +extern int ffs_info(ffs_t *, int, uint32_t *) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +/*! + * @brief Close a @em FFS object, after writting any modified in-core + * data to the file (or device). + * @memberof ffs + * @param self [in] Pointer to ffs object + * @return '0' on success, non-0 otherwise + */ +extern int ffs_close(ffs_t *) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +/*! + * @brief Flush all modified in-core metadata of a @em FFS object, + * to the underlying file (or device). + * @memberof ffs + * @param self [in] Pointer to ffs object + * @return '0' on success, non-0 otherwise + */ +extern int ffs_fsync(ffs_t *) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +/*! + * @brief Pretty print the entries of a @em FFS partition table to + * stream 'out' + * @memberof ffs + * @param self [in] Pointer to ffs object + * @param out [in] Pointer the output stream + * @return '0' on success, non-0 otherwise + */ +extern int ffs_list_entries(ffs_t *, FILE *) +/*! @cond */ __nonnull ((1)) /*! @endcond */ ; + +/*! + * @brief Iterate over the entries of a @em FFS partition table and + * call a callback function 'func' + * @note If the callback function returns non-0, the iteration function + * will return immediately. + * @memberof ffs + * @param self [in] Pointer to ffs object + * @param func [in] Pointer the callback function + * @return '0' on success, non-0 otherwise + */ +extern int ffs_iterate_entries(ffs_t *, int (*)(ffs_entry_t*)) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Find an entry in a @em FFS partition table and return + * a copy of the in 'entry' + * @memberof ffs + * @param self [in] Pointer to ffs object + * @param path [in] Name of a partition entry + * @param entry [out] Target entry object + * @return '1' == found, '0' == not-found, error otherwise + */ +extern int ffs_entry_find(ffs_t *, const char *, ffs_entry_t *) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Find the parent entry in a @em FFS partition table and + * return a copy of the in 'entry' + * @memberof ffs + * @param self [in] Pointer to ffs object + * @param path [in] Name of a partition entry + * @param parent [out] Target entry object + * @return '1' == found, '0' == not-found, error otherwise + */ +extern int ffs_entry_find_parent(ffs_t *, const char *, ffs_entry_t *) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Add a partition entry to a @em FFS partition table + * @memberof ffs + * @param self [in] Pointer to ffs object + * @param path [in] Name of a partition entry + * @param offset [in] Offset, in blocks, of the partition entry, from + * the beginning of the file (or device) + * @param size [in] Size, in blocks, of the partition entry + * @param type [in] Partition type. FFS_TYPE_LOGICAL can be used to + * to create a container for a set of partitions. A logical partition + * can be thought of as a directory. Use FFS_TYPE_DATA for data + * partitions. + * @param flags [in] Partition flags. FFS_FLAG_PROTECTED can be used to + * protect a partition from inadvertant updates. The ffs related tools + * should *not* overwrite protected partitions unless a --protected + * flag is specified. + * @return '0' on success, non-0 otherwise + */ +extern int ffs_entry_add(ffs_t *, const char *, off_t, size_t, ffs_type_t, uint32_t) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Delete a partition entry from the @em FFS partition table + * @memberof ffs + * @param self [in] Pointer to an ffs object + * @param path [in] Name of a partition entry + * @return '0' on success, non-0 otherwise + */ +extern int ffs_entry_delete(ffs_t *, const char *) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Get the value of a meta-data user word + * @memberof ffs + * @param self [in] Pointer to an ffs object + * @param name [in] Name of a partition entry + * @param user [in] User word number, in the range [0..FFS_USER_WORDS] + * @param word [in] User word value, in the range [0..UINT32_MAX] (optional) + * @note 'word' is optional, if omitted, the current value is returned + * @return '0' on success, non-0 otherwise + */ +extern int ffs_entry_user_get(ffs_t *, const char *, uint32_t, uint32_t *) +/*! @cond */ __nonnull ((1,2,4)) /*! @endcond */ ; + +/*! + * @brief Set the value of a meta-data user word + * @memberof ffs + * @param self [in] Pointer to an ffs object + * @param name [in] Name of a partition entry + * @param user [in] User word number, in the range [0..FFS_USER_WORDS] + * @param word [in] User word value, in the range [0..UINT32_MAX] (optional) + * @note 'word' is optional, if omitted, the current value is returned + * @return '0' on success, non-0 otherwise + */ +extern int ffs_entry_user_put(ffs_t *, const char *, uint32_t, uint32_t) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Hexdump the data contents of a partition entry to output stream + * 'out' + * @memberof ffs + * @param self [in] Pointer to an ffs object + * @param name [in] Name of a partition entry + * @param out [in] Output stream + * @return Negative on success, number of bytes written otherwise + */ +extern ssize_t ffs_entry_hexdump(ffs_t *, const char *, FILE *) +/*! @cond */ __nonnull ((1,2,3)) /*! @endcond */ ; + +/*! + * @brief Change the actual size of partition entry 'name' + * to 'offset' bytes from the beginning of the entry. + * @memberof ffs + * @param self [in] Pointer to an ffs object + * @param name [in] Name of a partition entry + * @param offset [in] Offset from the beginning of the partition (in bytes) + * @return Negative on failure, zero otherwise + */ +extern ssize_t ffs_entry_truncate_no_pad(ffs_t *, const char *, off_t) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Change the actual size of partition entry 'name' + * to 'offset' bytes from the beginning of the entry. + * @memberof ffs + * @param self [in] Pointer to an ffs object + * @param name [in] Name of a partition entry + * @param offset [in] Offset from the beginning of the partition (in bytes) + * @param pad [in] Pad character + * @return Negative on failure, zero otherwise + */ +extern ssize_t ffs_entry_truncate(ffs_t *, const char *, off_t, uint8_t) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Read 'count' data bytes of partition entry 'name' to into 'buf' at + * offset 'offset' bytes from the beginning of the entry + * @memberof ffs + * @param self [in] Pointer to an ffs object + * @param name [in] Name of a partition entry + * @param buf [out] Output data buffer + * @param offset [in] Offset from the beginning of the partition + * @param count [in] Number of bytes to read + * @return Negative on failure, number of bytes read otherwise + */ +extern ssize_t ffs_entry_read(ffs_t *, const char *, void *, off_t, size_t) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Write 'count' data bytes to partition entry 'name' at offset + * 'offset' bytes from the beginning of the entry, from input + * buffer 'buf' + * @memberof ffs + * @param self [in] Pointer to an ffs object + * @param name [in] Name of a partition entry + * @param buf [in] Input data buffer + * @param offset [in] Offset from the beginning of the partition + * @param count [in] Number of bytes to write + * @return Negative on failure, else number of bytes written otherwise + */ +extern ssize_t ffs_entry_write(ffs_t *, const char *, const void *, off_t, size_t) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +/*! + * @brief Return an array of entry_t structures, one each partition that + * exists in the partition table + * @memberof ffs + * @param self [in] Pointer to an ffs object + * @param list [out] Array of entry_t structures allocated on the heap + * @return Negative on failure, else number of entry_t's in the output + * array with a call to free() + * @note Caller is responsible for freeing array with a call to free() + */ +extern ssize_t ffs_entry_list(ffs_t *, ffs_entry_t **) +/*! @cond */ __nonnull ((1,2)) /*! @endcond */ ; + +#ifdef __cplusplus +} +#endif + +#endif /* __LIBFFS2_H__ */ diff --git a/ffs/src/ffs-fsp.h b/ffs/src/ffs-fsp.h new file mode 100644 index 0000000..f1b3374 --- /dev/null +++ b/ffs/src/ffs-fsp.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) International Business Machines Corp., 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * FSP Flash Structure, Boot specific adaptations + */ + +#ifndef __FFS_BOOT_H__ +#define __FFS_BOOT_H__ + +#include "ffs.h" + +/* + * Values to use in USER_DATA_VOL + * + * @note: Side 0/1 must be defined as even/odd values. Code in the IPL depends + * on this to be able to use the appropriate volume based on the boot bank. + */ +enum vol { + FFS_VOL_IPL0 = 0, + FFS_VOL_IPL1 = 1, + FFS_VOL_SPL0 = 2, + FFS_VOL_SPL1 = 3, + FFS_VOL_BOOTENV0 = 4, + FFS_VOL_BOOTENV1 = 5, + FFS_VOL_KERNEL0 = 6, + FFS_VOL_KERNEL1 = 7, + FFS_VOL_INITRAMFS0 = 8, + FFS_VOL_INITRAMFS1 = 9, + FFS_VOL_DTB0 = 10, + FFS_VOL_DTB1 = 11, + FFS_VOL_SERIES0 = 12, + FFS_VOL_SERIES1 = 13, + FFS_VOL_CARD0 = 14, + FFS_VOL_CARD1 = 15, + FFS_VOL_DUMP0 = 16, + FFS_VOL_DUMP1 = 17, + FFS_VOL_DUMP2 = 18, + FFS_VOL_DUMP3 = 19, +}; + +#endif /* __FFS_BOOT_H__ */ diff --git a/ffs/src/libffs.c b/ffs/src/libffs.c new file mode 100644 index 0000000..b912ede --- /dev/null +++ b/ffs/src/libffs.c @@ -0,0 +1,1520 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: libffs.c + * Author: Shaun Wetzstein + * Descr: FFS IO interface + * Note: + * Date: 05/07/12 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libffs.h" + +#include +#include +#include +#include +#include +#include + +#ifndef be32toh +#include +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define be32toh(x) __bswap_32(x) +#define htobe32(x) __bswap_32(x) +#else +#define be32toh(x) (x) +#define htobe32(x) (x) +#endif +#endif + +#define FFS_ENTRY_EXTENT 10UL + +/* ============================================================ */ + +static void __hdr_be32toh(ffs_hdr_t * hdr) +{ + assert(hdr != NULL); + + hdr->magic = be32toh(hdr->magic); + hdr->version = be32toh(hdr->version); + hdr->size = be32toh(hdr->size); + hdr->entry_size = be32toh(hdr->entry_size); + hdr->entry_count = be32toh(hdr->entry_count); + hdr->block_size = be32toh(hdr->block_size); + hdr->block_count = be32toh(hdr->block_count); + hdr->checksum = be32toh(hdr->checksum); +} + +static void __hdr_htobe32(ffs_hdr_t * hdr) +{ + assert(hdr != NULL); + + hdr->magic = htobe32(hdr->magic); + hdr->version = htobe32(hdr->version); + hdr->size = htobe32(hdr->size); + hdr->entry_size = htobe32(hdr->entry_size); + hdr->entry_count = htobe32(hdr->entry_count); + hdr->block_size = htobe32(hdr->block_size); + hdr->block_count = htobe32(hdr->block_count); + hdr->checksum = htobe32(hdr->checksum); +} + +static void __entry_be32toh(ffs_entry_t * entry) +{ + assert(entry != NULL); + + entry->base = be32toh(entry->base); + entry->size = be32toh(entry->size); + entry->pid = be32toh(entry->pid); + entry->id = be32toh(entry->id); + entry->type = be32toh(entry->type); + entry->flags = be32toh(entry->flags); + entry->actual = be32toh(entry->actual); + entry->checksum = be32toh(entry->checksum); + + entry->resvd[0] = be32toh(entry->resvd[0]); + entry->resvd[1] = be32toh(entry->resvd[1]); + entry->resvd[2] = be32toh(entry->resvd[2]); + entry->resvd[3] = be32toh(entry->resvd[3]); + + for (int j = 0; j < FFS_USER_WORDS; j++) + entry->user.data[j] = be32toh(entry->user.data[j]); +} + +static void __entry_htobe32(ffs_entry_t * entry) +{ + assert(entry != NULL); + + entry->base = htobe32(entry->base); + entry->size = htobe32(entry->size); + entry->pid = htobe32(entry->pid); + entry->id = htobe32(entry->id); + entry->type = htobe32(entry->type); + entry->flags = htobe32(entry->flags); + entry->actual = htobe32(entry->actual); + entry->checksum = htobe32(entry->checksum); + + entry->resvd[0] = htobe32(entry->resvd[0]); + entry->resvd[1] = htobe32(entry->resvd[1]); + entry->resvd[2] = htobe32(entry->resvd[2]); + entry->resvd[3] = htobe32(entry->resvd[3]); + + for (int j = 0; j < FFS_USER_WORDS; j++) + entry->user.data[j] = htobe32(entry->user.data[j]); +} + +static int __hdr_read(ffs_hdr_t * hdr, FILE * file, off_t offset) +{ + assert(hdr != NULL); + + if (fseeko(file, offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + size_t rc = fread(hdr, 1, sizeof(*hdr), file); + if (rc <= 0 && ferror(file)) { + ERRNO(errno); + return -1; + } + + uint32_t ck = memcpy_checksum(NULL, (void *)hdr, + offsetof(ffs_hdr_t, checksum)); + + __hdr_be32toh(hdr); + + if (hdr->magic != FFS_MAGIC) { + ERROR(ERR_UNEXPECTED, FFS_CHECK_HEADER_MAGIC, + "magic number mismatch '%x' != '%x'", + hdr->magic, FFS_MAGIC); + return -1; + } + + if (hdr->checksum != ck) { + ERROR(ERR_UNEXPECTED, FFS_CHECK_HEADER_CHECKSUM, + "header checksum mismatch '%x' != '%x'", + hdr->checksum, ck); + return -1; + } + + return 0; +} + +static int __hdr_write(ffs_hdr_t * hdr, FILE * file, off_t offset) +{ + assert(hdr != NULL); + assert(hdr->magic == FFS_MAGIC); + + hdr->checksum = memcpy_checksum(NULL, (void *)hdr, + offsetof(ffs_hdr_t, checksum)); + hdr->checksum = htobe32(hdr->checksum); + + if (fseeko(file, offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + size_t size = sizeof(*hdr); + + if (0 < hdr->entry_count) { + size += hdr->entry_count * hdr->entry_size; + + for (size_t i=0; ientry_count; i++) { + ffs_entry_t *e = hdr->entries + i; + + __entry_htobe32(e); + + e->checksum = memcpy_checksum(NULL, (void *)e, + offsetof(ffs_entry_t, + checksum)); + e->checksum = htobe32(e->checksum); + } + } + + __hdr_htobe32(hdr); + + size_t rc = fwrite(hdr, 1, size, file); + if (rc <= 0 && ferror(file)) { + ERRNO(errno); + return -1; + } + + __hdr_be32toh(hdr); + if (0 < hdr->entry_count) + for (size_t i=0; ientry_count; i++) + __entry_be32toh(hdr->entries + i); + + return 0; +} + +static int __entries_read(ffs_hdr_t * hdr, FILE * file, off_t offset) +{ + assert(hdr != NULL); + assert(hdr->magic == FFS_MAGIC); + + if (0 < hdr->entry_count) { + if (fseeko(file, offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + size_t size = hdr->entry_count * hdr->entry_size; + + size_t rc = fread(hdr->entries, 1, size, file); + if (rc <= 0 && ferror(file)) { + ERRNO(errno); + return -1; + } + + for (size_t i=0; ientry_count; i++) { + ffs_entry_t *e = hdr->entries + i; + + uint32_t ck = memcpy_checksum(NULL, (void *)e, + offsetof(ffs_entry_t, + checksum)); + __entry_be32toh(e); + + if (e->checksum != ck) { + ERROR(ERR_UNEXPECTED, FFS_CHECK_ENTRY_CHECKSUM, + "'%s' entry checksum mismatch '%x' != " + "'%x'", e->name, e->checksum, ck); + return -1; + } + } + } + + return 0; +} + +#if 0 +static void __entries_write(ffs_hdr_t * hdr, FILE * file, off_t offset) +{ + if (unlikely(hdr == NULL)) + ffs_throw(UNEX, 10400, "NULL hdr pointer"); + if (hdr->magic != FFS_MAGIC) + ffs_throw(UNEX, 10401, "magic number mismatch '%x' != " + "'%x'", hdr->magic, FFS_MAGIC); + + if (0 < hdr->entry_count) { + size_t size = hdr->entry_count * hdr->entry_size; + + for (size_t i = 0; i < hdr->entry_count; i++) { + ffs_entry_t *e = hdr->entries + i; + __entry_htobe32(e); + e->checksum = memcpy_checksum(NULL, (void *)e, + offsetof(ffs_entry_t, + checksum)); + e->checksum = htobe32(e->checksum); + } + + if (fseeko(file, offset, SEEK_SET) != 0) + ffs_throw(ERR, 10402, "%s (errno=%d)", + strerror(errno), errno); + + size_t rc = fwrite(hdr->entries, 1, size, file); + if (rc <= 0 && ferror(file)) + ffs_throw(ERR, 10403, "%s (errno=%d)", + strerror(errno), errno); + + fflush(file); + + for (size_t i = 0; i < hdr->entry_count; i++) + __entry_be32toh(hdr->entries + i); + } +} +#endif + +static ffs_entry_t *__iterate_entries(ffs_hdr_t * self, + int (*func) (ffs_entry_t *)) +{ + assert(self != NULL); + assert(func != NULL); + + for (uint32_t i = 0; i < self->entry_count; i++) { + if (func(self->entries + i) != 0) + return self->entries + i; + } + + return NULL; +} + +static ffs_entry_t *__find_entry(ffs_hdr_t * self, const char *path) +{ + assert(self != NULL); + + if (path == NULL || *path == '\0') + return NULL; + + if (*path == '/') + path++; + + char __path[strlen(path) + 1]; + strcpy(__path, path); + path = __path; + + ffs_entry_t root = {.id = FFS_PID_TOPLEVEL }, *parent = &root; + + char *name; + while (parent != NULL && (name = strtok((char *)path, "/")) != NULL) { + path = NULL; + + int find_entry(ffs_entry_t * child) { + return (parent->id == child->pid && + strncmp(name, child->name, + sizeof(child->name)) == 0); + } + + parent = __iterate_entries(self, find_entry); + } + + return parent; +} + +/* ============================================================ */ + +int __ffs_fcheck(FILE *file, off_t offset) +{ + assert(file != NULL); + + RAII(ffs_hdr_t*, hdr, malloc(sizeof(*hdr)), free); + if (hdr == NULL) { + ERRNO(errno); + return -1; + } + memset(hdr, 0, sizeof(*hdr)); + + if (fseeko(file, offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + if (fread(hdr, 1, sizeof(*hdr), file) <= 0 && ferror(file)) { + ERRNO(errno); + return -1; + } + + uint32_t ck = memcpy_checksum(NULL, (void *)hdr, + offsetof(ffs_hdr_t, checksum)); + __hdr_be32toh(hdr); + + if (hdr->magic != FFS_MAGIC) { + ERROR(ERR_UNEXPECTED, FFS_CHECK_HEADER_MAGIC, + "header magic mismatch '%x' != '%x'", + hdr->magic, FFS_MAGIC); + return FFS_CHECK_HEADER_MAGIC; + } + if (hdr->checksum != ck) { + ERROR(ERR_UNEXPECTED, FFS_CHECK_HEADER_CHECKSUM, + "header checksum mismatch '%x' != '%x'", + hdr->checksum, ck); + return FFS_CHECK_HEADER_CHECKSUM; + } + + size_t size = hdr->entry_count * hdr->entry_size; + + hdr = (ffs_hdr_t *)realloc(hdr, sizeof(*hdr) + size); + if (hdr == NULL) { + ERRNO(errno); + return -1; + } + memset(hdr->entries, 0, size); + + if (0 < hdr->entry_count) { + if (fread(hdr->entries, 1, size, file) <= 0 && ferror(file)) { + ERRNO(errno); + return -1; + } + + for (size_t i = 0; i < hdr->entry_count; i++) { + ffs_entry_t *e = hdr->entries + i; + + uint32_t ck = memcpy_checksum(NULL, (void *)e, + offsetof(ffs_entry_t, + checksum)); + + __entry_be32toh(e); + + if (e->checksum != ck) { + ERROR(ERR_UNEXPECTED, FFS_CHECK_ENTRY_CHECKSUM, + "'%s' entry checksum mismatch '%x' != " + "'%x'", e->name, e->checksum, ck); + return FFS_CHECK_ENTRY_CHECKSUM; + } + } + } + + return 0; +} + +int __ffs_check(const char *path, off_t offset) +{ + if (path == NULL || *path == '\0') { + UNEXPECTED("invalid path '%s'\n", path); + return -1; + } + + RAII(FILE*, file, fopen(path, "r"), fclose); + if (file == NULL) { + ERRNO(errno); + return -1; + } + + return __ffs_fcheck(file, offset); +} + +ffs_t *__ffs_fcreate(FILE *file, off_t offset, uint32_t block_size, + uint32_t block_count) +{ + assert(file != NULL); + + if (!is_pow2(block_size)) { + UNEXPECTED("'%d' invalid block size (must be non-0 and a " + "power of 2)", block_size); + return NULL; + } + if (!is_pow2(block_count)) { + UNEXPECTED("'%d' invalid block count (must be non-0 and a " + "power of 2)", block_count); + return NULL; + } + if (offset & (block_size - 1)) { + UNEXPECTED("'%lld' invalid offset (must be 'block size' " + "aligned)", offset); + return NULL; + } + + ffs_t *self = (ffs_t *) malloc(sizeof(*self)); + if (self == NULL) { + ERRNO(errno); + goto error; + } + + memset(self, 0, sizeof(*self)); + self->file = file; + self->offset = offset; + self->count = FFS_ENTRY_EXTENT; + self->dirty = true; + + self->hdr = (ffs_hdr_t *) malloc(sizeof(*self->hdr)); + if (self->hdr == NULL) { + ERRNO(errno); + goto error; + } + + self->hdr->magic = FFS_MAGIC; + self->hdr->version = FFS_VERSION_1; + self->hdr->size = 1; + self->hdr->entry_size = sizeof(*self->hdr->entries); + self->hdr->entry_count = 0; + self->hdr->block_size = block_size; + self->hdr->block_count = block_count; + self->hdr->checksum = 0; + + size_t size = self->count * self->hdr->entry_size; + + self->hdr = (ffs_hdr_t *) realloc(self->hdr, sizeof(*self->hdr) + size); + if (self->hdr == NULL) { + ERRNO(errno); + goto error; + } + memset(self->hdr->entries, 0, size); + + if (__ffs_entry_add(self, FFS_PARTITION_NAME, offset, block_size, + FFS_TYPE_PARTITION, FFS_FLAGS_PROTECTED) < 0) + goto error; + if (__ffs_entry_truncate(self, FFS_PARTITION_NAME, block_size) < 0) + goto error; + + if (false) { + error: + if (self != NULL) { + if (self->file != NULL) + fclose(self->file), self->file = NULL; + if (self->path != NULL) + free(self->path), self->path = NULL; + if (self->hdr != NULL) + free(self->hdr), self->hdr = NULL; + free(self), self = NULL; + } + } + + return self; +} + +ffs_t *__ffs_create(const char *path, off_t offset, uint32_t block_size, + uint32_t block_count) +{ + assert(path != NULL); + + FILE * file = fopen(path, "r+"); + if (file == NULL) { + ERRNO(errno); + return NULL; + } + + ffs_t * self = __ffs_fcreate(file, offset, block_size, block_count); + if (self != NULL) + self->path = strdup(path); + + return self; +} + +ffs_t *__ffs_fopen(FILE * file, off_t offset) +{ + assert(file != NULL); + + ffs_t *self = (ffs_t *) malloc(sizeof(*self)); + if (self == NULL) { + ERRNO(errno); + goto error; + } + + memset(self, 0, sizeof(*self)); + self->file = file; + self->count = 0; + self->offset = offset; + self->dirty = false; + + self->hdr = (ffs_hdr_t *) malloc(sizeof(*self->hdr)); + if (self->hdr == NULL) { + ERRNO(errno); + goto error; + } + memset(self->hdr, 0, sizeof(*self->hdr)); + + if (__hdr_read(self->hdr, self->file, self->offset) < 0) + goto error; + + self->count = max(self->hdr->entry_count, FFS_ENTRY_EXTENT); + size_t size = self->count * self->hdr->entry_size; + + self->hdr = (ffs_hdr_t *)realloc(self->hdr, sizeof(*self->hdr) + size); + if (self->hdr == NULL) { + ERRNO(errno); + goto error; + } + memset(self->hdr->entries, 0, size); + + if (0 < self->hdr->entry_count) { + if (__entries_read(self->hdr, self->file, + self->offset + sizeof(*self->hdr)) < 0) + goto error; + } + + self->buf_count = 1; // default to 1 + + self->buf = malloc(self->buf_count * self->hdr->block_size); + if (self->hdr == NULL) { + ERRNO(errno); + goto error; + } + + if (setvbuf(self->file, self->buf, _IOFBF, + self->buf_count * self->hdr->block_size) != 0) { + ERRNO(errno); + goto error; + } + + if (false) { + error: + if (self != NULL) { + if (self->buf != NULL) + free(self->buf), self->buf = NULL; + if (self->hdr != NULL) + free(self->hdr), self->hdr = NULL; + + free(self), self = NULL; + } + } + + return self; +} + +ffs_t *__ffs_open(const char *path, off_t offset) +{ + assert(path != NULL); + + FILE *file = fopen(path, "r+"); + if (file == NULL) { + ERRNO(errno); + return NULL; + } + + ffs_t *self = __ffs_fopen(file, offset); + if (self != NULL) + self->path = strdup(path); + + return self; +} + +static int ffs_flush(ffs_t * self) +{ + assert(self != NULL); + + if (__hdr_write(self->hdr, self->file, self->offset) < 0) + return -1; + + if (fflush(self->file) != 0) { + ERRNO(errno); + return -1; + } + + if (fileno(self->file) != -1) { + if (fsync(fileno(self->file)) < 0) { + ERRNO(errno); + return -1; + } + } + + + self->dirty = false; + + return 0; +} + +int __ffs_info(ffs_t * self, int name, uint32_t *value) +{ + assert(self != NULL); + assert(value != NULL); + + switch (name) { + case FFS_INFO_MAGIC: + *value = self->hdr->magic; + break; + case FFS_INFO_VERSION: + *value = self->hdr->version; + break; + case FFS_INFO_ENTRY_SIZE: + *value = self->hdr->entry_size; + break; + case FFS_INFO_ENTRY_COUNT: + *value = self->hdr->entry_count; + break; + case FFS_INFO_BLOCK_SIZE: + *value = self->hdr->block_size; + break; + case FFS_INFO_BLOCK_COUNT: + *value = self->hdr->block_count; + break; + case FFS_INFO_BUFFER_COUNT: + *value = self->buf_count; + break; + case FFS_INFO_OFFSET: + *value = self->offset; + break; + default: + UNEXPECTED("'%d' invalid info field", name); + return -1; + } + + return 0; +} + +int __ffs_buffer(ffs_t * self, size_t size) +{ + assert(self != NULL); + + if (size == 0) + size = self->hdr->block_size; + + if (self->buf != NULL) { + free(self->buf); + self->buf_count = 0; + } + + self->buf_count = size / self->hdr->block_size; + size = self->buf_count * self->hdr->block_size; + + self->buf = malloc(size); + if (self->buf == NULL) { + ERRNO(errno); + return -1; + } + + if (setvbuf(self->file, self->buf, _IOFBF, size) < 0) { + ERRNO(errno); + return -1; + } + + return 0; +} + +int __ffs_fclose(ffs_t * self) +{ + if (self == NULL) + return 0; + + if (self->dirty == true) + if (ffs_flush(self) < 0) + return -1; + + if (self->buf != NULL) + free(self->buf), self->buf = NULL; + if (self->hdr != NULL) + free(self->hdr), self->hdr = NULL; + + memset(self, 0, sizeof(*self)); + free(self); + + return 0; +} + +int __ffs_close(ffs_t * self) +{ + if (unlikely(self == NULL)) + return 0; + + if (self->dirty == true) + if (ffs_flush(self) < 0) + return -1; + + if (self->path != NULL) + free(self->path), self->path = NULL; + if (self->file != NULL) + fclose(self->file), self->file = NULL; + + return __ffs_fclose(self); +} + +int __ffs_fsync(ffs_t * self) +{ + assert(self != NULL); + + if (fflush(self->file) < 0) { + ERRNO(errno); + return -1; + } + + if (fileno(self->file) != -1) + if (fsync(fileno(self->file)) < 0) + return -1; + + return 0; +} + +static ffs_entry_t *__add_entry_check(ffs_hdr_t * self, off_t offset, + size_t size) +{ + assert(self != NULL); + + int find_overlap(ffs_entry_t * entry) { + if (entry->type == FFS_TYPE_LOGICAL) + return 0; + + off_t entry_start = entry->base; + off_t entry_end = entry_start + entry->size - 1; + + off_t new_start = offset / self->block_size; + off_t new_end = new_start + (size / self->block_size) - 1; + + return !(new_start < entry_start && new_end < entry_start) && + !(entry_end < new_start && entry_end < new_end); + } + + return __iterate_entries(self, find_overlap); +} + +int __ffs_iterate_entries(ffs_t * self, int (*func) (ffs_entry_t *)) +{ + return __iterate_entries(self->hdr, func) != NULL; +} + +int __ffs_list_entries(ffs_t * self, const char * name, bool user, FILE * out) +{ + assert(self != NULL); + + if (out == NULL) + out = stdout; + + char full_name[4096]; + regex_t rx; + + int print_entry(ffs_entry_t * entry) + { + size_t offset = entry->base * self->hdr->block_size; + size_t size = entry->size * self->hdr->block_size; + + if (__ffs_entry_name(self, entry, full_name, + sizeof full_name) < 0) + return -1; + + if (regexec(&rx, full_name, 0, NULL, 0) == REG_NOMATCH) + return 0; + + fprintf(stdout, "%3d [%08x-%08x:%8x] " + "[%c%c%c%c%c%c%c%c%c%c] %s\n", + entry->id, offset, offset+size-1, entry->actual, + entry->type == FFS_TYPE_LOGICAL ? 'l' : 'd', + /* reserved */ '-', '-', '-', '-', '-', '-', '-', + entry->flags & FFS_FLAGS_U_BOOT_ENV ? 'b' : '-', + entry->flags & FFS_FLAGS_PROTECTED ? 'p' : '-', + full_name); + + if (user == true) { + for (int i=0; iuser.data[i]); + if ((i+1) % 4 == 0) + fprintf(stdout, "\n"); + } + } + + return 0; + } + + if (0 < self->count) { + if (regcomp(&rx, name, REG_ICASE | REG_NOSUB) != 0) { + ERRNO(errno); + return-1; + } + + fprintf(out, "========================[ PARTITION TABLE 0x%llx " + "]=======================\n", self->offset); + fprintf(out, "vers:%04x size:%04x * blk:%06x blk(s):%06x * " + "entsz:%06x ent(s):%06x\n", + self->hdr->version, self->hdr->size, + self->hdr->block_size, self->hdr->block_count, + self->hdr->entry_size, self->hdr->entry_count); + fprintf(out, "------------------------------------------------" + "---------------------------\n"); + + (void)__iterate_entries(self->hdr, print_entry); + + fprintf(stdout, "\n"); + + regfree(&rx); + } + + return 0; +} + +int __ffs_entry_find(ffs_t *self, const char *path, ffs_entry_t *entry) +{ + assert(self != NULL); + assert(path != NULL); + + ffs_entry_t *__entry = __find_entry(self->hdr, path); + if (__entry != NULL && entry != NULL) + *entry = *__entry; + + return __entry != NULL; +} + +int __ffs_entry_find_parent(ffs_t *self, const char *path, ffs_entry_t *entry) +{ + assert(self != NULL); + assert(path != NULL); + + if (*path == '/') + path++; + + char __path[strlen(path) + 1]; + strcpy(__path, path); + char *parent_path = dirname(__path); + + int found = 0; + + if (strcmp(parent_path, ".") != 0) { + ffs_entry_t parent; + + found = __ffs_entry_find(self, parent_path, &parent); + + if (found && entry != NULL) + *entry = parent; + } + + return found; +} + +int __ffs_entry_name(ffs_t *self, ffs_entry_t *entry, char *name, size_t size) +{ + assert(self != NULL); + assert(entry != NULL); + + ffs_hdr_t *hdr = self->hdr; + + int __entry_name(ffs_entry_t *parent, char *name, size_t size) { + assert(parent != NULL); + assert(name != NULL); + + if (parent->pid != FFS_PID_TOPLEVEL) { + for (uint32_t i = 0; i < hdr->entry_count; i++) { + if (hdr->entries[i].id == parent->pid) { + __entry_name(hdr->entries + i, name, + size); + break; + } + } + } + + if (strlen(name) + strlen(parent->name) < size) + strcat(name, parent->name); + + if (parent->id != entry->id) { + if (strlen(name) + strlen("/") < size) + strcat(name, "/"); + } + + return 0; + } + + memset(name, 0, size); + + return __entry_name(entry, name, size); +} + +int __ffs_entry_add(ffs_t * self, const char *path, off_t offset, size_t size, + ffs_type_t type, uint32_t flags) +{ + assert(self != NULL); + assert(path != NULL); + + if (__ffs_entry_find(self, path, NULL) == true) { + UNEXPECTED("'%s' entry already exists", path); + return -1; + } + + ffs_entry_t parent = {.id = FFS_PID_TOPLEVEL }; + (void)__ffs_entry_find_parent(self, path, &parent); + + ffs_hdr_t *hdr = self->hdr; + + if (type != FFS_TYPE_LOGICAL) { + ffs_entry_t *overlap = __add_entry_check(hdr, offset, size); + if (overlap != NULL) { + UNEXPECTED("'%s' at offset %lld and size %d overlaps " + "'%s' at offset %d and size %d", + path, offset, size, overlap->name, + overlap->base * hdr->block_size, + overlap->size * hdr->block_size); + return -1; + } + } + + int find_empty(ffs_entry_t * empty) { + return empty->type == 0; + } + + ffs_entry_t *entry = __iterate_entries(hdr, find_empty); + if (entry == NULL) { + if (self->count <= hdr->entry_count) { + size_t new_size; + new_size = hdr->entry_size * + (self->count + FFS_ENTRY_EXTENT); + + self->hdr = (ffs_hdr_t *) realloc(self->hdr, + sizeof(*self->hdr) + + new_size); + assert(self->hdr != NULL); + + if (hdr != self->hdr) + hdr = self->hdr; + + memset(hdr->entries + self->count, 0, + FFS_ENTRY_EXTENT * hdr->entry_size); + + self->count += FFS_ENTRY_EXTENT; + } + + entry = hdr->entries + hdr->entry_count; + } + + uint32_t max_id = 0; + + int find_max_id(ffs_entry_t * max) { + if (max_id < max->id) + max_id = max->id; + return 0; + } + + (void)__iterate_entries(hdr, find_max_id); + + char name[strlen(path) + 1]; + strcpy(name, path); + strncpy(entry->name, basename(name), sizeof(entry->name)); + entry->id = max_id + 1; + entry->pid = parent.id; + entry->base = offset / hdr->block_size; + entry->size = size / hdr->block_size; + entry->type = type; + entry->flags = flags; + entry->checksum = 0; + + hdr->entry_count++; + + self->dirty = true; + + return 0; +} + +int __ffs_entry_delete(ffs_t * self, const char *path) +{ + assert(self != NULL); + assert(path != NULL); + + ffs_entry_t entry; + if (__ffs_entry_find(self, path, &entry) == false) { + UNEXPECTED("entry '%s' not found in table at offset '%llx'", + path, self->offset); + return -1; + } + + if (entry.type == FFS_TYPE_PARTITION) { + UNEXPECTED("'%s' cannot --delete partition type entries", path); + return -1; + } + + uint32_t children = 0; + + int find_children(ffs_entry_t * child) { + if (entry.id == child->pid) + children++; + return 0; + } + + ffs_hdr_t *hdr = self->hdr; + + (void)__iterate_entries(hdr, find_children); + + if (0 < children) { + UNEXPECTED("'%s' has '%d' children, --delete those first", + path, children); + return -1; + } + + int find_entry_id(ffs_entry_t * __entry) { + return entry.id == __entry->id; + } + + ffs_entry_t *entry_p = __iterate_entries(hdr, find_entry_id); + assert(entry_p != NULL); + + int start = entry_p - hdr->entries; + int count = hdr->entry_count - start; + + memmove(entry_p, entry_p + 1, hdr->entry_size * count); + + hdr->entry_count = max(0UL, hdr->entry_count - 1); + memset(hdr->entries + hdr->entry_count, 0, hdr->entry_size); + + self->dirty = true; + + return 0; +} + +int __ffs_entry_user_get(ffs_t *self, const char *path, uint32_t word, + uint32_t *value) +{ + assert(self != NULL); + assert(path != NULL); + assert(value != NULL); + + if (FFS_USER_WORDS <= word) { + UNEXPECTED("word '%d' outside range [0..%d]", + word, FFS_USER_WORDS - 1); + return -1; + } + + ffs_entry_t *entry = __find_entry(self->hdr, path); + if (entry == NULL) { + UNEXPECTED("entry '%s' not found in partition table at " + "offset '%llx'", path, self->offset); + return -1; + } + + *value = entry->user.data[word]; + + return 0; +} + +int __ffs_entry_user_put(ffs_t *self, const char *path, uint32_t word, + uint32_t value) +{ + assert(self != NULL); + assert(path != NULL); + + if (FFS_USER_WORDS <= word) { + UNEXPECTED("word '%d' outside range [0..%d]", + word, FFS_USER_WORDS - 1); + return -1; + } + + ffs_entry_t *entry = __find_entry(self->hdr, path); + if (entry == NULL) { + UNEXPECTED("entry '%s' not found in partition table at " + "offset '%llx'", path, self->offset); + return -1; + } + + entry->user.data[word] = value; + self->dirty = true; + + return 0; +} + +ssize_t __ffs_entry_hexdump(ffs_t * self, const char *path, FILE * out) +{ + assert(self != NULL); + assert(path != NULL); + + ffs_entry_t entry; + if (__ffs_entry_find(self, path, &entry) == false) { + UNEXPECTED("entry '%s' not found in table at offset '%llx'", + path, self->offset); + return -1; + } + + size_t size = entry.size * self->hdr->block_size; + if (entry.actual < size) + size = entry.actual; + + off_t offset = entry.base * self->hdr->block_size; + + if (fseeko(self->file, offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + ssize_t total = 0; + + size_t block_size = self->hdr->block_size; + char block[block_size]; + while (0 < size) { + size_t rc = fread(block, 1, min(block_size, size), + self->file); + if (rc <= 0) { + if (ferror(self->file)) { + ERRNO(errno); + return -1; + } + break; + } + + dump_memory(out, offset + total, block, rc); + + total += rc; + size -= rc; + } + + return total; +} + +ssize_t __ffs_entry_truncate(ffs_t * self, const char *path, size_t size) +{ + assert(self != NULL); + assert(path != NULL); + + ffs_entry_t * entry = __find_entry(self->hdr, path); + if (entry == NULL) { + UNEXPECTED("entry '%s' not found in partition table at " + "offset '%llx'", path, self->offset); + return -1; + } + + if ((entry->size * self->hdr->block_size) < size) { + errno = EFBIG; + ERRNO(errno); + return -1; + } else { + entry->actual = size; + self->dirty = true; + } + + return 0; +} + +ssize_t __ffs_entry_read(ffs_t * self, const char *path, void *buf, + off_t offset, size_t count) +{ + assert(self != NULL); + assert(path != NULL); + assert(buf != NULL); + + if (count == 0) + return 0; + + ffs_entry_t entry; + if (__ffs_entry_find(self, path, &entry) == false) { + UNEXPECTED("entry '%s' not found in partition table at " + "offset '%llx'", path, self->offset); + return -1; + } + + size_t entry_size = entry.size * self->hdr->block_size; + if (entry.actual < entry_size) + entry_size = entry.actual; + off_t entry_offset = entry.base * self->hdr->block_size; + + if (entry_size <= offset) + return 0; + else + count = min(count, (entry_offset + entry_size) - offset); + + ssize_t total = 0; + + if (fseeko(self->file, entry_offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + if (fseeko(self->file, offset, SEEK_CUR) != 0) { + ERRNO(errno); + return -1; + } + + while (0 < count) { + size_t rc = fread(buf + total, 1, count, self->file); + if (rc <= 0) { + if (ferror(self->file)) { + ERRNO(errno); + return -1; + } + break; + } + + total += rc; + count -= rc; + } + + return total; +} + +ssize_t __ffs_entry_write(ffs_t * self, const char *path, const void *buf, + off_t offset, size_t count) +{ + assert(self != NULL); + assert(path != NULL); + assert(buf != NULL); + + if (count == 0) + return 0; + + ffs_entry_t *entry = __find_entry(self->hdr, path); + if (entry == NULL) { + UNEXPECTED("entry '%s' not found in partition table at " + "offset '%llx'", path, self->offset); + return -1; + } + + size_t entry_size = entry->size * self->hdr->block_size; + off_t entry_offset = entry->base * self->hdr->block_size; + + if (entry_size <= offset) + return 0; + else + count = min(count, (entry_offset + entry_size) - offset); + + ssize_t total = 0; + + if (fseeko(self->file, entry_offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + if (fseeko(self->file, offset, SEEK_CUR) != 0) { + ERRNO(errno); + return -1; + } + + while (0 < count) { + size_t rc = fwrite(buf + total, 1, count, self->file); + if (rc <= 0) { + if (ferror(self->file)) { + ERRNO(errno); + return -1; + } + break; + } + total += rc; + count -= rc; + } + + fflush(self->file); + + if (entry->actual < (uint32_t) total) { + entry->actual = (uint32_t) total; + self->dirty = true; + } + + return total; +} + +#if 0 +ssize_t __ffs_entry_copy(ffs_t *self, ffs_t *in, const char *path) +{ + assert(self != NULL); + assert(in != NULL); + assert(path != NULL); + + if (unlikely(*path == '\0')) + return 0; + + ffs_entry_t *src = __find_entry(in->hdr, path); + if (src == NULL) { + UNEXPECTED("entry '%s' not found in table at offset '%llx'", + path, in->offset); + return -1; + } + + ffs_entry_t *dest = __find_entry(self->hdr, path); + if (dest == NULL) { + UNEXPECTED("entry '%s' not found in table at offset '%llx'", + path, self->offset); + return -1; + } + + if (src->base != dest->base) { + UNEXPECTED("partition '%s' offsets differ '%x' != '%x'", + path, src->base, dest->base); + return -1; + } + + if (src->size != dest->size) { + UNEXPECTED("partition '%s' sizes differ '%x' != '%x'", + path, src->size, dest->size); + return -1; + } + + size_t block_size = self->hdr->block_size; + off_t src_offset = src->base * in->hdr->block_size; + size_t src_actual = src->actual; + off_t dest_offset = dest->base * self->hdr->block_size; + + if (fseeko(in->file, src_offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + if (fseeko(self->file, dest_offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + ssize_t total = 0; + char block[block_size]; + + while (0 < src_actual) { + size_t count = min(src_actual, block_size); + + size_t rc = fread(block, 1, count, in->file); + if (rc <= 0 && ferror(in->file)) { + ERRNO(errno); + return -1; + } + + rc = fwrite(block, 1, rc, self->file); + if (rc <= 0 && ferror(self->file)) { + ERRNO(errno); + return -1; + } + + total += rc; + src_actual -= rc; + } + + if (dest->actual != (uint32_t)total) { + dest->actual = (uint32_t) total; + self->dirty = true; + } + + return total; +} + +ssize_t __ffs_entry_compare(ffs_t * self, ffs_t * in, const char *path) +{ + assert(self != NULL); + assert(in != NULL); + assert(path != NULL); + + if (unlikely(*path == '\0')) + return 0; + + ffs_entry_t *src = __find_entry(in->hdr, path); + if (src == NULL) { + UNEXPECTED("entry '%s' not found in table at offset '%llx'", + path, in->offset); + return -1; + } + + ffs_entry_t *dest = __find_entry(self->hdr, path); + if (dest == NULL) { + UNEXPECTED("entry '%s' not found in table at offset '%llx'", + path, self->offset); + return -1; + } + + if (src->base != dest->base) { + UNEXPECTED("partition '%s' offsets differ '%x' != '%x'", + path, src->base, dest->base); + return -1; + } + + if (src->size != dest->size) { + UNEXPECTED("partition '%s' sizes differ '%x' != '%x'", + path, src->size, dest->size); + return -1; + } + + if (src->actual != dest->actual) { + UNEXPECTED("partition '%s' actual sizes differ '%x' != '%x'", + path, src->actual, dest->actual); + return -1; + } + + off_t offset = src->base * self->hdr->block_size; + size_t actual = src->actual; + + if (fseeko(in->file, offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + if (fseeko(self->file, offset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + ssize_t total = 0; + size_t size = 256; + char __src[size], __dest[size]; + + while (0 < actual) { + size = min(actual, size); + + size_t rc = fread(__src, 1, size, in->file); + if (rc <= 0 && ferror(in->file)) { + ERRNO(errno); + return -1; + } + + rc = fread(__dest, 1, size, self->file); + if (rc <= 0 && ferror(self->file)) { + ERRNO(errno); + return -1; + } + + if (memcmp(__src, __dest, size) != 0) { + printf("==========> '%s'\n", self->path); + dump_memory(stdout, offset + total, __dest, size); + + printf("==========> '%s'\n", in->path); + dump_memory(stdout, offset + total, __src, size); + + break; + } + + actual -= size; + total += size; + } + + return total; +} +#endif + +int __ffs_entry_list(ffs_t * self, ffs_entry_t ** list) +{ + assert(self != NULL); + assert(list != NULL); + + size_t size = 0, count = 0; + *list = NULL; + + int name_list(ffs_entry_t * entry) { + if (size <= count) { + size += 10; + + *list = realloc(*list, size * sizeof(**list)); + if (*list == NULL) { + ERRNO(errno); + return -1; + } + } + + (*list)[count++] = *entry; + + return 0; + } + + if (__ffs_iterate_entries(self, name_list) != 0) { + if (*list != NULL) + free(*list), *list = NULL; + count = 0; + return -1; + } + + return count; +} + +/* ============================================================ */ diff --git a/ffs/src/libffs2.c b/ffs/src/libffs2.c new file mode 100644 index 0000000..0c83cf0 --- /dev/null +++ b/ffs/src/libffs2.c @@ -0,0 +1,489 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: libffs2.c + * Author: Shaun Wetzstein + * Descr: FFS IO interface + * Note: + * Date: 05/07/12 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libffs2.h" + +#include +#include +#include +#include +#include +#include + +#define FFS_ERRSTR_MAX 1024 + +struct ffs_error { + int errnum; + char errstr[FFS_ERRSTR_MAX]; +}; + +typedef struct ffs_error ffs_error_t; + +static ffs_error_t __error; + +/* ============================================================ */ + +extern void ffs_errclr(void) +{ + __error.errnum = __error.errstr[0] = 0; +} + +extern int ffs_errnum(void) +{ + return __error.errnum; +} + +extern const char *ffs_errstr(void) +{ + return __error.errnum ? __error.errstr : NULL; +} + +int ffs_check(const char *path, off_t offset) +{ + RAII(FILE*, file, fopen(path, "r"), fclose); + if (file == NULL) { + __error.errnum = -1; + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : %s (errno=%d)\n", + program_invocation_short_name, "errno", + __FILE__, __LINE__, strerror(errno), errno); + + return -1; + } + + int rc = __ffs_fcheck(file, offset); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + // __ffs_check will return FFS_CHECK_* const's + } + + return rc; +} + +ffs_t *ffs_create(const char *path, off_t offset, uint32_t block_size, + uint32_t block_count) +{ + ffs_t *self = __ffs_create(path, offset, block_size, block_count); + if (self == NULL) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + } + + return self; +} + +ffs_t *ffs_open(const char *path, off_t offset) +{ + FILE * file = fopen(path, "r+"); + if (file == NULL) { + __error.errnum = -1; + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : %s (errno=%d)\n", + program_invocation_short_name, "errno", + __FILE__, __LINE__, strerror(errno), errno); + + return NULL; + } + + ffs_t *self = __ffs_fopen(file, offset); + if (self == NULL) { + fclose(file); + + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + } + + return self; +} + +int ffs_info(ffs_t *self, int name, uint32_t *value) +{ + int rc = __ffs_info(self, name, value); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_close(ffs_t * self) +{ + int rc = __ffs_close(self); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_fsync(ffs_t * self) +{ + int rc = __ffs_fsync(self); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_list_entries(ffs_t * self, FILE * out) +{ + int rc = __ffs_list_entries(self, ".*", true, out); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_iterate_entries(ffs_t * self, int (*func) (ffs_entry_t *)) +{ + int rc = __ffs_iterate_entries(self, func); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_entry_find(ffs_t * self, const char *path, ffs_entry_t * entry) +{ + int rc = __ffs_entry_find(self, path, entry); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_entry_find_parent(ffs_t * self, const char *path, ffs_entry_t * entry) +{ + int rc = __ffs_entry_find_parent(self, path, entry); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_entry_add(ffs_t * self, const char *path, off_t offset, size_t size, + ffs_type_t type, uint32_t flags) +{ + int rc = __ffs_entry_add(self, path, offset, size, type, flags); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_entry_delete(ffs_t * self, const char *path) +{ + int rc = __ffs_entry_delete(self, path); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_entry_user_get(ffs_t * self, const char *path, uint32_t word, + uint32_t * value) +{ + int rc = __ffs_entry_user_get(self, path, word, value); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +int ffs_entry_user_put(ffs_t * self, const char *path, uint32_t word, + uint32_t value) +{ + int rc = __ffs_entry_user_put(self, path, word, value); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +ssize_t ffs_entry_hexdump(ffs_t * self, const char *path, FILE * out) +{ + ssize_t rc = __ffs_entry_hexdump(self, path, out); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +ssize_t ffs_entry_truncate(ffs_t * self, const char *path, off_t offset, + uint8_t pad __unused__) +{ + return ffs_entry_truncate_no_pad(self, path, offset); +} + +ssize_t ffs_entry_truncate_no_pad(ffs_t * self, const char *path, off_t offset) +{ + ssize_t rc = __ffs_entry_truncate(self, path, offset); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +ssize_t ffs_entry_read(ffs_t * self, const char *path, void *buf, off_t offset, + size_t count) +{ + ssize_t rc = __ffs_entry_read(self, path, buf, offset, count); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +ssize_t ffs_entry_write(ffs_t * self, const char *path, const void *buf, + off_t offset, size_t count) +{ + ssize_t rc = __ffs_entry_write(self, path, buf, offset, count); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +ssize_t ffs_entry_list(ffs_t * self, ffs_entry_t ** list) +{ + ssize_t rc = __ffs_entry_list(self, list); + if (rc < 0) { + err_t *err = err_get(); + assert(err != NULL); + + __error.errnum = err_code(err); + snprintf(__error.errstr, sizeof __error.errstr, + "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + + rc = -1; + } + + return rc; +} + +/* ============================================================ */ diff --git a/ffs/test/Makefile b/ffs/test/Makefile new file mode 100644 index 0000000..37a4d0d --- /dev/null +++ b/ffs/test/Makefile @@ -0,0 +1,8 @@ +DEPTH = ../../.. +include $(DEPTH)/integration/Rules.mk +include $(DEPTH)/integration/Rules.ppc.mk + +ARCH=ppc +TEST_LIBFFS_INSTALL = $(INST_TESTS) + +include ../Rules.test.mk diff --git a/ffs/test/ffs_tool_test.sh b/ffs/test/ffs_tool_test.sh new file mode 100755 index 0000000..37f5fe0 --- /dev/null +++ b/ffs/test/ffs_tool_test.sh @@ -0,0 +1,244 @@ +#!/bin/bash +# ffs_tool_test.sh +# +# Test case to perform tests for supported options in ffs tool +# +# Author: Shekar Babu +# + +FFS_TOOL="ffs" +NOR_IMAGE="/tmp/pnor" +OFFSET="0x3F0000" +SIZE="8MiB" +BLOCK="64KiB" +LOGICAL="logical" +DATA="data" +PAD="0xFF" + +create_dummy_file() { + echo Creating a dummy file $1 of size $2 with sample data $3 + yes $3 | head -$2 > $1 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, creating dummy file $1 + exit $RC + fi + echo Success, creating $1 +} + +create_nor_image() { + + # Check if nor image already exist + if [ -f $1 ];then + rm $1 + fi + echo Creating nor image $1 + $FFS_TOOL --create $1 -p $2 -s $3 -b $4 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, creating $1 image + exit $RC + fi + echo Success, creating $1 image +} + +add_logical_partition() { + echo Adding logical partition $3 + echo $FFS_TOOL --add $1 -O $2 --flags 0x0 --pad $PAD -n $3 -t $4 + $FFS_TOOL --add $1 -O $2 --flags 0x0 --pad $PAD -n $3 -t $4 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, adding $4 partition $3 + exit $RC + fi + echo Success, adding $4 partition $3 +} + +add_data_partition() { + echo Adding data partition $3 + echo $FFS_TOOL --add $1 -O $2 --flags 0x0 --pad $PAD -s $5 -o $6 -n $3 -t $4 + $FFS_TOOL --add $1 -O $2 --flags 0x0 --pad $PAD -s $5 -o $6 -n $3 -t $4 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, adding $4 partition $3 + exit $RC + fi + echo Success, adding $4 partition $3 +} + +read_partition_entry() { + echo Reading partition entry $3 + echo $FFS_TOOL --read $1 -O $2 --name $3 -d $4 --force + $FFS_TOOL --read $1 -O $2 --name $3 -d $4 --force + RC=$? + if [ $RC -ne 0 ]; then + echo Error, reading partition entry $3 + exit $RC + fi + echo Success, reading partition entry $3 +} + +write_partition_entry() { + echo Writing to partition entry $3 + echo $FFS_TOOL --write $1 -O $2 --name $3 -d $4 --force + $FFS_TOOL --write $1 -O $2 --name $3 -d $4 --force + RC=$? + if [ $RC -ne 0 ]; then + echo Error, writing to partition entry $3 + exit $RC + fi + echo Success, writing to partition entry $3 +} + +list_partition_table_entries() { + echo Listing partition table entries in $1 + echo $FFS_TOOL --list $1 -O $2 + $FFS_TOOL --list $1 -O $2 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, Listing partition table entries in $1 + exit $RC + fi + echo Success, Listing partition table entries in $1 +} +hexdump_partition_entry() { + echo Hexdump partition entry $3 into $4 + echo "$FFS_TOOL --hexdump $1 -O $2 --name $3 > $4" + $FFS_TOOL --hexdump $1 -O $2 --name $3 > $4 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, hexdump partition entry $3 into $4 + exit $RC + fi + echo Success, hexdump partition entry $3 into $4 +} + +delete_partition_entry() { + echo Delete partition entry $3 + echo $FFS_TOOL --delete $1 -O $2 --name $3 + $FFS_TOOL --delete $1 -O $2 --name $3 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, deleting partition entry $3 + exit $RC + fi + echo Success, deleting partition entry $3 +} + +get_partition_entry_user_word() { + echo Get user word from partition entry $3 + echo $FFS_TOOL --modify $1 -O $2 --name $3 -u $4 + $FFS_TOOL --modify $1 -O $2 --name $3 -u $4 > /tmp/GETUW + sed 's/^\(.\)\{7\}//g' /tmp/GETUW > /tmp/chop_GETUW + RC=$? + if [ $RC -ne 0 ]; then + echo Error, Getting user word from partition entry $3 + exit $RC + fi + echo Success, Getting user word from partition entry $3 +} + +put_partition_entry_user_word() { + echo Put user word to partition entry $3 + echo $5 > /tmp/PUTUW + echo $FFS_TOOL --modify $1 -O $2 --name $3 -u $4 --value $5 + $FFS_TOOL --modify $1 -O $2 --name $3 -u $4 --value $5 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, Putting user word to partition entry $3 + exit $RC + fi + echo Success, Putting user word to partition entry $3 +} + +read_write_part_entry() { + write_partition_entry $1 $2 $3 $4 + read_partition_entry $1 $2 $3 $5 + cmp $4 $5 > /dev/null + RC=$? + if [ $RC -ne 0 ]; then + echo FAIL, data read/write mismatch -- entry $3 + exit $RC + fi + echo PASS, data read/write matches -- entry $3 +} + +get_put_user_word() { + put_partition_entry_user_word $1 $2 $3 $4 $5 + get_partition_entry_user_word $1 $2 $3 $4 + cmp /tmp/chop_GETUW /tmp/PUTUW > /dev/null + RC=$? + if [ $RC -ne 0 ]; then + echo FAIL, user word get/put mismatch -- entry $3 + exit $RC + fi + echo PASS, user word get/put matches -- entry $3 + rm /tmp/GETUW /tmp/PUTUW /tmp/chop_GETUW +} + +compare_hexdump() { + hexdump_partition_entry $1 $2 $3 $4 + HEXFILE=/tmp/hex_sz0 + stat -c %s $4 > $HEXFILE + if [[ -s $HEXFILE ]] ; then + echo PASS, hexdump test on entry $3 + else + echo FAIL, hexdump test on entry $3 + exit $RC + fi + rm $4 $HEXFILE +} + +clean_data() { + rm $NOR_IMAGE /tmp/in_file /tmp/out_file + exit 0 +} + +# Main program starts + +# Create a dummy file as 'filename size data' +create_dummy_file /tmp/in_file 131072 WELCOME + +# Create nor image +create_nor_image $NOR_IMAGE $OFFSET $SIZE $BLOCK + +# Add logical partition +add_logical_partition $NOR_IMAGE $OFFSET boot0 $LOGICAL + +# Creating data partition +add_data_partition $NOR_IMAGE $OFFSET boot0/bootenv $DATA 1MiB 0M +add_data_partition $NOR_IMAGE $OFFSET boot0/ipl $DATA 1MiB 2M +add_data_partition $NOR_IMAGE $OFFSET boot0/spl $DATA 960K 3M +# Add logical partition +add_logical_partition $NOR_IMAGE $OFFSET boot1 $LOGICAL +# Creating data partition +add_data_partition $NOR_IMAGE $OFFSET boot1/uboot $DATA 1MiB 4M +add_data_partition $NOR_IMAGE $OFFSET boot1/fsp $DATA 1MiB 6M +add_data_partition $NOR_IMAGE $OFFSET boot1/bootfsp $DATA 960K 7M +# Listing all created partition entries (logical+data) +list_partition_table_entries $NOR_IMAGE $OFFSET + +# Perform read and write operations on all partition entries +read_write_part_entry $NOR_IMAGE $OFFSET boot0/bootenv /tmp/in_file /tmp/out_file +read_write_part_entry $NOR_IMAGE $OFFSET boot0/ipl /tmp/in_file /tmp/out_file +read_write_part_entry $NOR_IMAGE $OFFSET boot1/uboot /tmp/in_file /tmp/out_file +read_write_part_entry $NOR_IMAGE $OFFSET boot1/fsp /tmp/in_file /tmp/out_file + +# Perform get and put user words on all partition entries +get_put_user_word $NOR_IMAGE $OFFSET boot0/bootenv 0 0x0000000a +get_put_user_word $NOR_IMAGE $OFFSET boot0/ipl 1 0x0000000b +get_put_user_word $NOR_IMAGE $OFFSET boot0/spl 2 0x0000000c +get_put_user_word $NOR_IMAGE $OFFSET boot1/uboot 3 0x0000000d +get_put_user_word $NOR_IMAGE $OFFSET boot1/fsp 4 0x0000000f + +# Hexdump partition entry +compare_hexdump $NOR_IMAGE $OFFSET boot0/bootenv /tmp/hexdump + +# Delete a partition entry +delete_partition_entry $NOR_IMAGE $OFFSET boot0/bootenv + +# Listing all created partition entries (logical+data) +list_partition_table_entries $NOR_IMAGE $OFFSET + +# Clean/remove all temporary files +clean_data diff --git a/ffs/test/libffs_test.sh b/ffs/test/libffs_test.sh new file mode 100755 index 0000000..a7e3adc --- /dev/null +++ b/ffs/test/libffs_test.sh @@ -0,0 +1,248 @@ +#!/bin/bash +# libffs_test.sh +# +# Test case to perform unit tests for all api's in libffs.so +# +# Author: Shekar Babu +# + + +NOR_IMAGE=/tmp/sunray2.nor +OFFSET=0 +#OFFSET=4128768 +SIZE=8388608 +#SIZE=67108864 #For 64MB nor +BLOCK=65536 +LOGICAL=logical +DATA=data + +create_dummy_file() { + echo Creating a dummy file $1 of size $2 with sample data $3 + yes $3 | head -$2 > $1 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, creating dummy file $1 + exit $RC + fi + echo Success, creating $1 +} + +create_nor_image() { + echo Creating nor image $1 + echo test_libffs -c $1 -O $2 -s $3 -b $4 + ./test_libffs -c $1 -O $2 -s $3 -b $4 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, creating $1 image + exit $RC + fi + echo Success, creating $1 image +} + +add_logical_partition() { + echo Adding logical partition $3 + echo test_libffs -a $1 -O $2 -n $3 -t $4 + ./test_libffs -a $1 -O $2 -n $3 -t $4 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, adding $4 partition $3 + exit $RC + fi + echo Success, adding $4 partition $3 +} + +add_data_partition() { + echo Adding data partition $3 + echo test_libffs -a $1 -O $2 -n $3 -t $4 -s $5 -o $6 + ./test_libffs -a $1 -O $2 -n $3 -t $4 -s $5 -o $6 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, adding $4 partition $3 + exit $RC + fi + echo Success, adding $4 partition $3 +} + +read_partition_entry() { + echo Reading partition entry $3 + echo test_libffs -r $1 -O $2 -n $3 -o $4 + ./test_libffs -r $1 -O $2 -n $3 -o $4 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, reading partition entry $3 + exit $RC + fi + echo Success, reading partition entry $3 +} + +write_partition_entry() { + echo Writing to partition entry $3 + echo test_libffs -w $1 -O $2 -n $3 -i $4 + ./test_libffs -w $1 -O $2 -n $3 -i $4 > /dev/null + RC=$? + if [ $RC -ne 0 ]; then + echo Error, writing to partition entry $3 + exit $RC + fi + echo Success, writing to partition entry $3 +} + +list_partition_table_entries() { + echo Listing partition table entries in $1 + echo test_libffs -l $1 -O $2 + ./test_libffs -l $1 -O $2 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, Listing partition table entries in $1 + exit $RC + fi + echo Success, Listing partition table entries in $1 +} +hexdump_partition_entry() { + echo Hexdump partition entry $3 into $4 + echo "test_libffs -h $1 -O $2 -n $3 > $4" + ./test_libffs -h $1 -O $2 -n $3 > $4 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, hexdump partition entry $3 into $4 + exit $RC + fi + echo Success, hexdump partition entry $3 into $4 +} + +delete_partition_entry() { + echo Delete partition entry $3 + echo test_libffs -d $1 -O $2 -n $3 + ./test_libffs -d $1 -O $2 -n $3 + RC=$? + if [ $RC -ne 0 ]; then + echo Error, deleting partition entry $3 + exit $RC + fi + echo Success, deleting partition entry $3 +} + +get_partition_entry_user_word() { + echo Get user word from partition entry $3 + echo test_libffs -m $1 -O $2 -n $3 -u $4 -g + ./test_libffs -m $1 -O $2 -n $3 -u $4 -g > /tmp/GETUW + RC=$? + if [ $RC -ne 0 ]; then + echo Error, Getting user word from partition entry $3 + exit $RC + fi + echo Success, Getting user word from partition entry $3 +} + +put_partition_entry_user_word() { + echo Put user word to partition entry $3 + echo test_libffs -m $1 -O $2 -n $3 -u $4 -p -v $5 + ./test_libffs -m $1 -O $2 -n $3 -u $4 -p -v $5 > /tmp/PUTUW + RC=$? + if [ $RC -ne 0 ]; then + echo Error, Putting user word to partition entry $3 + exit $RC + fi + echo Success, Putting user word to partition entry $3 +} + +read_write_part_entry() { + write_partition_entry $1 $2 $3 $4 + read_partition_entry $1 $2 $3 $5 + cmp $4 $5 > /dev/null + RC=$? + if [ $RC -ne 0 ]; then + echo FAIL, data read/write mismatch -- entry $3 + exit $RC + fi + echo PASS, data read/write matches -- entry $3 +} + +get_put_user_word() { + put_partition_entry_user_word $1 $2 $3 $4 $5 + get_partition_entry_user_word $1 $2 $3 $4 + cmp /tmp/GETUW /tmp/PUTUW > /dev/null + RC=$? + if [ $RC -ne 0 ]; then + echo FAIL, user word get/put mismatch -- entry $3 + exit $RC + fi + echo PASS, user word get/put matches -- entry $3 + rm /tmp/GETUW /tmp/PUTUW +} + +compare_hexdump() { + hexdump_partition_entry $1 $2 $3 $4 + HEXFILE=/tmp/hex_sz0 + stat -c %s $4 > $HEXFILE + if [[ -s $HEXFILE ]] ; then + echo PASS, hexdump test on entry $3 + else + echo FAIL, hexdump test on entry $3 + exit $RC + fi + rm $4 $HEXFILE +} + +clean_data() { + rm $NOR_IMAGE /tmp/in_file /tmp/out_file + exit 0 +} + +# Main program starts + +# Create a dummy file as 'filename size data' +create_dummy_file /tmp/in_file 131072 WELCOME + +# Create nor image +create_nor_image $NOR_IMAGE $OFFSET $SIZE $BLOCK + +# Add logical partition +add_logical_partition $NOR_IMAGE $OFFSET boot0 $LOGICAL +# Creating data partition +add_data_partition $NOR_IMAGE $OFFSET boot0/bootenv $DATA 1048576 65536 +add_data_partition $NOR_IMAGE $OFFSET boot0/ipl $DATA 1048576 2097152 +add_data_partition $NOR_IMAGE $OFFSET boot0/spl $DATA 1048576 3145728 +# Add logical partition +add_logical_partition $NOR_IMAGE $OFFSET boot1 $LOGICAL +# Creating data partition +add_data_partition $NOR_IMAGE $OFFSET boot1/uboot $DATA 1048576 4194304 +add_data_partition $NOR_IMAGE $OFFSET boot1/fsp $DATA 1048576 5242880 +# Add logical partition +add_logical_partition $NOR_IMAGE $OFFSET linux0 $LOGICAL +# Creating data partition +add_data_partition $NOR_IMAGE $OFFSET linux0/vpd $DATA 1048576 6291456 +add_data_partition $NOR_IMAGE $OFFSET linux0/hostboot $DATA 1048576 7340032 + +# Listing all created partition entries (logical+data) +list_partition_table_entries $NOR_IMAGE $OFFSET + +# Perform read and write operations on all partition entries +read_write_part_entry $NOR_IMAGE $OFFSET boot0/bootenv /tmp/in_file /tmp/out_file +read_write_part_entry $NOR_IMAGE $OFFSET boot0/ipl /tmp/in_file /tmp/out_file +read_write_part_entry $NOR_IMAGE $OFFSET boot0/spl /tmp/in_file /tmp/out_file +read_write_part_entry $NOR_IMAGE $OFFSET boot1/uboot /tmp/in_file /tmp/out_file +read_write_part_entry $NOR_IMAGE $OFFSET boot1/fsp /tmp/in_file /tmp/out_file +read_write_part_entry $NOR_IMAGE $OFFSET linux0/vpd /tmp/in_file /tmp/out_file +read_write_part_entry $NOR_IMAGE $OFFSET linux0/hostboot /tmp/in_file /tmp/out_file + +# Perform get and put user words on all partition entries +get_put_user_word $NOR_IMAGE $OFFSET boot0/bootenv 0 28 +get_put_user_word $NOR_IMAGE $OFFSET boot0/ipl 1 56 +get_put_user_word $NOR_IMAGE $OFFSET boot0/spl 2 96 +get_put_user_word $NOR_IMAGE $OFFSET boot1/uboot 3 16 +get_put_user_word $NOR_IMAGE $OFFSET boot1/fsp 4 84 +get_put_user_word $NOR_IMAGE $OFFSET linux0/vpd 8 64 +get_put_user_word $NOR_IMAGE $OFFSET linux0/hostboot 15 42 + +# Hexdump partition entry +compare_hexdump $NOR_IMAGE $OFFSET boot0/bootenv /tmp/hexdump + +# Delete a partition entry +delete_partition_entry $NOR_IMAGE $OFFSET boot0/bootenv + +# Listing all created partition entries (logical+data) +list_partition_table_entries $NOR_IMAGE $OFFSET + +# Clean/remove all temporary files +clean_data diff --git a/ffs/test/test_libffs.c b/ffs/test/test_libffs.c new file mode 100644 index 0000000..f8c7966 --- /dev/null +++ b/ffs/test/test_libffs.c @@ -0,0 +1,654 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: test_libffs.c + * Author: Shekar Babu S + * Descr: unit test tool for api's in libffs.so + * Date: 06/26/2012 + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "test_libffs.h" + +FILE* +log_open(void) { + + FILE *logfile = NULL; + logfile = fopen("test_libffs.log", "w"); + if (logfile == NULL) { + perror("logfile"); + exit(EXIT_FAILURE); + } + setvbuf(logfile, NULL, _IOLBF, 0); + return logfile; +} + +void +create_regular_file(ffs_ops_t *ffs_ops) { + int rc = 0; + size_t size = ffs_ops->device_size; + mode_t mode = (S_IRUSR | S_IWUSR) | (S_IRGRP | S_IRGRP) | S_IROTH; + + fprintf(ffs_ops->log, "%s: Creating regular file\n", __func__); + + int fd = open(ffs_ops->nor_image, O_RDWR | O_CREAT | O_TRUNC, mode); + if (fd == -1) { + fprintf(ffs_ops->log, "%s: Error creating regular file '%s'", + __func__, ffs_ops->nor_image); + rc = FFS_ERROR; + return; + } + + if (ftruncate(fd, size) != 0) { + fprintf(ffs_ops->log, "%s: Error truncating '%s'", + __func__, ffs_ops->nor_image); + rc = FFS_ERROR; + return; + } + + uint32_t page_size = sysconf(_SC_PAGESIZE); + char buf[page_size]; + memset(buf, 0xFF, page_size); + + while (0 < size) { + ssize_t rc = write(fd, buf, min(sizeof(buf), size)); + if (rc == -1) { + fprintf(ffs_ops->log, "%s: Error writing to '%s'", + __func__, ffs_ops->nor_image); + rc = FFS_ERROR; + return; + } else if(rc == 0) { + break; + } + size -= rc; + } + + if(fd == 0) { + close(fd); + } +} + +int +create_partition(ffs_ops_t *ffs_ops) { + + uint32_t rc = 0; + ffs_t *ffs = NULL; + + create_regular_file(ffs_ops); + + ffs = __ffs_create(ffs_ops->nor_image, ffs_ops->part_off, + ffs_ops->blk_sz, + ffs_ops->device_size / ffs_ops->blk_sz); + + if(ffs != NULL) { + __ffs_close(ffs); + fprintf(ffs_ops->log, "%s: Creating pnor image '%s' success\n", + __func__, ffs_ops->nor_image); + } else { + fprintf(ffs_ops->log, "%s: Creating pnor image '%s' failed\n", + __func__, ffs_ops->nor_image); + rc = FFS_ERROR; + } + + return rc; +} + +int +add_partition_entry(ffs_ops_t *ffs_ops) { + + uint32_t rc = 0; + ffs_t *ffs = NULL; + ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off); + if(ffs != NULL) { + __ffs_entry_add(ffs, ffs_ops->part_entry, ffs_ops->entry_off, + ffs_ops->entry_sz, ffs_ops->type, 0x00); + __ffs_close(ffs); + fprintf(ffs_ops->log, "%s: Adding partition entry '%s' " + "success\n", __func__, ffs_ops->part_entry); + } else { + fprintf(ffs_ops->log, "%s: Adding partition entry '%s' " + "failed\n", __func__, ffs_ops->part_entry); + } + return rc; +} + +uint32_t +read_partition(ffs_ops_t *ffs_ops) { + + uint32_t rc = 0; + ffs_t *ffs = NULL; + + FILE *fp = fopen(ffs_ops->o_file, "w+"); + if (fp == NULL) { + fprintf(ffs_ops->log, "%s: Error opening file '%s'", __func__, + ffs_ops->o_file); + rc = FFS_ERROR; + goto error; + } + + ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off); + if(ffs == NULL) { + fprintf(ffs_ops->log, "%s: Error, opening nor " + "image '%s'\n", __func__, ffs_ops->nor_image); + rc = FFS_ERROR; + goto error; + } + fprintf(ffs_ops->log, "%s: Successfully opened " + "nor image\n", __func__); + ffs_entry_t entry; + + if (__ffs_entry_find(ffs, ffs_ops->part_entry, &entry) == false) { + fprintf(ffs_ops->log, "%s: Error, '%s' not found\n", __func__, + ffs_ops->part_entry); + rc = FFS_ERROR; + goto error; + } + + if(false) { +error: + if(fp != NULL) + fclose(fp); + if(ffs != NULL) + __ffs_close(ffs); + return rc; + } + + fprintf(ffs_ops->log, "%s: Finding entry '%s' success\n", __func__, + ffs_ops->part_entry); + + uint32_t block_size = ffs->hdr->block_size; + char block[block_size]; + memset(block, 0, block_size); + + if (setvbuf(fp, NULL, _IOFBF, block_size) != 0) { + fprintf(ffs_ops->log, "%s: Error, setvbuf failed " + "%s (errno=%d)", __func__, strerror(errno), errno); + rc = FFS_ERROR; + goto out; + } + + for (uint32_t i=0; ipart_entry, block, + i * block_size, block_size); + if(rc != block_size) { + fprintf(ffs_ops->log, "%s: Error, ffs_entry_read" + " '%s'\n", __func__, ffs_ops->part_entry); + rc = FFS_ERROR; + goto out; + } + + rc = fwrite(block, rc, 1, fp); + if (rc != 1) { + fprintf(ffs_ops->log, "%s: Error, fwrite " + "%s (errno=%d)\n", __func__, + strerror(ferror(fp)), ferror(fp)); + rc = FFS_ERROR; + goto out; + } + else if (rc == 0) + break; + } + + uint32_t bytes_read = entry.size * block_size; + + fprintf(ffs_ops->log, "%s: Read %d bytes from partition '%s' Success\n", + __func__, bytes_read, ffs_ops->part_entry); + + if (fclose(fp) == EOF) { + fprintf(ffs_ops->log, "%s: Error, flose '%s' " + "=> %s (errno=%d)", __func__, ffs_ops->o_file, + strerror(errno), errno); + fp = NULL; + rc = FFS_ERROR; + goto out; + } + + __ffs_close(ffs); + + fprintf(ffs_ops->log, "%s: Writing %d bytes from partition '%s' to " + "file '%s' Success\n", __func__, bytes_read, + ffs_ops->part_entry, ffs_ops->o_file); + + if(false) { +out: + if(fp != NULL) + fclose(fp); + if(ffs != NULL) + __ffs_close(ffs); + } + return rc; +} + +uint32_t +write_partition(ffs_ops_t * ffs_ops) { + + struct stat st; + uint32_t rc = 0; + ffs_t * ffs = NULL; + + + if (stat(ffs_ops->i_file, &st) != 0) { + fprintf(ffs_ops->log, "%s: '%s' => %s (errno=%d)", __func__, + ffs_ops->i_file, strerror(errno), errno); + rc = FFS_ERROR; + goto error; + } + + ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off); + if(ffs == NULL) { + fprintf(ffs_ops->log, "%s: Error, opening nor " + "image '%s'\n", __func__, ffs_ops->nor_image); + rc = FFS_ERROR; + goto error; + } + + fprintf(ffs_ops->log, "%s: Successfully opened nor image '%s' for " + "writing\n", __func__, ffs_ops->nor_image); + + ffs_entry_t entry; + if (__ffs_entry_find(ffs, ffs_ops->part_entry, &entry) == false) { + fprintf(ffs_ops->log, "%s: Error, '%s' not found\n", __func__, + ffs_ops->part_entry); + rc = FFS_ERROR; + goto error; + } + + size_t entry_size = entry.size * ffs->hdr->block_size; + + + fprintf(ffs_ops->log, "%s: Found entry '%s' with size=%d\n", __func__, + ffs_ops->part_entry, entry_size); + + if (entry_size < st.st_size) { + fprintf(ffs_ops->log, "%s: '%s' of size '%lld' too big for " + "partition '%s' of size '%d'\n", __func__, + ffs_ops->i_file, st.st_size, ffs_ops->nor_image, + entry_size); + rc = FFS_ERROR; + goto error; + } + + FILE * fp = fopen(ffs_ops->i_file, "r+"); + if (fp == NULL) { + fprintf(ffs_ops->log, "%s: Error opening file '%s'", __func__, + ffs_ops->i_file); + rc = FFS_ERROR; + goto error; + } + + if(false) { +error: + if(ffs != NULL) + __ffs_close(ffs); + return rc; + } + + uint32_t block_size = ffs->hdr->block_size; + char block[block_size]; + + if (setvbuf(fp, NULL, _IOFBF, block_size) != 0) { + fprintf(ffs_ops->log, "%s: Error, setvbuf failed " + "%s (errno=%d)", __func__, strerror(errno), errno); + rc = FFS_ERROR; + goto out; + } + + fprintf(ffs_ops->log, "%s: Writing data file into partition\n", + __func__); + + for (uint32_t j=0; jpart_entry, block, + j * block_size, bytes_read); + if (bytes_read == 0) { + int err = ferror(fp); + if (err) { + fprintf(ffs_ops->log, "%s: Error, setvbuf " + "failed %s (errno=%d)", __func__, + strerror(errno), errno); + rc = FFS_ERROR; + goto out; + } + else { + break; + } + } + } + + if (fclose(fp) == EOF) { + fprintf(ffs_ops->log, "%s: Error, flose '%s' " + "=> %s (errno=%d)", __func__, ffs_ops->o_file, + strerror(errno), errno); + fp = NULL; + rc = FFS_ERROR; + goto out; + } + fprintf(ffs_ops->log, "%s: Writing to partition '%s' from data file " + "'%s' Success\n", __func__, ffs_ops->part_entry, + ffs_ops->i_file); + + if(false) { +out: + __ffs_close(ffs); + } + return rc; +} + +uint32_t +list_partition(ffs_ops_t * ffs_ops) { + + uint32_t rc = 0; + ffs_t * ffs = NULL; + + ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off); + if(ffs == NULL) { + fprintf(ffs_ops->log, "%s: Error, opening nor " + "image '%s'\n", __func__, ffs_ops->nor_image); + fprintf(ffs_ops->log, "%s: Listing partition entries in '%s'" + " image failed\n", __func__, ffs_ops->nor_image); + rc = FFS_ERROR; + return rc; + } + //!< List all entries in the partition table + __ffs_list_entries(ffs, ".*", true, stdout); + fprintf(ffs_ops->log, "%s: Listing partition entries in '%s' image " + "success\n", __func__, ffs_ops->nor_image); + + __ffs_close(ffs); + + return rc; +} + +uint32_t +hexdump_entry(ffs_ops_t * ffs_ops) { + + uint32_t rc = 0; + ffs_t * ffs = NULL; + + ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off); + if(ffs == NULL) { + fprintf(ffs_ops->log, "%s: Error, opening nor " + "image '%s'\n", __func__, ffs_ops->nor_image); + fprintf(ffs_ops->log, "%s: Hexdump of partition entries in '%s'" + " image failed\n", __func__, ffs_ops->nor_image); + rc = FFS_ERROR; + return rc; + } + //!< Hexdump the entry in the partition table + __ffs_entry_hexdump(ffs, ffs_ops->part_entry, stdout); + fprintf(ffs_ops->log, "%s: Hexdump of partition entries in '%s' image " + "success\n", __func__, ffs_ops->nor_image); + __ffs_close(ffs); + + return rc; +} + +uint32_t +delete_entry(ffs_ops_t * ffs_ops) { + + uint32_t rc = 0; + ffs_t * ffs = NULL; + + ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off); + if(ffs == NULL) { + fprintf(ffs_ops->log, "%s: Error, opening nor " + "image '%s'\n", __func__, ffs_ops->nor_image); + fprintf(ffs_ops->log, "%s: Delete entry '%s' failed\n", + __func__, ffs_ops->part_entry); + rc = FFS_ERROR; + return rc; + } + //!< Delete the entry from the partition table + __ffs_entry_delete(ffs, ffs_ops->part_entry); + fprintf(ffs_ops->log, "%s: Delete entry '%s' success\n", __func__, + ffs_ops->part_entry); + + __ffs_close(ffs); + + return rc; +} + +uint32_t +modify_entry_get(ffs_ops_t * ffs_ops) { + + uint32_t rc = 0; + uint32_t value = 0; + ffs_t * ffs = NULL; + + ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off); + if(ffs == NULL) { + fprintf(ffs_ops->log, "%s: Error, opening nor " + "image '%s'\n", __func__, ffs_ops->nor_image); + fprintf(ffs_ops->log, "%s: Get user word at '%d' failed\n", + __func__, ffs_ops->user); + rc = FFS_ERROR; + return rc; + } + //!< Get the user word at index + __ffs_entry_user_get(ffs, ffs_ops->part_entry, ffs_ops->user, &value); + fprintf(ffs_ops->log, "%s: Get user word at '%d' --> %x success\n", + __func__, ffs_ops->user, value); + //!< Required to check what is put + fprintf(stdout, "UW: %d-->%d\n", ffs_ops->user, value); + + __ffs_close(ffs); + + return rc; +} + +uint32_t +modify_entry_put(ffs_ops_t * ffs_ops) { + + uint32_t rc = 0; + ffs_t * ffs = NULL; + + ffs = __ffs_open(ffs_ops->nor_image, ffs_ops->part_off); + if(ffs == NULL) { + fprintf(ffs_ops->log, "%s: Error, opening nor " + "image '%s'\n", __func__, ffs_ops->nor_image); + fprintf(ffs_ops->log, "%s: Put user word at '%d' failed\n", + __func__, ffs_ops->user); + rc = FFS_ERROR; + return rc; + } + //!< Put the user word at index + __ffs_entry_user_put(ffs, ffs_ops->part_entry, ffs_ops->user, + ffs_ops->value); + fprintf(ffs_ops->log, "%s: Put user word at '%d' --> %d success\n", + __func__, ffs_ops->user, ffs_ops->value); + //!< Required to check what is get + fprintf(stdout, "UW: %d-->%d\n", ffs_ops->user, ffs_ops->value); + + __ffs_close(ffs); + + return rc; +} + +void +usage(void) { + printf("This program is a unit test tool, its callers responsibility " + "to pass the correct parameters.\nNote: No usage errors are " + "displayed, any mistake in params may result in unexpected " + "results\n"); +} + +int +main(int argc, char * argv[]) { + + int32_t rc = 0; + ffs_ops_t ffs_ops; + + memset(&ffs_ops, 0, sizeof(ffs_ops_t)); + ffs_ops.part_off = PART_OFFSET; + ffs_ops.log = log_open(); + + while ((argc > 1) && (argv[1][0] == '-')) + { + switch (argv[1][1]) + { + +//test_libffs -c pnor -O part_offset -s dev_size -b block size +//test_libffs -c pnor -O 4128768 -s 67108864 -b 65536 + case 'c': + ffs_ops.nor_image = argv[2]; //!< nor image + ffs_ops.part_off = atoll(argv[4]); + ffs_ops.device_size = atoi(argv[6]); + ffs_ops.blk_sz = atoi(argv[8]); + rc = create_partition(&ffs_ops); + if(rc == FFS_ERROR) { + goto out; + } + break; +//test_libffs -a pnor -O part_offset -n part_name -t part_type +//test_libffs -a sunray.pnor -O 4128768 -n boot0 -t logical +//test_libffs -a pnor -O part_off -n part_name -t type -s size -o entry_off +//test_libffs -a sunray.pnor -O 4128768 -n boot0/bootenv -t data -s 1048576 -o 0 + case 'a': + ffs_ops.nor_image = argv[2]; //!< nor image + ffs_ops.part_off = atoll(argv[4]); + ffs_ops.part_entry = argv[6]; + if (!strcasecmp(argv[8], "logical")) { + ffs_ops.type = FFS_TYPE_LOGICAL; + ffs_ops.entry_sz = 0; + ffs_ops.entry_off = 0; + } + else if (!strcasecmp(argv[8], "data")) { + ffs_ops.type = FFS_TYPE_DATA; + ffs_ops.entry_sz = atol(argv[10]); + ffs_ops.entry_off = atoll(argv[12]); + } + rc = add_partition_entry(&ffs_ops); + if(rc == FFS_ERROR) { + goto out; + } + break; +//test_libffs -r pnor -O part_off -n part_name -o out_file +//test_libffs -r sunray.pnor -O 4128768 -n boot0/bootenv -o out_file + case 'r': + ffs_ops.nor_image = argv[2]; //!< nor image + ffs_ops.part_off = atoll(argv[4]); + ffs_ops.part_entry = argv[6]; //!< part entry + ffs_ops.o_file = argv[8]; //!< out put file + ffs_ops.i_file = NULL; + rc = read_partition(&ffs_ops); + if(rc == FFS_ERROR) { + goto out; + } + break; +//test_libffs -w pnor -O part_off -n part_name -i in_file +//test_libffs -w sunray.pnor -O 4128768 -n boot0/bootenv -i in_file + case 'w': + ffs_ops.nor_image = argv[2]; //!< nor image + ffs_ops.part_off = atoll(argv[4]); + ffs_ops.part_entry = argv[6]; //!< part entry + ffs_ops.i_file = argv[8]; //!< out put file + ffs_ops.o_file = NULL; + rc = write_partition(&ffs_ops); + if(rc == FFS_ERROR) { + goto out; + } +//test_libffs -l pnor -O part_off +//test_libffs -l sunray.pnor -O 4128768 + case 'l': + ffs_ops.nor_image = argv[2]; //!< nor image + ffs_ops.part_off = atoll(argv[4]); + rc = list_partition(&ffs_ops); + if(rc == FFS_ERROR) { + goto out; + } + break; +//test_libffs -h pnor -O part_off -n part_entry +//test_libffs -h sunray.pnor -O 4128768 -n boot0/bootenv + case 'h': + ffs_ops.nor_image = argv[2]; //!< nor image + ffs_ops.part_off = atoll(argv[4]); + ffs_ops.part_entry = argv[6]; //!< part entry + rc = hexdump_entry(&ffs_ops); + if(rc == FFS_ERROR) { + goto out; + } + break; +//test_libffs -d pnor -O part_off -n part_entry +//test_libffs -d sunray.pnor -O 4128768 -n boot0/bootenv + case 'd': + ffs_ops.nor_image = argv[2]; //!< nor image + ffs_ops.part_off = atoll(argv[4]); + ffs_ops.part_entry = argv[6]; //!< part entry + rc = delete_entry(&ffs_ops); + if(rc == FFS_ERROR) { + goto out; + } + break; +//test_libffs -m pnor -O part_off -n part_name -u index -g +//test_libffs -m sunray.pnor -O 4128768 -n boot0/bootenv -u 0 -g +//test_libffs -m pnor -O part_off -n part_name -u index -p -v some_value +//test_libffs -m sunray.pnor -O 4128768 -n boot0/bootenv -u 0 -p -v 1024 + case 'm': + ffs_ops.nor_image = argv[2]; //!< nor image + ffs_ops.part_off = atoll(argv[4]); + ffs_ops.part_entry = argv[6]; //!< part entry + ffs_ops.user = atol(argv[8]); + if(!strcmp(argv[9], "-g")) { + rc = modify_entry_get(&ffs_ops); + if(rc == FFS_ERROR) { + goto out; + } + } else if(!strcmp(argv[9], "-p")) { + ffs_ops.value = atol(argv[11]); + rc = modify_entry_put(&ffs_ops); + if(rc == FFS_ERROR) { + goto out; + } + } + break; + default: + usage(); + break; + } + break; + } + +out: + if(ffs_ops.log != NULL) { + fclose(ffs_ops.log); + } + return rc; +} diff --git a/ffs/test/test_libffs.h b/ffs/test/test_libffs.h new file mode 100644 index 0000000..c9ec613 --- /dev/null +++ b/ffs/test/test_libffs.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: test_libffs.c + * Author: Shekar Babu S + * Descr: unit test tool for api's in libffs.so + * Date: 06/26/2012 + */ + +#include "libffs.h" + +#define FFS_ERROR -1 +#define PART_OFFSET 0x3F0000 + +typedef struct ffs_operations{ + const char * nor_image; //!< Path to nor image special/regular file + const char * part_entry; //!< Logical partition/entry name + const char * i_file; //!< Input file + const char * o_file; //!< Output file + FILE * log; //!< Log file + size_t device_size;//!< Size of the nor flash + off_t part_off; //!< Offset of partition table + size_t blk_sz; //!< Block size + size_t entry_sz; //!< Partition entry size + off_t entry_off; //!< Offset of partition entry + uint32_t user; //!< Index to user word in any entry + uint32_t value; //!< User word at index in entry + ffs_type_t type; //!< Partition type +} ffs_ops_t; diff --git a/ffs/x86/Makefile b/ffs/x86/Makefile new file mode 100644 index 0000000..af907da --- /dev/null +++ b/ffs/x86/Makefile @@ -0,0 +1,7 @@ +DEPTH = .. + +ARCH=x86 +FFS_INSTALL = $(INST_USR_X86) +FFS_INSTALL_TEST = $(INST_TESTS_X86) + +include ../Rules.mk diff --git a/fpart/.gitignore b/fpart/.gitignore new file mode 100644 index 0000000..d09ad9f --- /dev/null +++ b/fpart/.gitignore @@ -0,0 +1 @@ +*/fpart diff --git a/fpart/Makefile b/fpart/Makefile new file mode 100644 index 0000000..e08925f --- /dev/null +++ b/fpart/Makefile @@ -0,0 +1,10 @@ +SUBDIRS=x86 + +.PHONY: subdirs $(SUBDIRS) + +subdirs: $(SUBDIRS) + +$(SUBDIRS):: + $(MAKE) -C $@ $(MAKECMDGOALS) + +all clean install: $(SUBDIRS) diff --git a/fpart/Rules.mk b/fpart/Rules.mk new file mode 100644 index 0000000..c5aa849 --- /dev/null +++ b/fpart/Rules.mk @@ -0,0 +1,30 @@ +CFLAGS += -m32 -D_GNU_SOURCE -std=gnu99 -D_FILE_OFFSET_BITS=64 +CFLAGS += -I$(DEPTH)/.. -iquote.. + +LDFLAGS += -L. -m32 + +NAME=fpart + +CLIB=$(DEPTH)/../clib/x86/libclib.a +FFS=$(DEPTH)/../ffs/x86/libffs.a + +OBJS=cmd_create.o cmd_add.o cmd_delete.o cmd_list.o \ + cmd_erase.o cmd_trunc.o cmd_user.o command.o main.o + +TARGETS=$(NAME) + +vpath %.c ../src +vpath %.h .. + +all: $(TARGETS) + +$(NAME): $(OBJS) $(FFS) $(DBGX) $(SPINOR) $(CLIB) + $(CC) $(LDFLAGS) -Wl,-whole-archive $(DBGX) -Wl,-no-whole-archive \ + -o $@ $^ -lpthread -ldl -lrt + +install: $(TARGETS) + $(INSTALL) fpart $(FPART_INSTALL)/bin + $(INSTALL) ../fpart.sh $(FPART_INSTALL_TEST) + +clean distclean: + $(RM) -f $(TARGETS) $(OBJS) diff --git a/fpart/Rules.test.mk b/fpart/Rules.test.mk new file mode 100644 index 0000000..9b63533 --- /dev/null +++ b/fpart/Rules.test.mk @@ -0,0 +1,25 @@ +CFLAGS += -m32 -D_GNU_SOURCE -std=gnu99 -D_FILE_OFFSET_BITS=64 -I$(DEPTH)/apps -iquote.. +LDFLAGS += -L. -L$(DEPTH)/apps/clib/$(ARCH_DEP_DIR) + +OBJS=test_libffs.o + +TARGETS=test_libffs + +vpath %.c ../test +vpath %.h .. + +all: $(TARGETS) + +#libffs.a: $(OBJS) +# $(AR) -r $@ $^ + +test_libffs: test_libffs.o ../ppc/libffs.a $(DEPTH)/apps/clib/$(ARCH_DEP_DIR)/libclib.a + $(CC) $(LDFLAGS) -o $@ $^ -lpthread + +install: $(TARGETS) + $(INSTALL) ../test/test_libffs $(TEST_LIBFFS_INSTALL) + $(INSTALL) ../test/libffs_test.sh $(TEST_LIBFFS_INSTALL) + $(INSTALL) ../test/ffs_tool_test.sh $(TEST_LIBFFS_INSTALL) + +clean distclean: + $(RM) -f $(TARGETS) $(OBJS) diff --git a/fpart/fpart.sh b/fpart/fpart.sh new file mode 100755 index 0000000..af73d6e --- /dev/null +++ b/fpart/fpart.sh @@ -0,0 +1,398 @@ +#!/bin/bash +# =================================================================== +# Copyright (c) International Business Machines Corp., 2012 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +# the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# File: ffs cmdline test script +# Author: Shaun wetzstein +# Date: 02/06/13 +# +# Todo: +# 1) function to check syntax of each command + +CP=cp +RM=rm +FPART=fpart +MKDIR=mkdir +GREP=grep +HEAD=head +HEX=hexdump +CRC=crc32 +DD=dd +DIFF=diff + +IMG=sunray2_nor64M_flash.mif +TMP=/tmp/ffs.$$ +ORIG=${TMP}/nor.orig +URANDOM=/dev/urandom + +FAIL=1 +PASS=0 + +KB=$((1*1024)) +MB=$(($KB*1024)) + +function expect() +{ + local expect=${1} + shift + local command=${*} + eval $command + local actual=${?} + + if [[ ${expect} -eq ${actual} ]]; then + echo "[PASSED] rc: '${command}' ===> expect=${expect}, actual=${actual}" >&2 + else + echo "[FAILED] rc: '${command}' ===> expect=${expect}, actual=${actual}" >&2 + exit 1 + fi +} + +function pass() +{ + expect ${PASS} ${*} +} + +function fail() +{ + expect ${FAIL} ${*} +} + +function size() +{ + local size=$(stat -L -c %s ${2}) + if [[ ${1} == ${size} ]]; then + echo "[PASSED] size: '${2}' ===> expect=${1}, actual=${size}" + else + echo "[FAILED] size: '${2}' ===> expect=${1}, actual=${size}" + exit 1 + fi +} + +function crc() +{ + local crc=$(${CRC} ${2}) + if [[ ${1} == ${crc} ]]; then + echo "[PASSED] crc: '${2}' ===> expect=${1}, actual=${crc}" + else + echo "[FAILED] crc: '${2}' ===> expect=${1}, actual=${crc}" + exit 1 + fi +} + +function setup() +{ + pass ${RM} -rf ${TMP} + pass ${MKDIR} -p ${TMP} +} + +function cleanup() +{ + pass ${RM} -rf ${TMP} +} + +function create() +{ + local target=${TMP}/create.nor + pass ${RM} -f ${target} + + pass ${FPART} -t ${target} -s 64M -b 64K -p 0x3f0000 -C + pass ${FPART} --target ${target} --size 64MiB --block 64kb \ + --partition-offset 0x7f0000 --create + + local output=${TMP}/create.txt + pass ${FPART} -t ${target} -L > ${output} + pass ${GREP} \'0x3f0000\' ${output} > /dev/null + pass ${GREP} \'0x7f0000\' ${output} > /dev/null + pass ${GREP} \'blk:010000\' ${output} > /dev/null + pass ${GREP} \'blk\(s\):000400\' ${output} > /dev/null + pass ${GREP} \'entsz:000080\' ${output} > /dev/null + pass ${GREP} \'ent\(s\):000001\' ${output} > /dev/null + pass ${RM} -f ${output} + + pass ${RM} -f ${target} +} + +function add() +{ + local target=${TMP}/add.nor + pass ${RM} -f ${target} + + pass ${FPART} -t ${target} -s 64M -b 64K --partition-offset 0x3f0000 -C + pass ${FPART} --target ${target} --size 64MiB --block 64kb -p 0x7f0000 --create + + local output=${TMP}/add.txt + local name="logical" + pass ${FPART} -t ${target} -l -n ${name} -g 0 -A + pass ${FPART} -t ${target} -n ${name} -L > ${output} + pass ${GREP} ${name} ${output} > /dev/null + pass ${GREP} "l-----" ${output} > /dev/null + + for ((i=0; i<9; i++)); do + local full="${name}/test${i}" + local offset=$((${i}*$MB)) + local output=${TMP}/add.txt + + # avoid clobbering 'part' + if [[ ${i} -eq 4 ]]; then + local size=$MB + else + local size=$(($MB-64*KB)) + fi + + pass ${FPART} -t ${target} -o ${offset} -s ${size} -g 0 -n ${full} -a ${i} -A + pass ${FPART} -t ${target} -n ${full} -T + pass ${FPART} -t ${target} -n ${full} -L > ${output} + pass ${GREP} ${full} ${output} > /dev/null + pass ${GREP} "d-----" ${output} > /dev/null + local range=$(printf "%.8x-%.8x" ${offset} $((${offset}+${size}-1))) + pass ${GREP} ${range} ${output} > /dev/null + pass ${GREP} $(printf "%x" ${size}) ${output} > /dev/null + pass ${RM} -f ${output} + done + + pass ${RM} -f ${target} +} + +function delete() +{ + local target=${TMP}/delete.nor + pass ${RM} -f ${target} + + pass ${FPART} -t ${target} -s 64M -b 64K --partition-offset 0x3f0000 -C + pass ${FPART} --target ${target} --size 64MiB --block 64kb -p 0x7f0000 --create + + local output=${TMP}/delete.txt + + for ((i=0; i<9; i++)); do + local full="test${i}" + local offset=$((${i}*$MB)) + local output=${TMP}/add.txt + + # avoid clobbering 'part' + if [[ ${i} -eq 4 ]]; then + local size=$MB + else + local size=$(($MB-64*KB)) + fi + + pass ${FPART} -t ${target} -o ${offset} -s ${size} -g 0 -n ${full} -a ${i} -A + pass ${FPART} -t ${target} -n ${full} -T + pass ${FPART} -t ${target} -n ${full} -L > ${output} + pass ${GREP} ${full} ${output} > /dev/null + pass ${FPART} -t ${target} -n ${full} -D + pass ${FPART} -t ${target} -n ${full} -L > ${output} + fail ${GREP} ${full} ${output} > /dev/null + + pass ${RM} -f ${output} + done + + pass ${RM} -f ${target} +} + +function hex() +{ + local target=${TMP}/hexdump.nor + pass ${RM} -f ${target} + + pass ${FPART} -t ${target} -s 64M -b 64K --partition-offset 0x3f0000 -C + pass ${FPART} --target ${target} --size 64MiB --block 64kb -p 0x7f0000 --create + + local name="logical" + pass ${FPART} -t ${target} -l -n ${name} -g 0 -A + + for ((i=0; i<9; i++)); do + local full="${name}/test${i}" + local offset=$((${i}*$MB)) + local output=${TMP}/hexdump.txt + + # avoid clobbering 'part' + if [[ ${i} -eq 4 ]]; then + local size=$MB + else + local size=$(($MB-64*KB)) + fi + + pass ${FPART} -t ${target} -o ${offset} -s ${size} -g 0 -n ${full} -a ${i} -A + pass ${FPART} -t ${target} -n ${full} -T + pass ${FPART} -t ${target} -n ${full} -H > ${output} + local p=$(printf "%2.2x%2.2x%2.2x%2.2x" $i $i $i $i) + pass ${GREP} \'$p $p $p $p\' ${output} > /dev/null + pass ${RM} -f ${output} + done + + pass ${RM} -f ${target} +} + +function read() +{ + local target=${TMP}/read.nor + pass ${RM} -f ${target} + + pass ${FPART} -t ${target} -s 64M -b 64K --partition-offset 0x3f0000 -C + pass ${FPART} --target ${target} --size 64MiB --block 64kb -p 0x7f0000 --create + + local name="logical" + pass ${FPART} -t ${target} -l -n ${name} -g 0 -A + + for ((i=0; i<9; i++)); do + local full="${name}/test${i}" + local offset=$((${i}*$MB)) + local output=${TMP}/${i}.bin + + # avoid clobbering 'part' + if [[ ${i} -eq 4 ]]; then + local size=$MB + else + local size=$(($MB-64*KB)) + fi + + pass ${FPART} -t ${target} -o ${offset} -s ${size} -g 0 -n ${full} -a ${i} -A + pass ${FPART} -t ${target} -n ${full} -T + pass ${FPART} -t ${target} -n ${full} -R ${output} + pass ${HEX} -C ${output} > ${output}.hex + local p=$(printf "%2.2x %2.2x %2.2x %2.2x" $i $i $i $i) + pass ${GREP} \"${p} ${p}\" ${output}.hex + pass ${RM} -f ${output} ${output}.hex + done + + pass ${RM} -f ${target} +} + +function write() +{ + local target=${TMP}/write.nor + pass ${RM} -f ${target} + + pass ${FPART} -t ${target} -s 64M -b 64K --partition-offset 0x3f0000 -C + pass ${FPART} --target ${target} --size 64MiB --block 64kb -p 0x7f0000 --create + + local name="write" + local input=${TMP}/write.in + local output=${TMP}/write.out + local block=${1} + local count=$(($MB/$block)) + + pass ${FPART} -t ${target} -o 1M -s 1M -g 0 -n ${name} -a 0xFF -A + for ((i=1; i<${count}; i++)); do + pass ${DD} if=${URANDOM} of=${input} bs=${block} count=${i} 2> /dev/null + local crc=$(${CRC} ${input}) + + pass ${FPART} -t ${target} -n ${name} -E -a 0xFF + pass ${FPART} -t ${target} -n ${name} -W ${input} + pass ${FPART} -t ${target} -n ${name} -U 0 -u ${crc} + pass ${FPART} -t ${target} -n ${name} -R ${output} + + size $((${i}*${block})) ${output} + crc ${crc} ${output} + + local crc=$(printf "%x" ${crc}) + pass "${FPART} -t ${target} -n ${name} -U 0 | ${GREP} ${crc}" > /dev/null + + pass ${RM} -f ${input} ${output} + done + pass ${FPART} -t ${target} -n ${name} -D + + pass ${RM} -f ${target} +} + +function copy() +{ + local src=${TMP}/copy.src + local dst=${TMP}/copy.dst + pass ${RM} -f ${src} ${dst} + + local part="0x0,0x20000" + pass ${FPART} -t ${src} -s 64M -b 64K -p ${part} -C + pass ${FPART} -t ${dst} -s 64M -b 64K -p ${part} -C + + local name="copy" + pass ${FPART} -t ${src} -o 1M -s 1M -g 0 -n ${name} -a 0xFF -p ${part} -A + pass ${FPART} -t ${dst} -o 1M -s 1M -g 0 -n ${name} -a 0x00 -p ${part} -A + + local input=${TMP}/copy.in + local output=${TMP}/copy.out + if [[ -z "${1}" ]]; then + local block=64 + else + local block=${1} + fi + local count=$(($MB/$block)) + + for ((i=1; i<${count}; i++)); do + pass ${DD} if=${URANDOM} of=${input} bs=${block} count=${i} 2> /dev/null + local crc=$(${CRC} ${input}) + + pass ${FPART} -t ${src} -n ${name} -p ${part} -E -a 0xFF + pass ${FPART} -t ${src} -n ${name} -p ${part} -W ${input} + pass ${FPART} -t ${src} -n ${name} -p ${part} -R ${output}.src + pass ${FPART} -t ${src} -n ${name} -p ${part} -U 0 -u ${crc} + pass ${FPART} -t ${dst} -n ${name} -p ${part} -O ${src} + pass ${FPART} -t ${dst} -n ${name} -p ${part} -M ${src} + pass ${FPART} -t ${src} -n ${name} -p ${part} -R ${output}.dst + + size $((${i}*${block})) ${output}.src + size $((${i}*${block})) ${output}.dst + crc ${crc} ${output}.src + crc ${crc} ${output}.dst + pass ${DIFF} ${output}.src ${output}.dst + + local crc=$(printf "%x" ${crc}) + pass "${FPART} -t ${src} -n ${name} -p ${part} -U 0 | ${GREP} ${crc}" > /dev/null + pass "${FPART} -t ${dst} -n ${name} -p ${part} -U 0 | ${GREP} ${crc}" > /dev/null + + pass ${RM} -f ${input} ${output}.* + + done + + expect 2 ${DIFF} ${src} ${dst} + + pass ${RM} -f ${src} ${dst} +} + +function main() +{ + create + add + delete +# hex +# read +# copy $((15*$KB)) +# copy $((21*$KB)) +# copy $((64*$KB)) +# write $((15*$KB)) +# write $((21*$KB)) +# write $((64*$KB)) +} + +setup +if [[ -z "${1:-}" ]]; then + main +else + case "$1" in + create ) create ;; + add ) add ;; + delete ) delete ;; +# hex ) hex ;; +# read ) read ;; +# copy ) copy $((${2}*$KB)) ;; +# write ) write $((${2}*$KB)) ;; + * ) echo "$1 not implemented"; exit 1 ;; + esac + + exit 0; +fi +cleanup diff --git a/fpart/ppc/Makefile b/fpart/ppc/Makefile new file mode 100644 index 0000000..99e6198 --- /dev/null +++ b/fpart/ppc/Makefile @@ -0,0 +1,9 @@ +DEPTH = ../../.. +include $(DEPTH)/integration/Rules.mk +include $(DEPTH)/integration/Rules.ppc.mk + +ARCH=ppc +FPART_INSTALL = $(INST_USR) +FPART_INSTALL_TEST = $(INST_TESTS) + +include ../Rules.mk diff --git a/fpart/src/cmd_add.c b/fpart/src/cmd_add.c new file mode 100644 index 0000000..342df05 --- /dev/null +++ b/fpart/src/cmd_add.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: add.c + * Author: Shaun Wetzstein + * Descr: --add implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + +int command_add(args_t * args) +{ + assert(args != NULL); + + /* ========================= */ + + int add(args_t * args, off_t poffset) + { + int rc = 0; + + off_t offset = 0; + size_t size = 0; + uint32_t flags = 0; + + rc = parse_offset(args->offset, &offset); + if (rc < 0) + return rc; + rc = parse_size(args->size, &size); + if (rc < 0) + return rc; + rc = parse_size(args->flags, &flags); + if (rc < 0) + return rc; + + ffs_type_t type = FFS_TYPE_DATA; + if (args->logical == f_LOGICAL) + type = FFS_TYPE_LOGICAL; + + const char * target = args->target; + int debug = args->debug; + + RAII(FILE *, file, fopen_generic(target, "r+", debug), fclose); + RAII(ffs_t *, ffs, __ffs_fopen(file, poffset), __ffs_fclose); + + rc = __ffs_entry_add(ffs, args->name, offset, size, + type, flags); + if (rc < 0) + return rc; + + if (args->verbose == f_VERBOSE) + printf("%llx: %s: add partition at offset '%llx' size " + "'%x' type '%d' flags '%x'\n", poffset, + args->name, offset, size, type, flags); + + return rc; + } + + /* ========================= */ + + return command(args, add); +} diff --git a/fpart/src/cmd_compare.c b/fpart/src/cmd_compare.c new file mode 100644 index 0000000..95b64d0 --- /dev/null +++ b/fpart/src/cmd_compare.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: compare.c + * Author: Shaun Wetzstein + * Descr: --compare implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + +int command_compare(args_t * args) +{ + assert(args != NULL); + + char name[strlen(args->name) + 2]; + strcpy(name, args->name); + if (strrchr(name, '$') == NULL) + strcat(name, "$"); + + char full_name[page_size]; + regex_t rx; + + off_t __poffset; + ffs_t * __in, * __out; + + list_t list; + list_init(&list); + + list_iter_t it; + ffs_entry_node_t * node; + + /* ========================= */ + + int compare_entry(ffs_entry_t * src) + { + if (__ffs_entry_name(__in, src, full_name, + sizeof full_name) < 0) + return -1; + if (regexec(&rx, full_name, 0, NULL, 0) == REG_NOMATCH) + return 0; + + if (src->flags & FFS_FLAGS_PROTECTED && + args->force != f_FORCE) { + if (args->verbose == f_VERBOSE) + printf("%llx: %s: protected, skipping " + "compare\n", __poffset, full_name); + return 0; + } + + list_iter_init(&it, &list, LI_FLAG_FWD); + list_for_each(&it, node, node) { + if (node->entry.base == src->base && + node->entry.size == src->size) + return 0; + } + + node = (ffs_entry_node_t *)malloc(sizeof(*node)); + assert(node != NULL); + + memcpy(&node->entry, src, sizeof(node->entry)); + list_add_tail(&list, &node->node); + + ffs_entry_t dst; + if (__ffs_entry_find(__out, full_name, &dst) == false) { + UNEXPECTED("'%s' entry not found '%s'", + args->target, args->name); + return -1; + } + + if (verify_operation(full_name, __in, src, __out, &dst) < 0) + return -1; + + size_t block_size = __in->hdr->block_size; + + if (args->block != NULL) { + if (parse_size(args->block, &block_size) < 0) + return -1; + if (block_size & (__in->hdr->block_size-1)) { + UNEXPECTED("'%x' block size must be multiple " + "of target block size '%x'", + block_size, __in->hdr->block_size); + return -1; + } + } + + if (__ffs_buffer(__in, block_size) < 0) + return -1; + if (__ffs_buffer(__out, block_size) < 0) + return -1; + + RAII(void*, src_block, malloc(block_size), free); + if (src_block == NULL) { + ERRNO(errno); + return -1; + } + RAII(void*, dst_block, malloc(block_size), free); + if (dst_block == NULL) { + ERRNO(errno); + return -1; + } + + size_t data_size = src->actual; + off_t data_offset = 0; + + while (0 < data_size) { + ssize_t rc; + + rc = __ffs_entry_read(__in, full_name, src_block, + data_offset, + min(block_size, data_size)); + if (rc < 0) + return -1; + + rc = __ffs_entry_read(__out, full_name, dst_block, + data_offset, rc); + if (rc < 0) + return -1; + + if (memcmp(src_block, dst_block, block_size) != 0) { + if (args->verbose == f_VERBOSE) + printf("%llx: %s: miscompare at offset" + "'%llx'\n", __poffset, full_name, + data_offset); + UNEXPECTED("data miscompare at offset range " + "[%llx..%llx]", data_offset, + data_offset + block_size - 1); + return -1; + } + + + data_offset += rc; + data_size -= rc; + } + + if (args->verbose == f_VERBOSE) + printf("%llx: %s: compare to '%s'\n", __poffset, + full_name, args->path); + + return 0; + } + + int compare(args_t * args, off_t poffset) + { + __poffset = poffset; + + const char * path = args->path; + const char * target = args->target; + int debug = args->debug; + + RAII(FILE*, inf, fopen_generic(path, "r", debug), fclose); + if (inf == NULL) + return -1; + RAII(FILE*, outf, fopen_generic(target, "r", debug), fclose); + if (outf == NULL) + return -1; + + RAII(ffs_t*, in, __ffs_fopen(inf, poffset), __ffs_fclose); + if (in == NULL) + return -1; + RAII(ffs_t*, out, __ffs_fopen(outf, poffset), __ffs_fclose); + if (out == NULL) + return -1; + + __in = in; + __out = out; + + int rc = __ffs_iterate_entries(in, compare_entry); + if (rc == 1) + rc = -1; + + return rc; + } + + /* ========================= */ + + if (regcomp(&rx, name, REG_ICASE | REG_NOSUB) != 0) { + ERRNO(errno); + return -1; + } + + int rc = command(args, compare); + + regfree(&rx); + + while (!list_empty(&list)) + free(container_of(list_remove_head(&list), + ffs_entry_node_t, node)); + + return rc; +} diff --git a/fpart/src/cmd_copy.c b/fpart/src/cmd_copy.c new file mode 100644 index 0000000..9bbb8bf --- /dev/null +++ b/fpart/src/cmd_copy.c @@ -0,0 +1,282 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: copy.c + * Author: Shaun Wetzstein + * Descr: --copy implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + +int command_copy(args_t * args) +{ + assert(args != NULL); + + char name[strlen(args->name) + 2]; + strcpy(name, args->name); + if (strrchr(name, '$') == NULL) + strcat(name, "$"); + + char full_name[page_size]; + regex_t rx; + + ffs_t *__in, *__out; + off_t __poffset; + + list_t list; + list_init(&list); + + list_iter_t it; + ffs_entry_node_t * node; + + /* ========================= */ + + int copy_entry(ffs_entry_t * src) + { + if (__ffs_entry_name(__in, src, full_name, sizeof full_name)< 0) + return -1; + + if (regexec(&rx, full_name, 0, NULL, 0) == REG_NOMATCH) + return 0; + + if (src->flags & FFS_FLAGS_PROTECTED && + args->force != f_FORCE) { + if (args->verbose == f_VERBOSE) + printf("%llx: %s: protected, skipping copy\n", + __poffset, full_name); + return 0; + } + + __ffs_entry_truncate(__out, full_name, src->actual); + if (args->verbose == f_VERBOSE) + printf("%llx: %s: truncate size '%x'\n", + __poffset, full_name, src->actual); + + uint32_t src_val, dst_val; + for (uint32_t i=0; iverbose == f_VERBOSE) + printf("%llx: %s: user[] copy from '%s'\n", __poffset, + full_name, args->path); + + list_iter_init(&it, &list, LI_FLAG_FWD); + list_for_each(&it, node, node) { + ffs_entry_t * e = &node->entry; + if (e->base == src->base && e->size == src->size) { + if (args->verbose == f_VERBOSE) + printf("%llx: %s: skipping copy from " + "'%s'\n", __poffset, full_name, + args->path); + return 0; + } + } + + node = (ffs_entry_node_t *)malloc(sizeof(*node)); + assert(node != NULL); + + memcpy(&node->entry, src, sizeof(node->entry)); + list_add_tail(&list, &node->node); + + ffs_entry_t dst; + if (__ffs_entry_find(__out, full_name, &dst) == false) { + UNEXPECTED("'%s' entry not found '%s'", + args->target, args->name); + return -1; + } + + if (verify_operation(full_name, __in, src, __out, &dst) < 0) + return -1; + + size_t block_size = __in->hdr->block_size; + + if (args->block != NULL) { + if (parse_size(args->block, &block_size) < 0) + return -1; + if (block_size & (__in->hdr->block_size-1)) { + UNEXPECTED("'%x' block size must be multiple " + "of target block size '%x'", + block_size, __in->hdr->block_size); + return -1; + } + } + + if (__ffs_buffer(__in, block_size) < 0) + return -1; + if (__ffs_buffer(__out, block_size) < 0) + return -1; + + RAII(void*, block, malloc(block_size), free); + if (block == NULL) { + ERRNO(errno); + return -1; + } + + size_t data_size = src->actual; + off_t data_offset = 0; + + while (0 < data_size) { + ssize_t rc; + + rc = __ffs_entry_read(__in, full_name, block, + data_offset, + min(block_size, data_size)); + if (rc < 0) + return -1; + + rc = __ffs_entry_write(__out, full_name, block, + data_offset, rc); + if (rc < 0) + return -1; + + data_offset += rc; + data_size -= rc; + } + + __ffs_fsync(__out); + + if (args->verbose == f_VERBOSE) + printf("%llx: %s: copy from '%s'\n", __poffset, + full_name, args->path); + + return 0; + } + + int copy(args_t * args, off_t poffset) + { + __poffset = poffset; + + const char * path = args->path; + const char * target = args->target; + int debug = args->debug; + + RAII(FILE*, inf, fopen_generic(path, "r", debug), fclose); + if (inf == NULL) + return -1; + + RAII(ffs_t*, in, __ffs_fopen(inf, poffset), __ffs_fclose); + if (in == NULL) + return -1; + + if (args->force == f_FORCE) { + size_t block_size = in->hdr->block_size; + char block[block_size]; + + size_t rc; + rc = __ffs_entry_read(in, FFS_PARTITION_NAME, block, + 0, block_size); + if (rc != block_size) { + UNEXPECTED("'%s' unable to read '%s' from " + "offset '%llx'", args->path, + FFS_PARTITION_NAME, poffset); + return -1; + } + + RAII(FILE*, outf, + fopen_generic(target, "r+", debug), fclose); + if (outf == NULL) + return -1; + + if (fseeko(outf, poffset, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + rc = fwrite(block, 1, block_size, outf); + if (rc <= 0) { + if (ferror(outf)) { + ERRNO(errno); + return -1; + } + } + + fflush(outf); + if (fileno(outf) != -1) + fsync(fileno(outf)); + + if (args->verbose == f_VERBOSE) + printf("%llx: %s: force partition table\n", + poffset, FFS_PARTITION_NAME); + } + + RAII(FILE*, outf, fopen_generic(target, "r+", debug), fclose); + if (outf == NULL) + return -1; + RAII(ffs_t*, out, __ffs_fopen(outf, poffset), __ffs_fclose); + if (in == NULL) + return -1; + + __in = in; + __out = out; + + int rc = __ffs_iterate_entries(in, copy_entry); + if (rc == 1) + rc = -1; + + return rc; + } + + /* ========================= */ + + if (regcomp(&rx, name, REG_ICASE | REG_NOSUB) != 0) { + ERRNO(errno); + return -1; + } + + int rc = command(args, copy); + + regfree(&rx); + + while (!list_empty(&list)) + free(container_of(list_remove_head(&list), + ffs_entry_node_t, node)); + + return rc; +} diff --git a/fpart/src/cmd_create.c b/fpart/src/cmd_create.c new file mode 100644 index 0000000..f7448ec --- /dev/null +++ b/fpart/src/cmd_create.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: create.c + * Author: Shaun Wetzstein + * Descr: --create implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + +int command_create(args_t * args) +{ + assert(args != NULL); + + size_t block = 0; + size_t size = 0; + size_t pad = 0xff; + + if (parse_size(args->block, &block) < 0) + return -1; + if (parse_size(args->size, &size) < 0) + return -1; + if (args->pad != NULL) + if (parse_size(args->pad, &pad) < 0) + return -1; + + struct stat st; + if (stat(args->target, &st) < 0) { + if (errno == ENOENT) { + create_regular_file(args->target, size, (uint8_t)pad); + } else { + ERRNO(errno); + return -1; + } + } else { + if (st.st_size != size) { + create_regular_file(args->target, size, (uint8_t)pad); + } else { + if (args->force != f_FORCE && st.st_size != size) { + UNEXPECTED("--size '%d' differs from actual " + "size '%lld', use --force to " + "override", size, st.st_size); + return -1; + } + } + } + + /* ========================= */ + + int create(args_t * args, off_t poffset) + { + if (args->verbose == f_VERBOSE) + printf("%llx: create partition table\n", poffset); + + const char * target = args->target; + int debug = args->debug; + + RAII(FILE*, file, fopen_generic(target, "r+", debug), fclose); + if (file == NULL) + return -1; + RAII(ffs_t*, ffs, __ffs_fcreate(file, poffset, block, + size / block), __ffs_fclose); + if (ffs == NULL) + return -1; + + return 0; + } + + /* ========================= */ + + return command(args, create); +} diff --git a/fpart/src/cmd_delete.c b/fpart/src/cmd_delete.c new file mode 100644 index 0000000..26033da --- /dev/null +++ b/fpart/src/cmd_delete.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: delete.c + * Author: Shaun Wetzstein + * Descr: --delete implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + +int command_delete(args_t * args) +{ + assert(args != NULL); + + /* ========================= */ + + int delete(args_t * args, off_t poffset) + { + const char * target = args->target; + int debug = args->debug; + + RAII(FILE*, file, fopen_generic(target, "r+", debug), fclose); + RAII(ffs_t*, ffs, __ffs_fopen(file, poffset), __ffs_fclose); + + if (__ffs_entry_delete(ffs, args->name) < 0) + return -1; + + if (args->verbose == f_VERBOSE) + printf("%llx: %s: delete\n", poffset, args->name); + + return 0; + } + + /* ========================= */ + + return command(args, delete); +} diff --git a/fpart/src/cmd_erase.c b/fpart/src/cmd_erase.c new file mode 100644 index 0000000..aac75fb --- /dev/null +++ b/fpart/src/cmd_erase.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: erase.c + * Author: Shaun Wetzstein + * Descr: --erase implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + +int command_erase(args_t * args) +{ + assert(args != NULL); + + char name[strlen(args->name) + 2]; + strcpy(name, args->name); + if (strrchr(name, '$') == NULL) + strcat(name, "$"); + + size_t pad = 0xff; + if (args->pad != NULL) + if (parse_size(args->pad, &pad) < 0) + return -1; + + char full_name[page_size]; + regex_t rx; + + ffs_t * __ffs; + off_t __poffset; + + list_t list; + list_init(&list); + + list_iter_t it; + ffs_entry_node_t * node; + + /* ========================= */ + + int erase_entry(ffs_entry_t * entry) + { + if (__ffs_entry_name(__ffs, entry, full_name, + sizeof full_name) < 0) + return -1; + + if (regexec(&rx, full_name, 0, NULL, 0) == REG_NOMATCH) + return 0; + + if (entry->flags & FFS_FLAGS_PROTECTED && + args->force != f_FORCE) { + if (args->verbose == f_VERBOSE) + printf("%llx: %s: protected, skipping erase\n", + __poffset, full_name); + return 0; + } + + if (__ffs_entry_truncate(__ffs, full_name, 0) < 0) + return -1; + + if (args->verbose == f_VERBOSE) + printf("%llx: %s: truncate size '%x'\n", __poffset, + full_name, 0); + + for (uint32_t i=0; iverbose == f_VERBOSE) + printf("%llx: %s: user[] zero\n", __poffset, full_name); + + list_iter_init(&it, &list, LI_FLAG_FWD); + list_for_each(&it, node, node) { + if (node->entry.base == entry->base) { + if (args->verbose == f_VERBOSE) + printf("%llx: %s: skipping fill with " + "'%x'\n", __poffset, full_name, + (uint8_t)pad); + return 0; + } + } + + node = (ffs_entry_node_t *)malloc(sizeof(*node)); + assert(node != NULL); + + memcpy(&node->entry, entry, sizeof(node->entry)); + list_add_tail(&list, &node->node); + + uint32_t block_size = __ffs->hdr->block_size; + + if (args->block != NULL) { + if (parse_size(args->block, &block_size) < 0) + return -1; + if (block_size & (__ffs->hdr->block_size-1)) { + UNEXPECTED("'%x' block size must be multiple " + "of target block size '%x'", + block_size, __ffs->hdr->block_size); + return -1; + } + } + + if (__ffs_buffer(__ffs, block_size) < 0) + return -1; + + RAII(void*, block, malloc(block_size), free); + if (block == NULL) { + ERRNO(errno); + return -1; + } + memset(block, pad, block_size); + + for (uint32_t i = 0; i < entry->size; i++) + if(__ffs_entry_write(__ffs, full_name, block, i * block_size, block_size) < 0) + return -1; + + __ffs_fsync(__ffs); + + if (__ffs_entry_truncate(__ffs, full_name, 0) < 0) + return -1; + + if (args->verbose == f_VERBOSE) + printf("%llx: %s: filled with '%x'\n", __poffset, + full_name, (uint8_t)pad); + + return 0; + } + + int erase(args_t * args, off_t poffset) + { + __poffset = poffset; + + const char * target = args->target; + int debug = args->debug; + + RAII(FILE*, file, fopen_generic(target, "r+", debug), fclose); + if (file == NULL) + return -1; + RAII(ffs_t*, ffs, __ffs_fopen(file, poffset), __ffs_fclose); + if (ffs == NULL) + return -1; + + __ffs = ffs; + + int rc = __ffs_iterate_entries(ffs, erase_entry); + if (rc == 1) + rc = -1; + + return rc; + } + + /* ========================= */ + + if (regcomp(&rx, name, REG_ICASE | REG_NOSUB) != 0) { + ERRNO(errno); + return -1; + } + + int rc = command(args, erase); + + regfree(&rx); + + while (!list_empty(&list)) + free(container_of(list_remove_head(&list), + ffs_entry_node_t, node)); + + return rc; +} diff --git a/fpart/src/cmd_hexdump.c b/fpart/src/cmd_hexdump.c new file mode 100644 index 0000000..1a0086e --- /dev/null +++ b/fpart/src/cmd_hexdump.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: hexdump.c + * Author: Shaun Wetzstein + * Descr: --hexdump implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + +int command_hexdump(args_t * args) +{ + assert(args != NULL); + + char name[strlen(args->name) + 2]; + strcpy(name, args->name); + if (strrchr(name, '$') == NULL) + strcat(name, "$"); + + char full_name[page_size]; + regex_t rx; + + off_t __poffset; + ffs_t * __ffs; + + list_t list; + list_init(&list); + + list_iter_t it; + ffs_entry_node_t * node; + + /* ========================= */ + + int hexdump_entry(ffs_entry_t * entry) + { + if (__ffs_entry_name(__ffs, entry, full_name, + sizeof full_name) < 0) + return -1; + + if (regexec(&rx, full_name, 0, NULL, 0) == REG_NOMATCH) + return 0; + + if (entry->flags & FFS_FLAGS_PROTECTED && + args->force != f_FORCE) { + printf("%llx: %s: protected, skipping hexdump\n", + __poffset, full_name); + return 0; + } + + list_iter_init(&it, &list, LI_FLAG_FWD); + list_for_each(&it, node, node) { + if (node->entry.base == entry->base && + node->entry.size == entry->size) + return 0; + } + + node = (ffs_entry_node_t *)malloc(sizeof(*node)); + assert(node != NULL); + + memcpy(&node->entry, entry, sizeof(node->entry)); + list_add_tail(&list, &node->node); + + if (__ffs_entry_hexdump(__ffs, full_name, stdout) < 0) + return -1; + + if (args->verbose == f_VERBOSE) + printf("%llx: %s: partition actual '%x'\n", + __poffset, full_name, entry->actual); + + return 0; + } + + int hexdump(args_t * args, off_t poffset) + { + __poffset = poffset; + + const char * target = args->target; + int debug = args->debug; + + RAII(FILE*, file, fopen_generic(target, "r", debug), fclose); + if (file == NULL) + return -1; + RAII(ffs_t*, ffs, __ffs_fopen(file, poffset), __ffs_fclose); + if (ffs == NULL) + return -1; + + __ffs = ffs; + + int rc = __ffs_iterate_entries(ffs, hexdump_entry); + if (rc == 1) + rc = -1; + + return rc; + } + + /* ========================= */ + + if (regcomp(&rx, name, REG_ICASE | REG_NOSUB) != 0) { + ERRNO(errno); + return -1; + } + + int rc = command(args, hexdump); + + regfree(&rx); + + while (!list_empty(&list)) + free(container_of(list_remove_head(&list), + ffs_entry_node_t, node)); + + return rc; +} diff --git a/fpart/src/cmd_list.c b/fpart/src/cmd_list.c new file mode 100644 index 0000000..b9a4cbd --- /dev/null +++ b/fpart/src/cmd_list.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: list.c + * Author: Shaun Wetzstein + * Descr: --list implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + +int command_list(args_t * args) +{ + assert(args != NULL); + + char name[strlen(args->name) + 2]; + strcpy(name, args->name); + if (strrchr(name, '$') == NULL) + strcat(name, "$"); + + ffs_t *__ffs; + + char full_name[page_size]; + regex_t rx; + + /* ========================= */ + + int __list_entry(ffs_entry_t * entry) + { + + size_t offset = entry->base * __ffs->hdr->block_size; + size_t size = entry->size * __ffs->hdr->block_size; + + if (__ffs_entry_name(__ffs, entry, full_name, + sizeof full_name) < 0) + return -1; + + if (regexec(&rx, full_name, 0, NULL, 0) == REG_NOMATCH) + return 0; + + char type; + if (entry->type == FFS_TYPE_LOGICAL) { + type ='l'; + } else if (entry->type == FFS_TYPE_DATA) { + type ='d'; + } else if (entry->type == FFS_TYPE_PARTITION) { + type ='p'; + } + fprintf(stdout, "%3d [%08x-%08x] [%8x:%8x] ", + entry->id, offset, offset+size-1, + size, entry->actual); + + fprintf(stdout, "[%c%c%c%c%c%c] %s\n", + type, '-', '-', '-', + entry->flags & FFS_FLAGS_U_BOOT_ENV ? 'b' : '-', + entry->flags & FFS_FLAGS_PROTECTED ? 'p' : '-', + full_name); + + if (args->verbose == f_VERBOSE) { + for (int i=0; iuser.data[i]); + if ((i+1) % 4 == 0) + fprintf(stdout, "\n"); + } + } + + return 0; + } + + int list(args_t * args, off_t poffset) + { + const char * target = args->target; + int debug = args->debug; + int rc = 0; + + RAII(FILE*, file, fopen_generic(target, "r", debug), fclose); + if (file == NULL) + return -1; + + if (__ffs_fcheck(file, poffset) < 0) + return -1; + + RAII(ffs_t*, ffs, __ffs_fopen(file, poffset), __ffs_fclose); + if (ffs == NULL) + return -1; + + if (0 < ffs->count) { + printf("========================[ PARTITION TABLE" + " 0x%llx ]=======================\n", + ffs->offset); + printf("vers:%04x size:%04x * blk:%06x blk(s):" + "%06x * entsz:%06x ent(s):%06x\n", + ffs->hdr->version, + ffs->hdr->size, + ffs->hdr->block_size, + ffs->hdr->block_count, + ffs->hdr->entry_size, + ffs->hdr->entry_count); + printf("----------------------------------" + "----------------------------------" + "-------\n"); + + __ffs = ffs; + + rc = __ffs_iterate_entries(ffs, __list_entry); + if (rc == 1) + rc = -1; + + printf("\n"); + } + + return rc; + } + + /* ========================= */ + + if (regcomp(&rx, name, REG_ICASE | REG_NOSUB) != 0) { + ERRNO(errno); + return -1; + } + + int rc = command(args, list); + + regfree(&rx); + + return rc; +} diff --git a/fpart/src/cmd_read.c b/fpart/src/cmd_read.c new file mode 100644 index 0000000..e058377 --- /dev/null +++ b/fpart/src/cmd_read.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: read.c + * Author: Shaun Wetzstein + * Descr: --read implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + +int command_read(args_t * args) +{ + assert(args != NULL); + + if (access(args->path, F_OK) == 0 && args->force != f_FORCE) { + UNEXPECTED("output file '%s' already exists, use --force " + "to overwrite\n", args->path); + return -1; + } + + list_t list; + list_init(&list); + + list_iter_t it; + ffs_entry_node_t * node; + + /* ========================= */ + + int read(args_t * args, off_t poffset) + { + const char * target = args->target; + int debug = args->debug; + + RAII(FILE*, file, fopen_generic(target, "r", debug), fclose); + if (file == NULL) + return -1; + RAII(ffs_t*, ffs, __ffs_fopen(file, poffset), __ffs_fclose); + if (ffs == NULL) + return -1; + + size_t block_size = ffs->hdr->block_size; + + if (args->block != NULL) { + if (parse_size(args->block, &block_size) < 0) + return -1; + if (block_size & (ffs->hdr->block_size-1)) { + UNEXPECTED("'%x' block size must be multiple " + "of target block size '%x'", + block_size, ffs->hdr->block_size); + return -1; + } + } + + if (__ffs_buffer(ffs, block_size) < 0) + return -1; + + RAII(void*, block, malloc(block_size), free); + if (block == NULL) { + ERRNO(errno); + return -1; + } + + ffs_entry_t entry; + if (__ffs_entry_find(ffs, args->name, &entry) == false) { + UNEXPECTED("'%s' partition not found => %s", + args->target, args->name); + return -1; + } + + list_iter_init(&it, &list, LI_FLAG_FWD); + list_for_each(&it, node, node) { + if (node->entry.base == entry.base && + node->entry.size == entry.size) + return 0; + } + + node = (ffs_entry_node_t *)malloc(sizeof(*node)); + assert(node != NULL); + + memcpy(&node->entry, &entry, sizeof(node->entry)); + list_add_tail(&list, &node->node); + + RAII(FILE*, out, fopen(args->path, "w+"), fclose); + if (out == NULL) { + ERRNO(errno); + return -1; + } + + size_t data_size = entry.actual; + off_t data_offset = 0; + + while (0 < data_size) { + ssize_t rc; + rc = __ffs_entry_read(ffs, args->name, + block, data_offset, + min(block_size, data_size)); + + rc = fwrite(block, 1, rc, out); + if (rc <= 0 && ferror(out)) { + ERRNO(errno); + return -1; + } + + data_size -= rc; + data_offset += rc; + } + + if (args->verbose == f_VERBOSE) + printf("%llx: %s: wrote '%x' bytes to file '%s'\n", + poffset, args->name, entry.actual, args->path); + + return 0; + } + + /* ========================= */ + + int rc = command(args, read); + + while (!list_empty(&list)) + free(container_of(list_remove_head(&list), + ffs_entry_node_t, node)); + + return rc; +} + diff --git a/fpart/src/cmd_trunc.c b/fpart/src/cmd_trunc.c new file mode 100644 index 0000000..b946bdc --- /dev/null +++ b/fpart/src/cmd_trunc.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: trunc.c + * Author: Shaun Wetzstein + * Descr: --trunc implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + +int command_trunc(args_t * args) +{ + assert(args != NULL); + + char name[strlen(args->name) + 2]; + strcpy(name, args->name); + if (strrchr(name, '$') == NULL) + strcat(name, "$"); + + off_t __poffset; + ffs_t * __ffs; + + char full_name[page_size]; + regex_t rx; + + /* ========================= */ + + int trunc_entry(ffs_entry_t * entry) + { + if (__ffs_entry_name(__ffs, entry, full_name, + sizeof full_name) < 0) + return -1; + + if (regexec(&rx, full_name, 0, NULL, 0) == REG_NOMATCH) + return 0; + + if (entry->flags & FFS_FLAGS_PROTECTED && + args->force != f_FORCE) { + printf("%llx: %s: protected, skipping truncate\n", + __poffset, full_name); + return 0; + } + + size_t size = entry->size * __ffs->hdr->block_size; + if (args->size != NULL) + if (parse_size(args->size, &size) < 0) + return -1; + + if (__ffs_entry_truncate(__ffs, full_name, size) < 0) + return -1; + + if (args->verbose == f_VERBOSE) + printf("%llx: %s: truncate size '%x'\n", __poffset, + full_name, size); + + return 0; + } + + int trunc(args_t * args, off_t poffset) + { + __poffset = poffset; + + const char * target = args->target; + int debug = args->debug; + + RAII(FILE*, file, fopen_generic(target, "r+", debug), fclose); + if (file == NULL) + return -1; + RAII(ffs_t*, ffs, __ffs_fopen(file, poffset), __ffs_fclose); + if (ffs == NULL) + return -1; + + __ffs = ffs; + + int rc = __ffs_iterate_entries(ffs, trunc_entry); + if (rc == 1) + return -1; + + return rc; + } + + /* ========================= */ + + if (regcomp(&rx, name, REG_ICASE | REG_NOSUB) != 0) { + ERRNO(errno); + return -1; + } + + int rc = command(args, trunc); + + regfree(&rx); + + return rc; +} diff --git a/fpart/src/cmd_user.c b/fpart/src/cmd_user.c new file mode 100644 index 0000000..67abc50 --- /dev/null +++ b/fpart/src/cmd_user.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: user.c + * Author: Shaun Wetzstein + * Descr: --user implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + +int command_user(args_t * args) +{ + assert(args != NULL); + + char name[strlen(args->name) + 2]; + strcpy(name, args->name); + if (strrchr(name, '$') == NULL) + strcat(name, "$"); + + char full_name[page_size]; + regex_t rx; + + off_t __poffset; + ffs_t * __ffs; + + uint32_t user = 0; + if (args->user != NULL) + if (parse_size(args->user, &user) < 0) + return -1; + + if (FFS_USER_WORDS <= user) { + UNEXPECTED("invalid user word '%d', valid range [0..%d]", + user, FFS_USER_WORDS); + return -1; + } + + int user_entry(ffs_entry_t * entry) + { + assert(entry != NULL); + + if (__ffs_entry_name(__ffs, entry, full_name, + sizeof full_name) < 0) + return -1; + + if (regexec(&rx, full_name, 0, NULL, 0) == REG_NOMATCH) + return 0; + + if (entry->flags & FFS_FLAGS_PROTECTED && + args->force != f_FORCE) { + printf("%llx: %s: protected, skipping user[%d]\n", + __poffset, full_name, user); + return 0; + } + + uint32_t value = 0; + if (args->value != NULL) { + if (args->value != NULL) + if (parse_size(args->value, &value) < 0) + return -1; + + if (__ffs_entry_user_put(__ffs, full_name, + user, value) < 0) + return -1; + + if (args->verbose == f_VERBOSE) + printf("%llx: %s: user[%d] = '%x'\n", __poffset, + args->name, user, value); + } else { + if (__ffs_entry_user_get(__ffs, args->name, + user, &value) < 0) + return -1; + + printf("%llx: %s: user[%d] = '%x'\n", __poffset, + args->name, user, value); + } + + return 0; + } + + int __user(args_t * args, off_t poffset) + { + __poffset = poffset; + + const char * target = args->target; + int debug = args->debug; + + RAII(FILE*, file, fopen_generic(target, "r+", debug), fclose); + if (file == NULL) + return -1; + RAII(ffs_t*, ffs, __ffs_fopen(file, poffset), __ffs_fclose); + if (ffs == NULL) + return -1; + + __ffs = ffs; + + int rc = __ffs_iterate_entries(ffs, user_entry); + if (rc == 1) + return -1; + + return rc; + } + + /* ========================= */ + + if (regcomp(&rx, name, REG_ICASE | REG_NOSUB) != 0) { + ERRNO(errno); + return-1; + } + + int rc = command(args, __user); + + regfree(&rx); + + return rc; +} diff --git a/fpart/src/cmd_write.c b/fpart/src/cmd_write.c new file mode 100644 index 0000000..83f71d3 --- /dev/null +++ b/fpart/src/cmd_write.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: write.c + * Author: Shaun Wetzstein + * Descr: --write implementation + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + +int command_write(args_t * args) +{ + assert(args != NULL); + + char name[strlen(args->name) + 2]; + strcpy(name, args->name); + if (strrchr(name, '$') == NULL) + strcat(name, "$"); + + char full_name[page_size]; + struct stat st; + regex_t rx; + + off_t __poffset; + ffs_t *__ffs; + + RAII(FILE*, in, fopen(args->path, "r"), fclose); + + list_t list; + list_init(&list); + + list_iter_t it; + ffs_entry_node_t * node; + + /* ========================= */ + + int write_entry(ffs_entry_t * entry) + { + assert(entry != NULL); + + if (__ffs_entry_name(__ffs, entry, full_name, + sizeof full_name) < 0) + return -1; + + if (regexec(&rx, full_name, 0, NULL, 0) == REG_NOMATCH) + return 0; + + if (entry->flags & FFS_FLAGS_PROTECTED && + args->force != f_FORCE) { + if (args->verbose == f_VERBOSE) + printf("%llx: %s: protected, skipping write\n", + __poffset, full_name); + return 0; + } + + if (entry->actual < st.st_size) { + if (__ffs_entry_truncate(__ffs, full_name, + st.st_size) < 0) { + ERRNO(errno); + return -1; + } + + if (args->verbose == f_VERBOSE) + printf("%llx: %s: truncate size '%llx'\n", + __poffset, full_name, st.st_size); + } + + list_iter_init(&it, &list, LI_FLAG_FWD); + list_for_each(&it, node, node) { + if (node->entry.base == entry->base && + node->entry.size == entry->size) + return 0; + } + + node = (ffs_entry_node_t *)malloc(sizeof(*node)); + assert(node != NULL); + + memcpy(&node->entry, entry, sizeof(node->entry)); + list_add_tail(&list, &node->node); + + size_t block_size = __ffs->hdr->block_size; + size_t entry_size = entry->size * block_size; + size_t data_size = st.st_size; + off_t data_offset = 0; + + if (entry_size < data_size) { + UNEXPECTED("'%s' of size '%x' too big for partition " + "'%s' of size '%x'", args->path, data_size, + args->name, entry_size); + return -1; + } + + if (args->block != NULL) { + if (parse_size(args->block, &block_size) < 0) + return -1; + if (block_size & (__ffs->hdr->block_size-1)) { + UNEXPECTED("'%x' block size must be multiple " + "of target block size '%x'", + block_size, __ffs->hdr->block_size); + return -1; + } + } + + if (__ffs_buffer(__ffs, block_size) < 0) + return -1; + + RAII(void*, block, malloc(block_size), free); + if (block == NULL) { + ERRNO(errno); + return -1; + } + + if (fseeko(in, 0, SEEK_SET) != 0) { + ERRNO(errno); + return -1; + } + + while (0 < data_size) { + ssize_t rc = fread(block, 1, + min(block_size, data_size), in); + if (rc <= 0 && ferror(in)) { + ERRNO(errno); + return -1; + } + + rc = __ffs_entry_write(__ffs, full_name, block, + data_offset, rc); + if (rc < 0) + return -1; + + __ffs_fsync(__ffs); + + data_offset += rc; + data_size -= rc; + } + + if (args->verbose == f_VERBOSE) + printf("%llx: %s: read '%llx' bytes from file '%s'\n", + __poffset, full_name, st.st_size, args->path); + + return 0; + } + + int write(args_t * args, off_t poffset) + { + __poffset = poffset; + + const char * target = args->target; + int debug = args->debug; + + RAII(FILE*, file, fopen_generic(target, "r+", debug), fclose); + if (file == NULL) + return -1; + RAII(ffs_t*, ffs, __ffs_fopen(file, poffset), __ffs_fclose); + if (ffs == NULL) + return -1; + + __ffs = ffs; + + int rc = __ffs_iterate_entries(ffs, write_entry); + if (rc == 1) + rc = -1; + + return rc; + } + + /* ========================= */ + + if (stat(args->path, &st) < 0) { + ERRNO(errno); + return -1; + } + + if (regcomp(&rx, name, REG_ICASE | REG_NOSUB) != 0) { + ERRNO(errno); + return -1; + } + + int rc = command(args, write); + + regfree(&rx); + + while (!list_empty(&list)) + free(container_of(list_remove_head(&list), + ffs_entry_node_t, node)); + + return rc; +} diff --git a/fpart/src/command.c b/fpart/src/command.c new file mode 100644 index 0000000..9f53bde --- /dev/null +++ b/fpart/src/command.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: command.c + * Author: Shaun Wetzstein + * Descr: generic -- wrapper + * Date: 01/30/2013 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +//#include // --target aa: +//#include // --target rw: + +#include "main.h" + +int parse_offset(const char *str, off_t *offset) +{ + assert(offset != NULL); + + if (str == NULL) { + *offset = 0; + return 0; + } + + char *end = NULL; + + errno = 0; + *offset = strtoull(str, &end, 0); + if (errno != 0) { + ERRNO(errno); + return -1; + } + + if (*end != '\0') { + if (!strcmp(end, "KiB") || + !strcasecmp(end, "KB") || + !strcasecmp(end, "K")) + *offset <<= 10; + else if (!strcmp(end, "MiB") || + !strcasecmp(end, "MB") || + !strcasecmp(end, "M")) + *offset <<= 20; + else if (!strcmp(end, "GiB") || + !strcasecmp(end, "GB") || + !strcasecmp(end, "G")) + *offset <<= 30; + else { + UNEXPECTED("invalid offset specified '%s'", end); + return -1; + } + } + + return 0; +} + +int parse_size(const char *str, size_t *size) +{ + assert(size != NULL); + + if (str == NULL) { + *size = 0; + return 0; + } + + char *end = NULL; + + errno = 0; + *size = strtoul(str, &end, 0); + if (errno != 0) { + ERRNO(errno); + return -1; + } + + if (*end != '\0') { + if (!strcmp(end, "KiB") || !strcasecmp(end, "K") || + !strcasecmp(end, "KB")) + *size <<= 10; + else if (!strcmp(end, "MiB") || !strcasecmp(end, "M") || + !strcasecmp(end, "MB")) + *size <<= 20; + else if (!strcmp(end, "GiB") || !strcasecmp(end, "G") || + !strcasecmp(end, "GB")) + *size <<= 30; + else { + UNEXPECTED("invalid size specified '%s'", end); + return -1; + } + } + + return 0; +} + +int parse_number(const char *str, size_t *num) +{ + assert(num != NULL); + + if (str == NULL) { + *num = 0; + return 0; + } + + char *end = NULL; + + errno = 0; + *num = strtoul(str, &end, 0); + if (errno != 0) { + ERRNO(errno); + return -1; + } + + if (*end != '\0') { + UNEXPECTED("invalid number specified '%s'", end); + return -1; + } + + return 0; +} + +bool check_extension(const char *path, const char *ext) +{ + int len = strlen(path), ext_len = strlen(ext); + return (ext_len < len) + && (strncasecmp(path + len - ext_len, ext, ext_len) == 0); +} + +int create_regular_file(const char *path, size_t size, char pad) +{ + assert(path != NULL); + + RAII(FILE*, file, fopen(path, "w"), fclose); + if (file == NULL) { + ERRNO(errno); + return -1; + } + + if (ftruncate(fileno(file), size) < 0) { + ERRNO(errno); + return -1; + } + + uint32_t page_size = sysconf(_SC_PAGESIZE); + char buf[page_size]; + memset(buf, pad, page_size); + + while (0 < size) { + ssize_t rc = fwrite(buf, 1, min(sizeof(buf), size), file); + if (rc <= 0 && ferror(file)) { + ERRNO(errno); + return -1; + } + + size -= rc; + } + + return 0; +} + +int command(args_t * args, int (*cmd)(args_t *, off_t)) +{ + assert(args != NULL); + assert(cmd != NULL); + int rc = 0; + + char * end = (char *)args->poffset; + while (rc == 0 && end != NULL && *end != '\0') { + errno = 0; + off_t poffset = strtoull(end, &end, 0); + if (end == NULL || errno != 0) { + UNEXPECTED("invalid --partition-offset specified '%s'", + args->poffset); + return -1; + } + + if (*end != ',' && *end != ':' && *end != '\0') { + UNEXPECTED("invalid --partition-offset separator " + "character '%c'", *end); + return -1; + } + + if (cmd != NULL) + rc = cmd(args, poffset); + + if (*end == '\0') + break; + end++; + } + + return rc; +} + +FILE *fopen_generic(const char *path, const char *mode, int debug) +{ + assert(path != NULL); + assert(mode != NULL); + + FILE *file = NULL; + size_t port = 0; + + if (strncasecmp(path, "aa:", 3) == 0) { + if (parse_number(path + 3, &port) == 0) + assert(0); + //file = fopen_aaflash(port, mode, debug); + } else if (strncasecmp(path, "rw:", 3) == 0) { + assert(0); + //file = fopen_rwflash(path + 3, mode, debug); + } else { + file = fopen(path, mode); + } + + if (file == NULL) + ERRNO(errno); + + return file; +} + +int verify_operation(const char * name, ffs_t * in, ffs_entry_t * in_e, + ffs_t * out, ffs_entry_t * out_e) +{ + if (in->hdr->block_size != out->hdr->block_size) { + UNEXPECTED("block sizes differ '%x' != '%x'", + in->hdr->block_size, out->hdr->block_size); + return -1; + } + + if (in->hdr->block_count != out->hdr->block_count) { + UNEXPECTED("block counts differ '%x' != '%x'", + in->hdr->block_size, out->hdr->block_size); + return -1; + } + + if (in->hdr->entry_size != out->hdr->entry_size) { + UNEXPECTED("entry sizes differ '%x' != '%x'", + in->hdr->entry_size, out->hdr->entry_size); + return -1; + } + + if (in->hdr->entry_count != out->hdr->entry_count) { + UNEXPECTED("entry counts differ '%x' != '%x'", + in->hdr->entry_size, out->hdr->entry_size); + return -1; + } + + if (in_e->base != out_e->base) { + UNEXPECTED("partition '%s' offsets differ '%x' != '%x'", + name, in_e->base, out_e->base); + return -1; + } + + if (in_e->size != out_e->size) { + UNEXPECTED("partition '%s' sizes differ '%x' != '%x'", + name, in_e->size, out_e->size); + return -1; + } + + return 0; +} + diff --git a/fpart/src/main.c b/fpart/src/main.c new file mode 100644 index 0000000..a90b5f5 --- /dev/null +++ b/fpart/src/main.c @@ -0,0 +1,597 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: main.c + * Author: Shaun Wetzstein + * Descr: FFS partition tool + * Date: 05/12/2012 + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "main.h" + +args_t args; +size_t page_size; + +static const char copyright[] __unused__ = + "Licensed Internal Code - Property of IBM\n" + "IBM Support Processor Licensed Internal Code\n" + "(c) Copyright IBM Corp 2012 All Rights Reserved\n" + "US Government Users Restricted Rights - Use, duplication\n" + "or disclosure restricted by GSA ADP Schedule Contract\n" + "with IBM Corp."; + +static void usage(bool verbose) +{ + FILE *e = stderr; + + fprintf(e, + "fpart - FFS Partition Tool v%d.%d.%d -- Authors: " + "\n (c) Copyright IBM Corp 2012 All Rights " + "Reserved\n", FPART_MAJOR, FPART_MINOR, FPART_PATCH); + + fprintf(e, "\nUsage:\n"); + fprintf(e, " fpart ...\n"); + + fprintf(e, "\nExamples:\n"); + fprintf(e, " fpart -C -t nor -s 64MiB -b 64k -p 0x3f0000,0x7f0000\n"); + fprintf(e, " fpart -A -t nor -p 0x3f0000,0x7f0000 -s 1Mb -o 0M -g 0 " + "-n boot0 -l\n"); + fprintf(e, " fpart --add --target nor --size 1mb --offset 1MiB " + "--flags 0x0 --name boot0/ipl\n"); + fprintf(e, " fpart --delete --target nor nor --name boot0/ipl\n"); + fprintf(e, " fpart --target nor --write ipl.bin --name boot1/ipl\n"); + fprintf(e, " fpart --user 0 -t nor -n boot0/ipl --value 0xFF500FF5\n"); + fprintf(e, " fpart --copy new_nor -t nor -n ipl\n"); + fprintf(e, " fpart --compare new_nor -t nor -n bank0\n"); + + fprintf(e, "\nCommands:\n"); + fprintf(e, " -C, --create [options]\n"); + if (verbose) + fprintf(e, "\n Create an empty partition table at each" + " specified partition offest.\n\n"); + + fprintf(e, " -A, --add [options]\n"); + if (verbose) + fprintf(e, "\n Add partition entry(s) to each specified" + " partition offset.\n\n"); + + fprintf(e, " -D, --delete [options]\n"); + if (verbose) + fprintf(e, "\n Delete partition entry(s) from each specified" + " partition offset.\n\n"); + + fprintf(e, " -E, --erase [options]\n"); + if (verbose) + fprintf(e, "\n Fill partition entry(s) from each specified" + " partition offset.\n\n"); + + fprintf(e, " -L, --list [options]\n"); + if (verbose) + fprintf(e, "\n Display the list of matching partition" + " entry(s) of each specified\n" + " partition offset.\n\n"); + + fprintf(e, " -T, --trunc [options]\n"); + if (verbose) + fprintf(e, "\n Truncate the actual data size of matching" + " partition entry(s) of each\n specified partition" + " offset.\n\n"); + + fprintf(e, " -U, --user [options]\n"); + if (verbose) + fprintf(e, "\n Read or write user words of matching partition" + " entry(s) for each\n specified partition offset.\n"); + + /* =============================== */ + + fprintf(e, "\nOptions:\n"); + fprintf(e, " -p, --partition-offset \n"); + if (verbose) + fprintf(e, "\n Specifies a comma (,) separated list of" + " partition table offsets, in\n bytes from the start" + " of the target file (or device).\n\n"); + + fprintf(e, " -t, --target \n"); + if (verbose) { + fprintf(e, "\n Specifies the target of the operation."); + fprintf(e, " can be one of:\n"); + fprintf(e, "\t : Aardvard USB port number [0..9]\n"); + fprintf(e, "\t : RISCWatch probe hostname\n"); + fprintf(e, "\t : SFC device file, e.g. " + "/dev/mtdblock/sfc.of\n\n"); + } + + fprintf(e, " -n, --name \n"); + if (verbose) + fprintf(e, "\n Specifies the name of a partition entry(s)." + " For some commands\n (--hexdump, --add)" + " must be a fully qualified name.\n For other" + " commands, name can specify a regular " + "expression.\n\n"); + + fprintf(e, " -o, --offset \n"); + if (verbose) + fprintf(e, "\n Specifies the offset of a partition entry," + " in bytes from the beginning\n of the target file" + " (or device).\n\n"); + + fprintf(e, " -s, --size \n"); + if (verbose) + fprintf(e, "\n Specifies the size of a partition entry (in" + " bytes).\n is a decimal (or hex) number" + " (optionally) followed by 'k', 'K',\n 'KB' or 'KiB'" + " to specify.\n\n"); + + fprintf(e, " -b, --block-size \n"); + if (verbose) + fprintf(e, "\n Specifies the block size, in bytes. " + " is a decimal (or hex)\n number (optionally)" + " followed by 'k', 'K', 'KB' or 'KiB' to" + " specify.\n\n"); + + fprintf(e, " -u, --value \n"); + if (verbose) + fprintf(e, "\n Specifies the user word value. is a" + " decimal (or hex) number.\n\n"); + + fprintf(e, " -g, --flags \n"); + if (verbose) + fprintf(e, "\n Specifies the partition flags value." + " is a decimal (or hex)\n number.\n\n"); + + fprintf(e, " -a, --pad \n"); + if (verbose) + fprintf(e, + "\n Specifies the partition content initial value." + " is a decimal\n (or hex) number, default is" + " 0xFF.\n\n"); + + /* =============================== */ + + fprintf(e, "\nFlags:\n"); + fprintf(e, " -f, --force\n"); + if (verbose) + fprintf(e, "\n Override command safe guards.\n\n"); + + fprintf(e, " -l, --logical\n"); + if (verbose) + fprintf(e, "\n Specifies the partition entry is a logical" + " partition instead of a\n data partition.\n\n"); + + fprintf(e, " -v, --verbose\n"); + if (verbose) + fprintf(e, "\n Write progress messages to stdout\n\n"); + + fprintf(e, " -d, --debug\n"); + if (verbose) + fprintf(e, "\n Write debug messages to stdout\n\n"); + + fprintf(e, " -h, --help\n"); + if (verbose) + fprintf(e, "\n Write this help text to stdout and exit\n"); + + fprintf(e, "\n"); + + fprintf(e, "Report bugs to " + " (vendor='MCP for FSP*')\n"); +} + +static int process_argument(args_t * args, int opt, const char *optarg) +{ + assert(args != NULL); + + switch (opt) { + case c_CREATE: /* create */ + case c_ADD: /* add */ + case c_DELETE: /* delete */ + case c_LIST: /* list */ + case c_TRUNC: /* trunc */ + case c_ERASE: /* erase */ + case c_USER: /* user */ + if (args->cmd != c_ERROR) { + UNEXPECTED("commands '%c' and '%c' are mutually " + "exclusive", args->cmd, opt); + return -1; + } + + args->cmd = (cmd_t) opt; + if (args->cmd == c_USER) + args->user = strdup(optarg); + break; + case o_POFFSET: /* partition-offset */ + args->poffset = strdup(optarg); + break; + case o_TARGET: /* target */ + args->target = strdup(optarg); + break; + case o_NAME: /* name */ + args->name = strdup(optarg); + break; + case o_OFFSET: /* offset */ + args->offset = strdup(optarg); + break; + case o_SIZE: /* size */ + args->size = strdup(optarg); + break; + case o_BLOCK: /* block */ + args->block = strdup(optarg); + break; + case o_VALUE: /* value */ + args->value = strdup(optarg); + break; + case o_FLAGS: /* flags */ + args->flags = strdup(optarg); + break; + case o_PAD: /* pad */ + args->pad = strdup(optarg); + break; + case f_FORCE: /* force */ + args->force = (flag_t) opt; + break; + case f_LOGICAL: /* logical */ + args->logical = (flag_t) opt; + break; + case f_VERBOSE: /* verbose */ + args->verbose = (flag_t) opt; + break; + case f_DEBUG: /* debug */ + args->debug = (flag_t) opt; + break; + case f_HELP: /* help */ + usage(true); + exit(EXIT_SUCCESS); + break; + case '?': /* error */ + default: + usage(false); + UNEXPECTED("unknown option '%c', please see --help for " + "details\n", opt); + return -1; + } + + return 0; +} + +static int process_option(args_t * args, const char *opt) +{ + assert(args != NULL); + assert(opt != NULL); + + if (args->opt_sz <= args->opt_nr) { + size_t size = sizeof(*args->opt); + + args->opt_sz += 5; + args->opt = (const char **)realloc(args->opt, + size * args->opt_sz); + if (args->opt == NULL) { + ERRNO(errno); + return -1; + } + + memset(args->opt + args->opt_nr, 0, + size * (args->opt_sz - args->opt_nr)); + } + + args->opt[args->opt_nr] = strdup(opt); + args->opt_nr++; + + return 0; +} + +static int validate_args(args_t * args) +{ + assert(args != NULL); + + if (args->cmd == c_ERROR) { + usage(false); + UNEXPECTED("no command specified, please " + "see --help for details\n"); + return -1; + } + + if (args->poffset == NULL) { + UNEXPECTED("--partition-offset is required for all '%s' " + "commands", args->short_name); + return -1; + } + + if (args->target == NULL) { + UNEXPECTED("--target is required for all '%s' commands", + args->short_name); + return -1; + } + + #define REQUIRED(name,cmd) ({ \ + if (args->name == NULL) { \ + UNEXPECTED("--%s is required for the --%s command", \ + #name, #cmd); \ + return -1; \ + } \ + }) + + #define UNSUPPORTED(name,cmd) ({ \ + if (args->name != NULL) { \ + UNEXPECTED("--%s is unsupported for the --%s command", \ + #name, #cmd); \ + return -1; \ + } \ + }) + + if (args->cmd == c_CREATE) { + REQUIRED(size, create); + REQUIRED(block, create); + REQUIRED(target, create); + + UNSUPPORTED(offset, create); + UNSUPPORTED(flags, create); + UNSUPPORTED(value, create); + UNSUPPORTED(pad, create); + } else if (args->cmd == c_ADD) { + REQUIRED(name, add); + REQUIRED(flags, add); + + if (args->logical != f_LOGICAL) { + REQUIRED(size, add); + REQUIRED(offset, add); + } + + UNSUPPORTED(block, add); + UNSUPPORTED(value, add); + } else if (args->cmd == c_DELETE) { + REQUIRED(name, delete); + + UNSUPPORTED(size, delete); + UNSUPPORTED(offset, delete); + UNSUPPORTED(block, delete); + UNSUPPORTED(flags, delete); + UNSUPPORTED(value, delete); + UNSUPPORTED(pad, delete); + } else if (args->cmd == c_LIST) { + UNSUPPORTED(size, list); + UNSUPPORTED(offset, list); + UNSUPPORTED(block, list); + UNSUPPORTED(flags, list); + UNSUPPORTED(value, list); + UNSUPPORTED(pad, list); + } else if (args->cmd == c_TRUNC) { + REQUIRED(name, trunc); + + UNSUPPORTED(offset, trunc); + UNSUPPORTED(block, trunc); + UNSUPPORTED(flags, trunc); + UNSUPPORTED(value, trunc); + UNSUPPORTED(pad, trunc); + } else if (args->cmd == c_ERASE) { + REQUIRED(name, erase); + + UNSUPPORTED(size, erase); + UNSUPPORTED(offset, erase); + UNSUPPORTED(flags, erase); + UNSUPPORTED(value, erase); + } else if (args->cmd == c_USER) { + REQUIRED(name, user); + + UNSUPPORTED(size, user); + UNSUPPORTED(offset, user); + UNSUPPORTED(block, user); + UNSUPPORTED(flags, user); + UNSUPPORTED(pad, user); + } else { + UNEXPECTED("invalid command '%c'", args->cmd); + return -1; + } + + return 0; +} + +static int process_args(args_t * args) +{ + assert(args != NULL); + int rc = 0; + + switch (args->cmd) { + case c_CREATE: + rc = command_create(args); + break; + case c_ADD: + rc = command_add(args); + if (rc < 0) + break; + if (args->pad != NULL) + rc = command_erase(args); + break; + case c_DELETE: + rc = command_delete(args); + break; + case c_LIST: + rc = command_list(args); + break; + case c_TRUNC: + rc = command_trunc(args); + break; + case c_ERASE: + rc = command_erase(args); + break; + case c_USER: + rc = command_user(args); + break; + default: + UNEXPECTED("NOT IMPLEMENTED YET => '%c'", args->cmd); + rc = -1; + } + + return rc; +} + +static void args_dump(args_t * args) +{ + assert(args != NULL); + + if (args->short_name != NULL) + printf("short_name[%s]\n", args->short_name); + if (args->cmd != 0) + printf("cmd[%c]\n", args->cmd); + if (args->path != NULL) + printf("path[%s]\n", args->path); + if (args->target != NULL) + printf("target[%s]\n", args->target); + if (args->poffset != NULL) + printf("poffset[%s]\n", args->poffset); + if (args->name != NULL) + printf("name[%s]\n", args->name); + if (args->offset != NULL) + printf("offset[%s]\n", args->offset); + if (args->size != NULL) + printf("size[%s]\n", args->size); + if (args->block != NULL) + printf("block[%s]\n", args->block); + if (args->flags != NULL) + printf("flags[%s]\n", args->flags); + if (args->value != NULL) + printf("value[%s]\n", args->value); + if (args->pad != NULL) + printf("pad[%s]\n", args->pad); + for (int i = 0; i < args->opt_nr; i++) { + if (args->opt[i] != NULL) + printf("opt%d[%s]\n", i, args->opt[i]); + } + if (args->force != 0) + printf("force[%c]\n", args->force); + if (args->protected != 0) + printf("protected[%c]\n", args->protected); + if (args->logical != 0) + printf("logical[%c]\n", args->logical); + if (args->verbose != 0) + printf("verbose[%c]\n", args->verbose); + if (args->verbose != 0) + printf("debug[%c]\n", args->debug); +} + +int main(int argc, char *argv[]) +{ + static const struct option long_opt[] = { + /* commands */ + {"create", no_argument, NULL, c_CREATE}, + {"add", no_argument, NULL, c_ADD}, + {"delete", no_argument, NULL, c_DELETE}, + {"list", no_argument, NULL, c_LIST}, + {"trunc", no_argument, NULL, c_TRUNC}, + {"erase", no_argument, NULL, c_ERASE}, + {"user", required_argument, NULL, c_USER}, + /* options */ + {"partition-offset", required_argument, NULL, o_POFFSET}, + {"target", required_argument, NULL, o_TARGET}, + {"name", required_argument, NULL, o_NAME}, + {"offset", required_argument, NULL, o_OFFSET}, + {"size", required_argument, NULL, o_SIZE}, + {"block-size", required_argument, NULL, o_BLOCK}, + {"value", required_argument, NULL, o_VALUE}, + {"flags", required_argument, NULL, o_FLAGS}, + {"pad", required_argument, NULL, o_PAD}, + /* flags */ + {"force", no_argument, NULL, f_FORCE}, + {"protected", no_argument, NULL, f_PROTECTED}, + {"logical", no_argument, NULL, f_LOGICAL}, + {"verbose", no_argument, NULL, f_VERBOSE}, + {"debug", no_argument, NULL, f_DEBUG}, + {"help", no_argument, NULL, f_HELP}, + {0, 0, 0, 0} + }; + + static const char *short_opt; + short_opt = "CADLTEU:p:t:n:o:s:b:u:g:a:frlvdh"; + + int rc = EXIT_FAILURE; + + setlinebuf(stdout); + + if (argc == 1) + usage(false), exit(rc); + + int opt = 0, idx = 0; + while ((opt = getopt_long(argc, argv, short_opt, long_opt, &idx)) != -1) + if (process_argument(&args, opt, optarg) < 0) + goto error; + + /* getopt_long doesn't know what to do with orphans, */ + /* so we'll scoop them up here, and deal with them later */ + + while (optind < argc) + if (process_option(&args, argv[optind++]) < 0) + goto error; + + if (args.debug == f_DEBUG) + args_dump(&args); + + if (validate_args(&args) < 0) + goto error; + if (process_args(&args) < 0) + goto error; + + rc = EXIT_SUCCESS; + + if (false) { + err_t *err; +error: + err = err_get(); + assert(err != NULL); + + fprintf(stderr, "%s: %s : %s(%d) : (code=%d) %.*s\n", + program_invocation_short_name, + err_type_name(err), err_file(err), err_line(err), + err_code(err), err_size(err), (char *)err_data(err)); + } + + return rc; +} + +static void __ctor__(void) __constructor; +static void __ctor__(void) +{ + page_size = sysconf(_SC_PAGESIZE); + + args.short_name = program_invocation_short_name; + args.poffset = "0x3F0000,0x7F0000"; + args.target = "/dev/mtdblock/sfc.of"; + args.name = ".*"; +} diff --git a/fpart/src/main.h b/fpart/src/main.h new file mode 100644 index 0000000..eeb3b39 --- /dev/null +++ b/fpart/src/main.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * File: main.h + * Author: Shaun Wetzstein + * Descr: cmdline tool for libffs.so + * Date: 05/12/2012 + */ + +#ifndef __MAIN_H__ +#define __MAIN_H__ + +#include + +#include + +#define FPART_MAJOR 0x01 +#define FPART_MINOR 0x00 +#define FPART_PATCH 0x00 + +typedef enum { + c_ERROR = 0, + c_CREATE = 'C', + c_ADD = 'A', + c_DELETE = 'D', + c_ERASE = 'E', + c_LIST = 'L', + c_TRUNC = 'T', + c_USER = 'U', +} cmd_t; + +typedef enum { + o_ERROR = 0, + o_POFFSET = 'p', + o_TARGET = 't', + o_NAME = 'n', + o_OFFSET = 'o', + o_SIZE = 's', + o_BLOCK = 'b', + o_VALUE = 'u', + o_FLAGS = 'g', + o_PAD = 'a', +} option_t; + +typedef enum { + f_ERROR = 0, + f_FORCE = 'f', + f_PROTECTED = 'r', + f_LOGICAL = 'l', + f_VERBOSE = 'v', + f_DEBUG = 'd', + f_HELP = 'h', +} flag_t; + +typedef struct { + const char *short_name; + + /* target */ + const char *path; + + /* command */ + cmd_t cmd; + + /* options */ + const char *name, *target; + const char *offset, *poffset; + const char *size, *block; + const char *user, *value; + const char *flags, *pad; + + /* flags */ + flag_t force, logical; + flag_t verbose, debug; + flag_t protected; + + const char **opt; + int opt_sz, opt_nr; +} args_t; + +typedef struct ffs_entry_node ffs_entry_node_t; +struct ffs_entry_node { + list_node_t node; + ffs_entry_t entry; +}; + +extern args_t args; +extern size_t page_size; + +extern int parse_offset(const char *, off_t *); +extern int parse_size(const char *, size_t *); +extern int parse_number(const char *, size_t *); + +extern bool check_extension(const char *, const char *); +extern int create_regular_file(const char *, size_t, char); +extern FILE *fopen_generic(const char *, const char *, int); + +extern int command(args_t *, int (*)(args_t *, off_t)); +extern int verify_operation(const char *, ffs_t *, ffs_entry_t *, + ffs_t *, ffs_entry_t *); + +extern int command_create(args_t *); +extern int command_add(args_t *); +extern int command_delete(args_t *); +extern int command_list(args_t *); +extern int command_trunc(args_t *); +extern int command_erase(args_t *); +extern int command_user(args_t *); + +#endif /* __MAIN_H__ */ diff --git a/fpart/x86/Makefile b/fpart/x86/Makefile new file mode 100644 index 0000000..9ce2196 --- /dev/null +++ b/fpart/x86/Makefile @@ -0,0 +1,7 @@ +DEPTH = .. + +ARCH=x86 +FPART_INSTALL = $(INST_USR_X86) +FPART_INSTALL_TEST = $(INST_TESTS_X86) + +include ../Rules.mk -- cgit v1.2.1