]> Cypherpunks repositories - keks.git/commitdiff
Initial revision
authorSergey Matveev <stargrave@stargrave.org>
Sat, 5 Oct 2024 14:32:33 +0000 (17:32 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Sat, 5 Oct 2024 14:32:33 +0000 (17:32 +0300)
94 files changed:
README [new file with mode: 0644]
cyac/.gitignore [new file with mode: 0644]
cyac/COPYING [new file with mode: 0644]
cyac/COPYING.LESSER [new file with mode: 0644]
cyac/README [new file with mode: 0644]
cyac/atoms.h [new file with mode: 0644]
cyac/clean [new file with mode: 0755]
cyac/compile_flags.txt.do [new file with mode: 0644]
cyac/conf/ar [new file with mode: 0644]
cyac/conf/cc [new file with mode: 0644]
cyac/conf/cflags [new file with mode: 0644]
cyac/conf/ldflags [new file with mode: 0644]
cyac/conf/prefix [new file with mode: 0644]
cyac/dec.c [new file with mode: 0644]
cyac/dec.h [new file with mode: 0644]
cyac/dectai.c [new file with mode: 0644]
cyac/dectai.h [new file with mode: 0644]
cyac/default.o.do [new file with mode: 0644]
cyac/enc.c [new file with mode: 0644]
cyac/enc.h [new file with mode: 0644]
cyac/enctai.c [new file with mode: 0644]
cyac/enctai.h [new file with mode: 0644]
cyac/example/.gitignore [new file with mode: 0644]
cyac/example/all.do [new file with mode: 0644]
cyac/example/clean [new file with mode: 0755]
cyac/example/default.do [new file with mode: 0644]
cyac/example/print-map.c [new file with mode: 0644]
cyac/example/test-vector.c [new file with mode: 0644]
cyac/frombe.c [new file with mode: 0644]
cyac/frombe.h [new file with mode: 0644]
cyac/install.do [new file with mode: 0644]
cyac/iter.c [new file with mode: 0644]
cyac/iter.h [new file with mode: 0644]
cyac/leapsecs.c [new file with mode: 0644]
cyac/leapsecs.h [new file with mode: 0644]
cyac/libyac.a.do [new file with mode: 0644]
cyac/o.list [new file with mode: 0644]
cyac/tobe.c [new file with mode: 0644]
cyac/tobe.h [new file with mode: 0644]
cyac/utf8.c [new file with mode: 0644]
cyac/utf8.h [new file with mode: 0644]
gyac/COPYING [new file with mode: 0644]
gyac/COPYING.LESSER [new file with mode: 0644]
gyac/README [new file with mode: 0644]
gyac/atomtype_string.go [new file with mode: 0644]
gyac/be.go [new file with mode: 0644]
gyac/blob.go [new file with mode: 0644]
gyac/cmd/certool/basic.t [new file with mode: 0755]
gyac/cmd/certool/cer.go [new file with mode: 0644]
gyac/cmd/certool/go.mod [new file with mode: 0644]
gyac/cmd/certool/go.sum [new file with mode: 0644]
gyac/cmd/certool/main.go [new file with mode: 0644]
gyac/cmd/certool/prv.go [new file with mode: 0644]
gyac/cmd/print/main.go [new file with mode: 0644]
gyac/cmd/test-vector-anys/main.go [new file with mode: 0644]
gyac/cmd/test-vector-manual/main.go [new file with mode: 0644]
gyac/dec.go [new file with mode: 0644]
gyac/enc.go [new file with mode: 0644]
gyac/go.mod [new file with mode: 0644]
gyac/go.sum [new file with mode: 0644]
gyac/itemtype_string.go [new file with mode: 0644]
gyac/reflect.go [new file with mode: 0644]
gyac/sort.go [new file with mode: 0644]
pyac/COPYING [new file with mode: 0644]
pyac/COPYING.LESSER [new file with mode: 0644]
pyac/README [new file with mode: 0644]
pyac/pyac.py [new file with mode: 0644]
pyac/test-vector.py [new file with mode: 0644]
spec/.gitignore [new file with mode: 0644]
spec/THANKS [new file with mode: 0644]
spec/encoding/blob.texi [new file with mode: 0644]
spec/encoding/cont.texi [new file with mode: 0644]
spec/encoding/float.texi [new file with mode: 0644]
spec/encoding/index.texi [new file with mode: 0644]
spec/encoding/int.texi [new file with mode: 0644]
spec/encoding/prim.texi [new file with mode: 0644]
spec/encoding/str.texi [new file with mode: 0644]
spec/encoding/table.texi [new file with mode: 0644]
spec/encoding/tai64.texi [new file with mode: 0644]
spec/encoding/uuid.texi [new file with mode: 0644]
spec/format/cer.texi [new file with mode: 0644]
spec/format/hashed.texi [new file with mode: 0644]
spec/format/index.texi [new file with mode: 0644]
spec/format/prv.texi [new file with mode: 0644]
spec/format/signed.texi [new file with mode: 0644]
spec/index.texi [new file with mode: 0644]
spec/mk-info [new file with mode: 0755]
spec/registry.texi [new file with mode: 0644]
spec/schema.texi [new file with mode: 0644]
tyac/COPYING [new file with mode: 0644]
tyac/COPYING.LESSER [new file with mode: 0644]
tyac/README [new file with mode: 0644]
tyac/test-vector.tcl [new file with mode: 0644]
tyac/tyac.tcl [new file with mode: 0644]

diff --git a/README b/README
new file mode 100644 (file)
index 0000000..fbaf228
--- /dev/null
+++ b/README
@@ -0,0 +1,3 @@
+Yet Another Codec.
+Yet another format for binary reprensentation of structured data.
+Its implementations are not heavily tested, project was just created.
diff --git a/cyac/.gitignore b/cyac/.gitignore
new file mode 100644 (file)
index 0000000..a0169a4
--- /dev/null
@@ -0,0 +1,3 @@
+/*.o
+/compile_flags.txt
+/libyac.a
diff --git a/cyac/COPYING b/cyac/COPYING
new file mode 100644 (file)
index 0000000..58f2b48
--- /dev/null
@@ -0,0 +1,674 @@
+                   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>.
diff --git a/cyac/COPYING.LESSER b/cyac/COPYING.LESSER
new file mode 100644 (file)
index 0000000..2d9b9aa
--- /dev/null
@@ -0,0 +1,165 @@
+                  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.
diff --git a/cyac/README b/cyac/README
new file mode 100644 (file)
index 0000000..41c7d66
--- /dev/null
@@ -0,0 +1,27 @@
+C99 implementation of the YAC codec.
+
+* No FLOAT*, INTs greater than 64-bit, TAI64NA and nanoseconds support.
+  They are stored/decoded just as a raw value
+* Actual negative integer maximal size is -2^63-1
+
+dec.* contains the actual decoder, YACAtomDecode. Basically that is all
+you need in most cases. YACItemType is high-level type of the atom you
+decoded. YACErr tells about the possible decoding error. YACAtom is the
+decoded atom.
+
+dectai.* contains converter from TAI64 to UTC.
+leapsecs.* contains the leap seconds database itself.
+iter.* contains helpers that may pass over the iterables.
+
+enc.* contains encoders for various atoms. Containers and blobs must
+be made manually, by finishing them with proper EOC/BIN and sorting the
+keys. enctai.* contains converter from UTC to TAI64.
+
+example/test-vector.c is the same structure as in tyac/test-vector.tcl.
+Their outputs must be the same.
+
+Project uses redo (http://cr.yp.to/redo.html) build system, but it is
+trivial to compile and install manually. conf/* contains the compiler
+and linker configuration options.
+
+cyac is free software: see the file COPYING.LESSER for copying conditions.
diff --git a/cyac/atoms.h b/cyac/atoms.h
new file mode 100644 (file)
index 0000000..a2d87bb
--- /dev/null
@@ -0,0 +1,122 @@
+#ifndef YAC_ATOMS_H
+#define YAC_ATOMS_H
+
+enum YACAtomType {
+    YACAtomEOC = 0x00,
+    YACAtomNIL = 0x01,
+    YACAtomFalse = 0x02,
+    YACAtomTrue = 0x03,
+    YACAtomUUID = 0x04,
+    YACAtomList = 0x08,
+    YACAtomMap = 0x09,
+    YACAtomBlob = 0x0B,
+    YACAtomFloat16 = 0x10,
+    YACAtomFloat32 = 0x11,
+    YACAtomFloat64 = 0x12,
+    YACAtomFloat128 = 0x13,
+    YACAtomFloat256 = 0x14,
+    YACAtomTAI64 = 0x18,
+    YACAtomTAI64N = 0x19,
+    YACAtomTAI64NA = 0x1a,
+    YACAtomIntPos1 = 0x20,
+    YACAtomIntPos2 = 0x21,
+    YACAtomIntPos3 = 0x22,
+    YACAtomIntPos4 = 0x23,
+    YACAtomIntPos5 = 0x24,
+    YACAtomIntPos6 = 0x25,
+    YACAtomIntPos7 = 0x26,
+    YACAtomIntPos8 = 0x27,
+    YACAtomIntPos9 = 0x28,
+    YACAtomIntPos10 = 0x29,
+    YACAtomIntPos11 = 0x2A,
+    YACAtomIntPos12 = 0x2B,
+    YACAtomIntPos13 = 0x2C,
+    YACAtomIntPos14 = 0x2D,
+    YACAtomIntPos15 = 0x2E,
+    YACAtomIntPos16 = 0x2F,
+    YACAtomIntNeg1 = 0x30,
+    YACAtomIntNeg2 = 0x31,
+    YACAtomIntNeg3 = 0x32,
+    YACAtomIntNeg4 = 0x33,
+    YACAtomIntNeg5 = 0x34,
+    YACAtomIntNeg6 = 0x35,
+    YACAtomIntNeg7 = 0x36,
+    YACAtomIntNeg8 = 0x37,
+    YACAtomIntNeg9 = 0x38,
+    YACAtomIntNeg10 = 0x39,
+    YACAtomIntNeg11 = 0x3A,
+    YACAtomIntNeg12 = 0x3B,
+    YACAtomIntNeg13 = 0x3C,
+    YACAtomIntNeg14 = 0x3D,
+    YACAtomIntNeg15 = 0x3E,
+    YACAtomIntNeg16 = 0x3F,
+    YACAtomInt0 = 0x40,
+    YACAtomInt1 = 0x41,
+    YACAtomInt2 = 0x42,
+    YACAtomInt3 = 0x43,
+    YACAtomInt4 = 0x44,
+    YACAtomInt5 = 0x45,
+    YACAtomInt6 = 0x46,
+    YACAtomInt7 = 0x47,
+    YACAtomInt8 = 0x48,
+    YACAtomInt9 = 0x49,
+    YACAtomInt10 = 0x4A,
+    YACAtomInt11 = 0x4B,
+    YACAtomInt12 = 0x4C,
+    YACAtomInt13 = 0x4D,
+    YACAtomInt14 = 0x4E,
+    YACAtomInt15 = 0x4F,
+    YACAtomInt16 = 0x50,
+    YACAtomInt17 = 0x51,
+    YACAtomInt18 = 0x52,
+    YACAtomInt19 = 0x53,
+    YACAtomInt20 = 0x54,
+    YACAtomInt21 = 0x55,
+    YACAtomInt22 = 0x56,
+    YACAtomInt23 = 0x57,
+    YACAtomInt24 = 0x58,
+    YACAtomInt25 = 0x59,
+    YACAtomInt26 = 0x5A,
+    YACAtomInt27 = 0x5B,
+    YACAtomInt28 = 0x5C,
+    YACAtomInt29 = 0x5D,
+    YACAtomInt30 = 0x5E,
+    YACAtomInt31 = 0x5F,
+    YACAtomIntN1 = 0x60,
+    YACAtomIntN2 = 0x61,
+    YACAtomIntN3 = 0x62,
+    YACAtomIntN4 = 0x63,
+    YACAtomIntN5 = 0x64,
+    YACAtomIntN6 = 0x65,
+    YACAtomIntN7 = 0x66,
+    YACAtomIntN8 = 0x67,
+    YACAtomIntN9 = 0x68,
+    YACAtomIntN10 = 0x69,
+    YACAtomIntN11 = 0x6A,
+    YACAtomIntN12 = 0x6B,
+    YACAtomIntN13 = 0x6C,
+    YACAtomIntN14 = 0x6D,
+    YACAtomIntN15 = 0x6E,
+    YACAtomIntN16 = 0x6F,
+    YACAtomIntN17 = 0x70,
+    YACAtomIntN18 = 0x71,
+    YACAtomIntN19 = 0x72,
+    YACAtomIntN20 = 0x73,
+    YACAtomIntN21 = 0x74,
+    YACAtomIntN22 = 0x75,
+    YACAtomIntN23 = 0x76,
+    YACAtomIntN24 = 0x77,
+    YACAtomIntN25 = 0x78,
+    YACAtomIntN26 = 0x79,
+    YACAtomIntN27 = 0x7A,
+    YACAtomIntN28 = 0x7B,
+    YACAtomIntN29 = 0x7C,
+    YACAtomIntN30 = 0x7D,
+    YACAtomIntN31 = 0x7E,
+    YACAtomIntN32 = 0x7F,
+
+    YACAtomStrings = 0x80,
+    YACAtomIsUTF8 = 0x40,
+};
+
+#endif // YAC_ATOMS_H
diff --git a/cyac/clean b/cyac/clean
new file mode 100755 (executable)
index 0000000..4ced8c3
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh -e
+
+( cd example ; ./clean )
+exec rm -f *.o *.a compile_flags.txt
diff --git a/cyac/compile_flags.txt.do b/cyac/compile_flags.txt.do
new file mode 100644 (file)
index 0000000..1b9f125
--- /dev/null
@@ -0,0 +1,3 @@
+redo-ifchange conf/prefix
+read PREFIX <conf/prefix
+printf "%s\n" "-I$PREFIX/include"
diff --git a/cyac/conf/ar b/cyac/conf/ar
new file mode 100644 (file)
index 0000000..578b121
--- /dev/null
@@ -0,0 +1 @@
+ar
diff --git a/cyac/conf/cc b/cyac/conf/cc
new file mode 100644 (file)
index 0000000..dfd45f0
--- /dev/null
@@ -0,0 +1 @@
+cc
diff --git a/cyac/conf/cflags b/cyac/conf/cflags
new file mode 100644 (file)
index 0000000..c761868
--- /dev/null
@@ -0,0 +1,9 @@
+-std=c99
+-pipe
+-fno-omit-frame-pointer
+-fPIC
+-fstrict-aliasing
+-ffunction-sections
+-fdata-sections
+-fstack-protector-strong
+-fstack-clash-protection
diff --git a/cyac/conf/ldflags b/cyac/conf/ldflags
new file mode 100644 (file)
index 0000000..f4d5caa
--- /dev/null
@@ -0,0 +1,2 @@
+-Wl,--gc-sections
+-Wl,-z,relro,-z,now
diff --git a/cyac/conf/prefix b/cyac/conf/prefix
new file mode 100644 (file)
index 0000000..e06944e
--- /dev/null
@@ -0,0 +1 @@
+/usr/local
diff --git a/cyac/dec.c b/cyac/dec.c
new file mode 100644 (file)
index 0000000..35d3960
--- /dev/null
@@ -0,0 +1,233 @@
+// 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;
+}
diff --git a/cyac/dec.h b/cyac/dec.h
new file mode 100644 (file)
index 0000000..a2eef5d
--- /dev/null
@@ -0,0 +1,65 @@
+#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
diff --git a/cyac/dectai.c b/cyac/dectai.c
new file mode 100644 (file)
index 0000000..4e459e2
--- /dev/null
@@ -0,0 +1,52 @@
+// 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;
+}
diff --git a/cyac/dectai.h b/cyac/dectai.h
new file mode 100644 (file)
index 0000000..2341bd7
--- /dev/null
@@ -0,0 +1,13 @@
+
+#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
diff --git a/cyac/default.o.do b/cyac/default.o.do
new file mode 100644 (file)
index 0000000..013ecf6
--- /dev/null
@@ -0,0 +1,4 @@
+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
diff --git a/cyac/enc.c b/cyac/enc.c
new file mode 100644 (file)
index 0000000..84e95a5
--- /dev/null
@@ -0,0 +1,268 @@
+// 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);
+}
diff --git a/cyac/enc.h b/cyac/enc.h
new file mode 100644 (file)
index 0000000..78dfa8b
--- /dev/null
@@ -0,0 +1,71 @@
+#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
diff --git a/cyac/enctai.c b/cyac/enctai.c
new file mode 100644 (file)
index 0000000..bafbce9
--- /dev/null
@@ -0,0 +1,40 @@
+// 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);
+    }
+}
diff --git a/cyac/enctai.h b/cyac/enctai.h
new file mode 100644 (file)
index 0000000..7487ae6
--- /dev/null
@@ -0,0 +1,9 @@
+#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
diff --git a/cyac/example/.gitignore b/cyac/example/.gitignore
new file mode 100644 (file)
index 0000000..4681da6
--- /dev/null
@@ -0,0 +1,2 @@
+/print-map
+/test-vector
diff --git a/cyac/example/all.do b/cyac/example/all.do
new file mode 100644 (file)
index 0000000..e32e2ab
--- /dev/null
@@ -0,0 +1 @@
+redo-ifchange print-map test-vector
diff --git a/cyac/example/clean b/cyac/example/clean
new file mode 100755 (executable)
index 0000000..2403ed9
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh -e
+
+exec rm -f print-map test-vector
diff --git a/cyac/example/default.do b/cyac/example/default.do
new file mode 100644 (file)
index 0000000..e8eac11
--- /dev/null
@@ -0,0 +1,6 @@
+redo-ifchange $2.c ../conf/cc ../conf/cflags ../conf/ldflags ../conf/prefix
+read CC <../conf/cc
+CFLAGS=$(cat ../conf/cflags)
+LDFLAGS=$(cat ../conf/ldflags)
+read PREFIX <../conf/prefix
+$CC $CFLAGS -I$PREFIX/include -o $3 $2.c $LDFLAGS -L$PREFIX/lib -lyac
diff --git a/cyac/example/print-map.c b/cyac/example/print-map.c
new file mode 100644 (file)
index 0000000..632feb0
--- /dev/null
@@ -0,0 +1,258 @@
+// 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;
+}
diff --git a/cyac/example/test-vector.c b/cyac/example/test-vector.c
new file mode 100644 (file)
index 0000000..c341b84
--- /dev/null
@@ -0,0 +1,218 @@
+#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;
+}
diff --git a/cyac/frombe.c b/cyac/frombe.c
new file mode 100644 (file)
index 0000000..92f7c44
--- /dev/null
@@ -0,0 +1,16 @@
+#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;
+}
diff --git a/cyac/frombe.h b/cyac/frombe.h
new file mode 100644 (file)
index 0000000..49ff480
--- /dev/null
@@ -0,0 +1,10 @@
+#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
diff --git a/cyac/install.do b/cyac/install.do
new file mode 100644 (file)
index 0000000..b5dc6fa
--- /dev/null
@@ -0,0 +1,6 @@
+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
diff --git a/cyac/iter.c b/cyac/iter.c
new file mode 100644 (file)
index 0000000..7ac832b
--- /dev/null
@@ -0,0 +1,184 @@
+// 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;
+}
diff --git a/cyac/iter.h b/cyac/iter.h
new file mode 100644 (file)
index 0000000..268d151
--- /dev/null
@@ -0,0 +1,45 @@
+#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
diff --git a/cyac/leapsecs.c b/cyac/leapsecs.c
new file mode 100644 (file)
index 0000000..0f0f05f
--- /dev/null
@@ -0,0 +1,36 @@
+#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
+};
diff --git a/cyac/leapsecs.h b/cyac/leapsecs.h
new file mode 100644 (file)
index 0000000..9a643da
--- /dev/null
@@ -0,0 +1,11 @@
+#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
diff --git a/cyac/libyac.a.do b/cyac/libyac.a.do
new file mode 100644 (file)
index 0000000..8068afa
--- /dev/null
@@ -0,0 +1,6 @@
+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
diff --git a/cyac/o.list b/cyac/o.list
new file mode 100644 (file)
index 0000000..97f1fd7
--- /dev/null
@@ -0,0 +1,9 @@
+dec.o
+dectai.o
+enc.o
+enctai.o
+frombe.o
+iter.o
+leapsecs.o
+tobe.o
+utf8.o
diff --git a/cyac/tobe.c b/cyac/tobe.c
new file mode 100644 (file)
index 0000000..530a798
--- /dev/null
@@ -0,0 +1,15 @@
+#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
+}
diff --git a/cyac/tobe.h b/cyac/tobe.h
new file mode 100644 (file)
index 0000000..872e1d1
--- /dev/null
@@ -0,0 +1,10 @@
+#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
diff --git a/cyac/utf8.c b/cyac/utf8.c
new file mode 100644 (file)
index 0000000..a593370
--- /dev/null
@@ -0,0 +1,96 @@
+// This code is stripped copy-paste of https://libs.suckless.org/libgrapheme/
+// grapheme_decode_utf8 function, licenced under ISC-License.
+// Copyright 2019-2022 Laslo Hunhold <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;
+}
diff --git a/cyac/utf8.h b/cyac/utf8.h
new file mode 100644 (file)
index 0000000..4a3dd64
--- /dev/null
@@ -0,0 +1,12 @@
+#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
diff --git a/gyac/COPYING b/gyac/COPYING
new file mode 100644 (file)
index 0000000..58f2b48
--- /dev/null
@@ -0,0 +1,674 @@
+                   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>.
diff --git a/gyac/COPYING.LESSER b/gyac/COPYING.LESSER
new file mode 100644 (file)
index 0000000..2d9b9aa
--- /dev/null
@@ -0,0 +1,165 @@
+                  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.
diff --git a/gyac/README b/gyac/README
new file mode 100644 (file)
index 0000000..a49cdf6
--- /dev/null
@@ -0,0 +1,7 @@
+Go implementation of the YAC codec.
+
+* No FLOAT*, INTs greater than 64-bit, TAI64NA.
+  They are stored/decoded just as a raw value
+* Actual negative integer maximal size is -2^63-1
+
+gyac is free software: see the file COPYING.LESSER for copying conditions.
diff --git a/gyac/atomtype_string.go b/gyac/atomtype_string.go
new file mode 100644 (file)
index 0000000..ebb5f94
--- /dev/null
@@ -0,0 +1,163 @@
+// Code generated by "stringer -type=AtomType"; DO NOT EDIT.
+
+package gyac
+
+import "strconv"
+
+func _() {
+       // An "invalid array index" compiler error signifies that the constant values have changed.
+       // Re-run the stringer command to generate them again.
+       var x [1]struct{}
+       _ = x[AtomEOC-0]
+       _ = x[AtomNIL-1]
+       _ = x[AtomFalse-2]
+       _ = x[AtomTrue-3]
+       _ = x[AtomUUID-4]
+       _ = x[AtomList-8]
+       _ = x[AtomMap-9]
+       _ = x[AtomBlob-11]
+       _ = x[AtomFloat16-16]
+       _ = x[AtomFloat32-17]
+       _ = x[AtomFloat64-18]
+       _ = x[AtomFloat128-19]
+       _ = x[AtomFloat256-20]
+       _ = x[AtomTAI64-24]
+       _ = x[AtomTAI64N-25]
+       _ = x[AtomTAI64NA-26]
+       _ = x[AtomIntPos1-32]
+       _ = x[AtomIntPos2-33]
+       _ = x[AtomIntPos3-34]
+       _ = x[AtomIntPos4-35]
+       _ = x[AtomIntPos5-36]
+       _ = x[AtomIntPos6-37]
+       _ = x[AtomIntPos7-38]
+       _ = x[AtomIntPos8-39]
+       _ = x[AtomIntPos9-40]
+       _ = x[AtomIntPos10-41]
+       _ = x[AtomIntPos11-42]
+       _ = x[AtomIntPos12-43]
+       _ = x[AtomIntPos13-44]
+       _ = x[AtomIntPos14-45]
+       _ = x[AtomIntPos15-46]
+       _ = x[AtomIntPos16-47]
+       _ = x[AtomIntNeg1-48]
+       _ = x[AtomIntNeg2-49]
+       _ = x[AtomIntNeg3-50]
+       _ = x[AtomIntNeg4-51]
+       _ = x[AtomIntNeg5-52]
+       _ = x[AtomIntNeg6-53]
+       _ = x[AtomIntNeg7-54]
+       _ = x[AtomIntNeg8-55]
+       _ = x[AtomIntNeg9-56]
+       _ = x[AtomIntNeg10-57]
+       _ = x[AtomIntNeg11-58]
+       _ = x[AtomIntNeg12-59]
+       _ = x[AtomIntNeg13-60]
+       _ = x[AtomIntNeg14-61]
+       _ = x[AtomIntNeg15-62]
+       _ = x[AtomIntNeg16-63]
+       _ = x[AtomInt0-64]
+       _ = x[AtomInt1-65]
+       _ = x[AtomInt2-66]
+       _ = x[AtomInt3-67]
+       _ = x[AtomInt4-68]
+       _ = x[AtomInt5-69]
+       _ = x[AtomInt6-70]
+       _ = x[AtomInt7-71]
+       _ = x[AtomInt8-72]
+       _ = x[AtomInt9-73]
+       _ = x[AtomInt10-74]
+       _ = x[AtomInt11-75]
+       _ = x[AtomInt12-76]
+       _ = x[AtomInt13-77]
+       _ = x[AtomInt14-78]
+       _ = x[AtomInt15-79]
+       _ = x[AtomInt16-80]
+       _ = x[AtomInt17-81]
+       _ = x[AtomInt18-82]
+       _ = x[AtomInt19-83]
+       _ = x[AtomInt20-84]
+       _ = x[AtomInt21-85]
+       _ = x[AtomInt22-86]
+       _ = x[AtomInt23-87]
+       _ = x[AtomInt24-88]
+       _ = x[AtomInt25-89]
+       _ = x[AtomInt26-90]
+       _ = x[AtomInt27-91]
+       _ = x[AtomInt28-92]
+       _ = x[AtomInt29-93]
+       _ = x[AtomInt30-94]
+       _ = x[AtomInt31-95]
+       _ = x[AtomIntN1-96]
+       _ = x[AtomIntN2-97]
+       _ = x[AtomIntN3-98]
+       _ = x[AtomIntN4-99]
+       _ = x[AtomIntN5-100]
+       _ = x[AtomIntN6-101]
+       _ = x[AtomIntN7-102]
+       _ = x[AtomIntN8-103]
+       _ = x[AtomIntN9-104]
+       _ = x[AtomIntN10-105]
+       _ = x[AtomIntN11-106]
+       _ = x[AtomIntN12-107]
+       _ = x[AtomIntN13-108]
+       _ = x[AtomIntN14-109]
+       _ = x[AtomIntN15-110]
+       _ = x[AtomIntN16-111]
+       _ = x[AtomIntN17-112]
+       _ = x[AtomIntN18-113]
+       _ = x[AtomIntN19-114]
+       _ = x[AtomIntN20-115]
+       _ = x[AtomIntN21-116]
+       _ = x[AtomIntN22-117]
+       _ = x[AtomIntN23-118]
+       _ = x[AtomIntN24-119]
+       _ = x[AtomIntN25-120]
+       _ = x[AtomIntN26-121]
+       _ = x[AtomIntN27-122]
+       _ = x[AtomIntN28-123]
+       _ = x[AtomIntN29-124]
+       _ = x[AtomIntN30-125]
+       _ = x[AtomIntN31-126]
+       _ = x[AtomIntN32-127]
+}
+
+const (
+       _AtomType_name_0 = "AtomEOCAtomNILAtomFalseAtomTrueAtomUUID"
+       _AtomType_name_1 = "AtomListAtomMap"
+       _AtomType_name_2 = "AtomBlob"
+       _AtomType_name_3 = "AtomFloat16AtomFloat32AtomFloat64AtomFloat128AtomFloat256"
+       _AtomType_name_4 = "AtomTAI64AtomTAI64NAtomTAI64NA"
+       _AtomType_name_5 = "AtomIntPos1AtomIntPos2AtomIntPos3AtomIntPos4AtomIntPos5AtomIntPos6AtomIntPos7AtomIntPos8AtomIntPos9AtomIntPos10AtomIntPos11AtomIntPos12AtomIntPos13AtomIntPos14AtomIntPos15AtomIntPos16AtomIntNeg1AtomIntNeg2AtomIntNeg3AtomIntNeg4AtomIntNeg5AtomIntNeg6AtomIntNeg7AtomIntNeg8AtomIntNeg9AtomIntNeg10AtomIntNeg11AtomIntNeg12AtomIntNeg13AtomIntNeg14AtomIntNeg15AtomIntNeg16AtomInt0AtomInt1AtomInt2AtomInt3AtomInt4AtomInt5AtomInt6AtomInt7AtomInt8AtomInt9AtomInt10AtomInt11AtomInt12AtomInt13AtomInt14AtomInt15AtomInt16AtomInt17AtomInt18AtomInt19AtomInt20AtomInt21AtomInt22AtomInt23AtomInt24AtomInt25AtomInt26AtomInt27AtomInt28AtomInt29AtomInt30AtomInt31AtomIntN1AtomIntN2AtomIntN3AtomIntN4AtomIntN5AtomIntN6AtomIntN7AtomIntN8AtomIntN9AtomIntN10AtomIntN11AtomIntN12AtomIntN13AtomIntN14AtomIntN15AtomIntN16AtomIntN17AtomIntN18AtomIntN19AtomIntN20AtomIntN21AtomIntN22AtomIntN23AtomIntN24AtomIntN25AtomIntN26AtomIntN27AtomIntN28AtomIntN29AtomIntN30AtomIntN31AtomIntN32"
+)
+
+var (
+       _AtomType_index_0 = [...]uint8{0, 7, 14, 23, 31, 39}
+       _AtomType_index_1 = [...]uint8{0, 8, 15}
+       _AtomType_index_3 = [...]uint8{0, 11, 22, 33, 45, 57}
+       _AtomType_index_4 = [...]uint8{0, 9, 19, 30}
+       _AtomType_index_5 = [...]uint16{0, 11, 22, 33, 44, 55, 66, 77, 88, 99, 111, 123, 135, 147, 159, 171, 183, 194, 205, 216, 227, 238, 249, 260, 271, 282, 294, 306, 318, 330, 342, 354, 366, 374, 382, 390, 398, 406, 414, 422, 430, 438, 446, 455, 464, 473, 482, 491, 500, 509, 518, 527, 536, 545, 554, 563, 572, 581, 590, 599, 608, 617, 626, 635, 644, 653, 662, 671, 680, 689, 698, 707, 716, 725, 735, 745, 755, 765, 775, 785, 795, 805, 815, 825, 835, 845, 855, 865, 875, 885, 895, 905, 915, 925, 935, 945, 955}
+)
+
+func (i AtomType) String() string {
+       switch {
+       case i <= 4:
+               return _AtomType_name_0[_AtomType_index_0[i]:_AtomType_index_0[i+1]]
+       case 8 <= i && i <= 9:
+               i -= 8
+               return _AtomType_name_1[_AtomType_index_1[i]:_AtomType_index_1[i+1]]
+       case i == 11:
+               return _AtomType_name_2
+       case 16 <= i && i <= 20:
+               i -= 16
+               return _AtomType_name_3[_AtomType_index_3[i]:_AtomType_index_3[i+1]]
+       case 24 <= i && i <= 26:
+               i -= 24
+               return _AtomType_name_4[_AtomType_index_4[i]:_AtomType_index_4[i+1]]
+       case 32 <= i && i <= 127:
+               i -= 32
+               return _AtomType_name_5[_AtomType_index_5[i]:_AtomType_index_5[i+1]]
+       default:
+               return "AtomType(" + strconv.FormatInt(int64(i), 10) + ")"
+       }
+}
diff --git a/gyac/be.go b/gyac/be.go
new file mode 100644 (file)
index 0000000..bf87384
--- /dev/null
@@ -0,0 +1,29 @@
+// 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)
+       }
+}
diff --git a/gyac/blob.go b/gyac/blob.go
new file mode 100644 (file)
index 0000000..75d87d9
--- /dev/null
@@ -0,0 +1,44 @@
+// 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
+}
diff --git a/gyac/cmd/certool/basic.t b/gyac/cmd/certool/basic.t
new file mode 100755 (executable)
index 0000000..9db0be1
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+testname=`basename "$0"`
+test_description="Check that basic functionality works"
+. $SHARNESS_TEST_SRCDIR/sharness.sh
+
+TMPDIR=${TMPDIR:-/tmp}
+
+subj='{"CN": "CA", "C": "RU"}'
+test_expect_success "CA generation" "certool \
+    -algo gost3410-512C \
+    -ku ca \
+    -prv $TMPDIR/ca.prv -cer $TMPDIR/ca.cer \
+    -subj '$subj'"
+
+test_expect_success "CA regeneration" "certool \
+    -algo gost3410-512C \
+    -ku ca \
+    -prv $TMPDIR/ca.prv -cer $TMPDIR/ca.cer \
+    -reuse-key \
+    -subj '$subj'"
+test_expect_success "CA self-signature" "certool \
+    -ca-cer $TMPDIR/ca.cer \
+    -cer $TMPDIR/ca.cer \
+    -verify"
+
+subj='{"CN": "EE", "C": "RU"}'
+test_expect_success "EE generation" "certool \
+    -algo gost3410-256A \
+    -ca-prv $TMPDIR/ca.prv -ca-cer $TMPDIR/ca.cer \
+    -prv $TMPDIR/ee.prv -cer $TMPDIR/ee.cer \
+    -subj '$subj'"
+test_expect_success "EE chain" "certool \
+    -ca-cer $TMPDIR/ca.cer \
+    -cer $TMPDIR/ee.cer \
+    -verify"
+
+test_done
diff --git a/gyac/cmd/certool/cer.go b/gyac/cmd/certool/cer.go
new file mode 100644 (file)
index 0000000..4ebc352
--- /dev/null
@@ -0,0 +1,78 @@
+package main
+
+import (
+       "errors"
+       "os"
+       "time"
+
+       "github.com/google/uuid"
+       "go.cypherpunks.su/yac/gyac"
+)
+
+type AV struct {
+       A string `yac:"a"`
+       V []byte `yac:"v"`
+}
+
+type CerTBS struct {
+       KU   []string          `yac:"ku,omitempty"`
+       Subj map[string]string `yac:"sub"`
+       Exp  []time.Time       `yac:"exp"`
+       Crit []map[string]any  `yac:"crit,omitempty"`
+       Pub  AV                `yac:"pub"`
+       KID  uuid.UUID         `yac:"kid"`
+}
+
+func (tbs *CerTBS) HasCA() (hasCA bool) {
+       for _, ku := range tbs.KU {
+               if ku == "ca" {
+                       hasCA = true
+               }
+       }
+       return
+}
+
+type SignedDataLoad struct {
+       V any    `yac:"v"`
+       T string `yac:"t"`
+}
+
+type SignedDataTBS struct {
+       V    any            `yac:"v"`
+       Load map[string]any `yac:"load"`
+       T    string         `yac:"t"`
+       KID  uuid.UUID      `yac:"kid"`
+}
+
+type Sig struct {
+       Load   map[string]any `yac:"load,omitempty"`
+       CerLoc []string       `yac:"cer-loc,omitempty"`
+       Sign   AV             `yac:"sign"`
+       KID    uuid.UUID      `yac:"kid"`
+}
+
+type SignedData struct {
+       Hashes []string       `yac:"hash,omitempty"`
+       Load   SignedDataLoad `yac:"load"`
+       Sigs   []Sig          `yac:"sigs"`
+       Cers   []SignedData   `yac:"certs,omitempty"`
+}
+
+func loadCerFromFile(pth string) (*SignedData, error) {
+       data, err := os.ReadFile(pth)
+       if err != nil {
+               return nil, err
+       }
+       var sd SignedData
+       err = gyac.DecodeToStruct(&sd, data)
+       if err != nil {
+               return nil, err
+       }
+       if sd.Load.T != "cer" {
+               err = errors.New("non \"cer\" in CA SignedData")
+       }
+       if len(sd.Sigs) == 0 {
+               err = errors.New("no signatures present")
+       }
+       return &sd, err
+}
diff --git a/gyac/cmd/certool/go.mod b/gyac/cmd/certool/go.mod
new file mode 100644 (file)
index 0000000..c8dfebd
--- /dev/null
@@ -0,0 +1,16 @@
+module go.cypherpunks.su/yac/gyac/cmd/certool
+
+go 1.22
+
+require (
+       github.com/google/uuid v1.6.0
+       go.cypherpunks.su/gogost/v6 v6.0.1
+       go.cypherpunks.su/yac/gyac v0.0.0-00010101000000-000000000000
+)
+
+require (
+       github.com/mitchellh/mapstructure v1.5.0 // indirect
+       go.cypherpunks.su/tai64n/v3 v3.1.0 // indirect
+)
+
+replace go.cypherpunks.su/yac/gyac => ../..
diff --git a/gyac/cmd/certool/go.sum b/gyac/cmd/certool/go.sum
new file mode 100644 (file)
index 0000000..90b7e96
--- /dev/null
@@ -0,0 +1,10 @@
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+go.cypherpunks.su/gogost/v6 v6.0.1 h1:PFjBnUmfdbx7L5R6hRt/+ZgGwWx45wTIWezFSgmknrs=
+go.cypherpunks.su/gogost/v6 v6.0.1/go.mod h1:qJm0B7KJY4/OD5nYqL10kXY09dUwu2AfwSPu72Otngs=
+go.cypherpunks.su/tai64n/v3 v3.1.0 h1:cdGnanxA5/H3hc37BO9D3h/exChVNEvrPWjTT/kuwQ4=
+go.cypherpunks.su/tai64n/v3 v3.1.0/go.mod h1:zGDFuyiFKJk+iem8lyBaFeCm+MNMOn7RRWy456n1J78=
+golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
+golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
diff --git a/gyac/cmd/certool/main.go b/gyac/cmd/certool/main.go
new file mode 100644 (file)
index 0000000..1a3cc65
--- /dev/null
@@ -0,0 +1,265 @@
+package main
+
+import (
+       "bytes"
+       "crypto/rand"
+       "encoding/json"
+       "flag"
+       "hash"
+       "io"
+       "log"
+       "os"
+       "sort"
+       "time"
+
+       "github.com/google/uuid"
+       "go.cypherpunks.su/gogost/v6/gost3410"
+       "go.cypherpunks.su/gogost/v6/gost34112012256"
+       "go.cypherpunks.su/gogost/v6/gost34112012512"
+       "go.cypherpunks.su/yac/gyac"
+)
+
+const (
+       AlgoStreebog256  = "streebog256"
+       AlgoGOST3410256A = "gost3410-256A"
+       AlgoGOST3410256B = "gost3410-256B"
+       AlgoGOST3410256C = "gost3410-256C"
+       AlgoGOST3410256D = "gost3410-256D"
+       AlgoGOST3410512A = "gost3410-512A"
+       AlgoGOST3410512B = "gost3410-512B"
+       AlgoGOST3410512C = "gost3410-512C"
+)
+
+func GOST3410CurveByName(name string) (curve *gost3410.Curve) {
+       switch name {
+       case AlgoGOST3410256A:
+               curve = gost3410.CurveIdtc26gost341012256paramSetA()
+       case AlgoGOST3410256B:
+               curve = gost3410.CurveIdtc26gost341012256paramSetB()
+       case AlgoGOST3410256C:
+               curve = gost3410.CurveIdtc26gost341012256paramSetC()
+       case AlgoGOST3410256D:
+               curve = gost3410.CurveIdtc26gost341012256paramSetD()
+       case AlgoGOST3410512A:
+               curve = gost3410.CurveIdtc26gost341012512paramSetA()
+       case AlgoGOST3410512B:
+               curve = gost3410.CurveIdtc26gost341012512paramSetB()
+       case AlgoGOST3410512C:
+               curve = gost3410.CurveIdtc26gost341012512paramSetC()
+       }
+       return
+}
+
+func HasherByKeyAlgo(a string) hash.Hash {
+       switch a {
+       case AlgoGOST3410256A, AlgoGOST3410256B, AlgoGOST3410256C, AlgoGOST3410256D:
+               return gost34112012256.New()
+       case AlgoGOST3410512A, AlgoGOST3410512B, AlgoGOST3410512C:
+               return gost34112012512.New()
+       default:
+               log.Fatal("unsupported CA algorithm")
+       }
+       return nil
+}
+
+func main() {
+       kuMap := make(map[string]struct{})
+       flag.Func(
+               "ku",
+               "Optional key usage, may be specified multiple times",
+               func(v string) error {
+                       kuMap[v] = struct{}{}
+                       return nil
+               },
+       )
+       sinceRaw := flag.String("since", "",
+               "Optional notBefore, \"2006-01-02 15:04:05\" format")
+       lifetime := flag.Uint("lifetime", 365,
+               "Lifetime of the certificate, days")
+       subjRaw := flag.String("subj", `{"CN": "test"}`,
+               "JSON map of the subject")
+       algo := flag.String("algo", "gost3410-256A", "Public key algorithm")
+       issuingCer := flag.String("ca-cer", "",
+               "Path to certificate file for issuing with")
+       issuingPrv := flag.String("ca-prv", "",
+               "Path to private key file for issuing with")
+       reuseKey := flag.Bool("reuse-key", false,
+               "Reuse the key, do not generate new one")
+       prvPath := flag.String("prv", "", "Path to private key file")
+       cerPath := flag.String("cer", "", "Path to certificate file")
+       verify := flag.Bool("verify", false, "Verify provided -cer with ca-cer")
+
+       flag.Parse()
+       log.SetFlags(log.Lshortfile)
+
+       if *cerPath == "" {
+               log.Fatal("no -cer is set")
+       }
+
+       var ku []string
+       for k := range kuMap {
+               ku = append(ku, k)
+       }
+       kuMap = nil
+       sort.Sort(gyac.ByLenFirst(ku))
+
+       var subj map[string]string
+       err := json.Unmarshal([]byte(*subjRaw), &subj)
+       if err != nil {
+               log.Fatalln("while parsing -subj:", err)
+       }
+
+       var since time.Time
+       if *sinceRaw == "" {
+               since = time.Now().UTC().Truncate(time.Second)
+       } else {
+               since, err = time.Parse(time.DateTime, *sinceRaw)
+               if err != nil {
+                       log.Fatalln("while parsing -since:", err)
+               }
+       }
+       till := since.Add(time.Duration(*lifetime) * 24 * time.Hour)
+
+       var caPrv *gost3410.PrivateKey
+       var caCerTBS CerTBS
+       if *issuingCer != "" {
+               var sd *SignedData
+               sd, err = loadCerFromFile(*issuingCer)
+               if err != nil {
+                       log.Fatal(err)
+               }
+               err = gyac.MapToStruct(&caCerTBS, sd.Load.V.(map[string]any))
+               if err != nil {
+                       log.Fatal(err)
+               }
+               if !*verify {
+                       if *issuingPrv == "" {
+                               log.Fatal("no -issuing-key is set")
+                       }
+                       caPrv, err = loadPrvFromFile(*issuingPrv)
+                       if err != nil {
+                               log.Fatal(err)
+                       }
+               }
+       }
+
+       if *verify {
+               var sd *SignedData
+               sd, err = loadCerFromFile(*cerPath)
+               if err != nil {
+                       log.Fatal(err)
+               }
+               var tbs CerTBS
+               err = gyac.MapToStruct(&tbs, sd.Load.V.(map[string]any))
+               if err != nil {
+                       log.Fatal(err)
+               }
+               sig := sd.Sigs[0]
+               if sig.KID != caCerTBS.KID {
+                       log.Fatal("SKID != AKID")
+               }
+               if sig.KID != tbs.KID && !caCerTBS.HasCA() {
+                       log.Fatal("no \"ca\" KU met in CA")
+               }
+               sdTBS := SignedDataTBS{T: "cer", V: tbs, KID: sig.KID}
+               hasher := HasherByKeyAlgo(sig.Sign.A)
+               hasher.Write(gyac.EncodeItem(nil, gyac.ItemFromGo(sdTBS)))
+               var pub *gost3410.PublicKey
+               pub, err = gost3410.NewPublicKeyBE(
+                       GOST3410CurveByName(caCerTBS.Pub.A),
+                       caCerTBS.Pub.V,
+               )
+               if err != nil {
+                       log.Fatal(err)
+               }
+               var valid bool
+               valid, err = pub.VerifyDigest(hasher.Sum(nil), sig.Sign.V)
+               if err != nil {
+                       log.Fatal(err)
+               }
+               if !valid {
+                       os.Exit(1)
+               }
+               return
+       }
+
+       if *prvPath == "" {
+               log.Fatal("no -prv is set")
+       }
+
+       curve := GOST3410CurveByName(*algo)
+       if curve == nil {
+               log.Fatal("unknown -algo specified")
+       }
+       var prv *gost3410.PrivateKey
+       if *reuseKey {
+               prv, err = loadPrvFromFile(*prvPath)
+               if err != nil {
+                       log.Fatal(err)
+               }
+               if prv.C.Name != curve.Name {
+                       log.Fatal("-algo is not same with private key")
+               }
+       } else {
+               prvRaw := make([]byte, curve.PointSize())
+               if _, err = io.ReadFull(rand.Reader, prvRaw); err != nil {
+                       log.Fatal(err)
+               }
+               prv, err = gost3410.NewPrivateKeyBE(curve, prvRaw)
+               if err != nil {
+                       log.Fatal(err)
+               }
+               err = os.WriteFile(*prvPath, gyac.EncodeItem(nil,
+                       gyac.ItemFromGo(AV{A: *algo, V: prv.RawBE()})), 0o600)
+               if err != nil {
+                       log.Fatal(err)
+               }
+       }
+
+       var pub *gost3410.PublicKey
+       pub, err = prv.PublicKey()
+       if err != nil {
+               log.Fatal(err)
+       }
+       pubTBS := AV{A: *algo, V: pub.RawBE()}
+       var spki uuid.UUID
+       {
+               hasher := gost34112012256.New()
+               hasher.Write(gyac.EncodeItem(nil, gyac.ItemFromGo(pubTBS)))
+               spki, err = uuid.NewRandomFromReader(bytes.NewReader(hasher.Sum(nil)))
+               if err != nil {
+                       log.Fatal(err)
+               }
+       }
+       cerTBS := CerTBS{
+               KU:   ku,
+               Exp:  []time.Time{since, till},
+               KID:  spki,
+               Subj: subj,
+               Pub:  pubTBS,
+       }
+       if caPrv == nil {
+               caPrv = prv
+               caCerTBS = cerTBS
+       } else {
+               if !caCerTBS.HasCA() {
+                       log.Fatal("no \"ca\" KU met in CA")
+               }
+       }
+       sig := Sig{KID: caCerTBS.KID}
+       sdTBS := SignedDataTBS{T: "cer", V: cerTBS, KID: sig.KID}
+       {
+               hasher := HasherByKeyAlgo(caCerTBS.Pub.A)
+               hasher.Write(gyac.EncodeItem(nil, gyac.ItemFromGo(sdTBS)))
+               sig.Sign = AV{A: caCerTBS.Pub.A}
+               sig.Sign.V, err = caPrv.SignDigest(hasher.Sum(nil), rand.Reader)
+               if err != nil {
+                       log.Fatal(err)
+               }
+       }
+       cer := SignedData{Load: SignedDataLoad{T: "cer", V: cerTBS}, Sigs: []Sig{sig}}
+       err = os.WriteFile(*cerPath, gyac.EncodeItem(nil, gyac.ItemFromGo(cer)), 0o666)
+       if err != nil {
+               log.Fatal(err)
+       }
+}
diff --git a/gyac/cmd/certool/prv.go b/gyac/cmd/certool/prv.go
new file mode 100644 (file)
index 0000000..ac3cf3b
--- /dev/null
@@ -0,0 +1,56 @@
+package main
+
+import (
+       "fmt"
+       "os"
+
+       "go.cypherpunks.su/gogost/v6/gost3410"
+       "go.cypherpunks.su/yac/gyac"
+)
+
+func loadPrvFromFile(pth string) (prv *gost3410.PrivateKey, err error) {
+       var data []byte
+       data, err = os.ReadFile(pth)
+       if err != nil {
+               return
+       }
+       var av AV
+       err = gyac.DecodeToStruct(&av, data)
+       if err != nil {
+               return
+       }
+       switch av.A {
+       case AlgoGOST3410256A:
+               prv, err = gost3410.NewPrivateKeyBE(
+                       gost3410.CurveIdtc26gost341012256paramSetA(), av.V,
+               )
+       case AlgoGOST3410256B:
+               prv, err = gost3410.NewPrivateKeyBE(
+                       gost3410.CurveIdtc26gost341012256paramSetB(), av.V,
+               )
+       case AlgoGOST3410256C:
+               prv, err = gost3410.NewPrivateKeyBE(
+                       gost3410.CurveIdtc26gost341012256paramSetC(), av.V,
+               )
+       case AlgoGOST3410256D:
+               prv, err = gost3410.NewPrivateKeyBE(
+                       gost3410.CurveIdtc26gost341012256paramSetD(), av.V,
+               )
+       case AlgoGOST3410512A:
+               prv, err = gost3410.NewPrivateKeyBE(
+                       gost3410.CurveIdtc26gost341012512paramSetA(), av.V,
+               )
+       case AlgoGOST3410512B:
+               prv, err = gost3410.NewPrivateKeyBE(
+                       gost3410.CurveIdtc26gost341012512paramSetB(), av.V,
+               )
+       case AlgoGOST3410512C:
+               prv, err = gost3410.NewPrivateKeyBE(
+                       gost3410.CurveIdtc26gost341012512paramSetC(), av.V,
+               )
+       default:
+               err = fmt.Errorf("unknown private key algo: %s", av.A)
+               return
+       }
+       return
+}
diff --git a/gyac/cmd/print/main.go b/gyac/cmd/print/main.go
new file mode 100644 (file)
index 0000000..93d9369
--- /dev/null
@@ -0,0 +1,26 @@
+package main
+
+import (
+       "bufio"
+       "fmt"
+       "io"
+       "log"
+       "os"
+
+       "go.cypherpunks.su/yac/gyac"
+)
+
+func main() {
+       data, err := io.ReadAll(bufio.NewReader(os.Stdin))
+       if err != nil {
+               log.Fatal(err)
+       }
+       item, tail, err := gyac.DecodeItem(data)
+       if err != nil {
+               log.Fatal(err)
+       }
+       if len(tail) > 0 {
+               log.Fatalln("trailing data:", tail)
+       }
+       fmt.Printf("%v\n", item.ToGo())
+}
diff --git a/gyac/cmd/test-vector-anys/main.go b/gyac/cmd/test-vector-anys/main.go
new file mode 100644 (file)
index 0000000..92a2cde
--- /dev/null
@@ -0,0 +1,81 @@
+package main
+
+import (
+       "bytes"
+       "encoding/hex"
+       "fmt"
+       "time"
+
+       "github.com/google/uuid"
+       "go.cypherpunks.su/yac/gyac"
+)
+
+func main() {
+       data := map[string]any{
+               "ints": map[string]any{
+                       "pos": []any{
+                               uint64(0),
+                               uint64(1),
+                               uint64(31),
+                               uint64(32),
+                               uint64(123),
+                               uint64(1234),
+                               uint64(12345678),
+                               &gyac.Raw{T: gyac.AtomIntPos11, V: []byte("\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")},
+                       },
+                       "neg": []any{
+                               int64(-1),
+                               int64(-2),
+                               int64(-32),
+                               int64(-33),
+                               int64(-123),
+                               int64(-1234),
+                               int64(-12345678),
+                               &gyac.Raw{T: gyac.AtomIntNeg10, V: []byte("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff")},
+                       },
+               },
+               "nil":  nil,
+               "bool": []any{true, false},
+               "str": map[string]any{
+                       "bin": []any{
+                               []byte{},
+                               bytes.Repeat([]byte{'0'}, 60),
+                               bytes.Repeat([]byte{'1'}, 61),
+                               bytes.Repeat([]byte{'2'}, 255),
+                               bytes.Repeat([]byte{'3'}, 1024),
+                               bytes.Repeat([]byte{'4'}, 1<<16),
+                       },
+                       "utf8": "привет мир",
+               },
+               "blob": []any{
+                       gyac.MakeBlob(12, []byte{'5'}),
+                       gyac.MakeBlob(12, bytes.Repeat([]byte{'6'}, 12)),
+                       gyac.MakeBlob(12, bytes.Repeat([]byte{'7'}, 13)),
+                       gyac.MakeBlob(5, []byte("1234567890-")),
+               },
+               "empties": []any{
+                       []any{},
+                       map[string]any{},
+                       gyac.MakeBlob(123, []byte{}),
+                       uuid.MustParse("00000000-0000-0000-0000-000000000000"),
+                       &gyac.Raw{
+                               T: gyac.AtomTAI64,
+                               V: []byte("\x00\x00\x00\x00\x00\x00\x00\x00"),
+                       },
+               },
+               "dates": []any{
+                       time.Unix(1234567890, 0),
+                       time.Unix(1234567890, 456*1000),
+                       time.Unix(1234567890, 456789),
+                       &gyac.Raw{
+                               T: gyac.AtomTAI64NA,
+                               V: []byte("\x40\x00\x00\x00\x49\x96\x02\xF4\x00\x06\xF8\x55\x07\x5B\xCD\x15"),
+                       },
+               },
+               "floats": []any{
+                       &gyac.Raw{T: gyac.AtomFloat32, V: []byte("\x01\x02\x03\x04")},
+               },
+               "uuid": uuid.MustParse("0e875e3f-d385-49eb-87b4-be42d641c367"),
+       }
+       fmt.Println(hex.EncodeToString(gyac.EncodeItem(nil, gyac.ItemFromGo(data))))
+}
diff --git a/gyac/cmd/test-vector-manual/main.go b/gyac/cmd/test-vector-manual/main.go
new file mode 100644 (file)
index 0000000..b053753
--- /dev/null
@@ -0,0 +1,184 @@
+package main
+
+import (
+       "bytes"
+       "encoding/hex"
+       "fmt"
+       "time"
+
+       "github.com/google/uuid"
+       "go.cypherpunks.su/tai64n/v3"
+       "go.cypherpunks.su/yac/gyac"
+)
+
+func main() {
+       buf := make([]byte, 0, 68*1024)
+       {
+               buf = gyac.AtomMapEncode(buf)
+               {
+                       buf = gyac.AtomStrEncode(buf, "nil")
+                       buf = gyac.AtomNILEncode(buf)
+               }
+               {
+                       buf = gyac.AtomStrEncode(buf, "str")
+                       buf = gyac.AtomMapEncode(buf)
+                       {
+                               buf = gyac.AtomStrEncode(buf, "bin")
+                               buf = gyac.AtomListEncode(buf)
+                               {
+                                       buf = gyac.AtomBinEncode(buf, []byte(""))
+                                       buf = gyac.AtomBinEncode(buf, bytes.Repeat([]byte{'0'}, 60))
+                                       buf = gyac.AtomBinEncode(buf, bytes.Repeat([]byte{'1'}, 61))
+                                       buf = gyac.AtomBinEncode(buf, bytes.Repeat([]byte{'2'}, 255))
+                                       buf = gyac.AtomBinEncode(buf, bytes.Repeat([]byte{'3'}, 1024))
+                                       buf = gyac.AtomBinEncode(buf, bytes.Repeat([]byte{'4'}, 1<<16))
+                               }
+                               buf = gyac.AtomEOCEncode(buf)
+                               {
+                                       buf = gyac.AtomStrEncode(buf, "utf8")
+                                       buf = gyac.AtomStrEncode(buf, "привет мир")
+                               }
+                       }
+                       buf = gyac.AtomEOCEncode(buf)
+               }
+               {
+                       buf = gyac.AtomStrEncode(buf, "blob")
+                       buf = gyac.AtomListEncode(buf)
+                       {
+                               buf = gyac.AtomBlobEncode(buf, 12)
+                               buf = gyac.AtomBinEncode(buf, []byte{'5'})
+                       }
+                       {
+                               buf = gyac.AtomBlobEncode(buf, 12)
+                               buf = gyac.AtomChunkEncode(buf, bytes.Repeat([]byte{'6'}, 12))
+                               buf = gyac.AtomBinEncode(buf, []byte{})
+                       }
+                       {
+                               buf = gyac.AtomBlobEncode(buf, 12)
+                               buf = gyac.AtomChunkEncode(buf, bytes.Repeat([]byte{'7'}, 12))
+                               buf = gyac.AtomBinEncode(buf, []byte{'7'})
+                       }
+                       {
+                               buf = gyac.AtomBlobEncode(buf, 5)
+                               buf = gyac.AtomChunkEncode(buf, []byte("12345"))
+                               buf = gyac.AtomChunkEncode(buf, []byte("67890"))
+                               buf = gyac.AtomBinEncode(buf, []byte{'-'})
+                       }
+                       buf = gyac.AtomEOCEncode(buf)
+               }
+               {
+                       buf = gyac.AtomStrEncode(buf, "bool")
+                       buf = gyac.AtomListEncode(buf)
+                       buf = gyac.AtomBoolEncode(buf, true)
+                       buf = gyac.AtomBoolEncode(buf, false)
+                       buf = gyac.AtomEOCEncode(buf)
+               }
+               {
+                       buf = gyac.AtomStrEncode(buf, "ints")
+                       buf = gyac.AtomMapEncode(buf)
+                       {
+                               buf = gyac.AtomStrEncode(buf, "neg")
+                               buf = gyac.AtomListEncode(buf)
+                               buf = gyac.AtomIntEncode(buf, -1)
+                               buf = gyac.AtomIntEncode(buf, -2)
+                               buf = gyac.AtomIntEncode(buf, -32)
+                               buf = gyac.AtomIntEncode(buf, -33)
+                               buf = gyac.AtomIntEncode(buf, -123)
+                               buf = gyac.AtomIntEncode(buf, -1234)
+                               buf = gyac.AtomIntEncode(buf, -12345678)
+                               buf = gyac.AtomRawEncode(buf, &gyac.Raw{
+                                       T: gyac.AtomIntNeg10,
+                                       V: []byte("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"),
+                               })
+                               buf = gyac.AtomEOCEncode(buf)
+                       }
+                       {
+                               buf = gyac.AtomStrEncode(buf, "pos")
+                               buf = gyac.AtomListEncode(buf)
+                               buf = gyac.AtomUIntEncode(buf, 0)
+                               buf = gyac.AtomUIntEncode(buf, 1)
+                               buf = gyac.AtomUIntEncode(buf, 31)
+                               buf = gyac.AtomUIntEncode(buf, 32)
+                               buf = gyac.AtomUIntEncode(buf, 123)
+                               buf = gyac.AtomUIntEncode(buf, 1234)
+                               buf = gyac.AtomUIntEncode(buf, 12345678)
+                               buf = gyac.AtomRawEncode(buf, &gyac.Raw{
+                                       T: gyac.AtomIntPos11,
+                                       V: []byte("\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"),
+                               })
+                               buf = gyac.AtomEOCEncode(buf)
+                       }
+                       buf = gyac.AtomEOCEncode(buf)
+               }
+               {
+                       buf = gyac.AtomStrEncode(buf, "uuid")
+                       buf = gyac.AtomUUIDEncode(buf,
+                               uuid.MustParse("0e875e3f-d385-49eb-87b4-be42d641c367"))
+               }
+               {
+                       buf = gyac.AtomStrEncode(buf, "dates")
+                       buf = gyac.AtomListEncode(buf)
+                       {
+                               var tai tai64n.TAI64
+                               t := time.Unix(1234567890, 0)
+                               t = tai64n.LeapsecsAdd(t)
+                               tai.FromTime(t)
+                               buf = gyac.AtomTAI64Encode(buf, tai[:])
+                       }
+                       {
+                               var tai tai64n.TAI64N
+                               t := time.Unix(1234567890, 456*1000)
+                               t = tai64n.LeapsecsAdd(t)
+                               tai.FromTime(t)
+                               buf = gyac.AtomTAI64Encode(buf, tai[:])
+                       }
+                       {
+                               var tai tai64n.TAI64N
+                               t := time.Unix(1234567890, 456789)
+                               t = tai64n.LeapsecsAdd(t)
+                               tai.FromTime(t)
+                               buf = gyac.AtomTAI64Encode(buf, tai[:])
+                       }
+                       buf = gyac.AtomRawEncode(buf, &gyac.Raw{
+                               T: gyac.AtomTAI64NA,
+                               V: []byte("\x40\x00\x00\x00\x49\x96\x02\xF4\x00\x06\xF8\x55\x07\x5B\xCD\x15"),
+                       })
+                       buf = gyac.AtomEOCEncode(buf)
+               }
+               {
+                       buf = gyac.AtomStrEncode(buf, "floats")
+                       buf = gyac.AtomListEncode(buf)
+                       buf = gyac.AtomRawEncode(buf, &gyac.Raw{
+                               T: gyac.AtomFloat32,
+                               V: []byte("\x01\x02\x03\x04"),
+                       })
+                       buf = gyac.AtomEOCEncode(buf)
+               }
+               {
+                       buf = gyac.AtomStrEncode(buf, "empties")
+                       buf = gyac.AtomListEncode(buf)
+                       {
+                               buf = gyac.AtomListEncode(buf)
+                               buf = gyac.AtomEOCEncode(buf)
+                       }
+                       {
+                               buf = gyac.AtomMapEncode(buf)
+                               buf = gyac.AtomEOCEncode(buf)
+                       }
+                       {
+                               buf = gyac.AtomBlobEncode(buf, 123)
+                               buf = gyac.AtomBinEncode(buf, []byte{})
+                       }
+                       buf = gyac.AtomUUIDEncode(buf,
+                               uuid.MustParse("00000000-0000-0000-0000-000000000000"))
+                       buf = gyac.AtomRawEncode(buf, &gyac.Raw{
+                               T: gyac.AtomTAI64,
+                               V: []byte("\x00\x00\x00\x00\x00\x00\x00\x00"),
+                       })
+                       buf = gyac.AtomEOCEncode(buf)
+               }
+
+               buf = gyac.AtomEOCEncode(buf)
+       }
+       fmt.Println(hex.EncodeToString(buf))
+}
diff --git a/gyac/dec.go b/gyac/dec.go
new file mode 100644 (file)
index 0000000..c4ecb30
--- /dev/null
@@ -0,0 +1,382 @@
+// 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
+}
diff --git a/gyac/enc.go b/gyac/enc.go
new file mode 100644 (file)
index 0000000..a9350e7
--- /dev/null
@@ -0,0 +1,333 @@
+// 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
+}
diff --git a/gyac/go.mod b/gyac/go.mod
new file mode 100644 (file)
index 0000000..1b9af1b
--- /dev/null
@@ -0,0 +1,10 @@
+module go.cypherpunks.su/yac/gyac
+
+go 1.22
+
+require go.cypherpunks.su/tai64n/v3 v3.1.0
+
+require (
+       github.com/google/uuid v1.6.0
+       github.com/mitchellh/mapstructure v1.5.0
+)
diff --git a/gyac/go.sum b/gyac/go.sum
new file mode 100644 (file)
index 0000000..f78f8a5
--- /dev/null
@@ -0,0 +1,6 @@
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+go.cypherpunks.su/tai64n/v3 v3.1.0 h1:cdGnanxA5/H3hc37BO9D3h/exChVNEvrPWjTT/kuwQ4=
+go.cypherpunks.su/tai64n/v3 v3.1.0/go.mod h1:zGDFuyiFKJk+iem8lyBaFeCm+MNMOn7RRWy456n1J78=
diff --git a/gyac/itemtype_string.go b/gyac/itemtype_string.go
new file mode 100644 (file)
index 0000000..7016888
--- /dev/null
@@ -0,0 +1,36 @@
+// Code generated by "stringer -type=ItemType"; DO NOT EDIT.
+
+package gyac
+
+import "strconv"
+
+func _() {
+       // An "invalid array index" compiler error signifies that the constant values have changed.
+       // Re-run the stringer command to generate them again.
+       var x [1]struct{}
+       _ = x[ItemEOC-0]
+       _ = x[ItemNIL-1]
+       _ = x[ItemBool-2]
+       _ = x[ItemUUID-3]
+       _ = x[ItemUInt-4]
+       _ = x[ItemInt-5]
+       _ = x[ItemList-6]
+       _ = x[ItemMap-7]
+       _ = x[ItemBlob-8]
+       _ = x[ItemFloat-9]
+       _ = x[ItemTAI64-10]
+       _ = x[ItemBin-11]
+       _ = x[ItemStr-12]
+       _ = x[ItemRaw-13]
+}
+
+const _ItemType_name = "ItemEOCItemNILItemBoolItemUUIDItemUIntItemIntItemListItemMapItemBlobItemFloatItemTAI64ItemBinItemStrItemRaw"
+
+var _ItemType_index = [...]uint8{0, 7, 14, 22, 30, 38, 45, 53, 60, 68, 77, 86, 93, 100, 107}
+
+func (i ItemType) String() string {
+       if i >= ItemType(len(_ItemType_index)-1) {
+               return "ItemType(" + strconv.FormatInt(int64(i), 10) + ")"
+       }
+       return _ItemType_name[_ItemType_index[i]:_ItemType_index[i+1]]
+}
diff --git a/gyac/reflect.go b/gyac/reflect.go
new file mode 100644 (file)
index 0000000..b1be6ef
--- /dev/null
@@ -0,0 +1,240 @@
+// 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))
+}
diff --git a/gyac/sort.go b/gyac/sort.go
new file mode 100644 (file)
index 0000000..4259644
--- /dev/null
@@ -0,0 +1,21 @@
+package gyac
+
+type ByLenFirst []string
+
+func (a ByLenFirst) Len() int {
+       return len(a)
+}
+
+func (a ByLenFirst) Swap(i, j int) {
+       a[i], a[j] = a[j], a[i]
+}
+
+func (a ByLenFirst) Less(i, j int) bool {
+       if len(a[i]) < len(a[j]) {
+               return true
+       }
+       if len(a[i]) > len(a[j]) {
+               return false
+       }
+       return a[i] < a[j]
+}
diff --git a/pyac/COPYING b/pyac/COPYING
new file mode 100644 (file)
index 0000000..58f2b48
--- /dev/null
@@ -0,0 +1,674 @@
+                   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>.
diff --git a/pyac/COPYING.LESSER b/pyac/COPYING.LESSER
new file mode 100644 (file)
index 0000000..2d9b9aa
--- /dev/null
@@ -0,0 +1,165 @@
+                  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.
diff --git a/pyac/README b/pyac/README
new file mode 100644 (file)
index 0000000..a705a4d
--- /dev/null
@@ -0,0 +1,6 @@
+Python implementation of YAC codec.
+
+* No FLOAT*, TAI64NA, or nanoseconds support.
+  They are stored/decoded just as a raw value
+
+pyac is free software: see the file COPYING.LESSER for copying conditions.
diff --git a/pyac/pyac.py b/pyac/pyac.py
new file mode 100644 (file)
index 0000000..6c34d72
--- /dev/null
@@ -0,0 +1,676 @@
+# 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))
diff --git a/pyac/test-vector.py b/pyac/test-vector.py
new file mode 100644 (file)
index 0000000..9a775d4
--- /dev/null
@@ -0,0 +1,50 @@
+from pyac import *
+
+
+data = {
+    "ints": {
+        "pos": [0, 1, 31, 32, 123, 1234, 12345678, 1<<80],
+        "neg": [-1, -2, -32, -33, -123, -1234, -12345678, -1<<80],
+    },
+    "floats": [Raw((Float.tags[1], b"\x01\x02\x03\x04"))],
+    "nil": None,
+    "bool": [True, False],
+    "str": {
+        "bin": [
+            b"",
+            60 * b"0",
+            61 * b"1",
+            255 * b"2",
+            1024 * b"3",
+            (1<<16) * b"4",
+        ],
+        "utf8": "привет мир",
+    },
+    "blob": [
+        Blob(12, 1 * b"5"),
+        Blob(12, 12 * b"6"),
+        Blob(12, 13 * b"7"),
+        Blob(5, b"1234567890-"),
+    ],
+    "empties": [
+        [],
+        {},
+        Blob(123, b""),
+        UUID("00000000-0000-0000-0000-000000000000"),
+        Raw((TAI64.tags[0], bytes.fromhex("0000000000000000"))),
+    ],
+    "uuid": UUID("0e875e3f-d385-49eb-87b4-be42d641c367"),
+}
+from datetime import datetime
+data["dates"] = [
+    (datetime(1970, 1, 1) + timedelta(seconds=1234567890)),
+    (datetime(1970, 1, 1) + timedelta(seconds=1234567890)).replace(microsecond=456),
+    Raw((TAI64.tags[1], bytes.fromhex("40000000499602F40006F855"))),
+    Raw((TAI64.tags[2], bytes.fromhex("40000000499602F40006F855075BCD15"))),
+]
+raw = Encode(data)
+dec, tail = Decode(raw)
+assert tail == b""
+assert Encode(dec) == raw
+assert dec.py() == data
+print(raw.hex())
diff --git a/spec/.gitignore b/spec/.gitignore
new file mode 100644 (file)
index 0000000..4618054
--- /dev/null
@@ -0,0 +1 @@
+/index.info
diff --git a/spec/THANKS b/spec/THANKS
new file mode 100644 (file)
index 0000000..ddad4e1
--- /dev/null
@@ -0,0 +1 @@
+* Sergey Mayorov for his valuable consultation and suggestions
diff --git a/spec/encoding/blob.texi b/spec/encoding/blob.texi
new file mode 100644 (file)
index 0000000..68f2a4d
--- /dev/null
@@ -0,0 +1,27 @@
+@node Blobs
+@section Blobs
+
+Blob (binary large object) allows you to transfer binary data in chunks,
+in a streaming way, when data may not fit in memory at once.
+
+Positive non-zero @ref{Integers, INT} must follow the BLOB tag, setting
+the following chunks payload size. Then come zero or more NIL tags with
+fixed-length payload after each of them. Blob is terminated by
+@ref{Strings, BIN}, probably having zero length.
+
+Data format definition must specify exact chunk size expected to be
+used, if it needs deterministic encoding.
+
+@verbatim
+BLOB INT [NIL || payload0 || NIL || payload1 || ...] BIN
+@end verbatim
+
+@multitable @columnfractions .5 .5
+
+@item BLOB(5, "") @tab @code{0B 45 80}
+@item BLOB(5, "12345") @tab @code{0B 45 01 3132333435 80}
+@item BLOB(5, "123456") @tab @code{0B 45 01 3132333435 81 36}
+@item BLOB(500, "123") @tab @code{0B 2101F4 83 313233}
+@item BLOB(2, "12345") @tab @code{0B 42 01 3132 01 3334 81 35}
+
+@end multitable
diff --git a/spec/encoding/cont.texi b/spec/encoding/cont.texi
new file mode 100644 (file)
index 0000000..69bb1b5
--- /dev/null
@@ -0,0 +1,31 @@
+@node Containers
+@section Containers
+
+Containers do not have any explicit length, but are terminated by EOC
+(end of contents) tag.
+
+LIST contains a concatenation of items of arbitrary type.
+
+@verbatim
+LIST [ITEM0 || ITEM1 || ...] EOC
+@end verbatim
+
+MAP contains concatenation of @ref{Strings, STR(key)}-value pairs. Keys
+@strong{must} be unique and length-first bytewise ascending ordered.
+
+@verbatim
+MAP [STR(KEY0) || ITEM0 || STR(KEY1) || ITEM1 || ... ] EOC
+@end verbatim
+
+Hint: Encoding code for known format can be ordered itself to emit
+values in an already properly sorted way.
+
+Example representations:
+
+@multitable @columnfractions .5 .5
+
+@item LIST[] @tab @code{08 00}
+@item LIST[INT(123) FALSE] @tab @code{08 207B 02 00}
+@item MAP[foo: LIST["bar"]] @tab @code{09 C3666F6F 08 C3626172 00 00}
+
+@end multitable
diff --git a/spec/encoding/float.texi b/spec/encoding/float.texi
new file mode 100644 (file)
index 0000000..6ba4850
--- /dev/null
@@ -0,0 +1,13 @@
+@node Floats
+@section Floats
+
+Floats are encoded in IEEE 754 binary formats: half, single, double,
+quadruple, octuple precision ones.
+
+Negative zero @strong{must not} be used. Shortest possible form @strong{must}
+be used.
+
+Hint: look at CBOR's RFC for example code of float16 conversion.
+
+Maybe there appear additional restrictions and rules. Currently no
+implementation supports floats.
diff --git a/spec/encoding/index.texi b/spec/encoding/index.texi
new file mode 100644 (file)
index 0000000..640eeff
--- /dev/null
@@ -0,0 +1,70 @@
+@node Encoding
+@unnumbered Encoding
+
+YAC can store various primitive scalar types (strings, integers, ...),
+and container types (lists, maps, ...). Serialisation process is just
+emitting the TLV-like encoding for each item recursively.
+
+Possible values for the tag:
+
+@multitable {dec} {hex} {12345678} {float256}
+@headitem dec @tab hex @tab bin @tab description
+
+@item 000 @tab 00 @tab @code{00000000} @tab @ref{Containers, EOC}
+@item 001 @tab 01 @tab @code{00000001} @tab @ref{Primitives, NIL}
+@item 002 @tab 02 @tab @code{00000010} @tab @ref{Primitives, FALSE}
+@item 003 @tab 03 @tab @code{00000011} @tab @ref{Primitives, TRUE}
+@item 004 @tab 04 @tab @code{00000100} @tab @ref{UUID}
+@item [...]
+@item 008 @tab 08 @tab @code{00001000} @tab @ref{Containers, LIST}
+@item 009 @tab 09 @tab @code{00001001} @tab @ref{Containers, MAP}
+@item 010 @tab 0A @tab @code{00001010} @tab
+@item 011 @tab 0B @tab @code{00001011} @tab @ref{Blobs, BLOB}
+@item [...]
+@item 016 @tab 10 @tab @code{00010000} @tab @ref{Floats, FLOAT16}
+@item 017 @tab 11 @tab @code{00010001} @tab @ref{Floats, FLOAT32}
+@item 018 @tab 12 @tab @code{00010010} @tab @ref{Floats, FLOAT64}
+@item 019 @tab 13 @tab @code{00010011} @tab @ref{Floats, FLOAT128}
+@item 020 @tab 14 @tab @code{00010100} @tab @ref{Floats, FLOAT256}
+@item [...]
+@item 024 @tab 18 @tab @code{00011000} @tab @ref{TAI64, TAI64}
+@item 025 @tab 19 @tab @code{00011001} @tab @ref{TAI64, TAI64N}
+@item 026 @tab 1A @tab @code{00011010} @tab @ref{TAI64, TAI64NA}
+@item [...]
+@item 032 @tab 20 @tab @code{0010LLLL} @tab @ref{Integers, +INT(len=1)}
+@item [...]
+@item 047 @tab 2F @tab @code{0010LLLL} @tab @ref{Integers, +INT(len=16)}
+@item 048 @tab 30 @tab @code{0011LLLL} @tab @ref{Integers, -INT(len=1)}
+@item [...]
+@item 063 @tab 3F @tab @code{0011LLLL} @tab @ref{Integers, -INT(len=16)}
+@item 064 @tab 40 @tab @code{010VVVVV} @tab @ref{Integers, INT=0}
+@item [...]
+@item 095 @tab 5F @tab @code{010VVVVV} @tab @ref{Integers, INT=31}
+@item 096 @tab 60 @tab @code{011VVVVV} @tab @ref{Integers, INT=-1}
+@item [...]
+@item 127 @tab 7F @tab @code{011VVVVV} @tab @ref{Integers, INT=-32}
+@item 128 @tab 80 @tab @code{10LLLLLL} @tab @ref{Strings, BIN(len=0)}
+@item [...]
+@item 188 @tab BC @tab @code{10111100} @tab @ref{Strings, BIN(len=60)}
+@item 189 @tab BD @tab @code{10111101} @tab @ref{Strings, BIN(len of 8b)}
+@item 190 @tab BE @tab @code{10111110} @tab @ref{Strings, BIN(len of 16b)}
+@item 191 @tab BF @tab @code{10111111} @tab @ref{Strings, BIN(len of 64b)}
+@item 192 @tab C0 @tab @code{11LLLLLL} @tab @ref{Strings, STR(len=0)}
+@item [...]
+@item 252 @tab FC @tab @code{11111100} @tab @ref{Strings, STR(len=60)}
+@item 253 @tab FD @tab @code{11111101} @tab @ref{Strings, STR(len of 8b)}
+@item 254 @tab FE @tab @code{11111110} @tab @ref{Strings, STR(len of 16b)}
+@item 255 @tab FF @tab @code{11111111} @tab @ref{Strings, STR(len of 64b)}
+
+@end multitable
+
+@include encoding/table.texi
+
+@include encoding/prim.texi
+@include encoding/uuid.texi
+@include encoding/str.texi
+@include encoding/int.texi
+@include encoding/float.texi
+@include encoding/tai64.texi
+@include encoding/cont.texi
+@include encoding/blob.texi
diff --git a/spec/encoding/int.texi b/spec/encoding/int.texi
new file mode 100644 (file)
index 0000000..a1429cf
--- /dev/null
@@ -0,0 +1,52 @@
+@node Integers
+@section Integers
+
+Integers can be encoded in short or long form. Separate types are used
+for positive and negative values.
+
+Short form eats five bits of the tag
+for positive values in @code{[0..32)} range,
+and negative ones in @code{[-1..-32]} range.
+
+Long form is encoded as a big-endian number of varying length.
+@code{*INT(len=1)..*INT(len=16)} types are for 8-, 16-, 24-, ...,
+128-bit integer representations.
+
+Shortest possible form @strong{must} be used. Leading zero bytes are
+@strong{forbidden}. Short values (<32) @strong{must} be encoded in a
+short form.
+
+Negative integers store their absolute value the same way as positive
+integers. After decoding, their value is subtracted from -1. Negative
+value encoded as @code{0x02} means @code{-1 - 0x02 => -3}.
+
+Hint: both positive and negative long integer tag's value keeps the
+length in the last 16 bits. So there is no need in dealing with every
+reserved value. You can check first 4 bits of the header to determine is
+it positive or negative integer, and then treat remaining 4 bits as a
+length (+1).
+
+Example representations:
+
+@multitable @columnfractions .5 .5
+
+@item 0 @tab @code{40}
+@item 1 @tab @code{41}
+@item 10 @tab @code{4A}
+@item 31 @tab @code{5F}
+@item 32 @tab @code{20 20}
+@item 100 @tab @code{20 64}
+@item 65536 @tab @code{22 01 00 00}
+@item 1000000000000 @tab @code{24 E8 D4 A5 10 00}
+@item 18446744073709551615 @tab @code{27 FF FF FF FF FF FF FF FF}
+@item 18446744073709551616 @tab @code{28 01 00 00 00 00 00 00 00 00}
+@item -18446744073709551616 @tab @code{37 FF FF FF FF FF FF FF FF}
+@item -18446744073709551617 @tab @code{38 01 00 00 00 00 00 00 00 00}
+@item -1 @tab @code{60}
+@item -10 @tab @code{69}
+@item -32 @tab @code{7F}
+@item -33 @tab @code{30 20}
+@item -100 @tab @code{30 63}
+@item -65536 @tab @code{31 FF FF}
+
+@end multitable
diff --git a/spec/encoding/prim.texi b/spec/encoding/prim.texi
new file mode 100644 (file)
index 0000000..b2f48b2
--- /dev/null
@@ -0,0 +1,15 @@
+@node Primitives
+@section Primitives
+
+Very primitive types like NIL (None, Null), boolean FALSE and TRUE are
+encoded just as a single tag.
+
+Example representations:
+
+@multitable @columnfractions .5 .5
+
+@item NIL @tab @code{01}
+@item FALSE @tab @code{02}
+@item TRUE @tab @code{03}
+
+@end multitable
diff --git a/spec/encoding/str.texi b/spec/encoding/str.texi
new file mode 100644 (file)
index 0000000..edbe391
--- /dev/null
@@ -0,0 +1,46 @@
+@node Strings
+@section Strings
+
+There are two kinds of strings: binary and UTF-8 (human-readable ones).
+Most significant tag's bit is set for them. Seventh bit tells is it
+UTF-8 string, binary otherwise. Next six bits contain the length of the
+string.
+
+@verbatim
+       len
+     +------+
+    /        \
+1 U L L L L L L
+  ^
+  +-is it UTF-8?
+@end verbatim
+
+If length value equals to:
+
+@table @asis
+@item 0-60
+    Use as is.
+@item 61
+    Then next 8-bits are the actual length.
+@item 62
+    Then next 16-bits (big-endian) are the actual length.
+@item 63
+    Then next 64-bits (big-endian) are the actual length.
+@end table
+
+String's length @strong{must} be encoded in shortest possible form.
+
+UTF-8 strings @strong{must} be valid UTF-8 sequences. That also
+automatically assures that no zero byte will be met.
+
+Example representations:
+
+@multitable @columnfractions .5 .5
+
+@item 0-byte binary string @tab @code{80}
+@item 4-byte binary string @code{0x01 0x02 0x03 0x04} @tab @code{84 01 02 03 04}
+@item 64-byte binary string with 0x41 @tab @code{BD 40 41 41 .. 41}
+@item UTF-8 string "привет мир" ("hello world" on russian) @tab
+    @code{D3 D0 BF D1 80 D0 B8 D0 B2 D0 B5 D1 82 20 D0 BC D0 B8 D1 80}
+
+@end multitable
diff --git a/spec/encoding/table.texi b/spec/encoding/table.texi
new file mode 100644 (file)
index 0000000..8257098
--- /dev/null
@@ -0,0 +1,264 @@
+@node Encoding table
+@section Full encoding table
+
+@multitable {dec} {hex} {12345678} {float256}
+@headitem dec @tab hex @tab bin @tab description
+
+@item 000 @tab 00 @tab @code{00000000} @tab @ref{Containers, EOC}
+@item 001 @tab 01 @tab @code{00000001} @tab @ref{Primitives, NIL}
+@item 002 @tab 02 @tab @code{00000010} @tab @ref{Primitives, FALSE}
+@item 003 @tab 03 @tab @code{00000011} @tab @ref{Primitives, TRUE}
+@item 004 @tab 04 @tab @code{00000100} @tab @ref{UUID}
+@item 005 @tab 05 @tab @code{00000101} @tab
+@item 006 @tab 06 @tab @code{00000110} @tab
+@item 007 @tab 07 @tab @code{00000111} @tab
+@item 008 @tab 08 @tab @code{00001000} @tab @ref{Containers, LIST}
+@item 009 @tab 09 @tab @code{00001001} @tab @ref{Containers, MAP}
+@item 010 @tab 0A @tab @code{00001010} @tab
+@item 011 @tab 0B @tab @code{00001011} @tab @ref{Blobs, BLOB}
+@item 012 @tab 0C @tab @code{00001100} @tab
+@item 013 @tab 0D @tab @code{00001101} @tab
+@item 014 @tab 0E @tab @code{00001110} @tab
+@item 015 @tab 0F @tab @code{00001111} @tab
+@item 016 @tab 10 @tab @code{00010000} @tab @ref{Floats, FLOAT16}
+@item 017 @tab 11 @tab @code{00010001} @tab @ref{Floats, FLOAT32}
+@item 018 @tab 12 @tab @code{00010010} @tab @ref{Floats, FLOAT64}
+@item 019 @tab 13 @tab @code{00010011} @tab @ref{Floats, FLOAT128}
+@item 020 @tab 14 @tab @code{00010100} @tab @ref{Floats, FLOAT256}
+@item 021 @tab 15 @tab @code{00010101} @tab
+@item 022 @tab 16 @tab @code{00010110} @tab
+@item 023 @tab 17 @tab @code{00010111} @tab
+@item 024 @tab 18 @tab @code{00011000} @tab @ref{TAI64, TAI64}
+@item 025 @tab 19 @tab @code{00011001} @tab @ref{TAI64, TAI64N}
+@item 026 @tab 1A @tab @code{00011010} @tab @ref{TAI64, TAI64NA}
+@item 027 @tab 1B @tab @code{00011011} @tab
+@item 028 @tab 1C @tab @code{00011100} @tab
+@item 029 @tab 1D @tab @code{00011101} @tab
+@item 030 @tab 1E @tab @code{00011110} @tab
+@item 031 @tab 1F @tab @code{00011111} @tab
+@item 032 @tab 20 @tab @code{00100000} @tab @ref{Integers, +INT1}
+@item 033 @tab 21 @tab @code{00100001} @tab @ref{Integers, +INT2}
+@item 034 @tab 22 @tab @code{00100010} @tab @ref{Integers, +INT3}
+@item 035 @tab 23 @tab @code{00100011} @tab @ref{Integers, +INT4}
+@item 036 @tab 24 @tab @code{00100100} @tab @ref{Integers, +INT5}
+@item 037 @tab 25 @tab @code{00100101} @tab @ref{Integers, +INT6}
+@item 038 @tab 26 @tab @code{00100110} @tab @ref{Integers, +INT7}
+@item 039 @tab 27 @tab @code{00100111} @tab @ref{Integers, +INT8}
+@item 040 @tab 28 @tab @code{00101000} @tab @ref{Integers, +INT9}
+@item 041 @tab 29 @tab @code{00101001} @tab @ref{Integers, +INT10}
+@item 042 @tab 2A @tab @code{00101010} @tab @ref{Integers, +INT11}
+@item 043 @tab 2B @tab @code{00101011} @tab @ref{Integers, +INT12}
+@item 044 @tab 2C @tab @code{00101100} @tab @ref{Integers, +INT13}
+@item 045 @tab 2D @tab @code{00101101} @tab @ref{Integers, +INT14}
+@item 046 @tab 2E @tab @code{00101110} @tab @ref{Integers, +INT15}
+@item 047 @tab 2F @tab @code{00101111} @tab @ref{Integers, +INT16}
+@item 048 @tab 30 @tab @code{00110000} @tab @ref{Integers, -INT1}
+@item 049 @tab 31 @tab @code{00110001} @tab @ref{Integers, -INT2}
+@item 050 @tab 32 @tab @code{00110010} @tab @ref{Integers, -INT3}
+@item 051 @tab 33 @tab @code{00110011} @tab @ref{Integers, -INT4}
+@item 052 @tab 34 @tab @code{00110100} @tab @ref{Integers, -INT5}
+@item 053 @tab 35 @tab @code{00110101} @tab @ref{Integers, -INT6}
+@item 054 @tab 36 @tab @code{00110110} @tab @ref{Integers, -INT7}
+@item 055 @tab 37 @tab @code{00110111} @tab @ref{Integers, -INT8}
+@item 056 @tab 38 @tab @code{00111000} @tab @ref{Integers, -INT9}
+@item 057 @tab 39 @tab @code{00111001} @tab @ref{Integers, -INT10}
+@item 058 @tab 3A @tab @code{00111010} @tab @ref{Integers, -INT11}
+@item 059 @tab 3B @tab @code{00111011} @tab @ref{Integers, -INT12}
+@item 060 @tab 3C @tab @code{00111100} @tab @ref{Integers, -INT13}
+@item 061 @tab 3D @tab @code{00111101} @tab @ref{Integers, -INT14}
+@item 062 @tab 3E @tab @code{00111110} @tab @ref{Integers, -INT15}
+@item 063 @tab 3F @tab @code{00111111} @tab @ref{Integers, -INT16}
+@item 064 @tab 40 @tab @code{01000000} @tab @ref{Integers, INT=0}
+@item 065 @tab 41 @tab @code{01000001} @tab @ref{Integers, INT=1}
+@item 066 @tab 42 @tab @code{01000010} @tab @ref{Integers, INT=2}
+@item 067 @tab 43 @tab @code{01000011} @tab @ref{Integers, INT=3}
+@item 068 @tab 44 @tab @code{01000100} @tab @ref{Integers, INT=4}
+@item 069 @tab 45 @tab @code{01000101} @tab @ref{Integers, INT=5}
+@item 070 @tab 46 @tab @code{01000110} @tab @ref{Integers, INT=6}
+@item 071 @tab 47 @tab @code{01000111} @tab @ref{Integers, INT=7}
+@item 072 @tab 48 @tab @code{01001000} @tab @ref{Integers, INT=8}
+@item 073 @tab 49 @tab @code{01001001} @tab @ref{Integers, INT=9}
+@item 074 @tab 4A @tab @code{01001010} @tab @ref{Integers, INT=10}
+@item 075 @tab 4B @tab @code{01001011} @tab @ref{Integers, INT=11}
+@item 076 @tab 4C @tab @code{01001100} @tab @ref{Integers, INT=12}
+@item 077 @tab 4D @tab @code{01001101} @tab @ref{Integers, INT=13}
+@item 078 @tab 4E @tab @code{01001110} @tab @ref{Integers, INT=14}
+@item 079 @tab 4F @tab @code{01001111} @tab @ref{Integers, INT=15}
+@item 080 @tab 50 @tab @code{01010000} @tab @ref{Integers, INT=16}
+@item 081 @tab 51 @tab @code{01010001} @tab @ref{Integers, INT=17}
+@item 082 @tab 52 @tab @code{01010010} @tab @ref{Integers, INT=18}
+@item 083 @tab 53 @tab @code{01010011} @tab @ref{Integers, INT=19}
+@item 084 @tab 54 @tab @code{01010100} @tab @ref{Integers, INT=20}
+@item 085 @tab 55 @tab @code{01010101} @tab @ref{Integers, INT=21}
+@item 086 @tab 56 @tab @code{01010110} @tab @ref{Integers, INT=22}
+@item 087 @tab 57 @tab @code{01010111} @tab @ref{Integers, INT=23}
+@item 088 @tab 58 @tab @code{01011000} @tab @ref{Integers, INT=24}
+@item 089 @tab 59 @tab @code{01011001} @tab @ref{Integers, INT=25}
+@item 090 @tab 5A @tab @code{01011010} @tab @ref{Integers, INT=26}
+@item 091 @tab 5B @tab @code{01011011} @tab @ref{Integers, INT=27}
+@item 092 @tab 5C @tab @code{01011100} @tab @ref{Integers, INT=28}
+@item 093 @tab 5D @tab @code{01011101} @tab @ref{Integers, INT=29}
+@item 094 @tab 5E @tab @code{01011110} @tab @ref{Integers, INT=30}
+@item 095 @tab 5F @tab @code{01011111} @tab @ref{Integers, INT=31}
+@item 096 @tab 60 @tab @code{01100000} @tab @ref{Integers, INT=-1}
+@item 097 @tab 61 @tab @code{01100001} @tab @ref{Integers, INT=-2}
+@item 098 @tab 62 @tab @code{01100010} @tab @ref{Integers, INT=-3}
+@item 099 @tab 63 @tab @code{01100011} @tab @ref{Integers, INT=-4}
+@item 100 @tab 64 @tab @code{01100100} @tab @ref{Integers, INT=-5}
+@item 101 @tab 65 @tab @code{01100101} @tab @ref{Integers, INT=-6}
+@item 102 @tab 66 @tab @code{01100110} @tab @ref{Integers, INT=-7}
+@item 103 @tab 67 @tab @code{01100111} @tab @ref{Integers, INT=-8}
+@item 104 @tab 68 @tab @code{01101000} @tab @ref{Integers, INT=-9}
+@item 105 @tab 69 @tab @code{01101001} @tab @ref{Integers, INT=-10}
+@item 106 @tab 6A @tab @code{01101010} @tab @ref{Integers, INT=-11}
+@item 107 @tab 6B @tab @code{01101011} @tab @ref{Integers, INT=-12}
+@item 108 @tab 6C @tab @code{01101100} @tab @ref{Integers, INT=-13}
+@item 109 @tab 6D @tab @code{01101101} @tab @ref{Integers, INT=-14}
+@item 110 @tab 6E @tab @code{01101110} @tab @ref{Integers, INT=-15}
+@item 111 @tab 6F @tab @code{01101111} @tab @ref{Integers, INT=-16}
+@item 112 @tab 70 @tab @code{01110000} @tab @ref{Integers, INT=-17}
+@item 113 @tab 71 @tab @code{01110001} @tab @ref{Integers, INT=-18}
+@item 114 @tab 72 @tab @code{01110010} @tab @ref{Integers, INT=-19}
+@item 115 @tab 73 @tab @code{01110011} @tab @ref{Integers, INT=-20}
+@item 116 @tab 74 @tab @code{01110100} @tab @ref{Integers, INT=-21}
+@item 117 @tab 75 @tab @code{01110101} @tab @ref{Integers, INT=-22}
+@item 118 @tab 76 @tab @code{01110110} @tab @ref{Integers, INT=-23}
+@item 119 @tab 77 @tab @code{01110111} @tab @ref{Integers, INT=-24}
+@item 120 @tab 78 @tab @code{01111000} @tab @ref{Integers, INT=-25}
+@item 121 @tab 79 @tab @code{01111001} @tab @ref{Integers, INT=-26}
+@item 122 @tab 7A @tab @code{01111010} @tab @ref{Integers, INT=-27}
+@item 123 @tab 7B @tab @code{01111011} @tab @ref{Integers, INT=-28}
+@item 124 @tab 7C @tab @code{01111100} @tab @ref{Integers, INT=-29}
+@item 125 @tab 7D @tab @code{01111101} @tab @ref{Integers, INT=-30}
+@item 126 @tab 7E @tab @code{01111110} @tab @ref{Integers, INT=-31}
+@item 127 @tab 7F @tab @code{01111111} @tab @ref{Integers, INT=-32}
+@item 128 @tab 80 @tab @code{10000000} @tab @ref{Strings, BIN=0}
+@item 129 @tab 81 @tab @code{10000001} @tab @ref{Strings, BIN=1}
+@item 130 @tab 82 @tab @code{10000010} @tab @ref{Strings, BIN=2}
+@item 131 @tab 83 @tab @code{10000011} @tab @ref{Strings, BIN=3}
+@item 132 @tab 84 @tab @code{10000100} @tab @ref{Strings, BIN=4}
+@item 133 @tab 85 @tab @code{10000101} @tab @ref{Strings, BIN=5}
+@item 134 @tab 86 @tab @code{10000110} @tab @ref{Strings, BIN=6}
+@item 135 @tab 87 @tab @code{10000111} @tab @ref{Strings, BIN=7}
+@item 136 @tab 88 @tab @code{10001000} @tab @ref{Strings, BIN=8}
+@item 137 @tab 89 @tab @code{10001001} @tab @ref{Strings, BIN=9}
+@item 138 @tab 8A @tab @code{10001010} @tab @ref{Strings, BIN=10}
+@item 139 @tab 8B @tab @code{10001011} @tab @ref{Strings, BIN=11}
+@item 140 @tab 8C @tab @code{10001100} @tab @ref{Strings, BIN=12}
+@item 141 @tab 8D @tab @code{10001101} @tab @ref{Strings, BIN=13}
+@item 142 @tab 8E @tab @code{10001110} @tab @ref{Strings, BIN=14}
+@item 143 @tab 8F @tab @code{10001111} @tab @ref{Strings, BIN=15}
+@item 144 @tab 90 @tab @code{10010000} @tab @ref{Strings, BIN=16}
+@item 145 @tab 91 @tab @code{10010001} @tab @ref{Strings, BIN=17}
+@item 146 @tab 92 @tab @code{10010010} @tab @ref{Strings, BIN=18}
+@item 147 @tab 93 @tab @code{10010011} @tab @ref{Strings, BIN=19}
+@item 148 @tab 94 @tab @code{10010100} @tab @ref{Strings, BIN=20}
+@item 149 @tab 95 @tab @code{10010101} @tab @ref{Strings, BIN=21}
+@item 150 @tab 96 @tab @code{10010110} @tab @ref{Strings, BIN=22}
+@item 151 @tab 97 @tab @code{10010111} @tab @ref{Strings, BIN=23}
+@item 152 @tab 98 @tab @code{10011000} @tab @ref{Strings, BIN=24}
+@item 153 @tab 99 @tab @code{10011001} @tab @ref{Strings, BIN=25}
+@item 154 @tab 9A @tab @code{10011010} @tab @ref{Strings, BIN=26}
+@item 155 @tab 9B @tab @code{10011011} @tab @ref{Strings, BIN=27}
+@item 156 @tab 9C @tab @code{10011100} @tab @ref{Strings, BIN=28}
+@item 157 @tab 9D @tab @code{10011101} @tab @ref{Strings, BIN=29}
+@item 158 @tab 9E @tab @code{10011110} @tab @ref{Strings, BIN=30}
+@item 159 @tab 9F @tab @code{10011111} @tab @ref{Strings, BIN=31}
+@item 160 @tab A0 @tab @code{10100000} @tab @ref{Strings, BIN=32}
+@item 161 @tab A1 @tab @code{10100001} @tab @ref{Strings, BIN=33}
+@item 162 @tab A2 @tab @code{10100010} @tab @ref{Strings, BIN=34}
+@item 163 @tab A3 @tab @code{10100011} @tab @ref{Strings, BIN=35}
+@item 164 @tab A4 @tab @code{10100100} @tab @ref{Strings, BIN=36}
+@item 165 @tab A5 @tab @code{10100101} @tab @ref{Strings, BIN=37}
+@item 166 @tab A6 @tab @code{10100110} @tab @ref{Strings, BIN=38}
+@item 167 @tab A7 @tab @code{10100111} @tab @ref{Strings, BIN=39}
+@item 168 @tab A8 @tab @code{10101000} @tab @ref{Strings, BIN=40}
+@item 169 @tab A9 @tab @code{10101001} @tab @ref{Strings, BIN=41}
+@item 170 @tab AA @tab @code{10101010} @tab @ref{Strings, BIN=42}
+@item 171 @tab AB @tab @code{10101011} @tab @ref{Strings, BIN=43}
+@item 172 @tab AC @tab @code{10101100} @tab @ref{Strings, BIN=44}
+@item 173 @tab AD @tab @code{10101101} @tab @ref{Strings, BIN=45}
+@item 174 @tab AE @tab @code{10101110} @tab @ref{Strings, BIN=46}
+@item 175 @tab AF @tab @code{10101111} @tab @ref{Strings, BIN=47}
+@item 176 @tab B0 @tab @code{10110000} @tab @ref{Strings, BIN=48}
+@item 177 @tab B1 @tab @code{10110001} @tab @ref{Strings, BIN=49}
+@item 178 @tab B2 @tab @code{10110010} @tab @ref{Strings, BIN=50}
+@item 179 @tab B3 @tab @code{10110011} @tab @ref{Strings, BIN=51}
+@item 180 @tab B4 @tab @code{10110100} @tab @ref{Strings, BIN=52}
+@item 181 @tab B5 @tab @code{10110101} @tab @ref{Strings, BIN=53}
+@item 182 @tab B6 @tab @code{10110110} @tab @ref{Strings, BIN=54}
+@item 183 @tab B7 @tab @code{10110111} @tab @ref{Strings, BIN=55}
+@item 184 @tab B8 @tab @code{10111000} @tab @ref{Strings, BIN=56}
+@item 185 @tab B9 @tab @code{10111001} @tab @ref{Strings, BIN=57}
+@item 186 @tab BA @tab @code{10111010} @tab @ref{Strings, BIN=58}
+@item 187 @tab BB @tab @code{10111011} @tab @ref{Strings, BIN=59}
+@item 188 @tab BC @tab @code{10111100} @tab @ref{Strings, BIN=60}
+@item 189 @tab BD @tab @code{10111101} @tab @ref{Strings, BIN 8b}
+@item 190 @tab BE @tab @code{10111110} @tab @ref{Strings, BIN 16b}
+@item 191 @tab BF @tab @code{10111111} @tab @ref{Strings, BIN 64b}
+@item 192 @tab C0 @tab @code{11000000} @tab @ref{Strings, STR=0}
+@item 193 @tab C1 @tab @code{11000001} @tab @ref{Strings, STR=1}
+@item 194 @tab C2 @tab @code{11000010} @tab @ref{Strings, STR=2}
+@item 195 @tab C3 @tab @code{11000011} @tab @ref{Strings, STR=3}
+@item 196 @tab C4 @tab @code{11000100} @tab @ref{Strings, STR=4}
+@item 197 @tab C5 @tab @code{11000101} @tab @ref{Strings, STR=5}
+@item 198 @tab C6 @tab @code{11000110} @tab @ref{Strings, STR=6}
+@item 199 @tab C7 @tab @code{11000111} @tab @ref{Strings, STR=7}
+@item 200 @tab C8 @tab @code{11001000} @tab @ref{Strings, STR=8}
+@item 201 @tab C9 @tab @code{11001001} @tab @ref{Strings, STR=9}
+@item 202 @tab CA @tab @code{11001010} @tab @ref{Strings, STR=10}
+@item 203 @tab CB @tab @code{11001011} @tab @ref{Strings, STR=11}
+@item 204 @tab CC @tab @code{11001100} @tab @ref{Strings, STR=12}
+@item 205 @tab CD @tab @code{11001101} @tab @ref{Strings, STR=13}
+@item 206 @tab CE @tab @code{11001110} @tab @ref{Strings, STR=14}
+@item 207 @tab CF @tab @code{11001111} @tab @ref{Strings, STR=15}
+@item 208 @tab D0 @tab @code{11010000} @tab @ref{Strings, STR=16}
+@item 209 @tab D1 @tab @code{11010001} @tab @ref{Strings, STR=17}
+@item 210 @tab D2 @tab @code{11010010} @tab @ref{Strings, STR=18}
+@item 211 @tab D3 @tab @code{11010011} @tab @ref{Strings, STR=19}
+@item 212 @tab D4 @tab @code{11010100} @tab @ref{Strings, STR=20}
+@item 213 @tab D5 @tab @code{11010101} @tab @ref{Strings, STR=21}
+@item 214 @tab D6 @tab @code{11010110} @tab @ref{Strings, STR=22}
+@item 215 @tab D7 @tab @code{11010111} @tab @ref{Strings, STR=23}
+@item 216 @tab D8 @tab @code{11011000} @tab @ref{Strings, STR=24}
+@item 217 @tab D9 @tab @code{11011001} @tab @ref{Strings, STR=25}
+@item 218 @tab DA @tab @code{11011010} @tab @ref{Strings, STR=26}
+@item 219 @tab DB @tab @code{11011011} @tab @ref{Strings, STR=27}
+@item 220 @tab DC @tab @code{11011100} @tab @ref{Strings, STR=28}
+@item 221 @tab DD @tab @code{11011101} @tab @ref{Strings, STR=29}
+@item 222 @tab DE @tab @code{11011110} @tab @ref{Strings, STR=30}
+@item 223 @tab DF @tab @code{11011111} @tab @ref{Strings, STR=31}
+@item 224 @tab E0 @tab @code{11100000} @tab @ref{Strings, STR=32}
+@item 225 @tab E1 @tab @code{11100001} @tab @ref{Strings, STR=33}
+@item 226 @tab E2 @tab @code{11100010} @tab @ref{Strings, STR=34}
+@item 227 @tab E3 @tab @code{11100011} @tab @ref{Strings, STR=35}
+@item 228 @tab E4 @tab @code{11100100} @tab @ref{Strings, STR=36}
+@item 229 @tab E5 @tab @code{11100101} @tab @ref{Strings, STR=37}
+@item 230 @tab E6 @tab @code{11100110} @tab @ref{Strings, STR=38}
+@item 231 @tab E7 @tab @code{11100111} @tab @ref{Strings, STR=39}
+@item 232 @tab E8 @tab @code{11101000} @tab @ref{Strings, STR=40}
+@item 233 @tab E9 @tab @code{11101001} @tab @ref{Strings, STR=41}
+@item 234 @tab EA @tab @code{11101010} @tab @ref{Strings, STR=42}
+@item 235 @tab EB @tab @code{11101011} @tab @ref{Strings, STR=43}
+@item 236 @tab EC @tab @code{11101100} @tab @ref{Strings, STR=44}
+@item 237 @tab ED @tab @code{11101101} @tab @ref{Strings, STR=45}
+@item 238 @tab EE @tab @code{11101110} @tab @ref{Strings, STR=46}
+@item 239 @tab EF @tab @code{11101111} @tab @ref{Strings, STR=47}
+@item 240 @tab F0 @tab @code{11110000} @tab @ref{Strings, STR=48}
+@item 241 @tab F1 @tab @code{11110001} @tab @ref{Strings, STR=49}
+@item 242 @tab F2 @tab @code{11110010} @tab @ref{Strings, STR=50}
+@item 243 @tab F3 @tab @code{11110011} @tab @ref{Strings, STR=51}
+@item 244 @tab F4 @tab @code{11110100} @tab @ref{Strings, STR=52}
+@item 245 @tab F5 @tab @code{11110101} @tab @ref{Strings, STR=53}
+@item 246 @tab F6 @tab @code{11110110} @tab @ref{Strings, STR=54}
+@item 247 @tab F7 @tab @code{11110111} @tab @ref{Strings, STR=55}
+@item 248 @tab F8 @tab @code{11111000} @tab @ref{Strings, STR=56}
+@item 249 @tab F9 @tab @code{11111001} @tab @ref{Strings, STR=57}
+@item 250 @tab FA @tab @code{11111010} @tab @ref{Strings, STR=58}
+@item 251 @tab FB @tab @code{11111011} @tab @ref{Strings, STR=59}
+@item 252 @tab FC @tab @code{11111100} @tab @ref{Strings, STR=60}
+@item 253 @tab FD @tab @code{11111101} @tab @ref{Strings, STR 8b}
+@item 254 @tab FE @tab @code{11111110} @tab @ref{Strings, STR 16b}
+@item 255 @tab FF @tab @code{11111111} @tab @ref{Strings, STR 64b}
+
+@end multitable
diff --git a/spec/encoding/tai64.texi b/spec/encoding/tai64.texi
new file mode 100644 (file)
index 0000000..b8f694a
--- /dev/null
@@ -0,0 +1,30 @@
+@node TAI64
+@section TAI64
+
+Datetime is represented in @url{http://cr.yp.to/libtai/tai64.html, TAI64}
+format. TAI stands for Temps Atomique International, the current
+international real time standard. Unlike UTC, it takes leap seconds into
+account, making it monotonous.
+
+You can convert TAI to UTC by subtracting number of leap seconds.
+
+TAI64N format adds 32-bit big-endian number of nanoseconds (in up to
+999999999) count. TAI64NA adds another 32-big big-endian part of
+attoseconds count.
+
+Shortest form @strong{must} be used: if number of nanoseconds
+equals to zero, then use TAI64 format.
+
+Example representations:
+
+@multitable @columnfractions .5 .5
+
+@item 1970-01-01 TAI @tab @code{18 4000000000000000}
+@item 1970-01-01 UTC @tab @code{18 400000000000000A}
+@item 1969-12-31 23:59:59 TAI @tab @code{18 3FFFFFFFFFFFFFFF}
+@item 1997-10-03 18:15:19 TAI @tab @code{18 4000000034353637}
+@item 1997-10-03 18:14:48 UTC @tab @code{18 4000000034353637}
+@item 2024-10-01 12:39:07 UTC @tab @code{18 4000000066FC17C0}
+@item 2024-10-01 12:39:07 UTC 123456 μs @tab @code{19 4000000066FC17C0075BCA00}
+
+@end multitable
diff --git a/spec/encoding/uuid.texi b/spec/encoding/uuid.texi
new file mode 100644 (file)
index 0000000..7db1df2
--- /dev/null
@@ -0,0 +1,16 @@
+@node UUID
+@section UUID
+
+128-bit big-endian @url{https://datatracker.ietf.org/doc/html/rfc9562, UUID}'s
+value is placed after the tag.
+
+Example representations:
+
+@multitable @columnfractions .5 .5
+
+@item Nil UUID @tab @code{04 00000000000000000000000000000000}
+@item Max UUID @tab @code{04 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF}
+@item UUIDv4 @code{0e875e3f-d385-49eb-87b4-be42d641c367} @tab
+    @code{04 0E875E3FD38549EB87B4BE42D641C367}
+
+@end multitable
diff --git a/spec/format/cer.texi b/spec/format/cer.texi
new file mode 100644 (file)
index 0000000..9cc1a5d
--- /dev/null
@@ -0,0 +1,65 @@
+@node Certificate
+@section Certificate format
+
+Certificate is the @ref{SignedData} structure with @code{/load.t} equals
+to @code{cer} and following @code{/load.v} content (keys are sorted as they
+will be encoded):
+
+@verbatim
+{
+    ?"ku": ["...", ...], # "dh", "sig", "ca", ...
+    "exp": [TAI64, TAI64],
+    "kid": UUID(signer's public key-based UUID),
+    "pub": {"a": "ID", "v": BIN(marshalled public key)},
+    "sub": {"entity": "...", ...},
+    ?"crit": [{"t": "ID", ...}, ...],
+}
+@end verbatim
+
+@code{kid} is a hash calculated over the @code{pub} field and used to
+form UUIDv4. But it may be formed another way, no limitations. Depending
+on @code{pub.a}, that may be different hash like Streebog-256 or
+SHAKE-128 (let's stop using SHA-2!).
+
+@code{sub} is a subject name. Its values are UTF-8 strings. Currently no
+constraints on what fields must be present.
+
+@code{exp}iration period @strong{must} contain TAI64 datetime, without
+any nanoseconds part.
+
+@code{ku} (key usage) contains supposed usage contexts like being CA
+(@code{ca}), or using it solely for either signing (@code{sig}), or
+key-agreement (@code{dh}) purposes. It @strong{must} be absent if empty.
+Its items @strong{must} be length-first sorted (like MAP) and unique.
+Possibly some additional usages like @code{email}, @code{ike},
+@code{codeSign} may be thought out.
+
+Optional @code{crit} list may contain other critical extensions.
+Non-critical extensions may be placed just inside the map itself.
+It @strong{must} be absent if empty. Extension is a map with expected
+@code{t}ype field containing the identifier of the extension. Other
+extension's keys are defined by its type.
+
+Example minimal certificate may look like:
+
+@verbatim
+{
+    "load": {
+        "t": "cer",
+        "v": {
+            "exp": [TAI64, TAI64],
+            "kid": UUID(SKID),
+            "pub": {"a": "gost3410-256A", "v": BIN(...)},
+            "sub": {"CN": "Test", "O": "Testers"},
+        },
+    },
+    "sigs": [{"kid": UUID(AKID), "sign": {"a": "gost3410-256A", "v": BIN(...)}}],
+}
+@end verbatim
+
+@node Certificate-GOST3410
+@subsection Certificate with GOST R 34.10-2012
+
+Same rules of serialisation must be used as with
+@ref{SignedData-GOST3410, SignedData}. KID's UUIDv4 is advised to be
+calculated by using Streebog-256 hash over the encoded "pub" field.
diff --git a/spec/format/hashed.texi b/spec/format/hashed.texi
new file mode 100644 (file)
index 0000000..e7ed8cb
--- /dev/null
@@ -0,0 +1,25 @@
+@node HashedData
+@section HashedData format
+
+Integrity protected container, analogue to CMS'es DigestedData structure.
+
+@verbatim
+{
+    "a": ["ID", ..."],
+    "t": "ID",
+    "v": BIN/STR or BLOB or MAP/LIST,
+    "hash": [BIN(hash value), ...],
+}
+@end verbatim
+
+@code{/a} tells what algorithms will be used to hash the data.
+
+@code{/t} tells the type of the data inside.
+
+If @code{/v} is either a MAP or LIST, then its encoded binary
+representation is hashed. If it is BIN/STR or BLOB, then its binary
+contents are hashed. So hash will stay the same even if data is
+converted from BIN to BLOB.
+
+@code{/hash} contains the hash values for all corresponding @code{/a}
+algorithms.
diff --git a/spec/format/index.texi b/spec/format/index.texi
new file mode 100644 (file)
index 0000000..6780942
--- /dev/null
@@ -0,0 +1,9 @@
+@node Formats
+@unnumbered Formats
+
+Here are some suggested formats.
+
+@include format/prv.texi
+@include format/signed.texi
+@include format/cer.texi
+@include format/hashed.texi
diff --git a/spec/format/prv.texi b/spec/format/prv.texi
new file mode 100644 (file)
index 0000000..20bb566
--- /dev/null
@@ -0,0 +1,16 @@
+@node PrivateKey
+@section PrivateKey format
+
+Private key is stored in trivial map:
+
+@verbatim
+{
+    "a": "ID",
+    "v": BIN(private key's value),
+}
+@end verbatim
+
+@node PrivateKey-GOST3410
+@subsection PrivateKey with GOST R 34.10-2012
+
+Big-endian private key representation must be used.
diff --git a/spec/format/signed.texi b/spec/format/signed.texi
new file mode 100644 (file)
index 0000000..a0e4707
--- /dev/null
@@ -0,0 +1,109 @@
+@node SignedData
+@section SignedData format
+
+Signing of arbitrary data can be done in two ways: either with
+pre-hashing the data, or signing it directly. That resembles
+@url{https://datatracker.ietf.org/doc/html/rfc5652, CMS} (PKCS#7)
+ASN.1-based format.
+
+@table @asis
+
+@item Pre-hashed version:
+
+@verbatim
+{
+    "hash": ["ID", ...],
+    "load": {
+        "t": "ID",
+        ?"v": BIN/STR or BLOB or MAP/LIST,
+    },
+    "sigs": [
+        {
+            "kid": UUID(signer's public key-based UUID),
+            "load": {
+                "hash": {"ID": BIN(one of hashes value), ...},
+                ?"when": TAI64*,
+            },
+            "sign": {"a": "ID", "v": BIN(signature value)},
+            ?"cer-loc": ["URL", ...],
+        },
+        ...
+    ],
+    ?"certs": [...],
+}
+@end verbatim
+
+@code{/hash} tells what algorithms will be used to hash the data of
+@code{/load.v}. Signature is created by signing the following encoded MAP:
+
+@verbatim
+{
+    "t": "/load.t value",
+    "v": BIN(""),
+    "kid": UUID(/sigs.?.kid value)
+    "load": MAP(/sigs.?.load value),
+}
+@end verbatim
+
+If @code{/load.v} is either a MAP or LIST, then its encoded binary
+representation is hashed. If it is BIN/STR or BLOB, then its binary
+contents are hashed. So signature will stay the same even if data is
+converted from BIN to BLOB.
+
+@item Plain version:
+
+@verbatim
+{
+    "load": {
+        "t": "ID",
+        ?"v": BIN/STR or MAP/LIST,
+    },
+    "sigs": [
+        {
+            "kid": UUID(signer's public key-based UUID),
+            ?"load": {
+                ?"when": TAI64*,
+            },
+            "sign": {"a": "ID", "v": BIN(signature value)},
+            ?"cer-loc": ["URL", ...],
+        },
+        ...
+    ],
+    ?"certs": [...],
+}
+@end verbatim
+
+If @code{/sigs.?.load} is empty, then it @strong{must not} be present.
+Signature is created by signing the following encoded MAP:
+
+@verbatim
+{
+    "t": "/load.t value",
+    "v": /load.v's value,
+    "kid": UUID(/sigs.?.kid value)
+    "load": MAP(/sigs.?.load value or empty MAP),
+}
+@end verbatim
+
+@end table
+
+If @code{/load.v} is absent, then it is detached and must be provided as
+a binary data from outside.
+
+@code{/sigs.*.load.when} is optional signing time.
+
+Additional values that must be protected (covered by signature) are
+placed in @code{/sigs.*.load} map. Non-protected (informational) fields
+are placed outside it.
+
+@code{/cers} are optionally provided @ref{Certificate, certificates} to
+help creating the whole verification chain. They are placed outside
+@code{/sigs}, because some of them may be shared among signers.
+
+@node SignedData-GOST3410
+@subsection SignedData with GOST R 34.10-2012
+
+GOST R 34.10-2012 must be used with Streebog (GOST R 34.11-2012) hash
+function. Its digest must be big-endian serialised. Public key must be
+in @code{BE(X)||BE(Y)} format. Signature is in @code{BE(S)||BE(R)}
+format.
diff --git a/spec/index.texi b/spec/index.texi
new file mode 100644 (file)
index 0000000..7072e71
--- /dev/null
@@ -0,0 +1,83 @@
+\input texinfo
+@settitle YAC
+
+@copying
+Copyright @copyright{} 2024 @email{stargrave@@stargrave.org, Sergey Matveev}
+@end copying
+
+@node Top
+@top YAC
+
+YAC (Yet Another Codec) -- yet another format for binary reprensentation
+of structured data. But why!?
+
+@itemize
+@item It must be schema-less format. Schema-aware ones have their
+    definite valuable advantages, but also a complication drawbacks.
+@item Its encoder/decoder must be very compact and small in terms of
+    code amount, to reduce attack surface on the codec itself.
+@item It must be frugal to CPU usage for both performance/memory
+    constrained and high data volume applications.
+@item Its encoding must be reasonably compact, to be friendly to storage
+    space constrained systems.
+@item Its encoding must be deterministic -- there must be only a single
+    representation of the structured data, allowing its usage in
+    cryptography-related contexts.
+@item It should support enough data types to be able to replace JSON
+    transparently
+@end itemize
+
+Are not there any satisfiable codecs?
+
+@table @asis
+@item @url{https://wiki.theory.org/BitTorrentSpecification#Bencoding, Bencode}
+    Is very simple and deterministic. But it has a few data types, even
+    lacking human-readable strings.
+@item @url{https://www.ietf.org/archive/id/draft-rivest-sexp-08.html, Canonical S-Expressions}
+    Is another interesting candidate with relatively simple encoding,
+    but without enough data types.
+@item @url{https://bsonspec.org/, BSON}
+    Is pretty simple too, having enough data types. But it is not able
+    to transfer strings and lists longer than their 32-bit length. Also
+    that encoding is not compact.
+@item @url{https://msgpack.org/, MessagePack}
+    Is another good candidate with rather simple encoding. But it still
+    can not transfer large (>4GiB) strings. And it does not have
+    deterministic encoding rules.
+@item @url{https://datatracker.ietf.org/doc/html/rfc8949, CBOR}
+    Looked like the promising one, having nearly the same aims as we
+    did, even with Deterministic Encoded CBOR version description and
+    being pretty compact in binary form.
+
+    But it is the @strong{most} complicated from all the codecs
+    mentioned above, failing their main claim of being simple. Streaming
+    encoding can reduce both the code size and memory usage of your
+    application, but deterministic CBOR encoding prohibits it.
+
+    Moreover hardly you will find CBOR libraries supporting strict
+    validation of deterministically encoded CBOR structures.
+@end table
+
+YAC deals with those problems by using only streaming deterministic
+encoding. YAC is Ain't a CBOR. Its other important differences:
+
+@itemize
+@item It lacks any kind of tagging support, making the whole codec
+    simpler and less cumbersome when someone does not know how to deal
+    with the exact tag.
+@item MAP allows only string keys with strict length-first ordering,
+    eliminating questions about dealing with duplicate keys and making
+    code simpler.
+@item It has TAI64-based datetime representation as a base primitive
+    type. No more different and not always satisfying encodings.
+@item BLOB data type gives ability to transfer huge chunks of binary
+    data in a streaming way. But unlike ASN.1 CER, you still can use
+    continuous strings representation.
+@end itemize
+
+@insertcopying
+
+@include encoding/index.texi
+@include schema.texi
+@include format/index.texi
+@include registry.texi
diff --git a/spec/mk-info b/spec/mk-info
new file mode 100755 (executable)
index 0000000..1075232
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/sh -e
+
+makeinfo \
+    --set-customization-variable SECTION_NAME_IN_TITLE=1 \
+    --set-customization-variable TREE_TRANSFORMATIONS=complete_tree_nodes_menus \
+    --set-customization-variable ASCII_PUNCTUATION=1 \
+    index.texi
diff --git a/spec/registry.texi b/spec/registry.texi
new file mode 100644 (file)
index 0000000..735e0f4
--- /dev/null
@@ -0,0 +1,76 @@
+@node Registry
+@unnumbered AI registry
+
+There is example registry of known algorithm identifiers.
+
+@node AI Hashes
+@section Hashes
+
+@verbatim
+blake2b-256
+blake2b-512
+blake3-256
+sha2-224
+sha2-256
+sha2-384
+sha2-512
+sha3-224
+sha3-256
+sha3-384
+sha3-512
+shake128
+shake256
+skein-512
+streebog256
+streebog512
+xxh3-128
+@end verbatim
+
+@node AI DH
+@section DH
+
+@verbatim
+ecdsa-nist192p
+ecdsa-nist224p
+ecdsa-nist256p
+ecdsa-nist384p
+ecdsa-nist521p
+gost3410-256A
+gost3410-256B
+gost3410-256C
+gost3410-256D
+gost3410-512A
+gost3410-512B
+gost3410-512C
+x25519
+x448
+@end verbatim
+
+@node AI Sign
+@section Signatures
+
+@verbatim
+ecdsa-nist192p
+ecdsa-nist224p
+ecdsa-nist256p
+ecdsa-nist384p
+ecdsa-nist521p
+ed25519
+ed448
+gost3410-256A
+gost3410-256B
+gost3410-256C
+gost3410-256D
+gost3410-512A
+gost3410-512B
+gost3410-512C
+@end verbatim
+
+@node AI Content types
+@section Content types
+
+@verbatim
+cer
+data
+prv
+@end verbatim
diff --git a/spec/schema.texi b/spec/schema.texi
new file mode 100644 (file)
index 0000000..afcb2ad
--- /dev/null
@@ -0,0 +1,35 @@
+@node Schema
+@unnumbered Schema
+
+YAC can be decoded without any schema definition. But semantic meaning
+and constraints of various fields are expected to be written in a human
+readable language somewhere nearby.
+
+For being able to streamingly generate the serialised data, you have to
+worry about map's keys ordering. Remember that it is length-first one.
+
+By default binary data is expected to be encoded as BIN. If you expect
+to deal with huge amounts of data, then probably you should use the BLOB
+type. Generally you have to differentiate field names for ordinary
+strings and blobs.
+
+For relatively small structures it could be wise to use reasonably short
+key names. However it may be difficult and not obvious to keep the
+balance between compactness and understandability. For example signature
+consisting of algorithm identifier and (obviously) signature value can
+be made that way:
+
+@verbatim
+sig: {"a": "ed25519", "v": bytes(...)}
+@end verbatim
+
+@code{"t"} can be used as short name for the "type".
+
+You should use more or less human readable strings instead of object
+identifiers. OIDs database can be considered as an external schema.
+Lacking it, or lacking its actual state, you probably won't be able even
+guessing the context of the data inside.
+
+If you really desire more compact encoding, even agree to use schema
+definitions, then think about replacing MAPs with LISTs. Non-present
+values can be indicated by NIL tag.
diff --git a/tyac/COPYING b/tyac/COPYING
new file mode 100644 (file)
index 0000000..58f2b48
--- /dev/null
@@ -0,0 +1,674 @@
+                   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>.
diff --git a/tyac/COPYING.LESSER b/tyac/COPYING.LESSER
new file mode 100644 (file)
index 0000000..2d9b9aa
--- /dev/null
@@ -0,0 +1,165 @@
+                  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.
diff --git a/tyac/README b/tyac/README
new file mode 100644 (file)
index 0000000..ae48a30
--- /dev/null
@@ -0,0 +1,5 @@
+Tcl implementation of the YAC encoder.
+
+* No FLOAT* support. They is stored just as a raw value
+
+tyac is free software: see the file COPYING.LESSER for copying conditions.
diff --git a/tyac/test-vector.tcl b/tyac/test-vector.tcl
new file mode 100644 (file)
index 0000000..22813c8
--- /dev/null
@@ -0,0 +1,64 @@
+source tyac.tcl
+
+MAP {
+    ints {MAP {
+        pos {LIST {
+            {INT 0}
+            {INT 1}
+            {INT 31}
+            {INT 32}
+            {INT 123}
+            {INT 1234}
+            {INT 12345678}
+            {INT [expr {1 << 80}]}
+        }}
+        neg {LIST {
+            {INT -1}
+            {INT -2}
+            {INT -32}
+            {INT -33}
+            {INT -123}
+            {INT -1234}
+            {INT -12345678}
+            {INT -[expr {1 << 80}]}
+        }}
+    }}
+    floats {LIST {
+        {Raw [expr 0x11] [binary decode hex "01020304"]}
+    }}
+    nil NIL
+    bool {LIST {TRUE FALSE}}
+    str {MAP {
+        utf8 {STR "привет мир"}
+        bin {LIST {
+            {BIN ""}
+            {BIN [string repeat "0" 60]}
+            {BIN [string repeat "1" 61]}
+            {BIN [string repeat "2" 255]}
+            {BIN [string repeat "3" 1024]}
+            {BIN [string repeat "4" [expr {1 << 16}]]}
+        }}
+    }}
+    blob {LIST {
+        {BLOB 12 "5"}
+        {BLOB 12 [string repeat "6" 12]}
+        {BLOB 12 [string repeat "7" 13]}
+        {BLOB 5 "1234567890-"}
+    }}
+    empties {LIST {
+        {LIST {}}
+        {MAP {}}
+        {BLOB 123 ""}
+        {UUID "00000000-0000-0000-0000-000000000000"}
+        {Raw [expr 0x18] [binary decode hex "0000000000000000"]}
+    }}
+    dates {LIST {
+        {TAI64 1234567890}
+        {TAI64 1234567890 456000}
+        {TAI64 1234567890 456789}
+        {TAI64 1234567890 456789 123456789}
+    }}
+    uuid {UUID 0e875e3f-d385-49eb-87b4-be42d641c367}
+}
+
+puts [binary encode hex [YACBufGet]]
diff --git a/tyac/tyac.tcl b/tyac/tyac.tcl
new file mode 100644 (file)
index 0000000..52a55cc
--- /dev/null
@@ -0,0 +1,224 @@
+# 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]
+}