--- /dev/null
+Yet Another Codec.
+Yet another format for binary reprensentation of structured data.
+Its implementations are not heavily tested, project was just created.
--- /dev/null
+/*.o
+/compile_flags.txt
+/libyac.a
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 <http://www.gnu.org/licenses/>.
+
+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:
+
+ <program> Copyright (C) <year> <name of author>
+ 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
+<http://www.gnu.org/licenses/>.
+
+ 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
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
--- /dev/null
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ 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.
--- /dev/null
+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.
--- /dev/null
+#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
--- /dev/null
+#!/bin/sh -e
+
+( cd example ; ./clean )
+exec rm -f *.o *.a compile_flags.txt
--- /dev/null
+redo-ifchange conf/prefix
+read PREFIX <conf/prefix
+printf "%s\n" "-I$PREFIX/include"
--- /dev/null
+-std=c99
+-pipe
+-fno-omit-frame-pointer
+-fPIC
+-fstrict-aliasing
+-ffunction-sections
+-fdata-sections
+-fstack-protector-strong
+-fstack-clash-protection
--- /dev/null
+-Wl,--gc-sections
+-Wl,-z,relro,-z,now
--- /dev/null
+/usr/local
--- /dev/null
+// cyac -- C YAC encoder implementation
+// Copyright (C) 2024 Sergey Matveev <stargrave@stargrave.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#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;
+}
--- /dev/null
+#ifndef YAC_DEC_H
+#define YAC_DEC_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+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
--- /dev/null
+// cyac -- C YAC encoder implementation
+// Copyright (C) 2024 Sergey Matveev <stargrave@stargrave.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/time.h>
+
+#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;
+}
--- /dev/null
+
+#ifndef YAC_DECTAI_H
+#define YAC_DECTAI_H
+
+#include <stddef.h>
+#include <sys/time.h>
+
+#include "dec.h"
+
+enum YACErr
+YACTAI64ToTimeval(struct timeval *tv, const unsigned char *buf, const size_t len);
+
+#endif // YAC_DECTAI_H
--- /dev/null
+redo-ifchange $2.c $2.h conf/cc conf/cflags
+read CC <conf/cc
+CFLAGS=$(cat conf/cflags)
+$CC $CFLAGS -c -o $3 $2.c
--- /dev/null
+// cyac -- C YAC encoder implementation
+// Copyright (C) 2024 Sergey Matveev <stargrave@stargrave.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#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);
+}
--- /dev/null
+#ifndef YAC_ENC_H
+#define YAC_ENC_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+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
--- /dev/null
+// cyac -- C YAC encoder implementation
+// Copyright (C) 2024 Sergey Matveev <stargrave@stargrave.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/time.h>
+
+#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);
+ }
+}
--- /dev/null
+#ifndef YAC_ENCTAI_H
+#define YAC_ENCTAI_H
+
+#include <sys/time.h>
+
+void
+YACTimevalToTAI64(unsigned char *buf, const struct timeval *tv);
+
+#endif // YAC_ENCTAI_H
--- /dev/null
+/print-map
+/test-vector
--- /dev/null
+redo-ifchange print-map test-vector
--- /dev/null
+#!/bin/sh -e
+
+exec rm -f print-map test-vector
--- /dev/null
+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
--- /dev/null
+// cyac -- C YAC encoder implementation
+// Copyright (C) 2024 Sergey Matveev <stargrave@stargrave.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <yac/dec.h>
+#include <yac/dectai.h>
+#include <yac/iter.h>
+
+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;
+}
--- /dev/null
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <yac/atoms.h>
+#include <yac/enc.h>
+#include <yac/enctai.h>
+
+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;
+}
--- /dev/null
+#include <stddef.h>
+#include <stdint.h>
+
+#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;
+}
--- /dev/null
+#ifndef YAC_FROMBE_H
+#define YAC_FROMBE_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+uint64_t
+yacFromBE(const unsigned char *buf, const size_t len);
+
+#endif // YAC_FROMBE_H
--- /dev/null
+redo-ifchange *.h libyac.a conf/prefix
+read PREFIX <conf/prefix
+mkdir -p $PREFIX/include/yac $PREFIX/lib
+cp -f *.h $PREFIX/include/yac
+cp -f libyac.a $PREFIX/lib
+chmod 644 $PREFIX/include/yac/*.h $PREFIX/lib/libyac.a
--- /dev/null
+// cyac -- C YAC encoder implementation
+// Copyright (C) 2024 Sergey Matveev <stargrave@stargrave.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
+#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;
+}
--- /dev/null
+#ifndef YAC_ITER_H
+#define YAC_ITER_H
+
+#include <stddef.h>
+
+#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
--- /dev/null
+#include <stddef.h>
+#include <stdint.h>
+
+#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
+};
--- /dev/null
+#ifndef YAC_LEAPSECS_H
+#define YAC_LEAPSECS_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+extern const size_t YACLeapsecsN;
+extern const uint64_t YACLeapsecs1972;
+extern uint64_t YACLeapsecs[];
+
+#endif // YAC_LEAPSECS_H
--- /dev/null
+redo-ifchange o.list
+objs=$(cat o.list)
+redo-ifchange $objs conf/ar
+read AR <conf/ar
+$AR -rcs $3.a $objs
+mv $3.a $3
--- /dev/null
+dec.o
+dectai.o
+enc.o
+enctai.o
+frombe.o
+iter.o
+leapsecs.o
+tobe.o
+utf8.o
--- /dev/null
+#include <stddef.h>
+#include <stdint.h>
+
+#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
+}
--- /dev/null
+#ifndef YAC_TOBE_H
+#define YAC_TOBE_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+void
+yacToBE(unsigned char *buf, const size_t len, const uint64_t v);
+
+#endif // YAC_TOBE_H
--- /dev/null
+// 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 <dev@frign.de>
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#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;
+}
--- /dev/null
+#ifndef YAC_UTF8_H
+#define YAC_UTF8_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+extern uint32_t YACUTF8InvalidCp;
+
+size_t
+YACUTF8CpDecode(uint32_t *cp, const unsigned char *str, const size_t len);
+
+#endif // YAC_UTF8_H
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 <http://www.gnu.org/licenses/>.
+
+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:
+
+ <program> Copyright (C) <year> <name of author>
+ 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
+<http://www.gnu.org/licenses/>.
+
+ 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
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
--- /dev/null
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ 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.
--- /dev/null
+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.
--- /dev/null
+// 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) + ")"
+ }
+}
--- /dev/null
+// gyac -- Go YAC encoder implementation
+// Copyright (C) 2024 Sergey Matveev <stargrave@stargrave.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+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)
+ }
+}
--- /dev/null
+// gyac -- Go YAC encoder implementation
+// Copyright (C) 2024 Sergey Matveev <stargrave@stargrave.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+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
+}
--- /dev/null
+#!/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
--- /dev/null
+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
+}
--- /dev/null
+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 => ../..
--- /dev/null
+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=
--- /dev/null
+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)
+ }
+}
--- /dev/null
+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
+}
--- /dev/null
+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())
+}
--- /dev/null
+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))))
+}
--- /dev/null
+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))
+}
--- /dev/null
+// gyac -- Go YAC encoder implementation
+// Copyright (C) 2024 Sergey Matveev <stargrave@stargrave.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+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
+}
--- /dev/null
+// gyac -- Go YAC encoder implementation
+// Copyright (C) 2024 Sergey Matveev <stargrave@stargrave.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+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
+}
--- /dev/null
+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
+)
--- /dev/null
+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=
--- /dev/null
+// 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]]
+}
--- /dev/null
+// gyac -- Go YAC encoder implementation
+// Copyright (C) 2024 Sergey Matveev <stargrave@stargrave.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+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))
+}
--- /dev/null
+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]
+}
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 <http://www.gnu.org/licenses/>.
+
+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:
+
+ <program> Copyright (C) <year> <name of author>
+ 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
+<http://www.gnu.org/licenses/>.
+
+ 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
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
--- /dev/null
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ 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.
--- /dev/null
+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.
--- /dev/null
+# pyac -- Python YAC implementation
+# Copyright (C) 2024 Sergey Matveev <stargrave@stargrave.org>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+
+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<<bits):
+ break
+ else:
+ l = 15
+ return (tag | l).to_bytes(1, "big") + v.to_bytes(l+1, "big")
+
+ @classmethod
+ def decode(klass, data):
+ if (data[0] >= 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))
--- /dev/null
+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())
--- /dev/null
+/index.info
--- /dev/null
+* Sergey Mayorov for his valuable consultation and suggestions
--- /dev/null
+@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
--- /dev/null
+@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
--- /dev/null
+@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.
--- /dev/null
+@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
--- /dev/null
+@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
--- /dev/null
+@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
--- /dev/null
+@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
--- /dev/null
+@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
--- /dev/null
+@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
--- /dev/null
+@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
--- /dev/null
+@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.
--- /dev/null
+@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.
--- /dev/null
+@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
--- /dev/null
+@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.
--- /dev/null
+@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.
--- /dev/null
+\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
--- /dev/null
+#!/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
--- /dev/null
+@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
--- /dev/null
+@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.
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 <http://www.gnu.org/licenses/>.
+
+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:
+
+ <program> Copyright (C) <year> <name of author>
+ 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
+<http://www.gnu.org/licenses/>.
+
+ 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
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
--- /dev/null
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ 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.
--- /dev/null
+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.
--- /dev/null
+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]]
--- /dev/null
+# tyac -- Tcl YAC encoder implementation
+# Copyright (C) 2024 Sergey Matveev <stargrave@stargrave.org>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+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]
+}