From 2ba116dabddd03e7df56281675430e8d813633c05da3713a35a208b36cb868ac Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Sat, 5 Oct 2024 17:32:33 +0300 Subject: [PATCH 1/1] Initial revision --- README | 3 + cyac/.gitignore | 3 + cyac/COPYING | 674 +++++++++++++++++++++++++++ cyac/COPYING.LESSER | 165 +++++++ cyac/README | 27 ++ cyac/atoms.h | 122 +++++ cyac/clean | 4 + cyac/compile_flags.txt.do | 3 + cyac/conf/ar | 1 + cyac/conf/cc | 1 + cyac/conf/cflags | 9 + cyac/conf/ldflags | 2 + cyac/conf/prefix | 1 + cyac/dec.c | 233 ++++++++++ cyac/dec.h | 65 +++ cyac/dectai.c | 52 +++ cyac/dectai.h | 13 + cyac/default.o.do | 4 + cyac/enc.c | 268 +++++++++++ cyac/enc.h | 71 +++ cyac/enctai.c | 40 ++ cyac/enctai.h | 9 + cyac/example/.gitignore | 2 + cyac/example/all.do | 1 + cyac/example/clean | 3 + cyac/example/default.do | 6 + cyac/example/print-map.c | 258 +++++++++++ cyac/example/test-vector.c | 218 +++++++++ cyac/frombe.c | 16 + cyac/frombe.h | 10 + cyac/install.do | 6 + cyac/iter.c | 184 ++++++++ cyac/iter.h | 45 ++ cyac/leapsecs.c | 36 ++ cyac/leapsecs.h | 11 + cyac/libyac.a.do | 6 + cyac/o.list | 9 + cyac/tobe.c | 15 + cyac/tobe.h | 10 + cyac/utf8.c | 96 ++++ cyac/utf8.h | 12 + gyac/COPYING | 674 +++++++++++++++++++++++++++ gyac/COPYING.LESSER | 165 +++++++ gyac/README | 7 + gyac/atomtype_string.go | 163 +++++++ gyac/be.go | 29 ++ gyac/blob.go | 44 ++ gyac/cmd/certool/basic.t | 38 ++ gyac/cmd/certool/cer.go | 78 ++++ gyac/cmd/certool/go.mod | 16 + gyac/cmd/certool/go.sum | 10 + gyac/cmd/certool/main.go | 265 +++++++++++ gyac/cmd/certool/prv.go | 56 +++ gyac/cmd/print/main.go | 26 ++ gyac/cmd/test-vector-anys/main.go | 81 ++++ gyac/cmd/test-vector-manual/main.go | 184 ++++++++ gyac/dec.go | 382 ++++++++++++++++ gyac/enc.go | 333 ++++++++++++++ gyac/go.mod | 10 + gyac/go.sum | 6 + gyac/itemtype_string.go | 36 ++ gyac/reflect.go | 240 ++++++++++ gyac/sort.go | 21 + pyac/COPYING | 674 +++++++++++++++++++++++++++ pyac/COPYING.LESSER | 165 +++++++ pyac/README | 6 + pyac/pyac.py | 676 ++++++++++++++++++++++++++++ pyac/test-vector.py | 50 ++ spec/.gitignore | 1 + spec/THANKS | 1 + spec/encoding/blob.texi | 27 ++ spec/encoding/cont.texi | 31 ++ spec/encoding/float.texi | 13 + spec/encoding/index.texi | 70 +++ spec/encoding/int.texi | 52 +++ spec/encoding/prim.texi | 15 + spec/encoding/str.texi | 46 ++ spec/encoding/table.texi | 264 +++++++++++ spec/encoding/tai64.texi | 30 ++ spec/encoding/uuid.texi | 16 + spec/format/cer.texi | 65 +++ spec/format/hashed.texi | 25 + spec/format/index.texi | 9 + spec/format/prv.texi | 16 + spec/format/signed.texi | 109 +++++ spec/index.texi | 83 ++++ spec/mk-info | 7 + spec/registry.texi | 76 ++++ spec/schema.texi | 35 ++ tyac/COPYING | 674 +++++++++++++++++++++++++++ tyac/COPYING.LESSER | 165 +++++++ tyac/README | 5 + tyac/test-vector.tcl | 64 +++ tyac/tyac.tcl | 224 +++++++++ 94 files changed, 9272 insertions(+) create mode 100644 README create mode 100644 cyac/.gitignore create mode 100644 cyac/COPYING create mode 100644 cyac/COPYING.LESSER create mode 100644 cyac/README create mode 100644 cyac/atoms.h create mode 100755 cyac/clean create mode 100644 cyac/compile_flags.txt.do create mode 100644 cyac/conf/ar create mode 100644 cyac/conf/cc create mode 100644 cyac/conf/cflags create mode 100644 cyac/conf/ldflags create mode 100644 cyac/conf/prefix create mode 100644 cyac/dec.c create mode 100644 cyac/dec.h create mode 100644 cyac/dectai.c create mode 100644 cyac/dectai.h create mode 100644 cyac/default.o.do create mode 100644 cyac/enc.c create mode 100644 cyac/enc.h create mode 100644 cyac/enctai.c create mode 100644 cyac/enctai.h create mode 100644 cyac/example/.gitignore create mode 100644 cyac/example/all.do create mode 100755 cyac/example/clean create mode 100644 cyac/example/default.do create mode 100644 cyac/example/print-map.c create mode 100644 cyac/example/test-vector.c create mode 100644 cyac/frombe.c create mode 100644 cyac/frombe.h create mode 100644 cyac/install.do create mode 100644 cyac/iter.c create mode 100644 cyac/iter.h create mode 100644 cyac/leapsecs.c create mode 100644 cyac/leapsecs.h create mode 100644 cyac/libyac.a.do create mode 100644 cyac/o.list create mode 100644 cyac/tobe.c create mode 100644 cyac/tobe.h create mode 100644 cyac/utf8.c create mode 100644 cyac/utf8.h create mode 100644 gyac/COPYING create mode 100644 gyac/COPYING.LESSER create mode 100644 gyac/README create mode 100644 gyac/atomtype_string.go create mode 100644 gyac/be.go create mode 100644 gyac/blob.go create mode 100755 gyac/cmd/certool/basic.t create mode 100644 gyac/cmd/certool/cer.go create mode 100644 gyac/cmd/certool/go.mod create mode 100644 gyac/cmd/certool/go.sum create mode 100644 gyac/cmd/certool/main.go create mode 100644 gyac/cmd/certool/prv.go create mode 100644 gyac/cmd/print/main.go create mode 100644 gyac/cmd/test-vector-anys/main.go create mode 100644 gyac/cmd/test-vector-manual/main.go create mode 100644 gyac/dec.go create mode 100644 gyac/enc.go create mode 100644 gyac/go.mod create mode 100644 gyac/go.sum create mode 100644 gyac/itemtype_string.go create mode 100644 gyac/reflect.go create mode 100644 gyac/sort.go create mode 100644 pyac/COPYING create mode 100644 pyac/COPYING.LESSER create mode 100644 pyac/README create mode 100644 pyac/pyac.py create mode 100644 pyac/test-vector.py create mode 100644 spec/.gitignore create mode 100644 spec/THANKS create mode 100644 spec/encoding/blob.texi create mode 100644 spec/encoding/cont.texi create mode 100644 spec/encoding/float.texi create mode 100644 spec/encoding/index.texi create mode 100644 spec/encoding/int.texi create mode 100644 spec/encoding/prim.texi create mode 100644 spec/encoding/str.texi create mode 100644 spec/encoding/table.texi create mode 100644 spec/encoding/tai64.texi create mode 100644 spec/encoding/uuid.texi create mode 100644 spec/format/cer.texi create mode 100644 spec/format/hashed.texi create mode 100644 spec/format/index.texi create mode 100644 spec/format/prv.texi create mode 100644 spec/format/signed.texi create mode 100644 spec/index.texi create mode 100755 spec/mk-info create mode 100644 spec/registry.texi create mode 100644 spec/schema.texi create mode 100644 tyac/COPYING create mode 100644 tyac/COPYING.LESSER create mode 100644 tyac/README create mode 100644 tyac/test-vector.tcl create mode 100644 tyac/tyac.tcl diff --git a/README b/README new file mode 100644 index 0000000..fbaf228 --- /dev/null +++ b/README @@ -0,0 +1,3 @@ +Yet Another Codec. +Yet another format for binary reprensentation of structured data. +Its implementations are not heavily tested, project was just created. diff --git a/cyac/.gitignore b/cyac/.gitignore new file mode 100644 index 0000000..a0169a4 --- /dev/null +++ b/cyac/.gitignore @@ -0,0 +1,3 @@ +/*.o +/compile_flags.txt +/libyac.a diff --git a/cyac/COPYING b/cyac/COPYING new file mode 100644 index 0000000..58f2b48 --- /dev/null +++ b/cyac/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/cyac/COPYING.LESSER b/cyac/COPYING.LESSER new file mode 100644 index 0000000..2d9b9aa --- /dev/null +++ b/cyac/COPYING.LESSER @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/cyac/README b/cyac/README new file mode 100644 index 0000000..41c7d66 --- /dev/null +++ b/cyac/README @@ -0,0 +1,27 @@ +C99 implementation of the YAC codec. + +* No FLOAT*, INTs greater than 64-bit, TAI64NA and nanoseconds support. + They are stored/decoded just as a raw value +* Actual negative integer maximal size is -2^63-1 + +dec.* contains the actual decoder, YACAtomDecode. Basically that is all +you need in most cases. YACItemType is high-level type of the atom you +decoded. YACErr tells about the possible decoding error. YACAtom is the +decoded atom. + +dectai.* contains converter from TAI64 to UTC. +leapsecs.* contains the leap seconds database itself. +iter.* contains helpers that may pass over the iterables. + +enc.* contains encoders for various atoms. Containers and blobs must +be made manually, by finishing them with proper EOC/BIN and sorting the +keys. enctai.* contains converter from UTC to TAI64. + +example/test-vector.c is the same structure as in tyac/test-vector.tcl. +Their outputs must be the same. + +Project uses redo (http://cr.yp.to/redo.html) build system, but it is +trivial to compile and install manually. conf/* contains the compiler +and linker configuration options. + +cyac is free software: see the file COPYING.LESSER for copying conditions. diff --git a/cyac/atoms.h b/cyac/atoms.h new file mode 100644 index 0000000..a2d87bb --- /dev/null +++ b/cyac/atoms.h @@ -0,0 +1,122 @@ +#ifndef YAC_ATOMS_H +#define YAC_ATOMS_H + +enum YACAtomType { + YACAtomEOC = 0x00, + YACAtomNIL = 0x01, + YACAtomFalse = 0x02, + YACAtomTrue = 0x03, + YACAtomUUID = 0x04, + YACAtomList = 0x08, + YACAtomMap = 0x09, + YACAtomBlob = 0x0B, + YACAtomFloat16 = 0x10, + YACAtomFloat32 = 0x11, + YACAtomFloat64 = 0x12, + YACAtomFloat128 = 0x13, + YACAtomFloat256 = 0x14, + YACAtomTAI64 = 0x18, + YACAtomTAI64N = 0x19, + YACAtomTAI64NA = 0x1a, + YACAtomIntPos1 = 0x20, + YACAtomIntPos2 = 0x21, + YACAtomIntPos3 = 0x22, + YACAtomIntPos4 = 0x23, + YACAtomIntPos5 = 0x24, + YACAtomIntPos6 = 0x25, + YACAtomIntPos7 = 0x26, + YACAtomIntPos8 = 0x27, + YACAtomIntPos9 = 0x28, + YACAtomIntPos10 = 0x29, + YACAtomIntPos11 = 0x2A, + YACAtomIntPos12 = 0x2B, + YACAtomIntPos13 = 0x2C, + YACAtomIntPos14 = 0x2D, + YACAtomIntPos15 = 0x2E, + YACAtomIntPos16 = 0x2F, + YACAtomIntNeg1 = 0x30, + YACAtomIntNeg2 = 0x31, + YACAtomIntNeg3 = 0x32, + YACAtomIntNeg4 = 0x33, + YACAtomIntNeg5 = 0x34, + YACAtomIntNeg6 = 0x35, + YACAtomIntNeg7 = 0x36, + YACAtomIntNeg8 = 0x37, + YACAtomIntNeg9 = 0x38, + YACAtomIntNeg10 = 0x39, + YACAtomIntNeg11 = 0x3A, + YACAtomIntNeg12 = 0x3B, + YACAtomIntNeg13 = 0x3C, + YACAtomIntNeg14 = 0x3D, + YACAtomIntNeg15 = 0x3E, + YACAtomIntNeg16 = 0x3F, + YACAtomInt0 = 0x40, + YACAtomInt1 = 0x41, + YACAtomInt2 = 0x42, + YACAtomInt3 = 0x43, + YACAtomInt4 = 0x44, + YACAtomInt5 = 0x45, + YACAtomInt6 = 0x46, + YACAtomInt7 = 0x47, + YACAtomInt8 = 0x48, + YACAtomInt9 = 0x49, + YACAtomInt10 = 0x4A, + YACAtomInt11 = 0x4B, + YACAtomInt12 = 0x4C, + YACAtomInt13 = 0x4D, + YACAtomInt14 = 0x4E, + YACAtomInt15 = 0x4F, + YACAtomInt16 = 0x50, + YACAtomInt17 = 0x51, + YACAtomInt18 = 0x52, + YACAtomInt19 = 0x53, + YACAtomInt20 = 0x54, + YACAtomInt21 = 0x55, + YACAtomInt22 = 0x56, + YACAtomInt23 = 0x57, + YACAtomInt24 = 0x58, + YACAtomInt25 = 0x59, + YACAtomInt26 = 0x5A, + YACAtomInt27 = 0x5B, + YACAtomInt28 = 0x5C, + YACAtomInt29 = 0x5D, + YACAtomInt30 = 0x5E, + YACAtomInt31 = 0x5F, + YACAtomIntN1 = 0x60, + YACAtomIntN2 = 0x61, + YACAtomIntN3 = 0x62, + YACAtomIntN4 = 0x63, + YACAtomIntN5 = 0x64, + YACAtomIntN6 = 0x65, + YACAtomIntN7 = 0x66, + YACAtomIntN8 = 0x67, + YACAtomIntN9 = 0x68, + YACAtomIntN10 = 0x69, + YACAtomIntN11 = 0x6A, + YACAtomIntN12 = 0x6B, + YACAtomIntN13 = 0x6C, + YACAtomIntN14 = 0x6D, + YACAtomIntN15 = 0x6E, + YACAtomIntN16 = 0x6F, + YACAtomIntN17 = 0x70, + YACAtomIntN18 = 0x71, + YACAtomIntN19 = 0x72, + YACAtomIntN20 = 0x73, + YACAtomIntN21 = 0x74, + YACAtomIntN22 = 0x75, + YACAtomIntN23 = 0x76, + YACAtomIntN24 = 0x77, + YACAtomIntN25 = 0x78, + YACAtomIntN26 = 0x79, + YACAtomIntN27 = 0x7A, + YACAtomIntN28 = 0x7B, + YACAtomIntN29 = 0x7C, + YACAtomIntN30 = 0x7D, + YACAtomIntN31 = 0x7E, + YACAtomIntN32 = 0x7F, + + YACAtomStrings = 0x80, + YACAtomIsUTF8 = 0x40, +}; + +#endif // YAC_ATOMS_H diff --git a/cyac/clean b/cyac/clean new file mode 100755 index 0000000..4ced8c3 --- /dev/null +++ b/cyac/clean @@ -0,0 +1,4 @@ +#!/bin/sh -e + +( cd example ; ./clean ) +exec rm -f *.o *.a compile_flags.txt diff --git a/cyac/compile_flags.txt.do b/cyac/compile_flags.txt.do new file mode 100644 index 0000000..1b9f125 --- /dev/null +++ b/cyac/compile_flags.txt.do @@ -0,0 +1,3 @@ +redo-ifchange conf/prefix +read PREFIX +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, version 3 of the License. +// +// 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this program. If not, see . + +#include +#include + +#include "atoms.h" +#include "dec.h" +#include "frombe.h" +#include "utf8.h" + +enum YACErr +YACAtomDecode(struct YACAtom *atom, const unsigned char *buf, const size_t len) +{ + atom->off = 1; + if (len < 1) { + return YACErrNotEnough; + } + atom->tag = buf[0]; + + if ((atom->tag & YACAtomStrings) > 0) { + atom->typ = ((atom->tag & YACAtomIsUTF8) == 0) ? YACItemBin : YACItemStr; + size_t l = atom->tag & 63; + size_t ll = 0; + switch (l) { + case 61: + ll = 1; + break; + case 62: + ll = 2; + break; + case 63: + ll = 8; + break; + } + if (ll != 0) { + atom->off += ll; + if (len < atom->off) { + return YACErrNotEnough; + } + l = yacFromBE(buf + 1, ll); + if ((l < 61) || ((l < (1 << 8)) && (ll > 1)) || + ((l < (1 << 16)) && (ll > 2))) { + return YACErrLenNonMinimal; + } + } + atom->off += l; + if (len < atom->off) { + return YACErrNotEnough; + } + atom->len = l; + atom->val.buf = buf + 1 + ll; + if (atom->typ == YACItemStr) { + size_t cpl = 0; + uint32_t cp = YACUTF8InvalidCp; + for (ll = 0; ll < l;) { + cpl = YACUTF8CpDecode(&cp, atom->val.buf + ll, l - ll); + if (cp == YACUTF8InvalidCp) { + return YACErrBadUTF8; + } + ll += cpl; + } + } + return YACErrNo; + } + + if ((atom->tag >= YACAtomInt0) && (atom->tag < YACAtomInt0 + 32)) { + atom->val.uint = (uint64_t)(atom->tag - YACAtomInt0); + atom->typ = YACItemUint; + return YACErrNo; + } + if ((atom->tag >= YACAtomIntN1) && (atom->tag < YACAtomIntN1 + 32)) { + atom->val.sint = -1 - (int64_t)(atom->tag - YACAtomIntN1); + atom->typ = YACItemSint; + return YACErrNo; + } + switch (atom->tag & 0xF0) { + case YACAtomIntPos1: + case YACAtomIntNeg1: { + atom->typ = ((atom->tag & YACAtomIntNeg1) == YACAtomIntNeg1) ? YACItemSint : + YACItemUint; + const size_t l = (atom->tag & 0x0F) + 1; + atom->off += l; + if (len < atom->off) { + return YACErrNotEnough; + } + if (buf[1] == 0) { + return YACErrIntZeroByte; + } + if (l > 8) { + atom->typ = YACItemRaw; + atom->len = l; + atom->val.buf = buf + 1; + } else { + atom->val.uint = yacFromBE(buf + 1, l); + if ((atom->val.uint) < 32) { + return YACErrIntNonMinimal; + } + if (atom->typ == YACItemSint) { + atom->val.sint = -1 - (int64_t)(atom->val.uint); + } + } + return YACErrNo; + } + } + + switch (atom->tag) { + case YACAtomEOC: + atom->typ = YACItemEOC; + break; + case YACAtomNIL: + atom->typ = YACItemNIL; + break; + case YACAtomFalse: + atom->typ = YACItemFalse; + break; + case YACAtomTrue: + atom->typ = YACItemTrue; + break; + case YACAtomUUID: + atom->typ = YACItemUUID; + atom->off += 16; + if (len < atom->off) { + return YACErrNotEnough; + } + atom->len = 16; + atom->val.buf = buf + 1; + break; + + case YACAtomList: + atom->typ = YACItemList; + break; + case YACAtomMap: + atom->typ = YACItemMap; + break; + case YACAtomBlob: + atom->typ = YACItemBlob; + break; + + case YACAtomFloat16: + case YACAtomFloat32: + case YACAtomFloat64: + case YACAtomFloat128: + case YACAtomFloat256: { + size_t l = 0; + switch (atom->tag) { + case YACAtomFloat16: + l = 2; + break; + case YACAtomFloat32: + l = 4; + break; + case YACAtomFloat64: + l = 8; + break; + case YACAtomFloat128: + l = 16; + break; + case YACAtomFloat256: + l = 32; + break; + } + atom->typ = YACItemFloat; + atom->off += l; + if (len < atom->off) { + return YACErrNotEnough; + } + atom->typ = YACItemRaw; + atom->len = l; + atom->val.buf = buf + 1; + break; + } + + case YACAtomTAI64: + case YACAtomTAI64N: + case YACAtomTAI64NA: { + size_t l = 0; + switch (atom->tag) { + case YACAtomTAI64: + l = 8; + break; + case YACAtomTAI64N: + l = 12; + break; + case YACAtomTAI64NA: + l = 16; + break; + } + atom->typ = YACItemTAI64; + atom->off += l; + if (len < atom->off) { + return YACErrNotEnough; + } + atom->len = l; + atom->val.buf = buf + 1; + uint64_t v = yacFromBE(buf + 1, 8); + if (v > ((uint64_t)(1) << 63)) { + return YACErrTAI64TooBig; + } + switch (l) { + case 12: + v = yacFromBE(buf + 1 + 8, 4); + if (v > 999999999) { + return YACErrTAI64BadNsec; + } + break; + case 16: + v = yacFromBE(buf + 1 + 8 + 4, 4); + if (v > 999999999) { + return YACErrTAI64BadAsec; + } + break; + } + break; + } + default: + return YACErrUnknownType; + } + + return YACErrNo; +} diff --git a/cyac/dec.h b/cyac/dec.h new file mode 100644 index 0000000..a2eef5d --- /dev/null +++ b/cyac/dec.h @@ -0,0 +1,65 @@ +#ifndef YAC_DEC_H +#define YAC_DEC_H + +#include +#include + +enum YACItemType { + YACItemEOC = 0, + YACItemNIL = 1, + YACItemFalse, + YACItemTrue, + YACItemUUID, // atom.val.buf + YACItemUint, // atom.val.uint + YACItemSint, // atom.val.sint + YACItemList, + YACItemMap, + YACItemBlob, + YACItemFloat, // atom.val.flt + YACItemTAI64, // atom.val.buf, atom.len + YACItemBin, // atom.val.buf, atom.len + YACItemStr, // atom.val.buf, atom.len + YACItemChunk, // atom.val.buf, atom.len, thrown by YACIterBlob + YACItemChunkLen, // atom.val.uint, thrown by YACIterBlob + YACItemRaw, // atom.tag, atom.val.buf, atom.len +}; + +enum YACErr { + YACErrInvalid = 0, // unset error + YACErrNo = 1, // everything is good + YACErrNotEnough, // not enough data (atom.off is how much) + YACErrUnknownType, // unknown atom's type + YACErrLenNonMinimal, // non-minimal string length coding + YACErrBadUTF8, // invalid UTF-8 codepoint + YACErrIntZeroByte, // non-minimal integer coding + YACErrIntNonMinimal, // non-minimal integer coding + YACErrBlobBadLen, // absent or invalid chunk length + YACErrBlobBadAtom, // unexpected atom inside + YACErrBlobBadTerm, // invalid termination atom + YACErrBlobShortChunk, // not enough data + YACErrTAI64TooBig, // use of reserved values + YACErrTAI64BadNsec, // invalid nanoseconds value + YACErrTAI64BadAsec, // invalid attoseconds value + YACErrMapBadKey, // bad type of a key + YACErrMapNoVal, // missing value + YACErrMapUnordered, // unordered keys + YACErrUnexpectedEOC, // unexpected EOC caught +}; + +struct YACAtom { + size_t off; // length of the whole atom + size_t len; // length of the strings, TAI64, raw values + union { + uint64_t uint; // unsigned integer's value + int64_t sint; // signed integer's value + const unsigned char *buf; // strings and TAI64 value + } val; + enum YACItemType typ; // type of the item, consolidated + unsigned char tag; // real type of the atom + char _pad[3]; +}; + +enum YACErr +YACAtomDecode(struct YACAtom *atom, const unsigned char *buf, const size_t len); + +#endif // YAC_DEC_H diff --git a/cyac/dectai.c b/cyac/dectai.c new file mode 100644 index 0000000..4e459e2 --- /dev/null +++ b/cyac/dectai.c @@ -0,0 +1,52 @@ +// cyac -- C YAC encoder implementation +// Copyright (C) 2024 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, version 3 of the License. +// +// 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this program. If not, see . + +#include +#include +#include + +#include "dec.h" +#include "dectai.h" +#include "frombe.h" +#include "leapsecs.h" + +enum YACErr +YACTAI64ToTimeval(struct timeval *tv, const unsigned char *buf, const size_t len) +{ + if (len > 12) { + return YACErrTAI64BadAsec; + } + uint64_t v = yacFromBE(buf, 8); + v -= 0x4000000000000000; + { + uint64_t diff = 0; + for (size_t i = 0; i < sizeof YACLeapsecsN; i++) { + if (v > (YACLeapsecs[i] + YACLeapsecsN - i)) { + diff = YACLeapsecs1972 + YACLeapsecsN - i; + break; + } + } + v -= diff; + } + tv->tv_sec = (time_t)v; + if (len > 8) { + uint64_t n = (uint32_t)yacFromBE(buf + 8, 4); + if ((n % 1000) != 0) { + return YACErrTAI64BadNsec; + } + tv->tv_usec = n / 1000; + } + return YACErrNo; +} diff --git a/cyac/dectai.h b/cyac/dectai.h new file mode 100644 index 0000000..2341bd7 --- /dev/null +++ b/cyac/dectai.h @@ -0,0 +1,13 @@ + +#ifndef YAC_DECTAI_H +#define YAC_DECTAI_H + +#include +#include + +#include "dec.h" + +enum YACErr +YACTAI64ToTimeval(struct timeval *tv, const unsigned char *buf, const size_t len); + +#endif // YAC_DECTAI_H diff --git a/cyac/default.o.do b/cyac/default.o.do new file mode 100644 index 0000000..013ecf6 --- /dev/null +++ b/cyac/default.o.do @@ -0,0 +1,4 @@ +redo-ifchange $2.c $2.h conf/cc conf/cflags +read CC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, version 3 of the License. +// +// 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this program. If not, see . + +#include +#include +#include +#include + +#include "atoms.h" +#include "enc.h" +#include "tobe.h" + +ptrdiff_t +YACAtomEOCEncode(unsigned char *buf, const size_t cap) +{ + if (cap < 1) { + return -1; + } + buf[0] = YACAtomEOC; + return 1; +} + +ptrdiff_t +YACAtomNILEncode(unsigned char *buf, const size_t cap) +{ + if (cap < 1) { + return -1; + } + buf[0] = YACAtomNIL; + return 1; +} + +ptrdiff_t +YACAtomBoolEncode(unsigned char *buf, const size_t cap, const bool v) +{ + if (cap < 1) { + return -1; + } + buf[0] = v ? YACAtomTrue : YACAtomFalse; + return 1; +} + +ptrdiff_t +YACAtomUUIDEncode(unsigned char *buf, const size_t cap, const unsigned char v[16]) +{ + if (cap < 1 + 16) { + return -(1 + 16); + } + buf[0] = YACAtomUUID; + memcpy(buf + 1, v, 16); + return 1 + 16; +} + +static ptrdiff_t +yacAtomIntEncode(unsigned char *buf, const size_t cap, const uint64_t v) +{ + if (v < 32) { + if (cap < 1) { + return -1; + } + buf[0] = (unsigned char)v; + return 1; + } + size_t bits = 8; + size_t l = 0; + for (;;) { + if (v < (((uint64_t)1) << bits)) { + break; + } + l++; + bits += 8; + } + if (cap < (1 + l)) { + return -(1 + (ptrdiff_t)l); + } + buf[0] = (unsigned char)l; + l++; + yacToBE(buf + 1, l, v); + return 1 + (ptrdiff_t)l; +} + +ptrdiff_t +YACAtomUintEncode(unsigned char *buf, const size_t cap, uint64_t v) +{ + ptrdiff_t ret = yacAtomIntEncode(buf, cap, v); + if (ret < 0) { + return ret; + } + buf[0] |= (v < 32) ? YACAtomInt0 : YACAtomIntPos1; + return ret; +} + +ptrdiff_t +YACAtomSintEncode(unsigned char *buf, const size_t cap, int64_t v) +{ + if (v >= 0) { + return YACAtomUintEncode(buf, cap, (uint64_t)v); + } + const uint64_t vp = (uint64_t)(-(v + 1)); + ptrdiff_t ret = yacAtomIntEncode(buf, cap, vp); + if (ret < 0) { + return ret; + } + buf[0] |= (vp < 32) ? YACAtomIntN1 : YACAtomIntNeg1; + return ret; +} + +ptrdiff_t +YACAtomListEncode(unsigned char *buf, const size_t cap) +{ + if (cap < 1) { + return -1; + } + buf[0] = YACAtomList; + return 1; +} + +ptrdiff_t +YACAtomMapEncode(unsigned char *buf, const size_t cap) +{ + if (cap < 1) { + return -1; + } + buf[0] = YACAtomMap; + return 1; +} + +ptrdiff_t +YACAtomBlobEncode(unsigned char *buf, const size_t cap, const size_t chunkLen) +{ + if (cap < 1) { + return -1; + } + buf[0] = YACAtomBlob; + ptrdiff_t res = YACAtomUintEncode(buf + 1, cap - 1, (uint64_t)chunkLen); + if (res <= 0) { + return res; + } + return res + 1; +} + +static ptrdiff_t +yacAtomStrEncode( + unsigned char *buf, + const size_t cap, + const unsigned char *src, + const size_t l, + const bool utf8) +{ + unsigned char lv = 0; + size_t ll = 0; + unsigned char lBuf[8] = {0}; + if (l >= (((uint64_t)1) << 16)) { + lv = 63; + ll = 8; + yacToBE(lBuf, 8, l); + } else if (l >= (((uint64_t)1) << 8)) { + lv = 62; + ll = 2; + yacToBE(lBuf, 2, l); + } else if (l > 60) { + lv = 61; + ll = 1; + lBuf[0] = (unsigned char)(l & 0xFF); + } else { + lv = (unsigned char)l; + } + if (cap < (1 + ll + l)) { + return -(ptrdiff_t)(1 + ll + l); + } + buf[0] = YACAtomStrings | lv; + if (utf8) { + buf[0] |= YACAtomIsUTF8; + } + memcpy(buf + 1, lBuf, ll); + memcpy(buf + 1 + ll, src, l); + return (ptrdiff_t)(1 + ll + l); +} + +ptrdiff_t +YACAtomStrEncode( + unsigned char *buf, + const size_t cap, + const unsigned char *src, + const size_t len) +{ + return yacAtomStrEncode(buf, cap, src, len, true); +} + +ptrdiff_t +YACAtomBinEncode( + unsigned char *buf, + const size_t cap, + const unsigned char *src, + const size_t len) +{ + return yacAtomStrEncode(buf, cap, src, len, false); +} + +ptrdiff_t +YACAtomChunkEncode( + unsigned char *buf, + const size_t cap, + const unsigned char *src, + const size_t len) +{ + if (cap < (1 + len)) { + return -(ptrdiff_t)(1 + len); + } + buf[0] = YACAtomNIL; + memcpy(buf + 1, src, len); + return (ptrdiff_t)(1 + len); +} + +ptrdiff_t +YACAtomTAI64Encode( + unsigned char *buf, + const size_t cap, + const unsigned char *src, + const size_t len) +{ + switch (len) { + case 8: + buf[0] = YACAtomTAI64; + break; + case 12: + buf[0] = YACAtomTAI64N; + break; + case 16: + buf[0] = YACAtomTAI64NA; + break; + default: + return 0; + } + if (cap < (1 + len)) { + return -(ptrdiff_t)(1 + len); + } + memcpy(buf + 1, src, len); + return (ptrdiff_t)(1 + len); +} + +ptrdiff_t +YACAtomRawEncode( + unsigned char *buf, + const size_t cap, + const unsigned char typ, + const unsigned char *src, + const size_t len) +{ + if (cap < (1 + len)) { + return -(ptrdiff_t)(1 + len); + } + buf[0] = typ; + memcpy(buf + 1, src, len); + return (ptrdiff_t)(1 + len); +} diff --git a/cyac/enc.h b/cyac/enc.h new file mode 100644 index 0000000..78dfa8b --- /dev/null +++ b/cyac/enc.h @@ -0,0 +1,71 @@ +#ifndef YAC_ENC_H +#define YAC_ENC_H + +#include +#include +#include + +ptrdiff_t +YACAtomEOCEncode(unsigned char *buf, const size_t cap); + +ptrdiff_t +YACAtomNILEncode(unsigned char *buf, const size_t cap); + +ptrdiff_t +YACAtomBoolEncode(unsigned char *buf, const size_t cap, const bool v); + +ptrdiff_t +YACAtomUUIDEncode(unsigned char *buf, const size_t cap, const unsigned char v[16]); + +ptrdiff_t +YACAtomUintEncode(unsigned char *buf, const size_t cap, uint64_t v); + +ptrdiff_t +YACAtomSintEncode(unsigned char *buf, const size_t cap, int64_t v); + +ptrdiff_t +YACAtomListEncode(unsigned char *buf, const size_t cap); + +ptrdiff_t +YACAtomMapEncode(unsigned char *buf, const size_t cap); + +ptrdiff_t +YACAtomBlobEncode(unsigned char *buf, const size_t cap, const size_t chunkLen); + +ptrdiff_t +YACAtomStrEncode( + unsigned char *buf, + const size_t cap, + const unsigned char *src, + const size_t len); + +ptrdiff_t +YACAtomBinEncode( + unsigned char *buf, + const size_t cap, + const unsigned char *src, + const size_t len); + +ptrdiff_t +YACAtomChunkEncode( + unsigned char *buf, + const size_t cap, + const unsigned char *src, + const size_t len); + +ptrdiff_t +YACAtomTAI64Encode( + unsigned char *buf, + const size_t cap, + const unsigned char *src, + const size_t len); + +ptrdiff_t +YACAtomRawEncode( + unsigned char *buf, + const size_t cap, + const unsigned char typ, + const unsigned char *src, + const size_t len); + +#endif // YAC_ENC_H diff --git a/cyac/enctai.c b/cyac/enctai.c new file mode 100644 index 0000000..bafbce9 --- /dev/null +++ b/cyac/enctai.c @@ -0,0 +1,40 @@ +// cyac -- C YAC encoder implementation +// Copyright (C) 2024 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, version 3 of the License. +// +// 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this program. If not, see . + +#include +#include +#include + +#include "enctai.h" +#include "leapsecs.h" +#include "tobe.h" + +void +YACTimevalToTAI64(unsigned char *buf, const struct timeval *tv) +{ + uint64_t v = (uint64_t)(tv->tv_sec); + uint64_t diff = YACLeapsecs1972; + for (size_t i = 0; i < sizeof YACLeapsecsN; i++) { + if (v > YACLeapsecs[i]) { + diff += YACLeapsecsN - i; + break; + } + } + v += 0x4000000000000000 + diff; + yacToBE(buf, 8, v); + if (tv->tv_usec != 0) { + yacToBE(buf + 8, 4, (uint64_t)(tv->tv_usec) * 1000); + } +} diff --git a/cyac/enctai.h b/cyac/enctai.h new file mode 100644 index 0000000..7487ae6 --- /dev/null +++ b/cyac/enctai.h @@ -0,0 +1,9 @@ +#ifndef YAC_ENCTAI_H +#define YAC_ENCTAI_H + +#include + +void +YACTimevalToTAI64(unsigned char *buf, const struct timeval *tv); + +#endif // YAC_ENCTAI_H diff --git a/cyac/example/.gitignore b/cyac/example/.gitignore new file mode 100644 index 0000000..4681da6 --- /dev/null +++ b/cyac/example/.gitignore @@ -0,0 +1,2 @@ +/print-map +/test-vector diff --git a/cyac/example/all.do b/cyac/example/all.do new file mode 100644 index 0000000..e32e2ab --- /dev/null +++ b/cyac/example/all.do @@ -0,0 +1 @@ +redo-ifchange print-map test-vector diff --git a/cyac/example/clean b/cyac/example/clean new file mode 100755 index 0000000..2403ed9 --- /dev/null +++ b/cyac/example/clean @@ -0,0 +1,3 @@ +#!/bin/sh -e + +exec rm -f print-map test-vector diff --git a/cyac/example/default.do b/cyac/example/default.do new file mode 100644 index 0000000..e8eac11 --- /dev/null +++ b/cyac/example/default.do @@ -0,0 +1,6 @@ +redo-ifchange $2.c ../conf/cc ../conf/cflags ../conf/ldflags ../conf/prefix +read CC <../conf/cc +CFLAGS=$(cat ../conf/cflags) +LDFLAGS=$(cat ../conf/ldflags) +read PREFIX <../conf/prefix +$CC $CFLAGS -I$PREFIX/include -o $3 $2.c $LDFLAGS -L$PREFIX/lib -lyac diff --git a/cyac/example/print-map.c b/cyac/example/print-map.c new file mode 100644 index 0000000..632feb0 --- /dev/null +++ b/cyac/example/print-map.c @@ -0,0 +1,258 @@ +// cyac -- C YAC encoder implementation +// Copyright (C) 2024 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, version 3 of the License. +// +// 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this program. If not, see . + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static const char hexdigits[] = "0123456789ABCDEF"; + +static char * +HexEnc(const unsigned char *src, const size_t srcLen) +{ + // it was based on libressl/crypto/x509v3/v3_utl.c:hex_to_string + char *dst = (char *)malloc(1 + srcLen * 2); + if (dst == NULL) { + return NULL; + } + size_t i = 0; + for (; i < srcLen; i++) { + dst[(i * 2) + 0] = hexdigits[(src[i] >> 4) & 0x0F]; + dst[(i * 2) + 1] = hexdigits[src[i] & 0x0F]; + } + dst[srcLen * 2] = 0; + return dst; +} + +struct CbState { + ptrdiff_t indent; +}; + +static enum YACErr +myCb( + const unsigned char *key, + const size_t keyLen, + const size_t idx, + void *cbState, + struct YACAtom *atom, + size_t *off, + const unsigned char *buf, + const size_t len) +{ + struct CbState *state = (struct CbState *)(cbState); + if ((atom->typ) == YACItemEOC) { + state->indent--; + if (state->indent < 0) { + return YACErrUnexpectedEOC; + } + } + for (ptrdiff_t i = 0; i < state->indent; i++) { + fputs(" ", stdout); + } + if (key != NULL) { + fwrite(key, keyLen, 1, stdout); + fputs(": ", stdout); + } else if (idx > 0) { + fprintf(stdout, "%zu: ", idx); + } + + char *hex = NULL; + enum YACErr err = YACErrInvalid; + switch (atom->typ) { + case YACItemEOC: + fputs("]\n", stdout); + break; + case YACItemNIL: + fputs("NIL\n", stdout); + break; + case YACItemFalse: + fputs("FALSE\n", stdout); + break; + case YACItemTrue: + fputs("TRUE\n", stdout); + break; + case YACItemUUID: + printf( + "UUID[%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x]\n", + atom->val.buf[0], + atom->val.buf[1], + atom->val.buf[2], + atom->val.buf[3], + atom->val.buf[4], + atom->val.buf[5], + atom->val.buf[6], + atom->val.buf[7], + atom->val.buf[8], + atom->val.buf[9], + atom->val.buf[10], + atom->val.buf[11], + atom->val.buf[12], + atom->val.buf[13], + atom->val.buf[14], + atom->val.buf[15]); + break; + case YACItemUint: + fprintf(stdout, "%zu\n", atom->val.uint); + break; + case YACItemSint: + fprintf(stdout, "%zd\n", atom->val.sint); + break; + case YACItemList: + fputs("LIST [\n", stdout); + state->indent++; + err = YACIterList(cbState, atom, off, buf, len, myCb); + if (err != YACErrNo) { + return err; + } + break; + case YACItemMap: + fputs("MAP [\n", stdout); + state->indent++; + err = YACIterMap(cbState, atom, off, buf, len, myCb); + if (err != YACErrNo) { + return err; + } + break; + case YACItemBlob: + fputs("BLOB [\n", stdout); + state->indent++; + err = YACIterBlob(cbState, atom, off, buf, len, myCb); + if (err != YACErrNo) { + return err; + } + break; + case YACItemFloat: + fputs("FLOAT: TODO\n", stdout); + break; + case YACItemTAI64: + if ((atom->len) == 16) { + hex = HexEnc(atom->val.buf, atom->len); + fprintf(stdout, "TAI64NA: %s\n", hex); + free(hex); + break; + } + switch (atom->len) { + case 8: + fputs("TAI64: ", stdout); + break; + case 12: + fputs("TAI64N: ", stdout); + break; + } + struct timeval tv; + err = YACTAI64ToTimeval(&tv, atom->val.buf, atom->len); + if (err == YACErrTAI64BadNsec) { + hex = HexEnc(atom->val.buf, atom->len); + fprintf(stdout, "unrepresentable: %s\n", hex); + free(hex); + break; + } + if (err != YACErrNo) { + return err; + } + time_t t = tv.tv_sec; + struct tm *tm = localtime(&t); + if (tm == NULL) { + hex = HexEnc(atom->val.buf, atom->len); + fprintf(stdout, "unrepresentable: %s\n", hex); + free(hex); + break; + } + char human[20] = {0}; + strftime(human, sizeof human, "%Y-%m-%d %H:%M:%S", tm); + fputs(human, stdout); + if ((atom->len) == 12) { + fprintf(stdout, ".%zu", tv.tv_usec); + } + fputs("\n", stdout); + break; + case YACItemBin: + hex = HexEnc(atom->val.buf, atom->len); + fprintf(stdout, "BIN(%s)\n", hex); + free(hex); + break; + case YACItemStr: + hex = strndup((const char *)atom->val.buf, atom->len); + fprintf(stdout, "\"%s\"\n", hex); + free(hex); + break; + case YACItemChunk: + hex = HexEnc(atom->val.buf, atom->len); + fprintf(stdout, "CHUNK(%s)\n", hex); + free(hex); + break; + case YACItemChunkLen: + fprintf(stdout, "chunkLen: %zu\n", atom->val.uint); + break; + case YACItemRaw: + hex = HexEnc(atom->val.buf, atom->len); + fprintf(stdout, "RAW: 0x%X %s\n", atom->tag, hex); + free(hex); + break; + default: + fprintf(stdout, "unknown atom\n"); + } + return YACErrNo; +} + +int +main(int argc, char **argv) +{ + size_t len = 0; + unsigned char *buf = NULL; + { + int fd = open(argv[1], O_RDONLY | O_CLOEXEC); + assert(fd != -1); + struct stat sb; + memset(&sb, 0, sizeof(struct stat)); + assert(fstat(fd, &sb) == 0); + len = (size_t)sb.st_size; + buf = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); + } + struct YACAtom atom; + memset(&atom, 0, sizeof(struct YACAtom)); + size_t off = 0; + enum YACErr err = YACAtomDecode(&atom, buf, len); + if (err != YACErrNo) { + fprintf(stderr, "map err: %d\n", err); + return EXIT_FAILURE; + } + if (atom.typ != YACItemMap) { + fputs("non map\n", stderr); + return EXIT_FAILURE; + } + off += atom.off; + struct CbState cbState = {.indent = 1}; + fputs("MAP [\n", stdout); + err = YACIterMap(&cbState, &atom, &off, buf, len, myCb); + if (err != YACErrNo) { + fprintf(stderr, "iter err: %d\n", err); + return EXIT_FAILURE; + } + assert(cbState.indent == 0); + free(buf); + return EXIT_SUCCESS; +} diff --git a/cyac/example/test-vector.c b/cyac/example/test-vector.c new file mode 100644 index 0000000..c341b84 --- /dev/null +++ b/cyac/example/test-vector.c @@ -0,0 +1,218 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static void +adder(size_t *off, const ptrdiff_t add) +{ + assert(add > 0); + (*off) += (size_t)add; +} + +int +main(int argc, char **argv) +{ + const size_t len = 68 * 1024; + unsigned char *buf = malloc(len); + assert(buf != NULL); + unsigned char *bin = malloc(1 << 16); + assert(bin != NULL); + size_t off = 0; + + adder(&off, YACAtomMapEncode(buf + off, len - off)); // . + + adder( + &off, YACAtomStrEncode(buf + off, len - off, (const unsigned char *)"nil", 3)); + adder(&off, YACAtomNILEncode(buf + off, len - off)); + + adder( + &off, YACAtomStrEncode(buf + off, len - off, (const unsigned char *)"str", 3)); + adder(&off, YACAtomMapEncode(buf + off, len - off)); // .str + + adder( + &off, YACAtomStrEncode(buf + off, len - off, (const unsigned char *)"bin", 3)); + adder(&off, YACAtomListEncode(buf + off, len - off)); // .str.bin + adder(&off, YACAtomBinEncode(buf + off, len - off, (const unsigned char *)"", 0)); + memset(bin, '0', 60); + adder(&off, YACAtomBinEncode(buf + off, len - off, bin, 60)); + memset(bin, '1', 61); + adder(&off, YACAtomBinEncode(buf + off, len - off, bin, 61)); + memset(bin, '2', 255); + adder(&off, YACAtomBinEncode(buf + off, len - off, bin, 255)); + memset(bin, '3', 1024); + adder(&off, YACAtomBinEncode(buf + off, len - off, bin, 1024)); + memset(bin, '4', 1 << 16); + adder(&off, YACAtomBinEncode(buf + off, len - off, bin, 1 << 16)); + adder(&off, YACAtomEOCEncode(buf + off, len - off)); // .str.bin + + adder( + &off, YACAtomStrEncode(buf + off, len - off, (const unsigned char *)"utf8", 4)); + adder( + &off, + YACAtomStrEncode( + buf + off, len - off, (const unsigned char *)"привет мир", 19)); + adder(&off, YACAtomEOCEncode(buf + off, len - off)); // .str + + adder( + &off, YACAtomStrEncode(buf + off, len - off, (const unsigned char *)"blob", 4)); + adder(&off, YACAtomListEncode(buf + off, len - off)); // .blob + + adder(&off, YACAtomBlobEncode(buf + off, len - off, 12)); // .blob.0 + memset(bin, '5', 1); + adder(&off, YACAtomBinEncode(buf + off, len - off, bin, 1)); + + adder(&off, YACAtomBlobEncode(buf + off, len - off, 12)); // .blob.1 + memset(bin, '6', 12); + adder(&off, YACAtomChunkEncode(buf + off, len - off, bin, 12)); + adder(&off, YACAtomBinEncode(buf + off, len - off, NULL, 0)); + + adder(&off, YACAtomBlobEncode(buf + off, len - off, 12)); // .blob.2 + memset(bin, '7', 12); + adder(&off, YACAtomChunkEncode(buf + off, len - off, bin, 12)); + adder(&off, YACAtomBinEncode(buf + off, len - off, bin, 1)); + + adder(&off, YACAtomBlobEncode(buf + off, len - off, 5)); // .blob.3 + adder( + &off, + YACAtomChunkEncode(buf + off, len - off, (const unsigned char *)"12345", 5)); + adder( + &off, + YACAtomChunkEncode(buf + off, len - off, (const unsigned char *)"67890", 5)); + adder(&off, YACAtomBinEncode(buf + off, len - off, (const unsigned char *)"-", 1)); + + adder(&off, YACAtomEOCEncode(buf + off, len - off)); // .blob + + adder( + &off, YACAtomStrEncode(buf + off, len - off, (const unsigned char *)"bool", 4)); + adder(&off, YACAtomListEncode(buf + off, len - off)); // .bool + adder(&off, YACAtomBoolEncode(buf + off, len - off, true)); + adder(&off, YACAtomBoolEncode(buf + off, len - off, false)); + adder(&off, YACAtomEOCEncode(buf + off, len - off)); // .bool + + adder( + &off, YACAtomStrEncode(buf + off, len - off, (const unsigned char *)"ints", 4)); + adder(&off, YACAtomMapEncode(buf + off, len - off)); // .ints + + adder( + &off, YACAtomStrEncode(buf + off, len - off, (const unsigned char *)"neg", 3)); + adder(&off, YACAtomListEncode(buf + off, len - off)); // .ints.neg + adder(&off, YACAtomSintEncode(buf + off, len - off, -1)); + adder(&off, YACAtomSintEncode(buf + off, len - off, -2)); + adder(&off, YACAtomSintEncode(buf + off, len - off, -32)); + adder(&off, YACAtomSintEncode(buf + off, len - off, -33)); + adder(&off, YACAtomSintEncode(buf + off, len - off, -123)); + adder(&off, YACAtomSintEncode(buf + off, len - off, -1234)); + adder(&off, YACAtomSintEncode(buf + off, len - off, -12345678)); + adder( + &off, + YACAtomRawEncode( + buf + off, + len - off, + YACAtomIntNeg10, + (const unsigned char *)"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", + 10)); + adder(&off, YACAtomEOCEncode(buf + off, len - off)); // .ints.neg + + adder( + &off, YACAtomStrEncode(buf + off, len - off, (const unsigned char *)"pos", 3)); + adder(&off, YACAtomListEncode(buf + off, len - off)); // .ints.pos + adder(&off, YACAtomUintEncode(buf + off, len - off, 0)); + adder(&off, YACAtomUintEncode(buf + off, len - off, 1)); + adder(&off, YACAtomUintEncode(buf + off, len - off, 31)); + adder(&off, YACAtomUintEncode(buf + off, len - off, 32)); + adder(&off, YACAtomUintEncode(buf + off, len - off, 123)); + adder(&off, YACAtomUintEncode(buf + off, len - off, 1234)); + adder(&off, YACAtomUintEncode(buf + off, len - off, 12345678)); + adder( + &off, + YACAtomRawEncode( + buf + off, + len - off, + YACAtomIntPos11, + (const unsigned char *)"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + 11)); + adder(&off, YACAtomEOCEncode(buf + off, len - off)); // .ints.pos + + adder(&off, YACAtomEOCEncode(buf + off, len - off)); // .ints + + adder( + &off, YACAtomStrEncode(buf + off, len - off, (const unsigned char *)"uuid", 4)); + adder( + &off, + YACAtomUUIDEncode( + buf + off, + len - off, + (const unsigned char + *)"\x0e\x87\x5e\x3f\xd3\x85\x49\xeb\x87\xb4\xbe\x42\xd6\x41\xc3\x67")); + + adder( + &off, + YACAtomStrEncode(buf + off, len - off, (const unsigned char *)"dates", 5)); + adder(&off, YACAtomListEncode(buf + off, len - off)); // .dates + { + struct timeval tv; + tv.tv_sec = 1234567890; + unsigned char tai[12] = {0}; + YACTimevalToTAI64(tai, &tv); + adder(&off, YACAtomTAI64Encode(buf + off, len - off, tai, 8)); + + tv.tv_usec = 456; + YACTimevalToTAI64(tai, &tv); + adder(&off, YACAtomTAI64Encode(buf + off, len - off, tai, 12)); + + adder( + &off, + YACAtomRawEncode( + buf + off, + len - off, + YACAtomTAI64NA, + (const unsigned char + *)"\x40\x00\x00\x00\x00\x00\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99", + 16)); + } + adder(&off, YACAtomEOCEncode(buf + off, len - off)); // .dates + + adder( + &off, + YACAtomStrEncode(buf + off, len - off, (const unsigned char *)"floats", 6)); + adder(&off, YACAtomListEncode(buf + off, len - off)); // .floats + adder( + &off, + YACAtomRawEncode( + buf + off, + len - off, + YACAtomFloat32, + (const unsigned char *)"\x01\x02\x03\x04", + 4)); + adder(&off, YACAtomEOCEncode(buf + off, len - off)); // .floats + + adder( + &off, + YACAtomStrEncode(buf + off, len - off, (const unsigned char *)"empties", 7)); + adder(&off, YACAtomListEncode(buf + off, len - off)); // .empties + adder(&off, YACAtomListEncode(buf + off, len - off)); + adder(&off, YACAtomEOCEncode(buf + off, len - off)); + adder(&off, YACAtomMapEncode(buf + off, len - off)); + adder(&off, YACAtomEOCEncode(buf + off, len - off)); + adder(&off, YACAtomBlobEncode(buf + off, len - off, 123)); + adder(&off, YACAtomBinEncode(buf + off, len - off, NULL, 0)); + memset(bin, '\x00', 16); + adder(&off, YACAtomUUIDEncode(buf + off, len - off, bin)); + adder(&off, YACAtomEOCEncode(buf + off, len - off)); // .empties + + adder(&off, YACAtomEOCEncode(buf + off, len - off)); // . + + free(bin); + assert(write(STDOUT_FILENO, buf, off) == (ssize_t)off); + free(buf); + + return EXIT_SUCCESS; +} diff --git a/cyac/frombe.c b/cyac/frombe.c new file mode 100644 index 0000000..92f7c44 --- /dev/null +++ b/cyac/frombe.c @@ -0,0 +1,16 @@ +#include +#include + +#include "frombe.h" + +uint64_t +yacFromBE(const unsigned char *buf, const size_t len) +{ + uint64_t v = 0; + for (size_t i = 0; i < len; i++) { + v |= (uint64_t)(buf[i]) << ((len - i - 1) * 8); + } + // this can be replaced by a switch with hard-coded decoding + // sequence without any loops for each of eight possible lengths + return v; +} diff --git a/cyac/frombe.h b/cyac/frombe.h new file mode 100644 index 0000000..49ff480 --- /dev/null +++ b/cyac/frombe.h @@ -0,0 +1,10 @@ +#ifndef YAC_FROMBE_H +#define YAC_FROMBE_H + +#include +#include + +uint64_t +yacFromBE(const unsigned char *buf, const size_t len); + +#endif // YAC_FROMBE_H diff --git a/cyac/install.do b/cyac/install.do new file mode 100644 index 0000000..b5dc6fa --- /dev/null +++ b/cyac/install.do @@ -0,0 +1,6 @@ +redo-ifchange *.h libyac.a conf/prefix +read PREFIX +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, version 3 of the License. +// +// 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this program. If not, see . + +#include +#include +#include + +#include "dec.h" +#include "iter.h" + +enum YACErr +YACIterList( + void *cbState, + struct YACAtom *atom, + size_t *off, + const unsigned char *buf, + const size_t len, + YACIterCb cb) +{ + enum YACErr err = YACErrInvalid; + bool eoc = false; + for (size_t n = 0;; n++) { + err = YACAtomDecode(atom, buf + *off, len - *off); + if (err != YACErrNo) { + return err; + } + (*off) += atom->off; + eoc = atom->typ == YACItemEOC; + err = cb(NULL, 0, eoc ? 0 : (n + 1), cbState, atom, off, buf, len); + if (err != YACErrNo) { + return err; + } + if (eoc) { + break; + } + } + return YACErrNo; +} + +enum YACErr +YACIterMap( + void *cbState, + struct YACAtom *atom, + size_t *off, + const unsigned char *buf, + const size_t len, + YACIterCb cb) +{ + const unsigned char *key = NULL; + size_t keyLen = 0; + enum YACErr err = YACErrInvalid; + for (;;) { + err = YACAtomDecode(atom, buf + *off, len - *off); + if (err != YACErrNo) { + return err; + } + (*off) += atom->off; + if (atom->typ == YACItemEOC) { + err = cb(NULL, 0, 0, cbState, atom, off, buf, len); + if (err != YACErrNo) { + return err; + } + break; + } + if (atom->typ != YACItemStr) { + return YACErrMapBadKey; + } + if (atom->len < keyLen) { + return YACErrMapUnordered; + } + if ((atom->len == keyLen) && (memcmp(key, atom->val.buf, keyLen) >= 0)) { + return YACErrMapUnordered; + } + keyLen = atom->len; + key = atom->val.buf; + err = YACAtomDecode(atom, buf + *off, len - *off); + if (err != YACErrNo) { + return err; + } + (*off) += atom->off; + if (atom->typ == YACItemEOC) { + return YACErrMapNoVal; + } + err = cb(key, keyLen, 0, cbState, atom, off, buf, len); + if (err != YACErrNo) { + return err; + } + } + return YACErrNo; +} + +enum YACErr +YACIterBlob( + void *cbState, + struct YACAtom *atom, + size_t *off, + const unsigned char *buf, + const size_t len, + YACIterCb cb) +{ + enum YACErr err = YACAtomDecode(atom, buf + *off, len - *off); + if (err != YACErrNo) { + return err; + } + (*off) += atom->off; + if (atom->typ != YACItemUint) { + return YACErrBlobBadLen; + } + const size_t chunkLen = atom->val.uint; + if (chunkLen == 0) { + return YACErrBlobBadLen; + } + atom->typ = YACItemChunkLen; + err = cb(NULL, 0, 0, cbState, atom, off, buf, len); + if (err != YACErrNo) { + return err; + } + bool eoc = false; + for (size_t n = 0; !eoc; n++) { + err = YACAtomDecode(atom, buf + *off, len - *off); + if (err != YACErrNo) { + return err; + } + (*off) += atom->off; + switch (atom->typ) { + case YACItemNIL: + if ((len - *off) <= chunkLen) { + return YACErrBlobShortChunk; + } + atom->val.buf = buf + *off; + atom->len = chunkLen; + (*off) += chunkLen; + break; + case YACItemBin: + if ((atom->len) >= chunkLen) { + return YACErrBlobBadTerm; + } + eoc = true; + break; + case YACItemEOC: + case YACItemFalse: + case YACItemTrue: + case YACItemUUID: + case YACItemUint: + case YACItemSint: + case YACItemList: + case YACItemMap: + case YACItemBlob: + case YACItemFloat: + case YACItemTAI64: + case YACItemStr: + case YACItemChunk: + case YACItemChunkLen: + case YACItemRaw: + default: + return YACErrBlobBadAtom; + } + if ((atom->len) > 0) { + atom->typ = YACItemChunk; + err = cb(NULL, 0, n + 1, cbState, atom, off, buf, len); + if (err != YACErrNo) { + return err; + } + } + } + atom->typ = YACItemEOC; + err = cb(NULL, 0, 0, cbState, atom, off, buf, len); + if (err != YACErrNo) { + return err; + } + return YACErrNo; +} diff --git a/cyac/iter.h b/cyac/iter.h new file mode 100644 index 0000000..268d151 --- /dev/null +++ b/cyac/iter.h @@ -0,0 +1,45 @@ +#ifndef YAC_ITER_H +#define YAC_ITER_H + +#include + +#include "dec.h" + +typedef enum YACErr (*YACIterCb)( + const unsigned char *key, + const size_t keyLen, + const size_t idx, + void *cbState, + struct YACAtom *atom, + size_t *off, + const unsigned char *buf, + const size_t len); + +enum YACErr +YACIterList( + void *cbState, + struct YACAtom *atom, + size_t *off, + const unsigned char *buf, + const size_t len, + YACIterCb cb); + +enum YACErr +YACIterMap( + void *cbState, + struct YACAtom *atom, + size_t *off, + const unsigned char *buf, + const size_t len, + YACIterCb cb); + +enum YACErr +YACIterBlob( + void *cbState, + struct YACAtom *atom, + size_t *off, + const unsigned char *buf, + const size_t len, + YACIterCb cb); + +#endif // YAC_ITER_H diff --git a/cyac/leapsecs.c b/cyac/leapsecs.c new file mode 100644 index 0000000..0f0f05f --- /dev/null +++ b/cyac/leapsecs.c @@ -0,0 +1,36 @@ +#include +#include + +#include "leapsecs.h" + +const size_t YACLeapsecsN = 27; +const uint64_t YACLeapsecs1972 = 10; +uint64_t YACLeapsecs[] = { + 1483228800, // 2017-01 + 1435708800, // 2015-07 + 1341100800, // 2012-07 + 1230768000, // 2009-01 + 1136073600, // 2006-01 + 915148800, // 1999-01 + 867715200, // 1997-07 + 820454400, // 1996-01 + 773020800, // 1994-07 + 741484800, // 1993-07 + 709948800, // 1992-07 + 662688000, // 1991-01 + 631152000, // 1990-01 + 567993600, // 1988-01 + 489024000, // 1985-07 + 425865600, // 1983-07 + 394329600, // 1982-07 + 362793600, // 1981-07 + 315532800, // 1980-01 + 283996800, // 1979-01 + 252460800, // 1978-01 + 220924800, // 1977-01 + 189302400, // 1976-01 + 157766400, // 1975-01 + 126230400, // 1974-01 + 94694400, // 1973-01 + 78796800 // 1972-07 +}; diff --git a/cyac/leapsecs.h b/cyac/leapsecs.h new file mode 100644 index 0000000..9a643da --- /dev/null +++ b/cyac/leapsecs.h @@ -0,0 +1,11 @@ +#ifndef YAC_LEAPSECS_H +#define YAC_LEAPSECS_H + +#include +#include + +extern const size_t YACLeapsecsN; +extern const uint64_t YACLeapsecs1972; +extern uint64_t YACLeapsecs[]; + +#endif // YAC_LEAPSECS_H diff --git a/cyac/libyac.a.do b/cyac/libyac.a.do new file mode 100644 index 0000000..8068afa --- /dev/null +++ b/cyac/libyac.a.do @@ -0,0 +1,6 @@ +redo-ifchange o.list +objs=$(cat o.list) +redo-ifchange $objs conf/ar +read AR +#include + +#include "tobe.h" + +void +yacToBE(unsigned char *buf, const size_t len, const uint64_t v) +{ + for (size_t i = 0; i < len; i++) { + buf[i] = + (unsigned char)(((v & ((uint64_t)0xFF << ((len - i - 1) * 8))) >> ((len - i - 1) * 8)) & 0xFF); + } + // this can be replaced by a switch with hard-coded decoding + // sequence without any loops for each of eight possible lengths +} diff --git a/cyac/tobe.h b/cyac/tobe.h new file mode 100644 index 0000000..872e1d1 --- /dev/null +++ b/cyac/tobe.h @@ -0,0 +1,10 @@ +#ifndef YAC_TOBE_H +#define YAC_TOBE_H + +#include +#include + +void +yacToBE(unsigned char *buf, const size_t len, const uint64_t v); + +#endif // YAC_TOBE_H diff --git a/cyac/utf8.c b/cyac/utf8.c new file mode 100644 index 0000000..a593370 --- /dev/null +++ b/cyac/utf8.c @@ -0,0 +1,96 @@ +// This code is stripped copy-paste of https://libs.suckless.org/libgrapheme/ +// grapheme_decode_utf8 function, licenced under ISC-License. +// Copyright 2019-2022 Laslo Hunhold + +#include +#include +#include + +#include "utf8.h" + +#define BETWEEN(c, l, u) (((c) >= (l)) && ((c) <= (u))) + +uint32_t YACUTF8InvalidCp = 0xFFFD; + +static const struct { + uint32_t mincp; + uint32_t maxcp; + uint8_t lower; + uint8_t upper; + char _pad[2]; +} lut[] = { + [0] = + { + /* 0xxxxxxx */ + .lower = 0x00, /* 00000000 */ + .upper = 0x7F, /* 01111111 */ + .mincp = (uint32_t)0, + .maxcp = ((uint32_t)1 << 7) - 1, /* 7 bits capacity */ + }, + [1] = + { + /* 110xxxxx */ + .lower = 0xC0, /* 11000000 */ + .upper = 0xDF, /* 11011111 */ + .mincp = (uint32_t)1 << 7, + .maxcp = ((uint32_t)1 << 11) - 1, /* 5+6=11 bits capacity */ + }, + [2] = + { + /* 1110xxxx */ + .lower = 0xE0, /* 11100000 */ + .upper = 0xEF, /* 11101111 */ + .mincp = (uint32_t)1 << 11, + .maxcp = ((uint32_t)1 << 16) - 1, /* 4+6+6=16 bits capacity */ + }, + [3] = + { + /* 11110xxx */ + .lower = 0xF0, /* 11110000 */ + .upper = 0xF7, /* 11110111 */ + .mincp = (uint32_t)1 << 16, + .maxcp = ((uint32_t)1 << 21) - 1, /* 3+6+6+6=21 bits capacity */ + }, +}; + +size_t +YACUTF8CpDecode(uint32_t *cp, const unsigned char *str, const size_t len) +{ + assert(cp != NULL); + if (str == NULL || len == 0) { + (*cp) = YACUTF8InvalidCp; + return 0; + } + size_t off = 0; + for (off = 0; off < 4; off++) { + if (BETWEEN(((const unsigned char *)str)[0], lut[off].lower, lut[off].upper)) { + (*cp) = ((const unsigned char *)str)[0] - lut[off].lower; + break; + } + } + if (off == 4) { + (*cp) = YACUTF8InvalidCp; + return 1; + } + size_t i = 0; + if ((1 + off) > len) { + (*cp) = YACUTF8InvalidCp; + for (i = 0; 1 + i < len; i++) { + if (!BETWEEN(((const unsigned char *)str)[1 + i], 0x80, 0xBF)) { + break; + } + } + return ((1 + i) < len) ? (1 + i) : (1 + off); + } + for (i = 1; i <= off; i++) { + if (!BETWEEN(((const unsigned char *)str)[i], 0x80, 0xBF)) { + (*cp) = YACUTF8InvalidCp; + return 1 + (i - 1); + } + (*cp) = (*cp << 6) | (((const unsigned char *)str)[i] & 0x3F); + } + if ((*cp < lut[off].mincp) || BETWEEN(*cp, 0xD800, 0xDFFF) || (*cp > 0x10FFFF)) { + (*cp) = YACUTF8InvalidCp; + } + return 1 + off; +} diff --git a/cyac/utf8.h b/cyac/utf8.h new file mode 100644 index 0000000..4a3dd64 --- /dev/null +++ b/cyac/utf8.h @@ -0,0 +1,12 @@ +#ifndef YAC_UTF8_H +#define YAC_UTF8_H + +#include +#include + +extern uint32_t YACUTF8InvalidCp; + +size_t +YACUTF8CpDecode(uint32_t *cp, const unsigned char *str, const size_t len); + +#endif // YAC_UTF8_H diff --git a/gyac/COPYING b/gyac/COPYING new file mode 100644 index 0000000..58f2b48 --- /dev/null +++ b/gyac/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/gyac/COPYING.LESSER b/gyac/COPYING.LESSER new file mode 100644 index 0000000..2d9b9aa --- /dev/null +++ b/gyac/COPYING.LESSER @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/gyac/README b/gyac/README new file mode 100644 index 0000000..a49cdf6 --- /dev/null +++ b/gyac/README @@ -0,0 +1,7 @@ +Go implementation of the YAC codec. + +* No FLOAT*, INTs greater than 64-bit, TAI64NA. + They are stored/decoded just as a raw value +* Actual negative integer maximal size is -2^63-1 + +gyac is free software: see the file COPYING.LESSER for copying conditions. diff --git a/gyac/atomtype_string.go b/gyac/atomtype_string.go new file mode 100644 index 0000000..ebb5f94 --- /dev/null +++ b/gyac/atomtype_string.go @@ -0,0 +1,163 @@ +// Code generated by "stringer -type=AtomType"; DO NOT EDIT. + +package gyac + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[AtomEOC-0] + _ = x[AtomNIL-1] + _ = x[AtomFalse-2] + _ = x[AtomTrue-3] + _ = x[AtomUUID-4] + _ = x[AtomList-8] + _ = x[AtomMap-9] + _ = x[AtomBlob-11] + _ = x[AtomFloat16-16] + _ = x[AtomFloat32-17] + _ = x[AtomFloat64-18] + _ = x[AtomFloat128-19] + _ = x[AtomFloat256-20] + _ = x[AtomTAI64-24] + _ = x[AtomTAI64N-25] + _ = x[AtomTAI64NA-26] + _ = x[AtomIntPos1-32] + _ = x[AtomIntPos2-33] + _ = x[AtomIntPos3-34] + _ = x[AtomIntPos4-35] + _ = x[AtomIntPos5-36] + _ = x[AtomIntPos6-37] + _ = x[AtomIntPos7-38] + _ = x[AtomIntPos8-39] + _ = x[AtomIntPos9-40] + _ = x[AtomIntPos10-41] + _ = x[AtomIntPos11-42] + _ = x[AtomIntPos12-43] + _ = x[AtomIntPos13-44] + _ = x[AtomIntPos14-45] + _ = x[AtomIntPos15-46] + _ = x[AtomIntPos16-47] + _ = x[AtomIntNeg1-48] + _ = x[AtomIntNeg2-49] + _ = x[AtomIntNeg3-50] + _ = x[AtomIntNeg4-51] + _ = x[AtomIntNeg5-52] + _ = x[AtomIntNeg6-53] + _ = x[AtomIntNeg7-54] + _ = x[AtomIntNeg8-55] + _ = x[AtomIntNeg9-56] + _ = x[AtomIntNeg10-57] + _ = x[AtomIntNeg11-58] + _ = x[AtomIntNeg12-59] + _ = x[AtomIntNeg13-60] + _ = x[AtomIntNeg14-61] + _ = x[AtomIntNeg15-62] + _ = x[AtomIntNeg16-63] + _ = x[AtomInt0-64] + _ = x[AtomInt1-65] + _ = x[AtomInt2-66] + _ = x[AtomInt3-67] + _ = x[AtomInt4-68] + _ = x[AtomInt5-69] + _ = x[AtomInt6-70] + _ = x[AtomInt7-71] + _ = x[AtomInt8-72] + _ = x[AtomInt9-73] + _ = x[AtomInt10-74] + _ = x[AtomInt11-75] + _ = x[AtomInt12-76] + _ = x[AtomInt13-77] + _ = x[AtomInt14-78] + _ = x[AtomInt15-79] + _ = x[AtomInt16-80] + _ = x[AtomInt17-81] + _ = x[AtomInt18-82] + _ = x[AtomInt19-83] + _ = x[AtomInt20-84] + _ = x[AtomInt21-85] + _ = x[AtomInt22-86] + _ = x[AtomInt23-87] + _ = x[AtomInt24-88] + _ = x[AtomInt25-89] + _ = x[AtomInt26-90] + _ = x[AtomInt27-91] + _ = x[AtomInt28-92] + _ = x[AtomInt29-93] + _ = x[AtomInt30-94] + _ = x[AtomInt31-95] + _ = x[AtomIntN1-96] + _ = x[AtomIntN2-97] + _ = x[AtomIntN3-98] + _ = x[AtomIntN4-99] + _ = x[AtomIntN5-100] + _ = x[AtomIntN6-101] + _ = x[AtomIntN7-102] + _ = x[AtomIntN8-103] + _ = x[AtomIntN9-104] + _ = x[AtomIntN10-105] + _ = x[AtomIntN11-106] + _ = x[AtomIntN12-107] + _ = x[AtomIntN13-108] + _ = x[AtomIntN14-109] + _ = x[AtomIntN15-110] + _ = x[AtomIntN16-111] + _ = x[AtomIntN17-112] + _ = x[AtomIntN18-113] + _ = x[AtomIntN19-114] + _ = x[AtomIntN20-115] + _ = x[AtomIntN21-116] + _ = x[AtomIntN22-117] + _ = x[AtomIntN23-118] + _ = x[AtomIntN24-119] + _ = x[AtomIntN25-120] + _ = x[AtomIntN26-121] + _ = x[AtomIntN27-122] + _ = x[AtomIntN28-123] + _ = x[AtomIntN29-124] + _ = x[AtomIntN30-125] + _ = x[AtomIntN31-126] + _ = x[AtomIntN32-127] +} + +const ( + _AtomType_name_0 = "AtomEOCAtomNILAtomFalseAtomTrueAtomUUID" + _AtomType_name_1 = "AtomListAtomMap" + _AtomType_name_2 = "AtomBlob" + _AtomType_name_3 = "AtomFloat16AtomFloat32AtomFloat64AtomFloat128AtomFloat256" + _AtomType_name_4 = "AtomTAI64AtomTAI64NAtomTAI64NA" + _AtomType_name_5 = "AtomIntPos1AtomIntPos2AtomIntPos3AtomIntPos4AtomIntPos5AtomIntPos6AtomIntPos7AtomIntPos8AtomIntPos9AtomIntPos10AtomIntPos11AtomIntPos12AtomIntPos13AtomIntPos14AtomIntPos15AtomIntPos16AtomIntNeg1AtomIntNeg2AtomIntNeg3AtomIntNeg4AtomIntNeg5AtomIntNeg6AtomIntNeg7AtomIntNeg8AtomIntNeg9AtomIntNeg10AtomIntNeg11AtomIntNeg12AtomIntNeg13AtomIntNeg14AtomIntNeg15AtomIntNeg16AtomInt0AtomInt1AtomInt2AtomInt3AtomInt4AtomInt5AtomInt6AtomInt7AtomInt8AtomInt9AtomInt10AtomInt11AtomInt12AtomInt13AtomInt14AtomInt15AtomInt16AtomInt17AtomInt18AtomInt19AtomInt20AtomInt21AtomInt22AtomInt23AtomInt24AtomInt25AtomInt26AtomInt27AtomInt28AtomInt29AtomInt30AtomInt31AtomIntN1AtomIntN2AtomIntN3AtomIntN4AtomIntN5AtomIntN6AtomIntN7AtomIntN8AtomIntN9AtomIntN10AtomIntN11AtomIntN12AtomIntN13AtomIntN14AtomIntN15AtomIntN16AtomIntN17AtomIntN18AtomIntN19AtomIntN20AtomIntN21AtomIntN22AtomIntN23AtomIntN24AtomIntN25AtomIntN26AtomIntN27AtomIntN28AtomIntN29AtomIntN30AtomIntN31AtomIntN32" +) + +var ( + _AtomType_index_0 = [...]uint8{0, 7, 14, 23, 31, 39} + _AtomType_index_1 = [...]uint8{0, 8, 15} + _AtomType_index_3 = [...]uint8{0, 11, 22, 33, 45, 57} + _AtomType_index_4 = [...]uint8{0, 9, 19, 30} + _AtomType_index_5 = [...]uint16{0, 11, 22, 33, 44, 55, 66, 77, 88, 99, 111, 123, 135, 147, 159, 171, 183, 194, 205, 216, 227, 238, 249, 260, 271, 282, 294, 306, 318, 330, 342, 354, 366, 374, 382, 390, 398, 406, 414, 422, 430, 438, 446, 455, 464, 473, 482, 491, 500, 509, 518, 527, 536, 545, 554, 563, 572, 581, 590, 599, 608, 617, 626, 635, 644, 653, 662, 671, 680, 689, 698, 707, 716, 725, 735, 745, 755, 765, 775, 785, 795, 805, 815, 825, 835, 845, 855, 865, 875, 885, 895, 905, 915, 925, 935, 945, 955} +) + +func (i AtomType) String() string { + switch { + case i <= 4: + return _AtomType_name_0[_AtomType_index_0[i]:_AtomType_index_0[i+1]] + case 8 <= i && i <= 9: + i -= 8 + return _AtomType_name_1[_AtomType_index_1[i]:_AtomType_index_1[i+1]] + case i == 11: + return _AtomType_name_2 + case 16 <= i && i <= 20: + i -= 16 + return _AtomType_name_3[_AtomType_index_3[i]:_AtomType_index_3[i+1]] + case 24 <= i && i <= 26: + i -= 24 + return _AtomType_name_4[_AtomType_index_4[i]:_AtomType_index_4[i+1]] + case 32 <= i && i <= 127: + i -= 32 + return _AtomType_name_5[_AtomType_index_5[i]:_AtomType_index_5[i+1]] + default: + return "AtomType(" + strconv.FormatInt(int64(i), 10) + ")" + } +} diff --git a/gyac/be.go b/gyac/be.go new file mode 100644 index 0000000..bf87384 --- /dev/null +++ b/gyac/be.go @@ -0,0 +1,29 @@ +// gyac -- Go YAC encoder implementation +// Copyright (C) 2024 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, version 3 of the License. +// +// 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this program. If not, see . + +package gyac + +func FromBE(buf []byte) (v uint64) { + for i := 0; i < len(buf); i++ { + v |= uint64(buf[i]) << ((len(buf) - i - 1) * 8) + } + return +} + +func ToBE(buf []byte, v uint64) { + for i := 0; i < len(buf); i++ { + buf[i] = byte((v & (0xFF << ((len(buf) - i - 1) * 8)) >> ((len(buf) - i - 1) * 8)) & 0xFF) + } +} diff --git a/gyac/blob.go b/gyac/blob.go new file mode 100644 index 0000000..75d87d9 --- /dev/null +++ b/gyac/blob.go @@ -0,0 +1,44 @@ +// gyac -- Go YAC encoder implementation +// Copyright (C) 2024 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, version 3 of the License. +// +// 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this program. If not, see . + +package gyac + +import "fmt" + +type Blob struct { + Chunks [][]byte + ChunkLen int +} + +func (blob *Blob) String() string { + var l int + for _, chunk := range blob.Chunks { + l += len(chunk) + } + return fmt.Sprintf("BLOB(%d, %d)", blob.ChunkLen, l) +} + +func MakeBlob(chunkLen int, data []byte) *Blob { + blob := Blob{ChunkLen: chunkLen} + n := len(data) / chunkLen + for i := 0; i < n; i++ { + blob.Chunks = append(blob.Chunks, data[i*chunkLen:(i+1)*chunkLen]) + } + left := len(data) - n*chunkLen + if left > 0 { + blob.Chunks = append(blob.Chunks, data[len(data)-left:]) + } + return &blob +} diff --git a/gyac/cmd/certool/basic.t b/gyac/cmd/certool/basic.t new file mode 100755 index 0000000..9db0be1 --- /dev/null +++ b/gyac/cmd/certool/basic.t @@ -0,0 +1,38 @@ +#!/bin/sh + +testname=`basename "$0"` +test_description="Check that basic functionality works" +. $SHARNESS_TEST_SRCDIR/sharness.sh + +TMPDIR=${TMPDIR:-/tmp} + +subj='{"CN": "CA", "C": "RU"}' +test_expect_success "CA generation" "certool \ + -algo gost3410-512C \ + -ku ca \ + -prv $TMPDIR/ca.prv -cer $TMPDIR/ca.cer \ + -subj '$subj'" + +test_expect_success "CA regeneration" "certool \ + -algo gost3410-512C \ + -ku ca \ + -prv $TMPDIR/ca.prv -cer $TMPDIR/ca.cer \ + -reuse-key \ + -subj '$subj'" +test_expect_success "CA self-signature" "certool \ + -ca-cer $TMPDIR/ca.cer \ + -cer $TMPDIR/ca.cer \ + -verify" + +subj='{"CN": "EE", "C": "RU"}' +test_expect_success "EE generation" "certool \ + -algo gost3410-256A \ + -ca-prv $TMPDIR/ca.prv -ca-cer $TMPDIR/ca.cer \ + -prv $TMPDIR/ee.prv -cer $TMPDIR/ee.cer \ + -subj '$subj'" +test_expect_success "EE chain" "certool \ + -ca-cer $TMPDIR/ca.cer \ + -cer $TMPDIR/ee.cer \ + -verify" + +test_done diff --git a/gyac/cmd/certool/cer.go b/gyac/cmd/certool/cer.go new file mode 100644 index 0000000..4ebc352 --- /dev/null +++ b/gyac/cmd/certool/cer.go @@ -0,0 +1,78 @@ +package main + +import ( + "errors" + "os" + "time" + + "github.com/google/uuid" + "go.cypherpunks.su/yac/gyac" +) + +type AV struct { + A string `yac:"a"` + V []byte `yac:"v"` +} + +type CerTBS struct { + KU []string `yac:"ku,omitempty"` + Subj map[string]string `yac:"sub"` + Exp []time.Time `yac:"exp"` + Crit []map[string]any `yac:"crit,omitempty"` + Pub AV `yac:"pub"` + KID uuid.UUID `yac:"kid"` +} + +func (tbs *CerTBS) HasCA() (hasCA bool) { + for _, ku := range tbs.KU { + if ku == "ca" { + hasCA = true + } + } + return +} + +type SignedDataLoad struct { + V any `yac:"v"` + T string `yac:"t"` +} + +type SignedDataTBS struct { + V any `yac:"v"` + Load map[string]any `yac:"load"` + T string `yac:"t"` + KID uuid.UUID `yac:"kid"` +} + +type Sig struct { + Load map[string]any `yac:"load,omitempty"` + CerLoc []string `yac:"cer-loc,omitempty"` + Sign AV `yac:"sign"` + KID uuid.UUID `yac:"kid"` +} + +type SignedData struct { + Hashes []string `yac:"hash,omitempty"` + Load SignedDataLoad `yac:"load"` + Sigs []Sig `yac:"sigs"` + Cers []SignedData `yac:"certs,omitempty"` +} + +func loadCerFromFile(pth string) (*SignedData, error) { + data, err := os.ReadFile(pth) + if err != nil { + return nil, err + } + var sd SignedData + err = gyac.DecodeToStruct(&sd, data) + if err != nil { + return nil, err + } + if sd.Load.T != "cer" { + err = errors.New("non \"cer\" in CA SignedData") + } + if len(sd.Sigs) == 0 { + err = errors.New("no signatures present") + } + return &sd, err +} diff --git a/gyac/cmd/certool/go.mod b/gyac/cmd/certool/go.mod new file mode 100644 index 0000000..c8dfebd --- /dev/null +++ b/gyac/cmd/certool/go.mod @@ -0,0 +1,16 @@ +module go.cypherpunks.su/yac/gyac/cmd/certool + +go 1.22 + +require ( + github.com/google/uuid v1.6.0 + go.cypherpunks.su/gogost/v6 v6.0.1 + go.cypherpunks.su/yac/gyac v0.0.0-00010101000000-000000000000 +) + +require ( + github.com/mitchellh/mapstructure v1.5.0 // indirect + go.cypherpunks.su/tai64n/v3 v3.1.0 // indirect +) + +replace go.cypherpunks.su/yac/gyac => ../.. diff --git a/gyac/cmd/certool/go.sum b/gyac/cmd/certool/go.sum new file mode 100644 index 0000000..90b7e96 --- /dev/null +++ b/gyac/cmd/certool/go.sum @@ -0,0 +1,10 @@ +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +go.cypherpunks.su/gogost/v6 v6.0.1 h1:PFjBnUmfdbx7L5R6hRt/+ZgGwWx45wTIWezFSgmknrs= +go.cypherpunks.su/gogost/v6 v6.0.1/go.mod h1:qJm0B7KJY4/OD5nYqL10kXY09dUwu2AfwSPu72Otngs= +go.cypherpunks.su/tai64n/v3 v3.1.0 h1:cdGnanxA5/H3hc37BO9D3h/exChVNEvrPWjTT/kuwQ4= +go.cypherpunks.su/tai64n/v3 v3.1.0/go.mod h1:zGDFuyiFKJk+iem8lyBaFeCm+MNMOn7RRWy456n1J78= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= diff --git a/gyac/cmd/certool/main.go b/gyac/cmd/certool/main.go new file mode 100644 index 0000000..1a3cc65 --- /dev/null +++ b/gyac/cmd/certool/main.go @@ -0,0 +1,265 @@ +package main + +import ( + "bytes" + "crypto/rand" + "encoding/json" + "flag" + "hash" + "io" + "log" + "os" + "sort" + "time" + + "github.com/google/uuid" + "go.cypherpunks.su/gogost/v6/gost3410" + "go.cypherpunks.su/gogost/v6/gost34112012256" + "go.cypherpunks.su/gogost/v6/gost34112012512" + "go.cypherpunks.su/yac/gyac" +) + +const ( + AlgoStreebog256 = "streebog256" + AlgoGOST3410256A = "gost3410-256A" + AlgoGOST3410256B = "gost3410-256B" + AlgoGOST3410256C = "gost3410-256C" + AlgoGOST3410256D = "gost3410-256D" + AlgoGOST3410512A = "gost3410-512A" + AlgoGOST3410512B = "gost3410-512B" + AlgoGOST3410512C = "gost3410-512C" +) + +func GOST3410CurveByName(name string) (curve *gost3410.Curve) { + switch name { + case AlgoGOST3410256A: + curve = gost3410.CurveIdtc26gost341012256paramSetA() + case AlgoGOST3410256B: + curve = gost3410.CurveIdtc26gost341012256paramSetB() + case AlgoGOST3410256C: + curve = gost3410.CurveIdtc26gost341012256paramSetC() + case AlgoGOST3410256D: + curve = gost3410.CurveIdtc26gost341012256paramSetD() + case AlgoGOST3410512A: + curve = gost3410.CurveIdtc26gost341012512paramSetA() + case AlgoGOST3410512B: + curve = gost3410.CurveIdtc26gost341012512paramSetB() + case AlgoGOST3410512C: + curve = gost3410.CurveIdtc26gost341012512paramSetC() + } + return +} + +func HasherByKeyAlgo(a string) hash.Hash { + switch a { + case AlgoGOST3410256A, AlgoGOST3410256B, AlgoGOST3410256C, AlgoGOST3410256D: + return gost34112012256.New() + case AlgoGOST3410512A, AlgoGOST3410512B, AlgoGOST3410512C: + return gost34112012512.New() + default: + log.Fatal("unsupported CA algorithm") + } + return nil +} + +func main() { + kuMap := make(map[string]struct{}) + flag.Func( + "ku", + "Optional key usage, may be specified multiple times", + func(v string) error { + kuMap[v] = struct{}{} + return nil + }, + ) + sinceRaw := flag.String("since", "", + "Optional notBefore, \"2006-01-02 15:04:05\" format") + lifetime := flag.Uint("lifetime", 365, + "Lifetime of the certificate, days") + subjRaw := flag.String("subj", `{"CN": "test"}`, + "JSON map of the subject") + algo := flag.String("algo", "gost3410-256A", "Public key algorithm") + issuingCer := flag.String("ca-cer", "", + "Path to certificate file for issuing with") + issuingPrv := flag.String("ca-prv", "", + "Path to private key file for issuing with") + reuseKey := flag.Bool("reuse-key", false, + "Reuse the key, do not generate new one") + prvPath := flag.String("prv", "", "Path to private key file") + cerPath := flag.String("cer", "", "Path to certificate file") + verify := flag.Bool("verify", false, "Verify provided -cer with ca-cer") + + flag.Parse() + log.SetFlags(log.Lshortfile) + + if *cerPath == "" { + log.Fatal("no -cer is set") + } + + var ku []string + for k := range kuMap { + ku = append(ku, k) + } + kuMap = nil + sort.Sort(gyac.ByLenFirst(ku)) + + var subj map[string]string + err := json.Unmarshal([]byte(*subjRaw), &subj) + if err != nil { + log.Fatalln("while parsing -subj:", err) + } + + var since time.Time + if *sinceRaw == "" { + since = time.Now().UTC().Truncate(time.Second) + } else { + since, err = time.Parse(time.DateTime, *sinceRaw) + if err != nil { + log.Fatalln("while parsing -since:", err) + } + } + till := since.Add(time.Duration(*lifetime) * 24 * time.Hour) + + var caPrv *gost3410.PrivateKey + var caCerTBS CerTBS + if *issuingCer != "" { + var sd *SignedData + sd, err = loadCerFromFile(*issuingCer) + if err != nil { + log.Fatal(err) + } + err = gyac.MapToStruct(&caCerTBS, sd.Load.V.(map[string]any)) + if err != nil { + log.Fatal(err) + } + if !*verify { + if *issuingPrv == "" { + log.Fatal("no -issuing-key is set") + } + caPrv, err = loadPrvFromFile(*issuingPrv) + if err != nil { + log.Fatal(err) + } + } + } + + if *verify { + var sd *SignedData + sd, err = loadCerFromFile(*cerPath) + if err != nil { + log.Fatal(err) + } + var tbs CerTBS + err = gyac.MapToStruct(&tbs, sd.Load.V.(map[string]any)) + if err != nil { + log.Fatal(err) + } + sig := sd.Sigs[0] + if sig.KID != caCerTBS.KID { + log.Fatal("SKID != AKID") + } + if sig.KID != tbs.KID && !caCerTBS.HasCA() { + log.Fatal("no \"ca\" KU met in CA") + } + sdTBS := SignedDataTBS{T: "cer", V: tbs, KID: sig.KID} + hasher := HasherByKeyAlgo(sig.Sign.A) + hasher.Write(gyac.EncodeItem(nil, gyac.ItemFromGo(sdTBS))) + var pub *gost3410.PublicKey + pub, err = gost3410.NewPublicKeyBE( + GOST3410CurveByName(caCerTBS.Pub.A), + caCerTBS.Pub.V, + ) + if err != nil { + log.Fatal(err) + } + var valid bool + valid, err = pub.VerifyDigest(hasher.Sum(nil), sig.Sign.V) + if err != nil { + log.Fatal(err) + } + if !valid { + os.Exit(1) + } + return + } + + if *prvPath == "" { + log.Fatal("no -prv is set") + } + + curve := GOST3410CurveByName(*algo) + if curve == nil { + log.Fatal("unknown -algo specified") + } + var prv *gost3410.PrivateKey + if *reuseKey { + prv, err = loadPrvFromFile(*prvPath) + if err != nil { + log.Fatal(err) + } + if prv.C.Name != curve.Name { + log.Fatal("-algo is not same with private key") + } + } else { + prvRaw := make([]byte, curve.PointSize()) + if _, err = io.ReadFull(rand.Reader, prvRaw); err != nil { + log.Fatal(err) + } + prv, err = gost3410.NewPrivateKeyBE(curve, prvRaw) + if err != nil { + log.Fatal(err) + } + err = os.WriteFile(*prvPath, gyac.EncodeItem(nil, + gyac.ItemFromGo(AV{A: *algo, V: prv.RawBE()})), 0o600) + if err != nil { + log.Fatal(err) + } + } + + var pub *gost3410.PublicKey + pub, err = prv.PublicKey() + if err != nil { + log.Fatal(err) + } + pubTBS := AV{A: *algo, V: pub.RawBE()} + var spki uuid.UUID + { + hasher := gost34112012256.New() + hasher.Write(gyac.EncodeItem(nil, gyac.ItemFromGo(pubTBS))) + spki, err = uuid.NewRandomFromReader(bytes.NewReader(hasher.Sum(nil))) + if err != nil { + log.Fatal(err) + } + } + cerTBS := CerTBS{ + KU: ku, + Exp: []time.Time{since, till}, + KID: spki, + Subj: subj, + Pub: pubTBS, + } + if caPrv == nil { + caPrv = prv + caCerTBS = cerTBS + } else { + if !caCerTBS.HasCA() { + log.Fatal("no \"ca\" KU met in CA") + } + } + sig := Sig{KID: caCerTBS.KID} + sdTBS := SignedDataTBS{T: "cer", V: cerTBS, KID: sig.KID} + { + hasher := HasherByKeyAlgo(caCerTBS.Pub.A) + hasher.Write(gyac.EncodeItem(nil, gyac.ItemFromGo(sdTBS))) + sig.Sign = AV{A: caCerTBS.Pub.A} + sig.Sign.V, err = caPrv.SignDigest(hasher.Sum(nil), rand.Reader) + if err != nil { + log.Fatal(err) + } + } + cer := SignedData{Load: SignedDataLoad{T: "cer", V: cerTBS}, Sigs: []Sig{sig}} + err = os.WriteFile(*cerPath, gyac.EncodeItem(nil, gyac.ItemFromGo(cer)), 0o666) + if err != nil { + log.Fatal(err) + } +} diff --git a/gyac/cmd/certool/prv.go b/gyac/cmd/certool/prv.go new file mode 100644 index 0000000..ac3cf3b --- /dev/null +++ b/gyac/cmd/certool/prv.go @@ -0,0 +1,56 @@ +package main + +import ( + "fmt" + "os" + + "go.cypherpunks.su/gogost/v6/gost3410" + "go.cypherpunks.su/yac/gyac" +) + +func loadPrvFromFile(pth string) (prv *gost3410.PrivateKey, err error) { + var data []byte + data, err = os.ReadFile(pth) + if err != nil { + return + } + var av AV + err = gyac.DecodeToStruct(&av, data) + if err != nil { + return + } + switch av.A { + case AlgoGOST3410256A: + prv, err = gost3410.NewPrivateKeyBE( + gost3410.CurveIdtc26gost341012256paramSetA(), av.V, + ) + case AlgoGOST3410256B: + prv, err = gost3410.NewPrivateKeyBE( + gost3410.CurveIdtc26gost341012256paramSetB(), av.V, + ) + case AlgoGOST3410256C: + prv, err = gost3410.NewPrivateKeyBE( + gost3410.CurveIdtc26gost341012256paramSetC(), av.V, + ) + case AlgoGOST3410256D: + prv, err = gost3410.NewPrivateKeyBE( + gost3410.CurveIdtc26gost341012256paramSetD(), av.V, + ) + case AlgoGOST3410512A: + prv, err = gost3410.NewPrivateKeyBE( + gost3410.CurveIdtc26gost341012512paramSetA(), av.V, + ) + case AlgoGOST3410512B: + prv, err = gost3410.NewPrivateKeyBE( + gost3410.CurveIdtc26gost341012512paramSetB(), av.V, + ) + case AlgoGOST3410512C: + prv, err = gost3410.NewPrivateKeyBE( + gost3410.CurveIdtc26gost341012512paramSetC(), av.V, + ) + default: + err = fmt.Errorf("unknown private key algo: %s", av.A) + return + } + return +} diff --git a/gyac/cmd/print/main.go b/gyac/cmd/print/main.go new file mode 100644 index 0000000..93d9369 --- /dev/null +++ b/gyac/cmd/print/main.go @@ -0,0 +1,26 @@ +package main + +import ( + "bufio" + "fmt" + "io" + "log" + "os" + + "go.cypherpunks.su/yac/gyac" +) + +func main() { + data, err := io.ReadAll(bufio.NewReader(os.Stdin)) + if err != nil { + log.Fatal(err) + } + item, tail, err := gyac.DecodeItem(data) + if err != nil { + log.Fatal(err) + } + if len(tail) > 0 { + log.Fatalln("trailing data:", tail) + } + fmt.Printf("%v\n", item.ToGo()) +} diff --git a/gyac/cmd/test-vector-anys/main.go b/gyac/cmd/test-vector-anys/main.go new file mode 100644 index 0000000..92a2cde --- /dev/null +++ b/gyac/cmd/test-vector-anys/main.go @@ -0,0 +1,81 @@ +package main + +import ( + "bytes" + "encoding/hex" + "fmt" + "time" + + "github.com/google/uuid" + "go.cypherpunks.su/yac/gyac" +) + +func main() { + data := map[string]any{ + "ints": map[string]any{ + "pos": []any{ + uint64(0), + uint64(1), + uint64(31), + uint64(32), + uint64(123), + uint64(1234), + uint64(12345678), + &gyac.Raw{T: gyac.AtomIntPos11, V: []byte("\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")}, + }, + "neg": []any{ + int64(-1), + int64(-2), + int64(-32), + int64(-33), + int64(-123), + int64(-1234), + int64(-12345678), + &gyac.Raw{T: gyac.AtomIntNeg10, V: []byte("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff")}, + }, + }, + "nil": nil, + "bool": []any{true, false}, + "str": map[string]any{ + "bin": []any{ + []byte{}, + bytes.Repeat([]byte{'0'}, 60), + bytes.Repeat([]byte{'1'}, 61), + bytes.Repeat([]byte{'2'}, 255), + bytes.Repeat([]byte{'3'}, 1024), + bytes.Repeat([]byte{'4'}, 1<<16), + }, + "utf8": "привет мир", + }, + "blob": []any{ + gyac.MakeBlob(12, []byte{'5'}), + gyac.MakeBlob(12, bytes.Repeat([]byte{'6'}, 12)), + gyac.MakeBlob(12, bytes.Repeat([]byte{'7'}, 13)), + gyac.MakeBlob(5, []byte("1234567890-")), + }, + "empties": []any{ + []any{}, + map[string]any{}, + gyac.MakeBlob(123, []byte{}), + uuid.MustParse("00000000-0000-0000-0000-000000000000"), + &gyac.Raw{ + T: gyac.AtomTAI64, + V: []byte("\x00\x00\x00\x00\x00\x00\x00\x00"), + }, + }, + "dates": []any{ + time.Unix(1234567890, 0), + time.Unix(1234567890, 456*1000), + time.Unix(1234567890, 456789), + &gyac.Raw{ + T: gyac.AtomTAI64NA, + V: []byte("\x40\x00\x00\x00\x49\x96\x02\xF4\x00\x06\xF8\x55\x07\x5B\xCD\x15"), + }, + }, + "floats": []any{ + &gyac.Raw{T: gyac.AtomFloat32, V: []byte("\x01\x02\x03\x04")}, + }, + "uuid": uuid.MustParse("0e875e3f-d385-49eb-87b4-be42d641c367"), + } + fmt.Println(hex.EncodeToString(gyac.EncodeItem(nil, gyac.ItemFromGo(data)))) +} diff --git a/gyac/cmd/test-vector-manual/main.go b/gyac/cmd/test-vector-manual/main.go new file mode 100644 index 0000000..b053753 --- /dev/null +++ b/gyac/cmd/test-vector-manual/main.go @@ -0,0 +1,184 @@ +package main + +import ( + "bytes" + "encoding/hex" + "fmt" + "time" + + "github.com/google/uuid" + "go.cypherpunks.su/tai64n/v3" + "go.cypherpunks.su/yac/gyac" +) + +func main() { + buf := make([]byte, 0, 68*1024) + { + buf = gyac.AtomMapEncode(buf) + { + buf = gyac.AtomStrEncode(buf, "nil") + buf = gyac.AtomNILEncode(buf) + } + { + buf = gyac.AtomStrEncode(buf, "str") + buf = gyac.AtomMapEncode(buf) + { + buf = gyac.AtomStrEncode(buf, "bin") + buf = gyac.AtomListEncode(buf) + { + buf = gyac.AtomBinEncode(buf, []byte("")) + buf = gyac.AtomBinEncode(buf, bytes.Repeat([]byte{'0'}, 60)) + buf = gyac.AtomBinEncode(buf, bytes.Repeat([]byte{'1'}, 61)) + buf = gyac.AtomBinEncode(buf, bytes.Repeat([]byte{'2'}, 255)) + buf = gyac.AtomBinEncode(buf, bytes.Repeat([]byte{'3'}, 1024)) + buf = gyac.AtomBinEncode(buf, bytes.Repeat([]byte{'4'}, 1<<16)) + } + buf = gyac.AtomEOCEncode(buf) + { + buf = gyac.AtomStrEncode(buf, "utf8") + buf = gyac.AtomStrEncode(buf, "привет мир") + } + } + buf = gyac.AtomEOCEncode(buf) + } + { + buf = gyac.AtomStrEncode(buf, "blob") + buf = gyac.AtomListEncode(buf) + { + buf = gyac.AtomBlobEncode(buf, 12) + buf = gyac.AtomBinEncode(buf, []byte{'5'}) + } + { + buf = gyac.AtomBlobEncode(buf, 12) + buf = gyac.AtomChunkEncode(buf, bytes.Repeat([]byte{'6'}, 12)) + buf = gyac.AtomBinEncode(buf, []byte{}) + } + { + buf = gyac.AtomBlobEncode(buf, 12) + buf = gyac.AtomChunkEncode(buf, bytes.Repeat([]byte{'7'}, 12)) + buf = gyac.AtomBinEncode(buf, []byte{'7'}) + } + { + buf = gyac.AtomBlobEncode(buf, 5) + buf = gyac.AtomChunkEncode(buf, []byte("12345")) + buf = gyac.AtomChunkEncode(buf, []byte("67890")) + buf = gyac.AtomBinEncode(buf, []byte{'-'}) + } + buf = gyac.AtomEOCEncode(buf) + } + { + buf = gyac.AtomStrEncode(buf, "bool") + buf = gyac.AtomListEncode(buf) + buf = gyac.AtomBoolEncode(buf, true) + buf = gyac.AtomBoolEncode(buf, false) + buf = gyac.AtomEOCEncode(buf) + } + { + buf = gyac.AtomStrEncode(buf, "ints") + buf = gyac.AtomMapEncode(buf) + { + buf = gyac.AtomStrEncode(buf, "neg") + buf = gyac.AtomListEncode(buf) + buf = gyac.AtomIntEncode(buf, -1) + buf = gyac.AtomIntEncode(buf, -2) + buf = gyac.AtomIntEncode(buf, -32) + buf = gyac.AtomIntEncode(buf, -33) + buf = gyac.AtomIntEncode(buf, -123) + buf = gyac.AtomIntEncode(buf, -1234) + buf = gyac.AtomIntEncode(buf, -12345678) + buf = gyac.AtomRawEncode(buf, &gyac.Raw{ + T: gyac.AtomIntNeg10, + V: []byte("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"), + }) + buf = gyac.AtomEOCEncode(buf) + } + { + buf = gyac.AtomStrEncode(buf, "pos") + buf = gyac.AtomListEncode(buf) + buf = gyac.AtomUIntEncode(buf, 0) + buf = gyac.AtomUIntEncode(buf, 1) + buf = gyac.AtomUIntEncode(buf, 31) + buf = gyac.AtomUIntEncode(buf, 32) + buf = gyac.AtomUIntEncode(buf, 123) + buf = gyac.AtomUIntEncode(buf, 1234) + buf = gyac.AtomUIntEncode(buf, 12345678) + buf = gyac.AtomRawEncode(buf, &gyac.Raw{ + T: gyac.AtomIntPos11, + V: []byte("\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), + }) + buf = gyac.AtomEOCEncode(buf) + } + buf = gyac.AtomEOCEncode(buf) + } + { + buf = gyac.AtomStrEncode(buf, "uuid") + buf = gyac.AtomUUIDEncode(buf, + uuid.MustParse("0e875e3f-d385-49eb-87b4-be42d641c367")) + } + { + buf = gyac.AtomStrEncode(buf, "dates") + buf = gyac.AtomListEncode(buf) + { + var tai tai64n.TAI64 + t := time.Unix(1234567890, 0) + t = tai64n.LeapsecsAdd(t) + tai.FromTime(t) + buf = gyac.AtomTAI64Encode(buf, tai[:]) + } + { + var tai tai64n.TAI64N + t := time.Unix(1234567890, 456*1000) + t = tai64n.LeapsecsAdd(t) + tai.FromTime(t) + buf = gyac.AtomTAI64Encode(buf, tai[:]) + } + { + var tai tai64n.TAI64N + t := time.Unix(1234567890, 456789) + t = tai64n.LeapsecsAdd(t) + tai.FromTime(t) + buf = gyac.AtomTAI64Encode(buf, tai[:]) + } + buf = gyac.AtomRawEncode(buf, &gyac.Raw{ + T: gyac.AtomTAI64NA, + V: []byte("\x40\x00\x00\x00\x49\x96\x02\xF4\x00\x06\xF8\x55\x07\x5B\xCD\x15"), + }) + buf = gyac.AtomEOCEncode(buf) + } + { + buf = gyac.AtomStrEncode(buf, "floats") + buf = gyac.AtomListEncode(buf) + buf = gyac.AtomRawEncode(buf, &gyac.Raw{ + T: gyac.AtomFloat32, + V: []byte("\x01\x02\x03\x04"), + }) + buf = gyac.AtomEOCEncode(buf) + } + { + buf = gyac.AtomStrEncode(buf, "empties") + buf = gyac.AtomListEncode(buf) + { + buf = gyac.AtomListEncode(buf) + buf = gyac.AtomEOCEncode(buf) + } + { + buf = gyac.AtomMapEncode(buf) + buf = gyac.AtomEOCEncode(buf) + } + { + buf = gyac.AtomBlobEncode(buf, 123) + buf = gyac.AtomBinEncode(buf, []byte{}) + } + buf = gyac.AtomUUIDEncode(buf, + uuid.MustParse("00000000-0000-0000-0000-000000000000")) + buf = gyac.AtomRawEncode(buf, &gyac.Raw{ + T: gyac.AtomTAI64, + V: []byte("\x00\x00\x00\x00\x00\x00\x00\x00"), + }) + buf = gyac.AtomEOCEncode(buf) + } + + buf = gyac.AtomEOCEncode(buf) + } + fmt.Println(hex.EncodeToString(buf)) +} diff --git a/gyac/dec.go b/gyac/dec.go new file mode 100644 index 0000000..c4ecb30 --- /dev/null +++ b/gyac/dec.go @@ -0,0 +1,382 @@ +// gyac -- Go YAC encoder implementation +// Copyright (C) 2024 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, version 3 of the License. +// +// 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this program. If not, see . + +package gyac + +import ( + "encoding/hex" + "errors" + "fmt" + "unicode/utf8" + "unsafe" + + "github.com/google/uuid" +) + +type ItemType byte + +//go:generate stringer -type=ItemType +const ( + ItemEOC ItemType = iota + ItemNIL + ItemBool + ItemUUID + ItemUInt + ItemInt + ItemList + ItemMap + ItemBlob + ItemFloat + ItemTAI64 + ItemBin + ItemStr + ItemRaw +) + +type Item struct { + V any + T byte +} + +type Raw struct { + V []byte + T AtomType +} + +func (raw *Raw) String() string { + return fmt.Sprintf("RAW(%v, %s)", raw.T, hex.EncodeToString(raw.V)) +} + +var ( + ErrNotEnough = errors.New("not enough data") + ErrLenNonMinimal = errors.New("non-minimal len") + ErrIntZeroByte = errors.New("zero byte int") + ErrUnknownType = errors.New("unknown type") + ErrBadUTF8 = errors.New("invalid UTF-8") + ErrMapBadKey = errors.New("map bad key") + ErrMapUnordered = errors.New("map unordered") + ErrMapBadVal = errors.New("map no value") + ErrBlobBadLen = errors.New("bad blob len") + ErrBlobBadAtom = errors.New("blob unexpected atom") + ErrBlobShortChunk = errors.New("blob short chunk") + ErrBlobBadTerm = errors.New("blob bad terminator") +) + +func AtomDecode(buf []byte) (item *Item, off int, err error) { + off++ + if len(buf) < 1 { + err = ErrNotEnough + return + } + item = &Item{T: buf[0]} + if (item.T & AtomStrings) > 0 { + l := int(item.T & 63) + if (item.T & AtomIsUTF8) == 0 { + item.T = byte(ItemBin) + } else { + item.T = byte(ItemStr) + } + ll := 0 + switch l { + case 61: + ll = 1 + case 62: + ll = 2 + case 63: + ll = 8 + } + if ll != 0 { + off += ll + if len(buf) < off { + err = ErrNotEnough + return + } + l = int(FromBE(buf[1 : 1+ll])) + if (l < 61) || ((l < (1 << 8)) && (ll > 1)) || ((l < (1 << 16)) && (ll > 2)) { + err = ErrLenNonMinimal + return + } + } + off += l + if len(buf) < off { + err = ErrNotEnough + return + } + if ItemType(item.T) == ItemBin { + item.V = buf[1+ll : 1+ll+l] + } else { + s := unsafe.String(unsafe.SliceData(buf[1+ll:]), l) + item.V = s + if !utf8.ValidString(s) { + err = ErrBadUTF8 + } + } + return + } + if AtomType(item.T) >= AtomInt0 && AtomType(item.T) < AtomInt0+32 { + item.V = uint64(item.T - byte(AtomInt0)) + item.T = byte(ItemUInt) + return + } + if AtomType(item.T) >= AtomIntN1 && AtomType(item.T) < AtomIntN1+32 { + item.V = -1 - int64(item.T-byte(AtomIntN1)) + item.T = byte(ItemInt) + return + } + switch item.T & 0xF0 { + case byte(AtomIntPos1), byte(AtomIntNeg1): + l := int((item.T & 0x0F) + 1) + if (item.T & byte(AtomIntNeg1)) == byte(AtomIntNeg1) { + item.T = byte(ItemInt) + } else { + item.T = byte(ItemUInt) + } + off += l + if len(buf) < off { + err = ErrNotEnough + return + } + if buf[1] == 0 { + err = ErrIntZeroByte + return + } + if l > 8 { + item.T = byte(ItemRaw) + item.V = &Raw{T: AtomType(buf[0]), V: buf[1 : 1+l]} + return + } + v := FromBE(buf[1 : 1+l]) + if v < 32 { + err = ErrLenNonMinimal + return + } + if ItemType(item.T) == ItemUInt { + item.V = v + } else { + item.V = -1 - int64(v) + } + return + } + switch AtomType(item.T) { + case AtomEOC: + item.T = byte(ItemEOC) + case AtomNIL: + item.T = byte(ItemNIL) + case AtomFalse: + item.T = byte(ItemBool) + item.V = false + case AtomTrue: + item.T = byte(ItemBool) + item.V = true + case AtomUUID: + off += 16 + item.T = byte(ItemUUID) + if len(buf) < off { + err = ErrNotEnough + return + } + item.V, err = uuid.FromBytes(buf[1 : 1+16]) + + case AtomList: + item.T = byte(ItemList) + case AtomMap: + item.T = byte(ItemMap) + case AtomBlob: + item.T = byte(ItemBlob) + + case AtomFloat16, AtomFloat32, AtomFloat64, AtomFloat128, AtomFloat256: + var l int + switch AtomType(item.T) { + case AtomFloat16: + l = 2 + case AtomFloat32: + l = 4 + case AtomFloat64: + l = 8 + case AtomFloat128: + l = 16 + case AtomFloat256: + l = 32 + } + off += l + if len(buf) < off { + item.T = byte(ItemFloat) + err = ErrNotEnough + return + } + item.T = byte(ItemRaw) + item.V = &Raw{T: AtomType(buf[0]), V: buf[1 : 1+l]} + + case AtomTAI64, AtomTAI64N, AtomTAI64NA: + var l int + switch item.T { + case byte(AtomTAI64): + l = 8 + case byte(AtomTAI64N): + l = 12 + case byte(AtomTAI64NA): + l = 16 + } + off += l + if len(buf) < off { + err = ErrNotEnough + return + } + item.T = byte(ItemTAI64) + item.V = buf[1 : 1+l] + if FromBE(buf[1:1+8]) > (1 << 63) { + err = errors.New("reserved TAI64 values in use") + return + } + switch l { + case 12: + if FromBE(buf[1+8:1+8+4]) > 999999999 { + err = errors.New("too many nanoseconds") + return + } + case 16: + if FromBE(buf[1+8+4:1+8+4+4]) > 999999999 { + err = errors.New("too many attoseconds") + return + } + } + + default: + err = ErrUnknownType + return + } + return +} + +func DecodeItem(buf []byte) (item *Item, tail []byte, err error) { + var off int + item, off, err = AtomDecode(buf) + buf = buf[off:] + tail = buf + if err != nil { + return + } + switch ItemType(item.T) { + case ItemList: + var sub *Item + var v []*Item + for { + sub, buf, err = DecodeItem(buf) + tail = buf + if err != nil { + tail = buf + return + } + if sub.T == byte(ItemEOC) { + break + } + v = append(v, sub) + } + item.V = v + return + case ItemMap: + v := make(map[string]*Item) + var sub *Item + var keyPrev string + for { + sub, buf, err = DecodeItem(buf) + tail = buf + if err != nil { + return + } + if sub.T == byte(ItemEOC) { + break + } + if sub.T != byte(ItemStr) { + err = ErrMapBadKey + return + } + { + s := sub.V.(string) + if len(s) < len(keyPrev) { + err = ErrMapUnordered + return + } else if (len(s) == len(keyPrev)) && s <= keyPrev { + err = ErrMapUnordered + return + } + keyPrev = s + } + sub, buf, err = DecodeItem(buf) + tail = buf + if err != nil { + return + } + if sub.T == byte(ItemEOC) { + err = ErrMapBadVal + return + } + v[keyPrev] = sub + } + item.V = v + return + case ItemBlob: + var sub *Item + sub, buf, err = DecodeItem(buf) + tail = buf + if err != nil { + return + } + if sub.T != byte(ItemUInt) { + err = ErrBlobBadLen + return + } + chunkLen := int(sub.V.(uint64)) + if chunkLen == 0 { + err = ErrBlobBadLen + return + } + v := &Blob{ChunkLen: chunkLen} + BlobCycle: + for { + sub, buf, err = DecodeItem(buf) + tail = buf + if err != nil { + return + } + switch sub.T { + case byte(ItemNIL): + if len(buf) <= chunkLen { + err = ErrBlobShortChunk + return + } + v.Chunks = append(v.Chunks, buf[:chunkLen]) + buf = buf[chunkLen:] + tail = buf + case byte(ItemBin): + b := sub.V.([]byte) + if len(b) >= chunkLen { + err = ErrBlobBadTerm + return + } + if len(b) != 0 { + v.Chunks = append(v.Chunks, b) + } + break BlobCycle + default: + err = ErrBlobBadAtom + return + } + } + item.V = v + return + } + return +} diff --git a/gyac/enc.go b/gyac/enc.go new file mode 100644 index 0000000..a9350e7 --- /dev/null +++ b/gyac/enc.go @@ -0,0 +1,333 @@ +// gyac -- Go YAC encoder implementation +// Copyright (C) 2024 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, version 3 of the License. +// +// 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this program. If not, see . + +package gyac + +import ( + "sort" + + "github.com/google/uuid" +) + +type AtomType byte + +//go:generate stringer -type=AtomType +const ( + AtomEOC AtomType = 0x00 + AtomNIL AtomType = 0x01 + AtomFalse AtomType = 0x02 + AtomTrue AtomType = 0x03 + AtomUUID AtomType = 0x04 + AtomList AtomType = 0x08 + AtomMap AtomType = 0x09 + AtomBlob AtomType = 0x0B + AtomFloat16 AtomType = 0x10 + AtomFloat32 AtomType = 0x11 + AtomFloat64 AtomType = 0x12 + AtomFloat128 AtomType = 0x13 + AtomFloat256 AtomType = 0x14 + AtomTAI64 AtomType = 0x18 + AtomTAI64N AtomType = 0x19 + AtomTAI64NA AtomType = 0x1A + AtomIntPos1 AtomType = 0x20 + AtomIntPos2 AtomType = 0x21 + AtomIntPos3 AtomType = 0x22 + AtomIntPos4 AtomType = 0x23 + AtomIntPos5 AtomType = 0x24 + AtomIntPos6 AtomType = 0x25 + AtomIntPos7 AtomType = 0x26 + AtomIntPos8 AtomType = 0x27 + AtomIntPos9 AtomType = 0x28 + AtomIntPos10 AtomType = 0x29 + AtomIntPos11 AtomType = 0x2A + AtomIntPos12 AtomType = 0x2B + AtomIntPos13 AtomType = 0x2C + AtomIntPos14 AtomType = 0x2D + AtomIntPos15 AtomType = 0x2E + AtomIntPos16 AtomType = 0x2F + AtomIntNeg1 AtomType = 0x30 + AtomIntNeg2 AtomType = 0x31 + AtomIntNeg3 AtomType = 0x32 + AtomIntNeg4 AtomType = 0x33 + AtomIntNeg5 AtomType = 0x34 + AtomIntNeg6 AtomType = 0x35 + AtomIntNeg7 AtomType = 0x36 + AtomIntNeg8 AtomType = 0x37 + AtomIntNeg9 AtomType = 0x38 + AtomIntNeg10 AtomType = 0x39 + AtomIntNeg11 AtomType = 0x3A + AtomIntNeg12 AtomType = 0x3B + AtomIntNeg13 AtomType = 0x3C + AtomIntNeg14 AtomType = 0x3D + AtomIntNeg15 AtomType = 0x3E + AtomIntNeg16 AtomType = 0x3F + AtomInt0 AtomType = 0x40 + AtomInt1 AtomType = 0x41 + AtomInt2 AtomType = 0x42 + AtomInt3 AtomType = 0x43 + AtomInt4 AtomType = 0x44 + AtomInt5 AtomType = 0x45 + AtomInt6 AtomType = 0x46 + AtomInt7 AtomType = 0x47 + AtomInt8 AtomType = 0x48 + AtomInt9 AtomType = 0x49 + AtomInt10 AtomType = 0x4A + AtomInt11 AtomType = 0x4B + AtomInt12 AtomType = 0x4C + AtomInt13 AtomType = 0x4D + AtomInt14 AtomType = 0x4E + AtomInt15 AtomType = 0x4F + AtomInt16 AtomType = 0x50 + AtomInt17 AtomType = 0x51 + AtomInt18 AtomType = 0x52 + AtomInt19 AtomType = 0x53 + AtomInt20 AtomType = 0x54 + AtomInt21 AtomType = 0x55 + AtomInt22 AtomType = 0x56 + AtomInt23 AtomType = 0x57 + AtomInt24 AtomType = 0x58 + AtomInt25 AtomType = 0x59 + AtomInt26 AtomType = 0x5A + AtomInt27 AtomType = 0x5B + AtomInt28 AtomType = 0x5C + AtomInt29 AtomType = 0x5D + AtomInt30 AtomType = 0x5E + AtomInt31 AtomType = 0x5F + AtomIntN1 AtomType = 0x60 + AtomIntN2 AtomType = 0x61 + AtomIntN3 AtomType = 0x62 + AtomIntN4 AtomType = 0x63 + AtomIntN5 AtomType = 0x64 + AtomIntN6 AtomType = 0x65 + AtomIntN7 AtomType = 0x66 + AtomIntN8 AtomType = 0x67 + AtomIntN9 AtomType = 0x68 + AtomIntN10 AtomType = 0x69 + AtomIntN11 AtomType = 0x6A + AtomIntN12 AtomType = 0x6B + AtomIntN13 AtomType = 0x6C + AtomIntN14 AtomType = 0x6D + AtomIntN15 AtomType = 0x6E + AtomIntN16 AtomType = 0x6F + AtomIntN17 AtomType = 0x70 + AtomIntN18 AtomType = 0x71 + AtomIntN19 AtomType = 0x72 + AtomIntN20 AtomType = 0x73 + AtomIntN21 AtomType = 0x74 + AtomIntN22 AtomType = 0x75 + AtomIntN23 AtomType = 0x76 + AtomIntN24 AtomType = 0x77 + AtomIntN25 AtomType = 0x78 + AtomIntN26 AtomType = 0x79 + AtomIntN27 AtomType = 0x7A + AtomIntN28 AtomType = 0x7B + AtomIntN29 AtomType = 0x7C + AtomIntN30 AtomType = 0x7D + AtomIntN31 AtomType = 0x7E + AtomIntN32 AtomType = 0x7F + + AtomStrings = 0x80 + AtomIsUTF8 = 0x40 +) + +func AtomEOCEncode(buf []byte) []byte { + return append(buf, byte(AtomEOC)) +} + +func AtomNILEncode(buf []byte) []byte { + return append(buf, byte(AtomNIL)) +} + +func AtomBoolEncode(buf []byte, v bool) []byte { + if v { + return append(buf, byte(AtomTrue)) + } + return append(buf, byte(AtomFalse)) +} + +func AtomUUIDEncode(buf []byte, v uuid.UUID) []byte { + return append(append(buf, byte(AtomUUID)), v[:]...) +} + +func atomUintEncode(v uint64) (buf []byte) { + if v < 32 { + return []byte{byte(v)} + } + bits := 8 + l := 0 + for { + if v < (1 << bits) { + break + } + l++ + bits += 8 + } + buf = make([]byte, 1+l+1) + buf[0] = byte(l) + ToBE(buf[1:], v) + return +} + +func AtomUIntEncode(buf []byte, v uint64) []byte { + b := atomUintEncode(v) + if v < 32 { + b[0] |= byte(AtomInt0) + } else { + b[0] |= byte(AtomIntPos1) + } + return append(buf, b...) +} + +func AtomIntEncode(buf []byte, v int64) []byte { + if v >= 0 { + return AtomUIntEncode(buf, uint64(v)) + } + vp := uint64(-(v + 1)) + b := atomUintEncode(vp) + if vp < 32 { + b[0] |= byte(AtomIntN1) + } else { + b[0] |= byte(AtomIntNeg1) + } + return append(buf, b...) +} + +func AtomListEncode(buf []byte) []byte { + return append(buf, byte(AtomList)) +} + +func AtomMapEncode(buf []byte) []byte { + return append(buf, byte(AtomMap)) +} + +func AtomBlobEncode(buf []byte, chunkLen int) []byte { + return AtomUIntEncode(append(buf, byte(AtomBlob)), uint64(chunkLen)) +} + +func atomStrEncode(buf, data []byte, utf8 bool) []byte { + lv := 0 + var l []byte + if len(data) >= (1 << 16) { + lv = 63 + l = make([]byte, 8) + ToBE(l, uint64(len(data))) + } else if len(data) >= (1 << 8) { + lv = 62 + l = make([]byte, 2) + ToBE(l, uint64(len(data))) + } else if len(data) > 60 { + lv = 61 + l = []byte{byte(len(data))} + } else { + lv = len(data) + } + b := byte(AtomStrings | lv) + if utf8 { + b |= AtomIsUTF8 + } + return append(append(append(buf, b), l...), data...) +} + +func AtomStrEncode(buf []byte, str string) []byte { + return atomStrEncode(buf, []byte(str), true) +} + +func AtomBinEncode(buf, bin []byte) []byte { + return atomStrEncode(buf, bin, false) +} + +func AtomChunkEncode(buf, chunk []byte) []byte { + return append(append(buf, byte(AtomNIL)), chunk...) +} + +func AtomTAI64Encode(buf, tai []byte) []byte { + switch len(tai) { + case 8: + return append(append(buf, byte(AtomTAI64)), tai...) + case 12: + return append(append(buf, byte(AtomTAI64N)), tai...) + case 16: + return append(append(buf, byte(AtomTAI64NA)), tai...) + default: + panic("wrong TAI64 value") + } +} + +func AtomRawEncode(buf []byte, raw *Raw) []byte { + return append(append(buf, byte(raw.T)), raw.V...) +} + +func EncodeItem(buf []byte, item *Item) []byte { + switch ItemType(item.T) { + case ItemNIL: + return AtomNILEncode(buf) + case ItemBool: + return AtomBoolEncode(buf, item.V.(bool)) + case ItemUUID: + return AtomUUIDEncode(buf, item.V.(uuid.UUID)) + case ItemUInt: + return AtomUIntEncode(buf, item.V.(uint64)) + case ItemInt: + return AtomIntEncode(buf, item.V.(int64)) + case ItemList: + buf = AtomListEncode(buf) + for _, v := range item.V.([]*Item) { + buf = EncodeItem(buf, v) + } + buf = AtomEOCEncode(buf) + case ItemMap: + m := item.V.(map[string]*Item) + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + sort.Sort(ByLenFirst(keys)) + buf = AtomMapEncode(buf) + for _, k := range keys { + buf = AtomStrEncode(buf, k) + buf = EncodeItem(buf, m[k]) + } + buf = AtomEOCEncode(buf) + case ItemBlob: + blob := item.V.(*Blob) + buf = AtomBlobEncode(buf, blob.ChunkLen) + for _, chunk := range blob.Chunks { + if len(chunk) == blob.ChunkLen { + buf = AtomChunkEncode(buf, chunk) + } + } + if len(blob.Chunks) == 0 { + buf = AtomBinEncode(buf, []byte{}) + } else { + last := blob.Chunks[len(blob.Chunks)-1] + if len(last) == blob.ChunkLen { + buf = AtomBinEncode(buf, []byte{}) + } else { + buf = AtomBinEncode(buf, last) + } + } + case ItemTAI64: + return AtomTAI64Encode(buf, item.V.([]byte)) + case ItemBin: + return AtomBinEncode(buf, item.V.([]byte)) + case ItemStr: + return AtomStrEncode(buf, item.V.(string)) + case ItemRaw: + return AtomRawEncode(buf, item.V.(*Raw)) + default: + panic("unhandled type") + } + return buf +} diff --git a/gyac/go.mod b/gyac/go.mod new file mode 100644 index 0000000..1b9af1b --- /dev/null +++ b/gyac/go.mod @@ -0,0 +1,10 @@ +module go.cypherpunks.su/yac/gyac + +go 1.22 + +require go.cypherpunks.su/tai64n/v3 v3.1.0 + +require ( + github.com/google/uuid v1.6.0 + github.com/mitchellh/mapstructure v1.5.0 +) diff --git a/gyac/go.sum b/gyac/go.sum new file mode 100644 index 0000000..f78f8a5 --- /dev/null +++ b/gyac/go.sum @@ -0,0 +1,6 @@ +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +go.cypherpunks.su/tai64n/v3 v3.1.0 h1:cdGnanxA5/H3hc37BO9D3h/exChVNEvrPWjTT/kuwQ4= +go.cypherpunks.su/tai64n/v3 v3.1.0/go.mod h1:zGDFuyiFKJk+iem8lyBaFeCm+MNMOn7RRWy456n1J78= diff --git a/gyac/itemtype_string.go b/gyac/itemtype_string.go new file mode 100644 index 0000000..7016888 --- /dev/null +++ b/gyac/itemtype_string.go @@ -0,0 +1,36 @@ +// Code generated by "stringer -type=ItemType"; DO NOT EDIT. + +package gyac + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[ItemEOC-0] + _ = x[ItemNIL-1] + _ = x[ItemBool-2] + _ = x[ItemUUID-3] + _ = x[ItemUInt-4] + _ = x[ItemInt-5] + _ = x[ItemList-6] + _ = x[ItemMap-7] + _ = x[ItemBlob-8] + _ = x[ItemFloat-9] + _ = x[ItemTAI64-10] + _ = x[ItemBin-11] + _ = x[ItemStr-12] + _ = x[ItemRaw-13] +} + +const _ItemType_name = "ItemEOCItemNILItemBoolItemUUIDItemUIntItemIntItemListItemMapItemBlobItemFloatItemTAI64ItemBinItemStrItemRaw" + +var _ItemType_index = [...]uint8{0, 7, 14, 22, 30, 38, 45, 53, 60, 68, 77, 86, 93, 100, 107} + +func (i ItemType) String() string { + if i >= ItemType(len(_ItemType_index)-1) { + return "ItemType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _ItemType_name[_ItemType_index[i]:_ItemType_index[i+1]] +} diff --git a/gyac/reflect.go b/gyac/reflect.go new file mode 100644 index 0000000..b1be6ef --- /dev/null +++ b/gyac/reflect.go @@ -0,0 +1,240 @@ +// gyac -- Go YAC encoder implementation +// Copyright (C) 2024 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, version 3 of the License. +// +// 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this program. If not, see . + +package gyac + +import ( + "errors" + "fmt" + "reflect" + "strings" + "time" + + "github.com/google/uuid" + "github.com/mitchellh/mapstructure" + "go.cypherpunks.su/tai64n/v3" +) + +func (v *Item) ToGo() any { + switch ItemType(v.T) { + case ItemNIL: + return nil + case ItemBool: + return v.V.(bool) + case ItemUUID: + return v.V.(uuid.UUID) + case ItemUInt: + return v.V.(uint64) + case ItemInt: + return v.V.(int64) + case ItemList: + var ret []any + for _, v := range v.V.([]*Item) { + ret = append(ret, v.ToGo()) + } + return ret + case ItemMap: + ret := make(map[string]any) + for k, v := range v.V.(map[string]*Item) { + ret[k] = v.ToGo() + } + return ret + case ItemBlob: + return v.V.(*Blob) + case ItemFloat: + panic("float is unsupported") + case ItemTAI64: + tai := v.V.([]byte) + if len(tai) == tai64n.TAI64NASize { + return Raw{T: AtomTAI64NA, V: tai} + } + return tai64n.LeapsecsSub(tai64n.ToTime(tai)) + case ItemBin: + return v.V.([]byte) + case ItemStr: + return v.V.(string) + case ItemRaw: + return v.V.(*Raw) + default: + panic(fmt.Errorf("unhandled type: %+v", v)) + } +} + +func structTagRead(f reflect.StructField) (name string, omit bool) { + name = f.Name + v, ok := f.Tag.Lookup("yac") + if !ok { + return + } + opts := strings.Split(v, ",") + if opts[0] != "" { + name = opts[0] + } + if len(opts) == 2 && opts[1] == "omitempty" { + omit = true + } + return +} + +func ItemFromGo(v any) *Item { + if v == nil { + return &Item{T: byte(ItemNIL)} + } + rv := reflect.ValueOf(v) + if b, ok := v.([]byte); ok { + return &Item{T: byte(ItemBin), V: b} + } + switch v := v.(type) { + case *Blob: + return &Item{T: byte(ItemBlob), V: v} + case time.Time: + t := tai64n.LeapsecsAdd(v) + var taiRaw []byte + if t.Nanosecond() > 0 { + var tai tai64n.TAI64N + tai.FromTime(t) + taiRaw = tai[:] + } else { + var tai tai64n.TAI64 + tai.FromTime(t) + taiRaw = tai[:] + } + return &Item{T: byte(ItemTAI64), V: taiRaw} + case *Raw: + return &Item{T: byte(ItemRaw), V: v} + } + switch reflect.TypeOf(v).Kind() { + case reflect.Pointer: + if rv.IsNil() { + return &Item{T: byte(ItemNIL)} + } + return ItemFromGo(rv.Elem().Interface()) + case reflect.Slice: + var ret []*Item + if anys, ok := v.([]any); ok { + for _, v := range anys { + ret = append(ret, ItemFromGo(v)) + } + } else { + rv = reflect.ValueOf(v) + for i := 0; i < rv.Len(); i++ { + ret = append(ret, ItemFromGo(rv.Index(i).Interface())) + } + } + return &Item{T: byte(ItemList), V: ret} + case reflect.Map: + ret := make(map[string]*Item) + iter := rv.MapRange() + for iter.Next() { + ret[iter.Key().String()] = ItemFromGo(iter.Value().Interface()) + } + return &Item{T: byte(ItemMap), V: ret} + } + { + t := rv.Type() + if t.Kind() == reflect.Struct { + ret := make(map[string]*Item) + for _, f := range reflect.VisibleFields(t) { + fv := rv.FieldByIndex(f.Index) + name, omit := structTagRead(f) + var empty bool + item := ItemFromGo(fv.Interface()) + switch ItemType(item.T) { + case ItemList: + if len(item.V.([]*Item)) == 0 { + empty = true + } + case ItemMap: + if len(item.V.(map[string]*Item)) == 0 { + empty = true + } + } + if !(omit && empty) { + ret[name] = item + } + } + return &Item{T: byte(ItemMap), V: ret} + } + } + switch v := v.(type) { + case bool: + return &Item{T: byte(ItemBool), V: v} + case uuid.UUID: + return &Item{T: byte(ItemUUID), V: v} + case uint: + return &Item{T: byte(ItemUInt), V: uint64(v)} + case uint8: + return &Item{T: byte(ItemUInt), V: uint64(v)} + case uint16: + return &Item{T: byte(ItemUInt), V: uint64(v)} + case uint32: + return &Item{T: byte(ItemUInt), V: uint64(v)} + case uint64: + return &Item{T: byte(ItemUInt), V: v} + case int: + if v >= 0 { + return &Item{T: byte(ItemUInt), V: uint64(v)} + } + return &Item{T: byte(ItemInt), V: int64(v)} + case int8: + if v >= 0 { + return &Item{T: byte(ItemUInt), V: uint64(v)} + } + return &Item{T: byte(ItemInt), V: int64(v)} + case int16: + if v >= 0 { + return &Item{T: byte(ItemUInt), V: uint64(v)} + } + return &Item{T: byte(ItemInt), V: int64(v)} + case int32: + if v >= 0 { + return &Item{T: byte(ItemUInt), V: uint64(v)} + } + return &Item{T: byte(ItemInt), V: int64(v)} + case int64: + if v >= 0 { + return &Item{T: byte(ItemUInt), V: uint64(v)} + } + return &Item{T: byte(ItemInt), V: v} + case string: + return &Item{T: byte(ItemStr), V: v} + default: + panic(fmt.Errorf("unhandled type: %+v", v)) + } +} + +func MapToStruct(dst any, src map[string]any) error { + decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + Result: dst, TagName: "yac", + }) + if err != nil { + return err + } + return decoder.Decode(src) +} + +func DecodeToStruct(dst any, raw []byte) error { + item, tail, err := DecodeItem(raw) + if err != nil { + return err + } + if len(tail) != 0 { + return errors.New("trailing data") + } + if ItemType(item.T) != ItemMap { + return errors.New("non-map") + } + return MapToStruct(dst, item.ToGo().(map[string]any)) +} diff --git a/gyac/sort.go b/gyac/sort.go new file mode 100644 index 0000000..4259644 --- /dev/null +++ b/gyac/sort.go @@ -0,0 +1,21 @@ +package gyac + +type ByLenFirst []string + +func (a ByLenFirst) Len() int { + return len(a) +} + +func (a ByLenFirst) Swap(i, j int) { + a[i], a[j] = a[j], a[i] +} + +func (a ByLenFirst) Less(i, j int) bool { + if len(a[i]) < len(a[j]) { + return true + } + if len(a[i]) > len(a[j]) { + return false + } + return a[i] < a[j] +} diff --git a/pyac/COPYING b/pyac/COPYING new file mode 100644 index 0000000..58f2b48 --- /dev/null +++ b/pyac/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/pyac/COPYING.LESSER b/pyac/COPYING.LESSER new file mode 100644 index 0000000..2d9b9aa --- /dev/null +++ b/pyac/COPYING.LESSER @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/pyac/README b/pyac/README new file mode 100644 index 0000000..a705a4d --- /dev/null +++ b/pyac/README @@ -0,0 +1,6 @@ +Python implementation of YAC codec. + +* No FLOAT*, TAI64NA, or nanoseconds support. + They are stored/decoded just as a raw value + +pyac is free software: see the file COPYING.LESSER for copying conditions. diff --git a/pyac/pyac.py b/pyac/pyac.py new file mode 100644 index 0000000..6c34d72 --- /dev/null +++ b/pyac/pyac.py @@ -0,0 +1,676 @@ +# pyac -- Python YAC implementation +# Copyright (C) 2024 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, version 3 of the License. +# +# 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program. If not, see . + + +class DecodeError(ValueError): + pass + + +WrongTag = DecodeError("wrong tag") +NonMinimal = DecodeError("non-miminal encoding") + + +class NotEnoughData(DecodeError): + def __init__(self, n): + self.n = n + super().__init__() + + def __str__(self): + return "%d bytes expected" % self.n + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, self) + + +class EOC: + tags = (0x00,) + + def encode(self): + return self.tags[0].to_bytes(1, "big") + + @classmethod + def decode(klass, data): + if data[0] != klass.tags[0]: + raise WrongTag + return EOC(), data[1:] + + def __repr__(self): + return "EOC" + + +class Nil: + tags = (0x01,) + + def encode(self): + return self.tags[0].to_bytes(1, "big") + + @classmethod + def decode(klass, data): + if data[0] != klass.tags[0]: + raise WrongTag + return Nil(), data[1:] + + def __repr__(self): + return "NIL" + + def py(self): + return None + + +class Bool: + tags = (0x02, 0x03) + + def __init__(self, v): + if isinstance(v, Bool): + v = v.v + self.v = v + + def encode(self): + if self.v is True: + return self.tags[1].to_bytes(1, "big") + return self.tags[0].to_bytes(1, "big") + + @classmethod + def decode(klass, data): + if data[0] == klass.tags[0]: + return klass(False), data[1:] + if data[0] == klass.tags[1]: + return klass(True), data[1:] + raise WrongTag + + def __repr__(self): + return "TRUE" if self.v is True else "FALSE" + + def py(self): + return self.v + + +from uuid import UUID as pyUUID + + +class UUID: + tags = (0x04,) + + def __init__(self, v): + if isinstance(v, UUID): + v = v.v + if isinstance(v, pyUUID): + self.v = v + else: + self.v = pyUUID(v) + + def encode(self): + return self.tags[0].to_bytes(1, "big") + self.v.bytes + + @classmethod + def decode(klass, data): + if data[0] != klass.tags[0]: + raise WrongTag + if len(data) < 1+16: + raise NotEnoughData(1+16) + return klass(pyUUID(bytes=data[1:1+16])), data[1+16:] + + def __repr__(self): + return "UUID[%s]" % str(self.v) + + def __eq__(self, their): + if isinstance(their, pyUUID): + return self.v == their + return self.v == their.v + + def __bytes__(self): + return self.v.bytes + + def py(self): + return self.v + + +class Int: + tags = tuple(v for v in range(0x20, 0x80)) + tagLenPositive = 0x20 + tagLenNegative = 0x30 + tagValPositive = 0x40 + tagValNegative = 0x60 + + def __init__(self, v=0): + if isinstance(v, Int): + v = v.v + self.v = v + + def encode(self): + neg = False + v = self.v + if v < 0: + neg = True + v = (-v) - 1 + if v < 32: + return ( + (self.tagValNegative if neg else self.tagValPositive) | v + ).to_bytes(1, "big") + tag = self.tagLenNegative if neg else self.tagLenPositive + for l, bits in enumerate(range(8, 128, 8)): + if v < (1<= klass.tagValPositive) and (data[0] < (klass.tagValPositive+32)): + v = data[0] - klass.tagValPositive + return klass(v), data[1:] + if (data[0] >= klass.tagValNegative) and (data[0] < (klass.tagValNegative+32)): + return klass(-1 - (data[0] - klass.tagValNegative)), data[1:] + if (data[0] & 0xF0) not in (klass.tagLenPositive, klass.tagLenNegative): + raise WrongTag + neg = (data[0] & 0xF0) == klass.tagLenNegative + l = (data[0] & 0x0F) + 1 + data = data[1:] + if len(data) < l: + raise NotEnoughData(l) + v = int.from_bytes(data[:l], "big") + if v < 32: + raise NonMinimal + if neg: + v = -1 - v + return klass(v), data[l:] + + def __repr__(self): + return "INT(%d)" % self.v + + def __int__(self): + return self.v + + def py(self): + return self.v + + +class List: + tags = (0x08,) + + def __init__(self, v=()): + if isinstance(v, List): + v = v.v + self.v = v + + def encode(self): + raws = [self.tags[0].to_bytes(1, "big")] + for v in self.v: + raws.append(Encode(v)) + raws.append(EOC.tags[0].to_bytes(1, "big")) + return b"".join(raws) + + @classmethod + def decode(klass, data): + if data[0] != klass.tags[0]: + raise WrongTag + data = data[1:] + vs = [] + while True: + v, data = Decode(data) + if isinstance(v, EOC): + break + vs.append(v) + return klass(vs), data + + def __repr__(self): + return "LIST[" + ", ".join(repr(v) for v in self.v) + "]" + + def py(self): + return [v.py() for v in self.v] + + +LenFirstSort = lambda x: (len(x), x) + + +class Map: + tags = (0x09,) + + def __init__(self, v=()): + if isinstance(v, Map): + v = v.v + self.v = v + + def encode(self): + raws = [self.tags[0].to_bytes(1, "big")] + for k in sorted(self.v.keys(), key=LenFirstSort): + assert isinstance(k, (str, Str)) + raws.append(Str(k).encode()) + raws.append(Encode(self.v[k])) + raws.append(EOC.tags[0].to_bytes(1, "big")) + return b"".join(raws) + + @classmethod + def decode(klass, data): + if data[0] != klass.tags[0]: + raise WrongTag + data = data[1:] + vs = {} + kPrev = "" + while True: + k, data = Decode(data) + if isinstance(k, EOC): + break + if not isinstance(k, Str): + raise DecodeError("non-string key") + k = str(k) + if (len(k) < len(kPrev)) or ((len(k) == len(kPrev)) and (k <= kPrev)): + raise DecodeError("unsorted keys") + v, data = Decode(data) + if isinstance(v, EOC): + raise DecodeError("unexpected EOC") + vs[k] = v + kPrev = k + return klass(vs), data + + def __repr__(self): + return "MAP[" + "; ".join("%s: %r" % (k, self.v[k]) + for k in sorted(self.v.keys(), key=LenFirstSort) + ) + "]" + + def py(self): + return {str(k): v.py() for k, v in self.v.items()} + + +class Blob: + tags = (0x0B,) + + def __init__(self, l, v=b""): + if isinstance(v, Blob): + v = v.v + l = v.l + assert (l > 0) and (l <= ((1<<64)-1)) + self.v = v + self.l = l + + def encode(self): + raws = [self.tags[0].to_bytes(1, "big"), Int(self.l).encode()] + chunks = len(self.v) // (self.l) + for i in range(chunks): + raws.append(Nil().encode()) + raws.append(self.v[i*(self.l):(i+1)*(self.l)]) + left = len(self.v) - chunks*(self.l) + assert left < (self.l) + if left == 0: + raws.append(Bin(b"").encode()) + else: + raws.append(Bin(self.v[-left:]).encode()) + return b"".join(raws) + + @classmethod + def decode(klass, data): + if data[0] != klass.tags[0]: + raise WrongTag + data = data[1:] + l, data = Int.decode(data) + l = l.v + vs = [] + while True: + v, data = Decode(data) + if isinstance(v, Nil): + if len(data) < l: + raise NotEnoughData(l) + vs.append(data[:l]) + data = data[l:] + elif isinstance(v, Bin): + v = bytes(v) + if len(v) >= l: + raise DecodeError("wrong terminator len") + vs.append(v) + break + else: + raise DecodeError("unexpected tag") + return klass(l, b"".join(vs)), data + + def __repr__(self): + return "BLOB(%d, %d)" % (self.l, len(self.v)) + + def __bytes__(self): + return self.v + + def __eq__(self, their): + return (self.l == their.l) and (self.v == their.v) + + def py(self): + return self + + +class Float: + tags = (0x10, 0x11, 0x12, 0x13, 0x14) + + @classmethod + def decode(klass, data): + t = data[0] + data = data[1:] + if t == klass.tags[0]: + l = 2 + elif t == klass.tags[1]: + l = 4 + elif t == klass.tags[2]: + l = 8 + elif t == klass.tags[3]: + l = 16 + elif t == klass.tags[4]: + l = 32 + if len(data) < l: + raise NotEnoughData(l) + return Raw((t, data[:l])), data[l:] + + +from datetime import datetime +from datetime import timedelta +from datetime import timezone + + +TAI64Base = 0x4000000000000000 +Leapsecs1972 = 10 +Leapsecs = tuple( + int(datetime(y, m, 1, 0, 0, 0, 0, tzinfo=timezone.utc).timestamp()) + for y, m in ( + (2017, 1), + (2015, 7), + (2012, 7), + (2009, 1), + (2006, 1), + (1999, 1), + (1997, 7), + (1996, 1), + (1994, 7), + (1993, 7), + (1992, 7), + (1991, 1), + (1990, 1), + (1988, 1), + (1985, 7), + (1983, 7), + (1982, 7), + (1981, 7), + (1980, 1), + (1979, 1), + (1978, 1), + (1977, 1), + (1976, 1), + (1975, 1), + (1974, 1), + (1973, 1), + (1972, 7), + ) +) + + +class TAI64: + tags = (0x18, 0x19, 0x1A) + + def __init__(self, v): + if isinstance(v, TAI64): + v = v.v + self.v = v + + def encode(self): + v = int(self.v.replace(tzinfo=timezone.utc).timestamp()) + diff = Leapsecs1972 + for n, leapsec in enumerate(Leapsecs): + if v > leapsec: + diff += len(Leapsecs) - n + break + v += TAI64Base + diff + if self.v.microsecond == 0: + return self.tags[0].to_bytes(1, "big") + v.to_bytes(8, "big") + return ( + self.tags[1].to_bytes(1, "big") + + v.to_bytes(8, "big") + + (self.v.microsecond * 1000).to_bytes(4, "big") + ) + + @classmethod + def decode(klass, data): + if data[0] not in (klass.tags[0], klass.tags[1], klass.tags[2]): + raise WrongTag + hdr = data[0] + data = data[1:] + if hdr == klass.tags[0]: + l = 8 + elif hdr == klass.tags[1]: + l = 12 + else: + l = 16 + if len(data) < l: + raise NotEnoughData(l) + + secs = int.from_bytes(data[:8], "big") + if secs > (1<<63): + raise DecodeError("reserved TAI64 values in use") + secs -= TAI64Base + diff = 0 + for n, leapsec in enumerate(Leapsecs): + if secs > (leapsec + len(Leapsecs) - n): + diff = 10 + len(Leapsecs) - n + break + secs -= diff + + nsecs = 0 + if l > 8: + nsecs = int.from_bytes(data[8:8+4], "big") + if nsecs > 999999999: + raise DecodeError("too many nanoseconds") + asecs = 0 + if l > 12: + asecs = int.from_bytes(data[8+4:8+4+4], "big") + if asecs > 999999999: + raise DecodeError("too many attoseconds") + + if (abs(secs) > (1<<60)) or (asecs > 0) or ((nsecs % 1000) > 0): + # Python can represent neither big values, nor nanoseconds + return Raw((hdr, data[:l])), data[l:] + + dt = datetime(1970, 1, 1) + timedelta(seconds=secs) + if nsecs > 0: + dt += timedelta(microseconds=nsecs // 1000) + return klass(dt), data[l:] + + def __repr__(self): + if self.v.microsecond > 0: + return "TAI64N(%s)" % str(self.v) + return "TAI64(%s)" % str(self.v) + + def py(self): + return self.v + + +class BaseString: + def __init__(self, v, utf8): + if isinstance(v, BaseString): + v = v.v + self.v = v + self.utf8 = utf8 + + def __lt__(self, their): + return self.v < their.v + + def __gt__(self, their): + return self.v > their.v + + def __eq__(self, their): + return self.v == their.v + + def __hash__(self): + return hash(self.v) + + def encode(self): + l = len(self.v) + if l >= (1<<16): + lv = 63 + lb = l.to_bytes(8, "big") + elif l >= (1<<8): + lv = 62 + lb = l.to_bytes(2, "big") + elif l >= 61: + lv = 61 + lb = l.to_bytes(1, "big") + else: + lv = l + lb = b"" + v = 0x80 + if self.utf8: + v |= 0x40 + return b"".join(((v | lv).to_bytes(1, "big"), lb, self.v)) + + @classmethod + def decode(klass, data): + if (data[0] & 0x80) == 0: + raise ValueError("wrong tag") + utf8 = (data[0] & 0x40) > 0 + l = data[0] & 0b00111111 + orig = data + if l < 61: + llen = 0 + elif l == 61: + llen = 1 + elif l == 62: + llen = 2 + elif l == 63: + llen = 8 + data = data[1:] + if llen > 0: + if len(data) < llen: + raise NotEnoughData(llen) + l = int.from_bytes(data[:llen], "big") + if ( + (l < 61) or ((l < (1<<8)) and (llen > 1)) or + ((l < (1<<16)) and (llen > 2)) + ): + raise NonMinimal + data = data[llen:] + if len(data) < l: + raise NotEnoughData(l) + return klass(data[:l], utf8=utf8), data[l:] + + +class Str(BaseString): + def __init__(self, v=""): + if isinstance(v, Str): + super().__init__(v.v, utf8=True) + else: + super().__init__(v.encode("utf-8"), utf8=True) + + @classmethod + def decode(klass, data): + obj, tail = BaseString.decode(data) + assert obj.utf8 is True + try: + return klass(obj.v.decode("utf-8")), tail + except UnicodeDecodeError as err: + raise DecodeError("invalid UTF-8") from err + + def __repr__(self): + return "STR(" + self.v.decode("utf-8") + ")" + + def __str__(self): + return self.v.decode("utf-8") + + def py(self): + return self.v.decode("utf-8") + + +class Bin(BaseString): + def __init__(self, v=b""): + if isinstance(v, Bin): + v = v.v + super().__init__(v, utf8=False) + + @classmethod + def decode(klass, data): + obj, tail = BaseString.decode(data) + assert obj.utf8 is False + return klass(obj.v), tail + + def __repr__(self): + return "BIN(" + self.v.hex() + ")" + + def __bytes__(self): + return self.v + + def py(self): + return self.v + + +class Raw: + def __init__(self, v=(0, b"")): + if isinstance(v, Raw): + v = v.v + self.v = v + + def encode(self): + t, v = self.v + return t.to_bytes(1, "big") + v + + def __eq__(self, their): + return self.v == their.v + + def __repr__(self): + return "RAW(%d, %s)" % self.v + + def __bytes__(self): + return self.v[1] + + def py(self): + return self + + +_tags = {} +for klass in (EOC, Nil, Bool, UUID, Int, List, Map, Blob, Float, TAI64): + for tag in klass.tags: + _tags[tag] = klass + + +def Decode(data): + hdr = data[0] + if hdr >= 0x80: # strings + klass = Str if ((hdr & 0x40) == 0x40) else Bin + else: + klass = _tags[hdr] + if klass is None: + raise ValueError("unknown tag") + v, data = klass.decode(data) + return v, data + + +def Encode(v): + if (v is None) or isinstance(v, Nil): + return Nil().encode() + if isinstance(v, (bool, Bool)): + return Bool(v).encode() + if isinstance(v, (pyUUID, UUID)): + return UUID(v).encode() + if isinstance(v, (int, Int)): + return Int(v).encode() + if isinstance(v, (list, tuple, List)): + return List(v).encode() + if isinstance(v, (dict, Map)): + return Map(v).encode() + if isinstance(v, Blob): + return v.encode() + if isinstance(v, float): + return Float(v).encode() + if isinstance(v, (datetime, TAI64)): + return TAI64(v).encode() + if isinstance(v, (bytes, Bin)): + return Bin(v).encode() + if isinstance(v, (str, Str)): + return Str(v).encode() + if isinstance(v, Raw): + return v.encode() + raise ValueError("unknown type", type(v)) diff --git a/pyac/test-vector.py b/pyac/test-vector.py new file mode 100644 index 0000000..9a775d4 --- /dev/null +++ b/pyac/test-vector.py @@ -0,0 +1,50 @@ +from pyac import * + + +data = { + "ints": { + "pos": [0, 1, 31, 32, 123, 1234, 12345678, 1<<80], + "neg": [-1, -2, -32, -33, -123, -1234, -12345678, -1<<80], + }, + "floats": [Raw((Float.tags[1], b"\x01\x02\x03\x04"))], + "nil": None, + "bool": [True, False], + "str": { + "bin": [ + b"", + 60 * b"0", + 61 * b"1", + 255 * b"2", + 1024 * b"3", + (1<<16) * b"4", + ], + "utf8": "привет мир", + }, + "blob": [ + Blob(12, 1 * b"5"), + Blob(12, 12 * b"6"), + Blob(12, 13 * b"7"), + Blob(5, b"1234567890-"), + ], + "empties": [ + [], + {}, + Blob(123, b""), + UUID("00000000-0000-0000-0000-000000000000"), + Raw((TAI64.tags[0], bytes.fromhex("0000000000000000"))), + ], + "uuid": UUID("0e875e3f-d385-49eb-87b4-be42d641c367"), +} +from datetime import datetime +data["dates"] = [ + (datetime(1970, 1, 1) + timedelta(seconds=1234567890)), + (datetime(1970, 1, 1) + timedelta(seconds=1234567890)).replace(microsecond=456), + Raw((TAI64.tags[1], bytes.fromhex("40000000499602F40006F855"))), + Raw((TAI64.tags[2], bytes.fromhex("40000000499602F40006F855075BCD15"))), +] +raw = Encode(data) +dec, tail = Decode(raw) +assert tail == b"" +assert Encode(dec) == raw +assert dec.py() == data +print(raw.hex()) diff --git a/spec/.gitignore b/spec/.gitignore new file mode 100644 index 0000000..4618054 --- /dev/null +++ b/spec/.gitignore @@ -0,0 +1 @@ +/index.info diff --git a/spec/THANKS b/spec/THANKS new file mode 100644 index 0000000..ddad4e1 --- /dev/null +++ b/spec/THANKS @@ -0,0 +1 @@ +* Sergey Mayorov for his valuable consultation and suggestions diff --git a/spec/encoding/blob.texi b/spec/encoding/blob.texi new file mode 100644 index 0000000..68f2a4d --- /dev/null +++ b/spec/encoding/blob.texi @@ -0,0 +1,27 @@ +@node Blobs +@section Blobs + +Blob (binary large object) allows you to transfer binary data in chunks, +in a streaming way, when data may not fit in memory at once. + +Positive non-zero @ref{Integers, INT} must follow the BLOB tag, setting +the following chunks payload size. Then come zero or more NIL tags with +fixed-length payload after each of them. Blob is terminated by +@ref{Strings, BIN}, probably having zero length. + +Data format definition must specify exact chunk size expected to be +used, if it needs deterministic encoding. + +@verbatim +BLOB INT [NIL || payload0 || NIL || payload1 || ...] BIN +@end verbatim + +@multitable @columnfractions .5 .5 + +@item BLOB(5, "") @tab @code{0B 45 80} +@item BLOB(5, "12345") @tab @code{0B 45 01 3132333435 80} +@item BLOB(5, "123456") @tab @code{0B 45 01 3132333435 81 36} +@item BLOB(500, "123") @tab @code{0B 2101F4 83 313233} +@item BLOB(2, "12345") @tab @code{0B 42 01 3132 01 3334 81 35} + +@end multitable diff --git a/spec/encoding/cont.texi b/spec/encoding/cont.texi new file mode 100644 index 0000000..69bb1b5 --- /dev/null +++ b/spec/encoding/cont.texi @@ -0,0 +1,31 @@ +@node Containers +@section Containers + +Containers do not have any explicit length, but are terminated by EOC +(end of contents) tag. + +LIST contains a concatenation of items of arbitrary type. + +@verbatim +LIST [ITEM0 || ITEM1 || ...] EOC +@end verbatim + +MAP contains concatenation of @ref{Strings, STR(key)}-value pairs. Keys +@strong{must} be unique and length-first bytewise ascending ordered. + +@verbatim +MAP [STR(KEY0) || ITEM0 || STR(KEY1) || ITEM1 || ... ] EOC +@end verbatim + +Hint: Encoding code for known format can be ordered itself to emit +values in an already properly sorted way. + +Example representations: + +@multitable @columnfractions .5 .5 + +@item LIST[] @tab @code{08 00} +@item LIST[INT(123) FALSE] @tab @code{08 207B 02 00} +@item MAP[foo: LIST["bar"]] @tab @code{09 C3666F6F 08 C3626172 00 00} + +@end multitable diff --git a/spec/encoding/float.texi b/spec/encoding/float.texi new file mode 100644 index 0000000..6ba4850 --- /dev/null +++ b/spec/encoding/float.texi @@ -0,0 +1,13 @@ +@node Floats +@section Floats + +Floats are encoded in IEEE 754 binary formats: half, single, double, +quadruple, octuple precision ones. + +Negative zero @strong{must not} be used. Shortest possible form @strong{must} +be used. + +Hint: look at CBOR's RFC for example code of float16 conversion. + +Maybe there appear additional restrictions and rules. Currently no +implementation supports floats. diff --git a/spec/encoding/index.texi b/spec/encoding/index.texi new file mode 100644 index 0000000..640eeff --- /dev/null +++ b/spec/encoding/index.texi @@ -0,0 +1,70 @@ +@node Encoding +@unnumbered Encoding + +YAC can store various primitive scalar types (strings, integers, ...), +and container types (lists, maps, ...). Serialisation process is just +emitting the TLV-like encoding for each item recursively. + +Possible values for the tag: + +@multitable {dec} {hex} {12345678} {float256} +@headitem dec @tab hex @tab bin @tab description + +@item 000 @tab 00 @tab @code{00000000} @tab @ref{Containers, EOC} +@item 001 @tab 01 @tab @code{00000001} @tab @ref{Primitives, NIL} +@item 002 @tab 02 @tab @code{00000010} @tab @ref{Primitives, FALSE} +@item 003 @tab 03 @tab @code{00000011} @tab @ref{Primitives, TRUE} +@item 004 @tab 04 @tab @code{00000100} @tab @ref{UUID} +@item [...] +@item 008 @tab 08 @tab @code{00001000} @tab @ref{Containers, LIST} +@item 009 @tab 09 @tab @code{00001001} @tab @ref{Containers, MAP} +@item 010 @tab 0A @tab @code{00001010} @tab +@item 011 @tab 0B @tab @code{00001011} @tab @ref{Blobs, BLOB} +@item [...] +@item 016 @tab 10 @tab @code{00010000} @tab @ref{Floats, FLOAT16} +@item 017 @tab 11 @tab @code{00010001} @tab @ref{Floats, FLOAT32} +@item 018 @tab 12 @tab @code{00010010} @tab @ref{Floats, FLOAT64} +@item 019 @tab 13 @tab @code{00010011} @tab @ref{Floats, FLOAT128} +@item 020 @tab 14 @tab @code{00010100} @tab @ref{Floats, FLOAT256} +@item [...] +@item 024 @tab 18 @tab @code{00011000} @tab @ref{TAI64, TAI64} +@item 025 @tab 19 @tab @code{00011001} @tab @ref{TAI64, TAI64N} +@item 026 @tab 1A @tab @code{00011010} @tab @ref{TAI64, TAI64NA} +@item [...] +@item 032 @tab 20 @tab @code{0010LLLL} @tab @ref{Integers, +INT(len=1)} +@item [...] +@item 047 @tab 2F @tab @code{0010LLLL} @tab @ref{Integers, +INT(len=16)} +@item 048 @tab 30 @tab @code{0011LLLL} @tab @ref{Integers, -INT(len=1)} +@item [...] +@item 063 @tab 3F @tab @code{0011LLLL} @tab @ref{Integers, -INT(len=16)} +@item 064 @tab 40 @tab @code{010VVVVV} @tab @ref{Integers, INT=0} +@item [...] +@item 095 @tab 5F @tab @code{010VVVVV} @tab @ref{Integers, INT=31} +@item 096 @tab 60 @tab @code{011VVVVV} @tab @ref{Integers, INT=-1} +@item [...] +@item 127 @tab 7F @tab @code{011VVVVV} @tab @ref{Integers, INT=-32} +@item 128 @tab 80 @tab @code{10LLLLLL} @tab @ref{Strings, BIN(len=0)} +@item [...] +@item 188 @tab BC @tab @code{10111100} @tab @ref{Strings, BIN(len=60)} +@item 189 @tab BD @tab @code{10111101} @tab @ref{Strings, BIN(len of 8b)} +@item 190 @tab BE @tab @code{10111110} @tab @ref{Strings, BIN(len of 16b)} +@item 191 @tab BF @tab @code{10111111} @tab @ref{Strings, BIN(len of 64b)} +@item 192 @tab C0 @tab @code{11LLLLLL} @tab @ref{Strings, STR(len=0)} +@item [...] +@item 252 @tab FC @tab @code{11111100} @tab @ref{Strings, STR(len=60)} +@item 253 @tab FD @tab @code{11111101} @tab @ref{Strings, STR(len of 8b)} +@item 254 @tab FE @tab @code{11111110} @tab @ref{Strings, STR(len of 16b)} +@item 255 @tab FF @tab @code{11111111} @tab @ref{Strings, STR(len of 64b)} + +@end multitable + +@include encoding/table.texi + +@include encoding/prim.texi +@include encoding/uuid.texi +@include encoding/str.texi +@include encoding/int.texi +@include encoding/float.texi +@include encoding/tai64.texi +@include encoding/cont.texi +@include encoding/blob.texi diff --git a/spec/encoding/int.texi b/spec/encoding/int.texi new file mode 100644 index 0000000..a1429cf --- /dev/null +++ b/spec/encoding/int.texi @@ -0,0 +1,52 @@ +@node Integers +@section Integers + +Integers can be encoded in short or long form. Separate types are used +for positive and negative values. + +Short form eats five bits of the tag +for positive values in @code{[0..32)} range, +and negative ones in @code{[-1..-32]} range. + +Long form is encoded as a big-endian number of varying length. +@code{*INT(len=1)..*INT(len=16)} types are for 8-, 16-, 24-, ..., +128-bit integer representations. + +Shortest possible form @strong{must} be used. Leading zero bytes are +@strong{forbidden}. Short values (<32) @strong{must} be encoded in a +short form. + +Negative integers store their absolute value the same way as positive +integers. After decoding, their value is subtracted from -1. Negative +value encoded as @code{0x02} means @code{-1 - 0x02 => -3}. + +Hint: both positive and negative long integer tag's value keeps the +length in the last 16 bits. So there is no need in dealing with every +reserved value. You can check first 4 bits of the header to determine is +it positive or negative integer, and then treat remaining 4 bits as a +length (+1). + +Example representations: + +@multitable @columnfractions .5 .5 + +@item 0 @tab @code{40} +@item 1 @tab @code{41} +@item 10 @tab @code{4A} +@item 31 @tab @code{5F} +@item 32 @tab @code{20 20} +@item 100 @tab @code{20 64} +@item 65536 @tab @code{22 01 00 00} +@item 1000000000000 @tab @code{24 E8 D4 A5 10 00} +@item 18446744073709551615 @tab @code{27 FF FF FF FF FF FF FF FF} +@item 18446744073709551616 @tab @code{28 01 00 00 00 00 00 00 00 00} +@item -18446744073709551616 @tab @code{37 FF FF FF FF FF FF FF FF} +@item -18446744073709551617 @tab @code{38 01 00 00 00 00 00 00 00 00} +@item -1 @tab @code{60} +@item -10 @tab @code{69} +@item -32 @tab @code{7F} +@item -33 @tab @code{30 20} +@item -100 @tab @code{30 63} +@item -65536 @tab @code{31 FF FF} + +@end multitable diff --git a/spec/encoding/prim.texi b/spec/encoding/prim.texi new file mode 100644 index 0000000..b2f48b2 --- /dev/null +++ b/spec/encoding/prim.texi @@ -0,0 +1,15 @@ +@node Primitives +@section Primitives + +Very primitive types like NIL (None, Null), boolean FALSE and TRUE are +encoded just as a single tag. + +Example representations: + +@multitable @columnfractions .5 .5 + +@item NIL @tab @code{01} +@item FALSE @tab @code{02} +@item TRUE @tab @code{03} + +@end multitable diff --git a/spec/encoding/str.texi b/spec/encoding/str.texi new file mode 100644 index 0000000..edbe391 --- /dev/null +++ b/spec/encoding/str.texi @@ -0,0 +1,46 @@ +@node Strings +@section Strings + +There are two kinds of strings: binary and UTF-8 (human-readable ones). +Most significant tag's bit is set for them. Seventh bit tells is it +UTF-8 string, binary otherwise. Next six bits contain the length of the +string. + +@verbatim + len + +------+ + / \ +1 U L L L L L L + ^ + +-is it UTF-8? +@end verbatim + +If length value equals to: + +@table @asis +@item 0-60 + Use as is. +@item 61 + Then next 8-bits are the actual length. +@item 62 + Then next 16-bits (big-endian) are the actual length. +@item 63 + Then next 64-bits (big-endian) are the actual length. +@end table + +String's length @strong{must} be encoded in shortest possible form. + +UTF-8 strings @strong{must} be valid UTF-8 sequences. That also +automatically assures that no zero byte will be met. + +Example representations: + +@multitable @columnfractions .5 .5 + +@item 0-byte binary string @tab @code{80} +@item 4-byte binary string @code{0x01 0x02 0x03 0x04} @tab @code{84 01 02 03 04} +@item 64-byte binary string with 0x41 @tab @code{BD 40 41 41 .. 41} +@item UTF-8 string "привет мир" ("hello world" on russian) @tab + @code{D3 D0 BF D1 80 D0 B8 D0 B2 D0 B5 D1 82 20 D0 BC D0 B8 D1 80} + +@end multitable diff --git a/spec/encoding/table.texi b/spec/encoding/table.texi new file mode 100644 index 0000000..8257098 --- /dev/null +++ b/spec/encoding/table.texi @@ -0,0 +1,264 @@ +@node Encoding table +@section Full encoding table + +@multitable {dec} {hex} {12345678} {float256} +@headitem dec @tab hex @tab bin @tab description + +@item 000 @tab 00 @tab @code{00000000} @tab @ref{Containers, EOC} +@item 001 @tab 01 @tab @code{00000001} @tab @ref{Primitives, NIL} +@item 002 @tab 02 @tab @code{00000010} @tab @ref{Primitives, FALSE} +@item 003 @tab 03 @tab @code{00000011} @tab @ref{Primitives, TRUE} +@item 004 @tab 04 @tab @code{00000100} @tab @ref{UUID} +@item 005 @tab 05 @tab @code{00000101} @tab +@item 006 @tab 06 @tab @code{00000110} @tab +@item 007 @tab 07 @tab @code{00000111} @tab +@item 008 @tab 08 @tab @code{00001000} @tab @ref{Containers, LIST} +@item 009 @tab 09 @tab @code{00001001} @tab @ref{Containers, MAP} +@item 010 @tab 0A @tab @code{00001010} @tab +@item 011 @tab 0B @tab @code{00001011} @tab @ref{Blobs, BLOB} +@item 012 @tab 0C @tab @code{00001100} @tab +@item 013 @tab 0D @tab @code{00001101} @tab +@item 014 @tab 0E @tab @code{00001110} @tab +@item 015 @tab 0F @tab @code{00001111} @tab +@item 016 @tab 10 @tab @code{00010000} @tab @ref{Floats, FLOAT16} +@item 017 @tab 11 @tab @code{00010001} @tab @ref{Floats, FLOAT32} +@item 018 @tab 12 @tab @code{00010010} @tab @ref{Floats, FLOAT64} +@item 019 @tab 13 @tab @code{00010011} @tab @ref{Floats, FLOAT128} +@item 020 @tab 14 @tab @code{00010100} @tab @ref{Floats, FLOAT256} +@item 021 @tab 15 @tab @code{00010101} @tab +@item 022 @tab 16 @tab @code{00010110} @tab +@item 023 @tab 17 @tab @code{00010111} @tab +@item 024 @tab 18 @tab @code{00011000} @tab @ref{TAI64, TAI64} +@item 025 @tab 19 @tab @code{00011001} @tab @ref{TAI64, TAI64N} +@item 026 @tab 1A @tab @code{00011010} @tab @ref{TAI64, TAI64NA} +@item 027 @tab 1B @tab @code{00011011} @tab +@item 028 @tab 1C @tab @code{00011100} @tab +@item 029 @tab 1D @tab @code{00011101} @tab +@item 030 @tab 1E @tab @code{00011110} @tab +@item 031 @tab 1F @tab @code{00011111} @tab +@item 032 @tab 20 @tab @code{00100000} @tab @ref{Integers, +INT1} +@item 033 @tab 21 @tab @code{00100001} @tab @ref{Integers, +INT2} +@item 034 @tab 22 @tab @code{00100010} @tab @ref{Integers, +INT3} +@item 035 @tab 23 @tab @code{00100011} @tab @ref{Integers, +INT4} +@item 036 @tab 24 @tab @code{00100100} @tab @ref{Integers, +INT5} +@item 037 @tab 25 @tab @code{00100101} @tab @ref{Integers, +INT6} +@item 038 @tab 26 @tab @code{00100110} @tab @ref{Integers, +INT7} +@item 039 @tab 27 @tab @code{00100111} @tab @ref{Integers, +INT8} +@item 040 @tab 28 @tab @code{00101000} @tab @ref{Integers, +INT9} +@item 041 @tab 29 @tab @code{00101001} @tab @ref{Integers, +INT10} +@item 042 @tab 2A @tab @code{00101010} @tab @ref{Integers, +INT11} +@item 043 @tab 2B @tab @code{00101011} @tab @ref{Integers, +INT12} +@item 044 @tab 2C @tab @code{00101100} @tab @ref{Integers, +INT13} +@item 045 @tab 2D @tab @code{00101101} @tab @ref{Integers, +INT14} +@item 046 @tab 2E @tab @code{00101110} @tab @ref{Integers, +INT15} +@item 047 @tab 2F @tab @code{00101111} @tab @ref{Integers, +INT16} +@item 048 @tab 30 @tab @code{00110000} @tab @ref{Integers, -INT1} +@item 049 @tab 31 @tab @code{00110001} @tab @ref{Integers, -INT2} +@item 050 @tab 32 @tab @code{00110010} @tab @ref{Integers, -INT3} +@item 051 @tab 33 @tab @code{00110011} @tab @ref{Integers, -INT4} +@item 052 @tab 34 @tab @code{00110100} @tab @ref{Integers, -INT5} +@item 053 @tab 35 @tab @code{00110101} @tab @ref{Integers, -INT6} +@item 054 @tab 36 @tab @code{00110110} @tab @ref{Integers, -INT7} +@item 055 @tab 37 @tab @code{00110111} @tab @ref{Integers, -INT8} +@item 056 @tab 38 @tab @code{00111000} @tab @ref{Integers, -INT9} +@item 057 @tab 39 @tab @code{00111001} @tab @ref{Integers, -INT10} +@item 058 @tab 3A @tab @code{00111010} @tab @ref{Integers, -INT11} +@item 059 @tab 3B @tab @code{00111011} @tab @ref{Integers, -INT12} +@item 060 @tab 3C @tab @code{00111100} @tab @ref{Integers, -INT13} +@item 061 @tab 3D @tab @code{00111101} @tab @ref{Integers, -INT14} +@item 062 @tab 3E @tab @code{00111110} @tab @ref{Integers, -INT15} +@item 063 @tab 3F @tab @code{00111111} @tab @ref{Integers, -INT16} +@item 064 @tab 40 @tab @code{01000000} @tab @ref{Integers, INT=0} +@item 065 @tab 41 @tab @code{01000001} @tab @ref{Integers, INT=1} +@item 066 @tab 42 @tab @code{01000010} @tab @ref{Integers, INT=2} +@item 067 @tab 43 @tab @code{01000011} @tab @ref{Integers, INT=3} +@item 068 @tab 44 @tab @code{01000100} @tab @ref{Integers, INT=4} +@item 069 @tab 45 @tab @code{01000101} @tab @ref{Integers, INT=5} +@item 070 @tab 46 @tab @code{01000110} @tab @ref{Integers, INT=6} +@item 071 @tab 47 @tab @code{01000111} @tab @ref{Integers, INT=7} +@item 072 @tab 48 @tab @code{01001000} @tab @ref{Integers, INT=8} +@item 073 @tab 49 @tab @code{01001001} @tab @ref{Integers, INT=9} +@item 074 @tab 4A @tab @code{01001010} @tab @ref{Integers, INT=10} +@item 075 @tab 4B @tab @code{01001011} @tab @ref{Integers, INT=11} +@item 076 @tab 4C @tab @code{01001100} @tab @ref{Integers, INT=12} +@item 077 @tab 4D @tab @code{01001101} @tab @ref{Integers, INT=13} +@item 078 @tab 4E @tab @code{01001110} @tab @ref{Integers, INT=14} +@item 079 @tab 4F @tab @code{01001111} @tab @ref{Integers, INT=15} +@item 080 @tab 50 @tab @code{01010000} @tab @ref{Integers, INT=16} +@item 081 @tab 51 @tab @code{01010001} @tab @ref{Integers, INT=17} +@item 082 @tab 52 @tab @code{01010010} @tab @ref{Integers, INT=18} +@item 083 @tab 53 @tab @code{01010011} @tab @ref{Integers, INT=19} +@item 084 @tab 54 @tab @code{01010100} @tab @ref{Integers, INT=20} +@item 085 @tab 55 @tab @code{01010101} @tab @ref{Integers, INT=21} +@item 086 @tab 56 @tab @code{01010110} @tab @ref{Integers, INT=22} +@item 087 @tab 57 @tab @code{01010111} @tab @ref{Integers, INT=23} +@item 088 @tab 58 @tab @code{01011000} @tab @ref{Integers, INT=24} +@item 089 @tab 59 @tab @code{01011001} @tab @ref{Integers, INT=25} +@item 090 @tab 5A @tab @code{01011010} @tab @ref{Integers, INT=26} +@item 091 @tab 5B @tab @code{01011011} @tab @ref{Integers, INT=27} +@item 092 @tab 5C @tab @code{01011100} @tab @ref{Integers, INT=28} +@item 093 @tab 5D @tab @code{01011101} @tab @ref{Integers, INT=29} +@item 094 @tab 5E @tab @code{01011110} @tab @ref{Integers, INT=30} +@item 095 @tab 5F @tab @code{01011111} @tab @ref{Integers, INT=31} +@item 096 @tab 60 @tab @code{01100000} @tab @ref{Integers, INT=-1} +@item 097 @tab 61 @tab @code{01100001} @tab @ref{Integers, INT=-2} +@item 098 @tab 62 @tab @code{01100010} @tab @ref{Integers, INT=-3} +@item 099 @tab 63 @tab @code{01100011} @tab @ref{Integers, INT=-4} +@item 100 @tab 64 @tab @code{01100100} @tab @ref{Integers, INT=-5} +@item 101 @tab 65 @tab @code{01100101} @tab @ref{Integers, INT=-6} +@item 102 @tab 66 @tab @code{01100110} @tab @ref{Integers, INT=-7} +@item 103 @tab 67 @tab @code{01100111} @tab @ref{Integers, INT=-8} +@item 104 @tab 68 @tab @code{01101000} @tab @ref{Integers, INT=-9} +@item 105 @tab 69 @tab @code{01101001} @tab @ref{Integers, INT=-10} +@item 106 @tab 6A @tab @code{01101010} @tab @ref{Integers, INT=-11} +@item 107 @tab 6B @tab @code{01101011} @tab @ref{Integers, INT=-12} +@item 108 @tab 6C @tab @code{01101100} @tab @ref{Integers, INT=-13} +@item 109 @tab 6D @tab @code{01101101} @tab @ref{Integers, INT=-14} +@item 110 @tab 6E @tab @code{01101110} @tab @ref{Integers, INT=-15} +@item 111 @tab 6F @tab @code{01101111} @tab @ref{Integers, INT=-16} +@item 112 @tab 70 @tab @code{01110000} @tab @ref{Integers, INT=-17} +@item 113 @tab 71 @tab @code{01110001} @tab @ref{Integers, INT=-18} +@item 114 @tab 72 @tab @code{01110010} @tab @ref{Integers, INT=-19} +@item 115 @tab 73 @tab @code{01110011} @tab @ref{Integers, INT=-20} +@item 116 @tab 74 @tab @code{01110100} @tab @ref{Integers, INT=-21} +@item 117 @tab 75 @tab @code{01110101} @tab @ref{Integers, INT=-22} +@item 118 @tab 76 @tab @code{01110110} @tab @ref{Integers, INT=-23} +@item 119 @tab 77 @tab @code{01110111} @tab @ref{Integers, INT=-24} +@item 120 @tab 78 @tab @code{01111000} @tab @ref{Integers, INT=-25} +@item 121 @tab 79 @tab @code{01111001} @tab @ref{Integers, INT=-26} +@item 122 @tab 7A @tab @code{01111010} @tab @ref{Integers, INT=-27} +@item 123 @tab 7B @tab @code{01111011} @tab @ref{Integers, INT=-28} +@item 124 @tab 7C @tab @code{01111100} @tab @ref{Integers, INT=-29} +@item 125 @tab 7D @tab @code{01111101} @tab @ref{Integers, INT=-30} +@item 126 @tab 7E @tab @code{01111110} @tab @ref{Integers, INT=-31} +@item 127 @tab 7F @tab @code{01111111} @tab @ref{Integers, INT=-32} +@item 128 @tab 80 @tab @code{10000000} @tab @ref{Strings, BIN=0} +@item 129 @tab 81 @tab @code{10000001} @tab @ref{Strings, BIN=1} +@item 130 @tab 82 @tab @code{10000010} @tab @ref{Strings, BIN=2} +@item 131 @tab 83 @tab @code{10000011} @tab @ref{Strings, BIN=3} +@item 132 @tab 84 @tab @code{10000100} @tab @ref{Strings, BIN=4} +@item 133 @tab 85 @tab @code{10000101} @tab @ref{Strings, BIN=5} +@item 134 @tab 86 @tab @code{10000110} @tab @ref{Strings, BIN=6} +@item 135 @tab 87 @tab @code{10000111} @tab @ref{Strings, BIN=7} +@item 136 @tab 88 @tab @code{10001000} @tab @ref{Strings, BIN=8} +@item 137 @tab 89 @tab @code{10001001} @tab @ref{Strings, BIN=9} +@item 138 @tab 8A @tab @code{10001010} @tab @ref{Strings, BIN=10} +@item 139 @tab 8B @tab @code{10001011} @tab @ref{Strings, BIN=11} +@item 140 @tab 8C @tab @code{10001100} @tab @ref{Strings, BIN=12} +@item 141 @tab 8D @tab @code{10001101} @tab @ref{Strings, BIN=13} +@item 142 @tab 8E @tab @code{10001110} @tab @ref{Strings, BIN=14} +@item 143 @tab 8F @tab @code{10001111} @tab @ref{Strings, BIN=15} +@item 144 @tab 90 @tab @code{10010000} @tab @ref{Strings, BIN=16} +@item 145 @tab 91 @tab @code{10010001} @tab @ref{Strings, BIN=17} +@item 146 @tab 92 @tab @code{10010010} @tab @ref{Strings, BIN=18} +@item 147 @tab 93 @tab @code{10010011} @tab @ref{Strings, BIN=19} +@item 148 @tab 94 @tab @code{10010100} @tab @ref{Strings, BIN=20} +@item 149 @tab 95 @tab @code{10010101} @tab @ref{Strings, BIN=21} +@item 150 @tab 96 @tab @code{10010110} @tab @ref{Strings, BIN=22} +@item 151 @tab 97 @tab @code{10010111} @tab @ref{Strings, BIN=23} +@item 152 @tab 98 @tab @code{10011000} @tab @ref{Strings, BIN=24} +@item 153 @tab 99 @tab @code{10011001} @tab @ref{Strings, BIN=25} +@item 154 @tab 9A @tab @code{10011010} @tab @ref{Strings, BIN=26} +@item 155 @tab 9B @tab @code{10011011} @tab @ref{Strings, BIN=27} +@item 156 @tab 9C @tab @code{10011100} @tab @ref{Strings, BIN=28} +@item 157 @tab 9D @tab @code{10011101} @tab @ref{Strings, BIN=29} +@item 158 @tab 9E @tab @code{10011110} @tab @ref{Strings, BIN=30} +@item 159 @tab 9F @tab @code{10011111} @tab @ref{Strings, BIN=31} +@item 160 @tab A0 @tab @code{10100000} @tab @ref{Strings, BIN=32} +@item 161 @tab A1 @tab @code{10100001} @tab @ref{Strings, BIN=33} +@item 162 @tab A2 @tab @code{10100010} @tab @ref{Strings, BIN=34} +@item 163 @tab A3 @tab @code{10100011} @tab @ref{Strings, BIN=35} +@item 164 @tab A4 @tab @code{10100100} @tab @ref{Strings, BIN=36} +@item 165 @tab A5 @tab @code{10100101} @tab @ref{Strings, BIN=37} +@item 166 @tab A6 @tab @code{10100110} @tab @ref{Strings, BIN=38} +@item 167 @tab A7 @tab @code{10100111} @tab @ref{Strings, BIN=39} +@item 168 @tab A8 @tab @code{10101000} @tab @ref{Strings, BIN=40} +@item 169 @tab A9 @tab @code{10101001} @tab @ref{Strings, BIN=41} +@item 170 @tab AA @tab @code{10101010} @tab @ref{Strings, BIN=42} +@item 171 @tab AB @tab @code{10101011} @tab @ref{Strings, BIN=43} +@item 172 @tab AC @tab @code{10101100} @tab @ref{Strings, BIN=44} +@item 173 @tab AD @tab @code{10101101} @tab @ref{Strings, BIN=45} +@item 174 @tab AE @tab @code{10101110} @tab @ref{Strings, BIN=46} +@item 175 @tab AF @tab @code{10101111} @tab @ref{Strings, BIN=47} +@item 176 @tab B0 @tab @code{10110000} @tab @ref{Strings, BIN=48} +@item 177 @tab B1 @tab @code{10110001} @tab @ref{Strings, BIN=49} +@item 178 @tab B2 @tab @code{10110010} @tab @ref{Strings, BIN=50} +@item 179 @tab B3 @tab @code{10110011} @tab @ref{Strings, BIN=51} +@item 180 @tab B4 @tab @code{10110100} @tab @ref{Strings, BIN=52} +@item 181 @tab B5 @tab @code{10110101} @tab @ref{Strings, BIN=53} +@item 182 @tab B6 @tab @code{10110110} @tab @ref{Strings, BIN=54} +@item 183 @tab B7 @tab @code{10110111} @tab @ref{Strings, BIN=55} +@item 184 @tab B8 @tab @code{10111000} @tab @ref{Strings, BIN=56} +@item 185 @tab B9 @tab @code{10111001} @tab @ref{Strings, BIN=57} +@item 186 @tab BA @tab @code{10111010} @tab @ref{Strings, BIN=58} +@item 187 @tab BB @tab @code{10111011} @tab @ref{Strings, BIN=59} +@item 188 @tab BC @tab @code{10111100} @tab @ref{Strings, BIN=60} +@item 189 @tab BD @tab @code{10111101} @tab @ref{Strings, BIN 8b} +@item 190 @tab BE @tab @code{10111110} @tab @ref{Strings, BIN 16b} +@item 191 @tab BF @tab @code{10111111} @tab @ref{Strings, BIN 64b} +@item 192 @tab C0 @tab @code{11000000} @tab @ref{Strings, STR=0} +@item 193 @tab C1 @tab @code{11000001} @tab @ref{Strings, STR=1} +@item 194 @tab C2 @tab @code{11000010} @tab @ref{Strings, STR=2} +@item 195 @tab C3 @tab @code{11000011} @tab @ref{Strings, STR=3} +@item 196 @tab C4 @tab @code{11000100} @tab @ref{Strings, STR=4} +@item 197 @tab C5 @tab @code{11000101} @tab @ref{Strings, STR=5} +@item 198 @tab C6 @tab @code{11000110} @tab @ref{Strings, STR=6} +@item 199 @tab C7 @tab @code{11000111} @tab @ref{Strings, STR=7} +@item 200 @tab C8 @tab @code{11001000} @tab @ref{Strings, STR=8} +@item 201 @tab C9 @tab @code{11001001} @tab @ref{Strings, STR=9} +@item 202 @tab CA @tab @code{11001010} @tab @ref{Strings, STR=10} +@item 203 @tab CB @tab @code{11001011} @tab @ref{Strings, STR=11} +@item 204 @tab CC @tab @code{11001100} @tab @ref{Strings, STR=12} +@item 205 @tab CD @tab @code{11001101} @tab @ref{Strings, STR=13} +@item 206 @tab CE @tab @code{11001110} @tab @ref{Strings, STR=14} +@item 207 @tab CF @tab @code{11001111} @tab @ref{Strings, STR=15} +@item 208 @tab D0 @tab @code{11010000} @tab @ref{Strings, STR=16} +@item 209 @tab D1 @tab @code{11010001} @tab @ref{Strings, STR=17} +@item 210 @tab D2 @tab @code{11010010} @tab @ref{Strings, STR=18} +@item 211 @tab D3 @tab @code{11010011} @tab @ref{Strings, STR=19} +@item 212 @tab D4 @tab @code{11010100} @tab @ref{Strings, STR=20} +@item 213 @tab D5 @tab @code{11010101} @tab @ref{Strings, STR=21} +@item 214 @tab D6 @tab @code{11010110} @tab @ref{Strings, STR=22} +@item 215 @tab D7 @tab @code{11010111} @tab @ref{Strings, STR=23} +@item 216 @tab D8 @tab @code{11011000} @tab @ref{Strings, STR=24} +@item 217 @tab D9 @tab @code{11011001} @tab @ref{Strings, STR=25} +@item 218 @tab DA @tab @code{11011010} @tab @ref{Strings, STR=26} +@item 219 @tab DB @tab @code{11011011} @tab @ref{Strings, STR=27} +@item 220 @tab DC @tab @code{11011100} @tab @ref{Strings, STR=28} +@item 221 @tab DD @tab @code{11011101} @tab @ref{Strings, STR=29} +@item 222 @tab DE @tab @code{11011110} @tab @ref{Strings, STR=30} +@item 223 @tab DF @tab @code{11011111} @tab @ref{Strings, STR=31} +@item 224 @tab E0 @tab @code{11100000} @tab @ref{Strings, STR=32} +@item 225 @tab E1 @tab @code{11100001} @tab @ref{Strings, STR=33} +@item 226 @tab E2 @tab @code{11100010} @tab @ref{Strings, STR=34} +@item 227 @tab E3 @tab @code{11100011} @tab @ref{Strings, STR=35} +@item 228 @tab E4 @tab @code{11100100} @tab @ref{Strings, STR=36} +@item 229 @tab E5 @tab @code{11100101} @tab @ref{Strings, STR=37} +@item 230 @tab E6 @tab @code{11100110} @tab @ref{Strings, STR=38} +@item 231 @tab E7 @tab @code{11100111} @tab @ref{Strings, STR=39} +@item 232 @tab E8 @tab @code{11101000} @tab @ref{Strings, STR=40} +@item 233 @tab E9 @tab @code{11101001} @tab @ref{Strings, STR=41} +@item 234 @tab EA @tab @code{11101010} @tab @ref{Strings, STR=42} +@item 235 @tab EB @tab @code{11101011} @tab @ref{Strings, STR=43} +@item 236 @tab EC @tab @code{11101100} @tab @ref{Strings, STR=44} +@item 237 @tab ED @tab @code{11101101} @tab @ref{Strings, STR=45} +@item 238 @tab EE @tab @code{11101110} @tab @ref{Strings, STR=46} +@item 239 @tab EF @tab @code{11101111} @tab @ref{Strings, STR=47} +@item 240 @tab F0 @tab @code{11110000} @tab @ref{Strings, STR=48} +@item 241 @tab F1 @tab @code{11110001} @tab @ref{Strings, STR=49} +@item 242 @tab F2 @tab @code{11110010} @tab @ref{Strings, STR=50} +@item 243 @tab F3 @tab @code{11110011} @tab @ref{Strings, STR=51} +@item 244 @tab F4 @tab @code{11110100} @tab @ref{Strings, STR=52} +@item 245 @tab F5 @tab @code{11110101} @tab @ref{Strings, STR=53} +@item 246 @tab F6 @tab @code{11110110} @tab @ref{Strings, STR=54} +@item 247 @tab F7 @tab @code{11110111} @tab @ref{Strings, STR=55} +@item 248 @tab F8 @tab @code{11111000} @tab @ref{Strings, STR=56} +@item 249 @tab F9 @tab @code{11111001} @tab @ref{Strings, STR=57} +@item 250 @tab FA @tab @code{11111010} @tab @ref{Strings, STR=58} +@item 251 @tab FB @tab @code{11111011} @tab @ref{Strings, STR=59} +@item 252 @tab FC @tab @code{11111100} @tab @ref{Strings, STR=60} +@item 253 @tab FD @tab @code{11111101} @tab @ref{Strings, STR 8b} +@item 254 @tab FE @tab @code{11111110} @tab @ref{Strings, STR 16b} +@item 255 @tab FF @tab @code{11111111} @tab @ref{Strings, STR 64b} + +@end multitable diff --git a/spec/encoding/tai64.texi b/spec/encoding/tai64.texi new file mode 100644 index 0000000..b8f694a --- /dev/null +++ b/spec/encoding/tai64.texi @@ -0,0 +1,30 @@ +@node TAI64 +@section TAI64 + +Datetime is represented in @url{http://cr.yp.to/libtai/tai64.html, TAI64} +format. TAI stands for Temps Atomique International, the current +international real time standard. Unlike UTC, it takes leap seconds into +account, making it monotonous. + +You can convert TAI to UTC by subtracting number of leap seconds. + +TAI64N format adds 32-bit big-endian number of nanoseconds (in up to +999999999) count. TAI64NA adds another 32-big big-endian part of +attoseconds count. + +Shortest form @strong{must} be used: if number of nanoseconds +equals to zero, then use TAI64 format. + +Example representations: + +@multitable @columnfractions .5 .5 + +@item 1970-01-01 TAI @tab @code{18 4000000000000000} +@item 1970-01-01 UTC @tab @code{18 400000000000000A} +@item 1969-12-31 23:59:59 TAI @tab @code{18 3FFFFFFFFFFFFFFF} +@item 1997-10-03 18:15:19 TAI @tab @code{18 4000000034353637} +@item 1997-10-03 18:14:48 UTC @tab @code{18 4000000034353637} +@item 2024-10-01 12:39:07 UTC @tab @code{18 4000000066FC17C0} +@item 2024-10-01 12:39:07 UTC 123456 μs @tab @code{19 4000000066FC17C0075BCA00} + +@end multitable diff --git a/spec/encoding/uuid.texi b/spec/encoding/uuid.texi new file mode 100644 index 0000000..7db1df2 --- /dev/null +++ b/spec/encoding/uuid.texi @@ -0,0 +1,16 @@ +@node UUID +@section UUID + +128-bit big-endian @url{https://datatracker.ietf.org/doc/html/rfc9562, UUID}'s +value is placed after the tag. + +Example representations: + +@multitable @columnfractions .5 .5 + +@item Nil UUID @tab @code{04 00000000000000000000000000000000} +@item Max UUID @tab @code{04 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF} +@item UUIDv4 @code{0e875e3f-d385-49eb-87b4-be42d641c367} @tab + @code{04 0E875E3FD38549EB87B4BE42D641C367} + +@end multitable diff --git a/spec/format/cer.texi b/spec/format/cer.texi new file mode 100644 index 0000000..9cc1a5d --- /dev/null +++ b/spec/format/cer.texi @@ -0,0 +1,65 @@ +@node Certificate +@section Certificate format + +Certificate is the @ref{SignedData} structure with @code{/load.t} equals +to @code{cer} and following @code{/load.v} content (keys are sorted as they +will be encoded): + +@verbatim +{ + ?"ku": ["...", ...], # "dh", "sig", "ca", ... + "exp": [TAI64, TAI64], + "kid": UUID(signer's public key-based UUID), + "pub": {"a": "ID", "v": BIN(marshalled public key)}, + "sub": {"entity": "...", ...}, + ?"crit": [{"t": "ID", ...}, ...], +} +@end verbatim + +@code{kid} is a hash calculated over the @code{pub} field and used to +form UUIDv4. But it may be formed another way, no limitations. Depending +on @code{pub.a}, that may be different hash like Streebog-256 or +SHAKE-128 (let's stop using SHA-2!). + +@code{sub} is a subject name. Its values are UTF-8 strings. Currently no +constraints on what fields must be present. + +@code{exp}iration period @strong{must} contain TAI64 datetime, without +any nanoseconds part. + +@code{ku} (key usage) contains supposed usage contexts like being CA +(@code{ca}), or using it solely for either signing (@code{sig}), or +key-agreement (@code{dh}) purposes. It @strong{must} be absent if empty. +Its items @strong{must} be length-first sorted (like MAP) and unique. +Possibly some additional usages like @code{email}, @code{ike}, +@code{codeSign} may be thought out. + +Optional @code{crit} list may contain other critical extensions. +Non-critical extensions may be placed just inside the map itself. +It @strong{must} be absent if empty. Extension is a map with expected +@code{t}ype field containing the identifier of the extension. Other +extension's keys are defined by its type. + +Example minimal certificate may look like: + +@verbatim +{ + "load": { + "t": "cer", + "v": { + "exp": [TAI64, TAI64], + "kid": UUID(SKID), + "pub": {"a": "gost3410-256A", "v": BIN(...)}, + "sub": {"CN": "Test", "O": "Testers"}, + }, + }, + "sigs": [{"kid": UUID(AKID), "sign": {"a": "gost3410-256A", "v": BIN(...)}}], +} +@end verbatim + +@node Certificate-GOST3410 +@subsection Certificate with GOST R 34.10-2012 + +Same rules of serialisation must be used as with +@ref{SignedData-GOST3410, SignedData}. KID's UUIDv4 is advised to be +calculated by using Streebog-256 hash over the encoded "pub" field. diff --git a/spec/format/hashed.texi b/spec/format/hashed.texi new file mode 100644 index 0000000..e7ed8cb --- /dev/null +++ b/spec/format/hashed.texi @@ -0,0 +1,25 @@ +@node HashedData +@section HashedData format + +Integrity protected container, analogue to CMS'es DigestedData structure. + +@verbatim +{ + "a": ["ID", ..."], + "t": "ID", + "v": BIN/STR or BLOB or MAP/LIST, + "hash": [BIN(hash value), ...], +} +@end verbatim + +@code{/a} tells what algorithms will be used to hash the data. + +@code{/t} tells the type of the data inside. + +If @code{/v} is either a MAP or LIST, then its encoded binary +representation is hashed. If it is BIN/STR or BLOB, then its binary +contents are hashed. So hash will stay the same even if data is +converted from BIN to BLOB. + +@code{/hash} contains the hash values for all corresponding @code{/a} +algorithms. diff --git a/spec/format/index.texi b/spec/format/index.texi new file mode 100644 index 0000000..6780942 --- /dev/null +++ b/spec/format/index.texi @@ -0,0 +1,9 @@ +@node Formats +@unnumbered Formats + +Here are some suggested formats. + +@include format/prv.texi +@include format/signed.texi +@include format/cer.texi +@include format/hashed.texi diff --git a/spec/format/prv.texi b/spec/format/prv.texi new file mode 100644 index 0000000..20bb566 --- /dev/null +++ b/spec/format/prv.texi @@ -0,0 +1,16 @@ +@node PrivateKey +@section PrivateKey format + +Private key is stored in trivial map: + +@verbatim +{ + "a": "ID", + "v": BIN(private key's value), +} +@end verbatim + +@node PrivateKey-GOST3410 +@subsection PrivateKey with GOST R 34.10-2012 + +Big-endian private key representation must be used. diff --git a/spec/format/signed.texi b/spec/format/signed.texi new file mode 100644 index 0000000..a0e4707 --- /dev/null +++ b/spec/format/signed.texi @@ -0,0 +1,109 @@ +@node SignedData +@section SignedData format + +Signing of arbitrary data can be done in two ways: either with +pre-hashing the data, or signing it directly. That resembles +@url{https://datatracker.ietf.org/doc/html/rfc5652, CMS} (PKCS#7) +ASN.1-based format. + +@table @asis + +@item Pre-hashed version: + +@verbatim +{ + "hash": ["ID", ...], + "load": { + "t": "ID", + ?"v": BIN/STR or BLOB or MAP/LIST, + }, + "sigs": [ + { + "kid": UUID(signer's public key-based UUID), + "load": { + "hash": {"ID": BIN(one of hashes value), ...}, + ?"when": TAI64*, + }, + "sign": {"a": "ID", "v": BIN(signature value)}, + ?"cer-loc": ["URL", ...], + }, + ... + ], + ?"certs": [...], +} +@end verbatim + +@code{/hash} tells what algorithms will be used to hash the data of +@code{/load.v}. Signature is created by signing the following encoded MAP: + +@verbatim +{ + "t": "/load.t value", + "v": BIN(""), + "kid": UUID(/sigs.?.kid value) + "load": MAP(/sigs.?.load value), +} +@end verbatim + +If @code{/load.v} is either a MAP or LIST, then its encoded binary +representation is hashed. If it is BIN/STR or BLOB, then its binary +contents are hashed. So signature will stay the same even if data is +converted from BIN to BLOB. + +@item Plain version: + +@verbatim +{ + "load": { + "t": "ID", + ?"v": BIN/STR or MAP/LIST, + }, + "sigs": [ + { + "kid": UUID(signer's public key-based UUID), + ?"load": { + ?"when": TAI64*, + }, + "sign": {"a": "ID", "v": BIN(signature value)}, + ?"cer-loc": ["URL", ...], + }, + ... + ], + ?"certs": [...], +} +@end verbatim + +If @code{/sigs.?.load} is empty, then it @strong{must not} be present. +Signature is created by signing the following encoded MAP: + +@verbatim +{ + "t": "/load.t value", + "v": /load.v's value, + "kid": UUID(/sigs.?.kid value) + "load": MAP(/sigs.?.load value or empty MAP), +} +@end verbatim + +@end table + +If @code{/load.v} is absent, then it is detached and must be provided as +a binary data from outside. + +@code{/sigs.*.load.when} is optional signing time. + +Additional values that must be protected (covered by signature) are +placed in @code{/sigs.*.load} map. Non-protected (informational) fields +are placed outside it. + +@code{/cers} are optionally provided @ref{Certificate, certificates} to +help creating the whole verification chain. They are placed outside +@code{/sigs}, because some of them may be shared among signers. + +@node SignedData-GOST3410 +@subsection SignedData with GOST R 34.10-2012 + +GOST R 34.10-2012 must be used with Streebog (GOST R 34.11-2012) hash +function. Its digest must be big-endian serialised. Public key must be +in @code{BE(X)||BE(Y)} format. Signature is in @code{BE(S)||BE(R)} +format. diff --git a/spec/index.texi b/spec/index.texi new file mode 100644 index 0000000..7072e71 --- /dev/null +++ b/spec/index.texi @@ -0,0 +1,83 @@ +\input texinfo +@settitle YAC + +@copying +Copyright @copyright{} 2024 @email{stargrave@@stargrave.org, Sergey Matveev} +@end copying + +@node Top +@top YAC + +YAC (Yet Another Codec) -- yet another format for binary reprensentation +of structured data. But why!? + +@itemize +@item It must be schema-less format. Schema-aware ones have their + definite valuable advantages, but also a complication drawbacks. +@item Its encoder/decoder must be very compact and small in terms of + code amount, to reduce attack surface on the codec itself. +@item It must be frugal to CPU usage for both performance/memory + constrained and high data volume applications. +@item Its encoding must be reasonably compact, to be friendly to storage + space constrained systems. +@item Its encoding must be deterministic -- there must be only a single + representation of the structured data, allowing its usage in + cryptography-related contexts. +@item It should support enough data types to be able to replace JSON + transparently +@end itemize + +Are not there any satisfiable codecs? + +@table @asis +@item @url{https://wiki.theory.org/BitTorrentSpecification#Bencoding, Bencode} + Is very simple and deterministic. But it has a few data types, even + lacking human-readable strings. +@item @url{https://www.ietf.org/archive/id/draft-rivest-sexp-08.html, Canonical S-Expressions} + Is another interesting candidate with relatively simple encoding, + but without enough data types. +@item @url{https://bsonspec.org/, BSON} + Is pretty simple too, having enough data types. But it is not able + to transfer strings and lists longer than their 32-bit length. Also + that encoding is not compact. +@item @url{https://msgpack.org/, MessagePack} + Is another good candidate with rather simple encoding. But it still + can not transfer large (>4GiB) strings. And it does not have + deterministic encoding rules. +@item @url{https://datatracker.ietf.org/doc/html/rfc8949, CBOR} + Looked like the promising one, having nearly the same aims as we + did, even with Deterministic Encoded CBOR version description and + being pretty compact in binary form. + + But it is the @strong{most} complicated from all the codecs + mentioned above, failing their main claim of being simple. Streaming + encoding can reduce both the code size and memory usage of your + application, but deterministic CBOR encoding prohibits it. + + Moreover hardly you will find CBOR libraries supporting strict + validation of deterministically encoded CBOR structures. +@end table + +YAC deals with those problems by using only streaming deterministic +encoding. YAC is Ain't a CBOR. Its other important differences: + +@itemize +@item It lacks any kind of tagging support, making the whole codec + simpler and less cumbersome when someone does not know how to deal + with the exact tag. +@item MAP allows only string keys with strict length-first ordering, + eliminating questions about dealing with duplicate keys and making + code simpler. +@item It has TAI64-based datetime representation as a base primitive + type. No more different and not always satisfying encodings. +@item BLOB data type gives ability to transfer huge chunks of binary + data in a streaming way. But unlike ASN.1 CER, you still can use + continuous strings representation. +@end itemize + +@insertcopying + +@include encoding/index.texi +@include schema.texi +@include format/index.texi +@include registry.texi diff --git a/spec/mk-info b/spec/mk-info new file mode 100755 index 0000000..1075232 --- /dev/null +++ b/spec/mk-info @@ -0,0 +1,7 @@ +#!/bin/sh -e + +makeinfo \ + --set-customization-variable SECTION_NAME_IN_TITLE=1 \ + --set-customization-variable TREE_TRANSFORMATIONS=complete_tree_nodes_menus \ + --set-customization-variable ASCII_PUNCTUATION=1 \ + index.texi diff --git a/spec/registry.texi b/spec/registry.texi new file mode 100644 index 0000000..735e0f4 --- /dev/null +++ b/spec/registry.texi @@ -0,0 +1,76 @@ +@node Registry +@unnumbered AI registry + +There is example registry of known algorithm identifiers. + +@node AI Hashes +@section Hashes + +@verbatim +blake2b-256 +blake2b-512 +blake3-256 +sha2-224 +sha2-256 +sha2-384 +sha2-512 +sha3-224 +sha3-256 +sha3-384 +sha3-512 +shake128 +shake256 +skein-512 +streebog256 +streebog512 +xxh3-128 +@end verbatim + +@node AI DH +@section DH + +@verbatim +ecdsa-nist192p +ecdsa-nist224p +ecdsa-nist256p +ecdsa-nist384p +ecdsa-nist521p +gost3410-256A +gost3410-256B +gost3410-256C +gost3410-256D +gost3410-512A +gost3410-512B +gost3410-512C +x25519 +x448 +@end verbatim + +@node AI Sign +@section Signatures + +@verbatim +ecdsa-nist192p +ecdsa-nist224p +ecdsa-nist256p +ecdsa-nist384p +ecdsa-nist521p +ed25519 +ed448 +gost3410-256A +gost3410-256B +gost3410-256C +gost3410-256D +gost3410-512A +gost3410-512B +gost3410-512C +@end verbatim + +@node AI Content types +@section Content types + +@verbatim +cer +data +prv +@end verbatim diff --git a/spec/schema.texi b/spec/schema.texi new file mode 100644 index 0000000..afcb2ad --- /dev/null +++ b/spec/schema.texi @@ -0,0 +1,35 @@ +@node Schema +@unnumbered Schema + +YAC can be decoded without any schema definition. But semantic meaning +and constraints of various fields are expected to be written in a human +readable language somewhere nearby. + +For being able to streamingly generate the serialised data, you have to +worry about map's keys ordering. Remember that it is length-first one. + +By default binary data is expected to be encoded as BIN. If you expect +to deal with huge amounts of data, then probably you should use the BLOB +type. Generally you have to differentiate field names for ordinary +strings and blobs. + +For relatively small structures it could be wise to use reasonably short +key names. However it may be difficult and not obvious to keep the +balance between compactness and understandability. For example signature +consisting of algorithm identifier and (obviously) signature value can +be made that way: + +@verbatim +sig: {"a": "ed25519", "v": bytes(...)} +@end verbatim + +@code{"t"} can be used as short name for the "type". + +You should use more or less human readable strings instead of object +identifiers. OIDs database can be considered as an external schema. +Lacking it, or lacking its actual state, you probably won't be able even +guessing the context of the data inside. + +If you really desire more compact encoding, even agree to use schema +definitions, then think about replacing MAPs with LISTs. Non-present +values can be indicated by NIL tag. diff --git a/tyac/COPYING b/tyac/COPYING new file mode 100644 index 0000000..58f2b48 --- /dev/null +++ b/tyac/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/tyac/COPYING.LESSER b/tyac/COPYING.LESSER new file mode 100644 index 0000000..2d9b9aa --- /dev/null +++ b/tyac/COPYING.LESSER @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/tyac/README b/tyac/README new file mode 100644 index 0000000..ae48a30 --- /dev/null +++ b/tyac/README @@ -0,0 +1,5 @@ +Tcl implementation of the YAC encoder. + +* No FLOAT* support. They is stored just as a raw value + +tyac is free software: see the file COPYING.LESSER for copying conditions. diff --git a/tyac/test-vector.tcl b/tyac/test-vector.tcl new file mode 100644 index 0000000..22813c8 --- /dev/null +++ b/tyac/test-vector.tcl @@ -0,0 +1,64 @@ +source tyac.tcl + +MAP { + ints {MAP { + pos {LIST { + {INT 0} + {INT 1} + {INT 31} + {INT 32} + {INT 123} + {INT 1234} + {INT 12345678} + {INT [expr {1 << 80}]} + }} + neg {LIST { + {INT -1} + {INT -2} + {INT -32} + {INT -33} + {INT -123} + {INT -1234} + {INT -12345678} + {INT -[expr {1 << 80}]} + }} + }} + floats {LIST { + {Raw [expr 0x11] [binary decode hex "01020304"]} + }} + nil NIL + bool {LIST {TRUE FALSE}} + str {MAP { + utf8 {STR "привет мир"} + bin {LIST { + {BIN ""} + {BIN [string repeat "0" 60]} + {BIN [string repeat "1" 61]} + {BIN [string repeat "2" 255]} + {BIN [string repeat "3" 1024]} + {BIN [string repeat "4" [expr {1 << 16}]]} + }} + }} + blob {LIST { + {BLOB 12 "5"} + {BLOB 12 [string repeat "6" 12]} + {BLOB 12 [string repeat "7" 13]} + {BLOB 5 "1234567890-"} + }} + empties {LIST { + {LIST {}} + {MAP {}} + {BLOB 123 ""} + {UUID "00000000-0000-0000-0000-000000000000"} + {Raw [expr 0x18] [binary decode hex "0000000000000000"]} + }} + dates {LIST { + {TAI64 1234567890} + {TAI64 1234567890 456000} + {TAI64 1234567890 456789} + {TAI64 1234567890 456789 123456789} + }} + uuid {UUID 0e875e3f-d385-49eb-87b4-be42d641c367} +} + +puts [binary encode hex [YACBufGet]] diff --git a/tyac/tyac.tcl b/tyac/tyac.tcl new file mode 100644 index 0000000..52a55cc --- /dev/null +++ b/tyac/tyac.tcl @@ -0,0 +1,224 @@ +# tyac -- Tcl YAC encoder implementation +# Copyright (C) 2024 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, version 3 of the License. +# +# 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program. If not, see . + +set YACBuf {} + +proc yacAdd {v} { + global YACBuf + lappend YACBuf [binary format c $v] +} + +proc EOC {} { yacAdd [expr 0x00] } +proc NIL {} { yacAdd [expr 0x01] } +proc FALSE {} { yacAdd [expr 0x02] } +proc TRUE {} { yacAdd [expr 0x03] } + +proc UUID {v} { + set v [binary decode hex [string map {- ""} $v]] + if {[string length $v] != 16} { error "bad UUID len" } + yacAdd [expr 0x04] + global YACBuf + lappend YACBuf $v +} + +proc toBE {l v} { + for {set i 0} {$i < $l} {incr i} { + set b [expr {($l - $i - 1) * 8}] + yacAdd [expr {($v & (0xFF << $b)) >> $b}] + } +} + +proc INT {v} { + set neg 0 + if {$v < 0} { + set neg 1 + set v [expr {- ($v + 1)}] + } + if {$v < 32} { + if {$neg} { + yacAdd [expr {0x60 | $v}] + } { + yacAdd [expr {0x40 | $v}] + } + return + } + if {$neg} { set b [expr 0x30] } { set b [expr 0x20] } + set bits 8 + set l 0 + while {1} { + if {$v < [expr {1 << $bits}]} { break } + incr l + incr bits 8 + } + yacAdd [expr {$b | $l}] + toBE [expr {$l + 1}] $v +} + +proc _str {atom v} { + set ll 0 + set vl [string length $v] + set lv $vl + if {$vl >= [expr {1 << 16}]} { + set lv 63 + set ll 8 + } elseif {$vl >= [expr {1 << 8}]} { + set lv 62 + set ll 2 + } elseif {$vl > 60} { + set lv 61 + set ll 1 + } + yacAdd [expr {$atom | $lv}] + if {$ll > 0} { toBE $ll $vl } + global YACBuf + lappend YACBuf $v +} + +proc STR {v} { _str [expr {0x80 | 0x40}] [encoding convertto utf-8 $v]} +proc BIN {v} { _str [expr 0x80] $v} + +proc LIST {v} { + yacAdd [expr 0x08] + foreach i $v { eval $i } + EOC +} + +proc lenFirstSort {a b} { + set a [encoding convertto utf-8 $a] + set b [encoding convertto utf-8 $b] + set al [string length $a] + set bl [string length $b] + if {$al < $bl} { return -1 } + if {$al > $bl} { return 1 } + for {set i 0} {$i < [string length $a]} {incr i} { + set av [lindex $a $i] + set bv [lindex $b $i] + if {$av < $bv} { return -1 } + if {$av > $bv} { return 1 } + } + error "non-unique keys" +} + +proc MAP {pairs} { + set d [dict create] + set keys [list] + for {set i 0} {$i < [llength $pairs]} {incr i 2} { + set k [lindex $pairs $i] + set v [lindex $pairs [expr {$i + 1}]] + lappend keys $k + dict set d $k $v + } + set keys [lsort -command lenFirstSort $keys] + yacAdd [expr 0x09] + foreach k $keys { + STR $k + eval [dict get $d $k] + } + EOC +} + +proc BLOB {chunkLen v} { + yacAdd [expr 0x0B] + INT $chunkLen + set vl [string length $v] + set chunks [expr {$vl / $chunkLen}] + global YACBuf + for {set i 0} {$i < $chunks} {incr i} { + NIL + lappend YACBuf [string range $v \ + [expr {$i * $chunkLen}] \ + [expr {(($i + 1) * $chunkLen) - 1}]] + } + set left [expr {$vl - ($chunks * $chunkLen)}] + if {$left == 0} { + BIN "" + } else { + BIN [string range $v [expr {$vl - $left}] end] + } +} + +set Leapsecs1972 10 +set Leapsecs [list \ + 1483228800 \ + 1435708800 \ + 1341100800 \ + 1230768000 \ + 1136073600 \ + 915148800 \ + 867715200 \ + 820454400 \ + 773020800 \ + 741484800 \ + 709948800 \ + 662688000 \ + 631152000 \ + 567993600 \ + 489024000 \ + 425865600 \ + 394329600 \ + 362793600 \ + 315532800 \ + 283996800 \ + 252460800 \ + 220924800 \ + 189302400 \ + 157766400 \ + 126230400 \ + 94694400 \ + 78796800] + +proc toTAI64 {v} { + global Leapsecs Leapsecs1972 + set diff $Leapsecs1972 + for {set i 0} {$i < [llength $Leapsecs]} {incr i} { + if {$v > [lindex $Leapsecs $i]} { + set diff [expr {$diff + [llength $Leapsecs] - $i}] + break + } + } + set v [expr {$v + 0x4000000000000000 + $diff}] + toBE 8 $v +} + +proc TAI64 {v {n 0} {a 0}} { + if {$a != 0} { + yacAdd [expr 0x1A] + toTAI64 $v + toBE 4 $n + toBE 4 $a + } elseif {$n != 0} { + yacAdd [expr 0x19] + toTAI64 $v + toBE 4 $n + } else { + yacAdd [expr 0x18] + toTAI64 $v + } +} + +proc UTCFromISO {v} { + return [clock scan $v -format {%Y-%m-%d %H:%M:%S} -gmt 1] +} + +proc Raw {t v} { + yacAdd $t + global YACBuf + lappend YACBuf $v +} + +proc YACBufGet {} { + global YACBuf + return [string cat {*}$YACBuf] +} -- 2.50.0