-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.
items.* contains a non-streamable parser.
-
-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.
-
-cmd/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.
struct timespec tv;
enum YACErr err = YACTAI64ToTimespec(&tv, buf, len);
#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wswitch-enum"
+ switch (err) {
+#pragma clang diagnostic pop
+ case YACErrNo:
+ break;
+ case YACErrTAI64InPast:
+ hex = HexEnc(buf, len);
+ fprintf(stdout, "in past: %s)\n", hex);
+ free(hex);
+ return YACErrNo;
+ default:
+ return err;
+ }
+ err = YACTimespecToUTC(&tv);
+#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wswitch-enum"
switch (err) {
#pragma clang diagnostic pop
&Got, buf + Off, len - Off, (const unsigned char *)"dates", 5));
adder(YACAtomListEncode(&Got, buf + Off, len - Off)); // .dates
{
- struct timespec tv;
- tv.tv_sec = 1234567890;
+ struct timespec ts;
+ ts.tv_sec = 1234567890;
+ assert(YACTimespecToTAI(&ts));
unsigned char tai[12] = {0};
- assert(YACTimespecToTAI64(tai, &tv));
+ assert(YACTimespecToTAI64(tai, &ts));
adder(YACAtomTAI64Encode(&Got, buf + Off, len - Off, tai, 8));
- tv.tv_nsec = 456000;
- assert(YACTimespecToTAI64(tai, &tv));
+ ts.tv_nsec = 456000;
+ assert(YACTimespecToTAI64(tai, &ts));
adder(YACAtomTAI64Encode(&Got, buf + Off, len - Off, tai, 12));
adder(YACAtomRawEncode(
--- /dev/null
+/build/
+/docstringer.log
--- /dev/null
+@node Atom
+@cindex atom
+@unnumbered Atom
+
+@anchor{YACAtom}
+@DOCSTRING YACAtom@
+
+@anchor{YACItemType}
+@DOCSTRING YACItemType@
+
+@DOCSTRING YACAtomDecode@
+
+@DOCSTRING YACAtomEOCEncode@
+@anchor{YACAtomNILEncode}
+@DOCSTRING YACAtomNILEncode@
+@DOCSTRING YACAtomBoolEncode@
+@DOCSTRING YACAtomUUIDEncode@
+@DOCSTRING YACAtomUintEncode@
+@DOCSTRING YACAtomSintEncode@
+@DOCSTRING YACAtomListEncode@
+@DOCSTRING YACAtomMapEncode@
+@DOCSTRING YACAtomBlobEncode@
+@DOCSTRING YACAtomStrEncode@
+@anchor{YACAtomBinEncode}
+@DOCSTRING YACAtomBinEncode@
+@anchor{YACAtomChunkEncode}
+@DOCSTRING YACAtomChunkEncode@
+@DOCSTRING YACAtomTAI64Encode@
+@DOCSTRING YACAtomRawEncode@
--- /dev/null
+#!/bin/sh -e
+
+rm -fr build cyac.info docstringer.log
--- /dev/null
+@node Commands
+@unnumbered Commands
+
+@table @command
+
+@pindex cmd/test-vector
+@item cmd/test-vector
+Example program that forms the same test vector as
+@file{tyac/test-vector.tcl} in a streaming way.
+
+@pindex cmd/print-itered
+@item cmd/test-itered
+Example program that decodes the provided file with iterated
+functions, that uses pretty printer as a callback.
+
+@pindex cmd/print-items
+@item cmd/test-items
+Example program that decodes the provided file into @ref{Items, items}
+and pretty prints it. It respects @env{$NO_COLOR} environment variable.
+If @env{$DO_ENCODE} is specified, then it encodes the decoded items into
+memory again and compares if it has the same representation.
+
+@pindex cmd/cer-verify
+@item cmd/test-verify
+Example program that accepts a list of certificate files. First one is
+that ought to be verified. Currently it installs GOST R 3410-2012
+cryptographic handlers for signature verification.
+
+@end table
--- /dev/null
+redo-ifchange docstringer.log
+cd build
+MAKEINFO_OPTS="$MAKEINFO_OPTS --set-customization-variable SECTION_NAME_IN_TITLE=1"
+MAKEINFO_OPTS="$MAKEINFO_OPTS --set-customization-variable TREE_TRANSFORMATIONS=complete_tree_nodes_menus"
+MAKEINFO_OPTS="$MAKEINFO_OPTS --set-customization-variable ASCII_PUNCTUATION=1"
+makeinfo $MAKEINFO_OPTS --output ../$3 index.texi
--- /dev/null
+redo-ifchange docstringer.pl *.texi ../lib/*.h ../lib/pki/*.h
+rm -rf build
+./docstringer.pl -v build ../lib .
--- /dev/null
+#!/usr/bin/env perl
+# Simple script for substitute placeholders in Texinfo files with
+# docstring values found in source code.
+#
+# If you C source code contains:
+#
+# // TEXINFO: SomeKey
+# // ...
+# // last line of docstring
+# some C code
+#
+# Then under "SomeKey" you will have the whole docstring (starting from
+# the line after "TEXINFO", till "last line"). You can include its
+# contents (excluding comment characters) in your .texi files placing
+#
+# @DOCSTRING SomeKey@
+
+use strict;
+use warnings;
+
+my $verbose = 0;
+if ($ARGV[0] eq "-v") {
+ $verbose = 1;
+ shift @ARGV;
+}
+my $outDir = $ARGV[0];
+my @srcDirs = split /:/, $ARGV[1];
+my @docDirs = split /:/, $ARGV[2];
+my @exts = split / /, (defined $ENV{EXTS}) ? $ENV{EXTS} : "c h h.in";
+
+my %docstrings;
+
+foreach my $srcDir (@srcDirs) {
+ print "src: $srcDir\n" if $verbose;
+ opendir(my $dir, $srcDir) or die "can not open $srcDir";
+ foreach my $fn (readdir $dir) {
+ next unless grep { $fn =~ /\.$_$/ } @exts;
+ open(my $src, "<:encoding(UTF-8)", "$srcDir/$fn") or
+ die "can not open $srcDir/$fn";
+ my $curEntry;
+ while (<$src>) {
+ chomp;
+ if (not /^\/\//) {
+ if (defined $curEntry) {
+ undef $curEntry;
+ }
+ next;
+ }
+ s/^\/\/ ?//;
+ if (/^TEXINFO: (.*)$/) {
+ $curEntry = $1;
+ $docstrings{$curEntry} = "";
+ print "\t$fn: $curEntry\n" if $verbose;
+ next;
+ }
+ ($docstrings{$curEntry} .= "$_\n") if defined $curEntry;
+ }
+ close $src;
+ }
+ closedir $dir;
+}
+
+foreach my $docDir (@docDirs) {
+ print "doc: $docDir\n" if $verbose;
+ opendir(my $dir, $docDir) or die "can not open $docDir";
+ ($docDir .= "/") unless $docDir =~ /\/$/;
+ ($docDir = "") if $docDir eq "./";
+ foreach my $fn (readdir $dir) {
+ next unless $fn =~ /\.texi$/;
+ $fn = $docDir . $fn;
+ open(my $src, "<:encoding(UTF-8)", $fn) or die "can not open $fn";
+ mkdir "$outDir/$docDir";
+ open(my $dst, ">:encoding(UTF-8)", "$outDir/$fn") or
+ die "can not open $outDir/$fn";
+ while (<$src>) {
+ (print($dst $_) and next) unless /^\s*\@DOCSTRING (.*)\@$/;
+ print "\t$fn: $1\n" if $verbose;
+ die "unable to find docstring: $1" unless defined $docstrings{$1};
+ print $dst $docstrings{$1};
+ }
+ close $src;
+ close $dst;
+ }
+ closedir $dir;
+}
--- /dev/null
+@node Errors
+@cindex errors
+@unnumbered Errors
+
+@DOCSTRING YACErr@
+
+@DOCSTRING YACErr2Str@
--- /dev/null
+\input texinfo
+@settitle cyac
+
+@copying
+Copyright @copyright{} 2024 @email{stargrave@@stargrave.org, Sergey Matveev}
+@end copying
+
+@node Top
+@top cyac
+
+C99 implementation of the @url{http://www.yac.cypherpunks.su, YAC}
+codec.
+
+@itemize
+@item No floats support.
+@item No unsigned integers support bigger than 64-bit.
+@item Negative integers are supported only up to 63-bits.
+@item No TAI64NA support.
+@end itemize
+
+cyac is
+@url{https://www.gnu.org/philosophy/pragmatic.html, copylefted}
+@url{https://www.gnu.org/philosophy/free-sw.html, free software}
+licenced under @url{https://www.gnu.org/licenses/lgpl-3.0.html, GNU LGPLv3}.
+
+@insertcopying
+
+@include install.texi
+@include cmd.texi
+
+@include err.texi
+@include atom.texi
+@include tai64.texi
+@include items.texi
+
+@node Indices
+@unnumbered Indices
+
+@node Concepts Index
+@section Concepts Index
+@printindex cp
+
+@node Programs Index
+@section Programs Index
+@printindex pg
+
+@node Variables Index
+@section Variables Index
+@printindex vr
+
+@bye
--- /dev/null
+@node Install
+@unnumbered Install
+
+Project uses @url{http://cr.yp.to/redo.html, redo} build system. There
+are plenty of implementation choices, but
+@url{http://www.goredo.cypherpunks.su, goredo} is recommended one.
+
+However it should be trivial to compile and install it manually, as it
+is ordinary C project.
+
+Whole configuration is in @file{conf/} directory, where you can override
+default command invocations and paths.
--- /dev/null
+@node Items
+@cindex items
+@unnumbered Items
+
+Streamable decoding by one atom is not very convenient in many cases.
+There is ability to recursively decode the whole structures.
+
+@anchor{YACItems}
+@DOCSTRING YACItems@
+@anchor{YACItem}
+@DOCSTRING YACItem@
+@DOCSTRING YACItemsInit@
+@anchor{YACItemsGrow}
+@DOCSTRING YACItemsGrow@
+@anchor{YACItemsParse}
+@DOCSTRING YACItemsParse@
+@DOCSTRING YACItemsEncode@
+@DOCSTRING YACItemsGetByKeyLen@
+@anchor{YACItemsGetByKey}
+@DOCSTRING YACItemsGetByKey@
+@DOCSTRING YACItemsGetByKeyAndType@
+@DOCSTRING YACStrEqual@
+@DOCSTRING YACListHasOnlyType@
+@DOCSTRING YACMapHasOnlyType@
--- /dev/null
+@node Datetime
+@cindex TAI64
+@cindex datetime
+@unnumbered Datetime
+
+YAC uses @url{http://cr.yp.to/libtai/tai64.html, TAI64} for datetime
+objects. There are helpers to convert it to UTC and vice versa.
+
+@DOCSTRING YACTAI64ToTimespec@
+@DOCSTRING YACTimespecToUTC@
+@DOCSTRING YACTimespecToTAI64@
+@DOCSTRING YACTimespecToTAI@
+@DOCSTRING YACLeapsecs@
#include "err.h"
+// TEXINFO: YACItemType
+// @deftp {Data type} {enum YACItemType}
+// High-level type of the atom.
+// @itemize
+// @item YACItemEOC
+// @item YACItemNIL
+// @item YACItemFalse
+// @item YACItemTrue
+// @item YACItemUUID
+// @item YACItemPint -- positive integer
+// @item YACItemNint -- negative integer
+// @item YACItemList
+// @item YACItemMap
+// @item YACItemBlob
+// @item YACItemFloat
+// @item YACItemTAI64
+// @item YACItemBin
+// @item YACItemStr
+// @item YACItemRaw -- raw value, non-decodable by the library
+// @end itemize
+// @end deftp
enum YACItemType {
YACItemEOC = 0,
YACItemNIL = 1,
YACItemRaw,
};
-// @deftypevar struct YACAtom
-// @code{.off}set is the length of the whole atom.
-// @code{.tag} contains the real type of the atom, its first byte.
-// @code{.typ} contains high-level atom type.
-// All other fields are interpreted according to the type:
+// TEXINFO: YACAtom
+// @deftp {Data type} {struct YACAtom}
+// Basic unit of the library, describing single TLV-like value.
// @table @code
-// @item YACItemEOC
-// No additional fields are used.
-// @item YACItemNIL
-// No additional fields are used.
-// @item YACItemFalse
-// No additional fields are used.
-// @item YACItemTrue
-// No additional fields are used.
-// @item YACItemUUID
-// @code{.v.uuid} contains the 16-byte UUID value.
-// @item YACItemPint
-// @code{.v.pint} contains positive integer's value.
-// @item YACItemNint
-// @code{.v.nint} contains negative integer's value.
-// @item YACItemList
-// No additional fields are used, if parsed through
-// @code{YACAtomDecode()}.
-// If parsed through the @code{YACItemsParse()}, then
-// @code{.v.list.len} contains the number of elements in a list.
-// @code{.v.list.head} is the items pool index of the first element.
-// @code{.v.list.head} equals to -1, if list is empty.
-// @item YACItemMap
-// No additional fields are used, if parsed through
-// @code{YACAtomDecode()}.
-// If parsed through the @code{YACItemsParse()}, then
-// @code{.v.list.len} contains the number of elements in a map,
-// @code{.v.list.head} is the items pool index of the first element's
-// key. Key's item @code{.next} points to the value, that points to
-// the next key, and so on.
-// @code{.v.list.head} equals to -1, if map is empty.
-// @item YACItemBlob
-// @code{.v.blob.chunkLen} contains the length of the chunk.
-// If parsed through the @code{YACItemsParse()}, then
-// @code{.v.blob.chunks} contains the number of chunks, including
-// the terminating binary string, that may be empty.
-// @item YACItemFloat
-// @code{.v.TODO} contains float's value.
-// @item YACItemTAI64
-// @code{.v.str.len} contains the length of the TAI64, that is
-// either 8, 12, or 16 bytes long. @code{.v.str.ptr} points to the
-// value itself.
-// @item YACItemBin
-// @code{.v.str.len} contains the length of the string.
-// @code{.v.str.ptr} points to the value itself.
-// @item YACItemStr
-// @code{.v.str.len} contains the length of the string.
-// @code{.v.str.ptr} points to the value itself.
-// @item YACItemRaw
-// @code{.tag} is the raw value's tag, its first byte.
-// @code{.v.str.len} contains the length of its value.
-// @code{.v.str.ptr} points to its value.
+// @item .tag
+// Real value of the atom's tag. May be used during debugging or
+// when constructing raw value.
+// @item .typ
+// High level @ref{YACItemType, type} of the atom's value. As a rule
+// you should look solely on it.
+// @item .v.uuid
+// Pointer to 16-byte UUID value.
+// @item .v.pint
+// Value of the positive integer.
+// @item .v.nint
+// Value of the negative integer.
+// @item .v.list
+// That value is filled only when dealing with @ref{Items, items}
+// for lists and maps.
+// Its @code{.head} is the index in items list to the first element
+// inside the container. If equals to 0, then it is empty. Its
+// @code{.len} contains number of the items (key-values pairs in
+// case of map) inside it.
+// @item .v.blob
+// That value is filled only when dealing with @ref{Items, items}.
+// @code{.chunkLen} is the length of the chunk. @code{.chunks} is
+// the number of chunks, including the terminating binary string.
+// @item .v.str
+// @code{.ptr} points to the start of the binary/UTF-8 string.
+// @code{.len} is its length in bytes. TAI64 datetimes are stored as
+// strings too. Raw values use it as a payload.
// @end table
-// @end deftypevar
+// @end deftp
struct YACAtom {
union {
const unsigned char *uuid;
char _pad[3];
};
+// TEXINFO: YACAtomDecode
+// @deftypefun {enum YACErr} YACAtomDecode( @
+// size_t *got, @
+// struct YACAtom *atom, @
+// const unsigned char *buf, @
+// const size_t len)
+// Decode the @ref{YACAtom, @var{atom}} from provided @var{buf} of
+// length @var{len}. If error is @code{YACErrNotEnough}, then @var{got}
+// will contain how many bytes is missing in the buffer for full atom
+// decoding. Otherwise it will contain the full atom's size.
+// @end deftypefun
enum YACErr
YACAtomDecode(
size_t *got,
- struct YACAtom *atom,
+ struct YACAtom *,
const unsigned char *buf,
const size_t len);
#include "leapsecs.h"
enum YACErr
-YACTAI64ToTimespec(struct timespec *tv, const unsigned char *buf, const size_t len)
+YACTAI64ToTimespec(struct timespec *ts, const unsigned char *buf, const size_t len)
{
if (len > 12) {
return YACErrTAI64BadAsec;
if (v <= 0) {
return YACErrTAI64InPast;
}
+ if (((uint64_t)1 << ((sizeof(time_t) * 8) - 1)) < (uint64_t)v) {
+ return YACErrTAI64TooBig;
+ }
+ ts->tv_sec = (time_t)v;
+ if (len > 8) {
+ uint32_t n = (uint32_t)yacFromBE(buf + 8, 4);
+ ts->tv_nsec = n;
+ }
+ return YACErrNo;
+}
+
+enum YACErr
+YACTimespecToUTC(struct timespec *ts)
+{
+ int64_t v = (int64_t)(ts->tv_sec);
{
int64_t diff = 0;
for (size_t i = 0; i < YACLeapsecsN; i++) {
if (v > (YACLeapsecs[i] + (int64_t)YACLeapsecsN - (int64_t)i)) {
- diff = YACLeapsecs1972 + (int64_t)YACLeapsecsN - (int64_t)i;
+ diff = 10 + (int64_t)YACLeapsecsN - (int64_t)i;
break;
}
}
if (v <= 0) {
return YACErrTAI64InPast;
}
- if (((uint64_t)1 << ((sizeof(time_t) * 8) - 1)) < (uint64_t)v) {
- return YACErrTAI64TooBig;
- }
- tv->tv_sec = (time_t)v;
- if (len > 8) {
- uint32_t n = (uint32_t)yacFromBE(buf + 8, 4);
- tv->tv_nsec = n;
- }
+ ts->tv_sec = (time_t)v;
return YACErrNo;
}
-
#ifndef YAC_DECTAI_H
#define YAC_DECTAI_H
#include "err.h"
+// TEXINFO: YACTAI64ToTimespec
+// @deftypefun {enum YACErr} YACTAI64ToTimespec( @
+// struct timespec *tv, const unsigned char *buf, const size_t len)
+// Convert TAI64* value from @var{buf} to @var{ts} structure.
+// @code{YACErrTAI64InPast} error is returned if value represents time
+// before 1970. Some systems support those dates, but not guaranteed to.
+// @code{YACErrTAI64TooBig} error is returned when time is out of bounds
+// of @code{time_t} representation. @code{YACErrTAI64BadAsec} is
+// returned if TAI64NA is provided, because currently no systems support
+// attoseconds.
+// @end deftypefun
+enum YACErr
+YACTAI64ToTimespec(struct timespec *ts, const unsigned char *buf, const size_t len);
+
+// TEXINFO: YACTimespecToUTC
+// @deftypefun {enum YACErr} YACTimespecToUTC(struct timespec *ts)
+// Convert TAI stored in @var{ts} structure to UTC. @code{YACErrTAI64InPast}
+// error is returned if value represents time before 1970.
+// @end deftypefun
enum YACErr
-YACTAI64ToTimespec(struct timespec *tv, const unsigned char *buf, const size_t len);
+YACTimespecToUTC(struct timespec *ts);
#endif // YAC_DECTAI_H
}
bool
-YACAtomUintEncode(size_t *len, unsigned char *buf, const size_t cap, uint64_t v)
+YACAtomUintEncode(size_t *len, unsigned char *buf, const size_t cap, const uint64_t v)
{
bool ok = yacAtomIntEncode(len, buf, cap, v);
if (ok) {
}
bool
-YACAtomSintEncode(size_t *len, unsigned char *buf, const size_t cap, int64_t v)
+YACAtomSintEncode(size_t *len, unsigned char *buf, const size_t cap, const int64_t v)
{
if (v >= 0) {
return YACAtomUintEncode(len, buf, cap, (uint64_t)v);
#include <stddef.h>
#include <stdint.h>
+// TEXINFO: YACAtomEOCEncode
+// @deftypefun bool YACAtomEOCEncode( @
+// size_t *len, unsigned char *buf, const size_t cap)
+// Encode EOC atom in provided @var{buf} with capacity of @var{cap}.
+// In case of success, true is returned and @var{len} will hold how many
+// bytes were written to buffer.
+// @end deftypefun
bool
YACAtomEOCEncode(size_t *len, unsigned char *buf, const size_t cap);
+// TEXINFO: YACAtomNILEncode
+// @deftypefun bool YACAtomNILEncode( @
+// size_t *len, unsigned char *buf, const size_t cap)
+// Encode NUL atom in provided @var{buf} with capacity of @var{cap}.
+// In case of success, true is returned and @var{len} will hold how many
+// bytes were written to buffer.
+// @end deftypefun
bool
YACAtomNILEncode(size_t *len, unsigned char *buf, const size_t cap);
+// TEXINFO: YACAtomBoolEncode
+// @deftypefun bool YACAtomBoolEncode( @
+// size_t *len, unsigned char *buf, const size_t cap, const bool v)
+// Encode boolean atom in provided @var{buf} with capacity of @var{cap}.
+// In case of success, true is returned and @var{len} will hold how many
+// bytes were written to buffer.
+// @end deftypefun
bool
YACAtomBoolEncode(size_t *len, unsigned char *buf, const size_t cap, const bool v);
+// TEXINFO: YACAtomUUIDEncode
+// @deftypefun bool YACAtomUUIDEncode( @
+// size_t *len, unsigned char *buf, const size_t cap, const unsigned char v[16])
+// Encode UUID atom in provided @var{buf} with capacity of @var{cap}.
+// In case of success, true is returned and @var{len} will hold how many
+// bytes were written to buffer.
+// @end deftypefun
bool
YACAtomUUIDEncode(
size_t *len,
const size_t cap,
const unsigned char v[16]);
+// TEXINFO: YACAtomUintEncode
+// @deftypefun bool YACAtomUintEncode( @
+// size_t *len, unsigned char *buf, const size_t cap, const uint64_t v)
+// Encode positive integer atom in provided @var{buf} with capacity of
+// @var{cap}.
+// In case of success, true is returned and @var{len} will hold how many
+// bytes were written to buffer.
+// @end deftypefun
bool
-YACAtomUintEncode(size_t *len, unsigned char *buf, const size_t cap, uint64_t v);
-
+YACAtomUintEncode(size_t *len, unsigned char *buf, const size_t cap, const uint64_t v);
+
+// TEXINFO: YACAtomSintEncode
+// @deftypefun bool YACAtomSintEncode( @
+// size_t *len, unsigned char *buf, const size_t cap, const int64_t v)
+// Encode signed integer atom in provided @var{buf} with capacity of
+// @var{cap}. If it is zero or positive, then it encodes unsigned one.
+// Negative one otherwise.
+// In case of success, true is returned and @var{len} will hold how many
+// bytes were written to buffer.
+// @end deftypefun
bool
-YACAtomSintEncode(size_t *len, unsigned char *buf, const size_t cap, int64_t v);
-
+YACAtomSintEncode(size_t *len, unsigned char *buf, const size_t cap, const int64_t v);
+
+// TEXINFO: YACAtomListEncode
+// @deftypefun bool YACAtomListEncode( @
+// size_t *len, unsigned char *buf, const size_t cap)
+// Encode LIST atom in provided @var{buf} with capacity of @var{cap}.
+// In case of success, true is returned and @var{len} will hold how many
+// bytes were written to buffer. Do not forget to add EOC atom at the end.
+// @end deftypefun
bool
YACAtomListEncode(size_t *len, unsigned char *buf, const size_t cap);
+// TEXINFO: YACAtomMapEncode
+// @deftypefun bool YACAtomMapEncode( @
+// size_t *len, unsigned char *buf, const size_t cap)
+// Encode MAP atom in provided @var{buf} with capacity of @var{cap}.
+// In case of success, true is returned and @var{len} will hold how many
+// bytes were written to buffer. Do not forget to add EOC atom at the end.
+// @end deftypefun
bool
YACAtomMapEncode(size_t *len, unsigned char *buf, const size_t cap);
+// TEXINFO: YACAtomBlobEncode
+// @deftypefun bool YACAtomBlobEncode( @
+// size_t *len, unsigned char *buf, const size_t cap, const size_t chunkLen)
+// Encode BLOB atom in provided @var{buf} with capacity of @var{cap}.
+// In case of success, true is returned and @var{len} will hold how many
+// bytes were written to buffer. You must call @ref{YACAtomChunkEncode}
+// functions for subsequent chunks, and terminate the blob with
+// @ref{YACAtomBinEncode}.
+// @end deftypefun
bool
YACAtomBlobEncode(
size_t *len,
const size_t cap,
const size_t chunkLen);
+// TEXINFO: YACAtomStrEncode
+// @deftypefun bool YACAtomStrEncode( @
+// size_t *len, unsigned char *buf, const size_t cap, @
+// const unsigned char *src, const size_t srcLen)
+// Encode STR atom in provided @var{buf} with capacity of @var{cap}.
+// In case of success, true is returned and @var{len} will hold how many
+// bytes were written to buffer.
+// @end deftypefun
bool
YACAtomStrEncode(
size_t *len,
const unsigned char *src,
const size_t srcLen);
+// TEXINFO: YACAtomBinEncode
+// @deftypefun bool YACAtomBinEncode( @
+// size_t *len, unsigned char *buf, const size_t cap, @
+// const unsigned char *src, const size_t srcLen)
+// Encode BIN atom in provided @var{buf} with capacity of @var{cap}.
+// In case of success, true is returned and @var{len} will hold how many
+// bytes were written to buffer.
+// @end deftypefun
bool
YACAtomBinEncode(
size_t *len,
const unsigned char *src,
const size_t srcLen);
+// TEXINFO: YACAtomChunkEncode
+// @deftypefun bool YACAtomChunkEncode( @
+// size_t *len, unsigned char *buf, const size_t cap, @
+// const unsigned char *src, const size_t srcLen)
+// Encode the chunk in provided @var{buf} with capacity of @var{cap}.
+// In case of success, true is returned and @var{len} will hold how many
+// bytes were written to buffer. It is just a convenient wrapper instead
+// of using @ref{YACAtomNILEncode} followed by @var{srcLen} bytes.
+// @end deftypefun
bool
YACAtomChunkEncode(
size_t *len,
const unsigned char *src,
const size_t srcLen);
+// TEXINFO: YACAtomTAI64Encode
+// @deftypefun bool YACAtomTAI64Encode( @
+// size_t *len, unsigned char *buf, const size_t cap, @
+// const unsigned char *src, const size_t srcLen)
+// Encode the TAI64* atom in provided @var{buf} with capacity of @var{cap}.
+// In case of success, true is returned and @var{len} will hold how many
+// bytes were written to buffer. You have to provide either 8, or 12, or
+// 16 byte string.
+// @end deftypefun
bool
YACAtomTAI64Encode(
size_t *len,
const unsigned char *src,
const size_t srcLen);
+// TEXINFO: YACAtomRawEncode
+// @deftypefun bool YACAtomRawEncode( @
+// size_t *len, unsigned char *buf, const size_t cap, @
+// const unsigned char typ, const unsigned char *src, const size_t srcLen)
+// Encode raw atom in provided @var{buf} with capacity of @var{cap}.
+// In case of success, true is returned and @var{len} will hold how many
+// bytes were written to buffer. It is just a convenient wrapper instead
+// of manual writing of the @code{typ} byte followed by @var{srcLen} bytes.
+// @end deftypefun
bool
YACAtomRawEncode(
size_t *len,
#include "tobe.h"
bool
-YACTimespecToTAI64(unsigned char *buf, const struct timespec *tv)
+YACTimespecToTAI64(unsigned char *buf, const struct timespec *ts)
{
- int64_t v = (int64_t)(tv->tv_sec);
- int64_t diff = YACLeapsecs1972;
+ int64_t v = (int64_t)(ts->tv_sec);
+ uint64_t val = (uint64_t)v + 0x4000000000000000;
+ if (val <= (uint64_t)v) {
+ return false;
+ }
+ yacToBE(buf, 8, val);
+ if (ts->tv_nsec != 0) {
+ yacToBE(buf + 8, 4, (uint64_t)(ts->tv_nsec));
+ }
+ return true;
+}
+
+bool
+YACTimespecToTAI(struct timespec *ts)
+{
+ int64_t v = (int64_t)(ts->tv_sec);
+ int64_t diff = 10;
for (size_t i = 0; i < YACLeapsecsN; i++) {
if (v > YACLeapsecs[i]) {
diff += YACLeapsecsN - i;
break;
}
}
- uint64_t val = (uint64_t)v + 0x4000000000000000 + (uint64_t)diff;
- if (val <= (uint64_t)v) {
+ v += diff;
+ if (((uint64_t)1 << ((sizeof(time_t) * 8) - 1)) < (uint64_t)v) {
return false;
}
- yacToBE(buf, 8, val);
- if (tv->tv_nsec != 0) {
- yacToBE(buf + 8, 4, (uint64_t)(tv->tv_nsec));
- }
+ ts->tv_sec = (time_t)v;
return true;
}
#include <stdbool.h>
#include <time.h>
+// TEXINFO: YACTimespecToTAI64
+// @deftypefun bool YACTimespecToTAI64( @
+// unsigned char *buf, const struct timespec *ts)
+// Convert @var{ts} structure to TAI64/TAI64N in the @var{buf}.
+// If @var{ts} contains non zero nanoseconds count, then be sure that
+// @var{buf} has enough capacity for 12-byte TAI64N value.
+// False is returned if represented time is out of allowable bounds.
+// @end deftypefun
bool
-YACTimespecToTAI64(unsigned char *buf, const struct timespec *tv);
+YACTimespecToTAI64(unsigned char *buf, const struct timespec *ts);
+
+// TEXINFO: YACTimespecToTAI
+// @deftypefun bool YACTimespecToTAI(struct timespec *ts)
+// Convert UTC stored in @var{ts} structure to TAI.
+// False is returned if represented time is out of allowable bounds.
+// @end deftypefun
+bool
+YACTimespecToTAI(struct timespec *ts);
#endif // YAC_ENCTAI_H
#ifndef YAC_ERR_H
#define YAC_ERR_H
-// @deftypevar enum YACErr
+// TEXINFO: YACErr
+// @deftp {Data type} {enum YACErr}
+// Errors are just a predefined enumeration value.
// @table @code
// @item YACErrInvalid
// Unset error, must be never met.
// @item YACErrUnsatisfiedSchema
// Unsatisfied structure's schema.
// @end table
-// @end deftypevar
+// @end deftp
enum YACErr {
YACErrInvalid = 0,
YACErrNo = 1,
YACErrUnsatisfiedSchema,
};
+// TEXINFO: YACErr2Str
+// @deftypefun {const char *} YACErr2Str(const enum YACErr)
+// Get human-readable string of the error.
+// @end deftypefun
const char *
YACErr2Str(const enum YACErr);
#include <stddef.h>
#include <stdint.h>
+// Decode big-endian integer of the length @var{len}.
uint64_t
yacFromBE(const unsigned char *buf, const size_t len);
}
enum YACErr
-yacItemsGrow(struct YACItems *items)
+YACItemsGrow(struct YACItems *items)
{
if (items->cap == -1) {
return YACErrNoMem;
{
enum YACErr err = YACErrInvalid;
if (items->len == (size_t)(items->cap)) {
- err = yacItemsGrow(items);
+ err = YACItemsGrow(items);
if (err != YACErrNo) {
return err;
}
#include "dec.h"
#include "err.h"
-// @deftypevar struct YACItem
-// Each item contains the atom structure. But item can be a part of the
-// list or map. @code{.next} contains the pool index value to the next
-// element of the list or map, following current one. It equals to -1,
-// then it is the last one.
-// Map is a list of pairs: first value is always a UTF-8 string with the
-// key name, next one is its value.
-// @code{.off} is the offset of item in the previously provided buffer.
+// TEXINFO: YACItem
+// @deftp {Data type} {struct YACItem}
+// Each item contain the @code{.atom} structure. But item can be a part
+// of the list or map, so @code{.next} contains the pool index value to
+// the next element of the list or map. It equals to 0, if it is the
+// last one.
//
-// Remember that @code{.next} of the list/map/blob is the (possible)
-// element after the whole list/map/blob. @code{.atom.val.first} is the
-// (possible) first element inside map/list.
+// Map is a list of pairs: first value is always an UTF-8 string with
+// the key name, next one is its value.
//
-// Blob's first element always present and it is the next binary string
-// item, until its length is less than chunk len.
-// @end deftypevar
+// Blob's first element always exists and it is the next binary string
+// item, until its length is less than chunk length.
+// @end deftp
struct YACItem {
size_t next;
struct YACAtom atom;
};
-// @deftypevar struct YACItems
-// Items pool contains concatenated @code{YACItem}s. Item's @{.next} can
-// be used as an index in that pool: @code{items->list[item.next]}.
-// @strong{Remember} that if there is not enough room for the next item,
-// then @code{.list} is reallocated, so previous pointers to the items
-// may become invalid! Using their indices will be safer.
-// @end deftypevar
+// TEXINFO: YACItems
+// @deftp {Data type} {struct YACItems}
+// Items pool @code{.list} contains concatenated @code{YACItem}s. Item's
+// @code{.next} can be used as an index in that pool:
+// @code{items->list[item.next]}.
+//
+// Items is just a list of possibly linked lists. You may insert an item
+// at arbitrary LIST place by appending it to the @code{.list} and by
+// modifying the necessary @code{.next} of the desired preceding
+// element.
+//
+// @code{.cap} and @code{.len} are capacity and length of the
+// @code{.list}. If you use @ref{YACItemsParse}, then it parses the
+// buffer and continuously appends new items to that list. If its length
+// matches with the capacity, then @ref{YACItemsGrow} is called to
+// enlarge the underlying buffer, possibly reallocating the whole of it.
+// So be careful with the pointers to the list's items, because they
+// could be invalidated after the growth. Using their indices instead
+// will be safer.
+//
+// Corresponding @code{.offsets} store the offset of the decoded item
+// relative to the previously provided buffer.
+// @end deftp
struct YACItems {
struct YACItem *list;
size_t *offsets;
ptrdiff_t cap;
};
+// TEXINFO: YACItemsInit
+// @deftypefun {enum YACErr} YACItemsInit(struct YACItems *items)
+// Initialise the @ref{YACItems} structure by allocating an initial
+// capacity for the underlying storage.
+//
+// If you do not want to use heap allocation and want to use
+// preallocated fixed buffer, then just point @code{.list} to it and set
+// the proper @code{.cap}acity.
+//
+// If you do not want to store items offsets during decoding, or you use
+// @ref{YACItems} only for encoding purposes, then set @code{.offsets}
+// to NULL (do not forget to free it, if it was initialised before).
+// @end deftypefun
enum YACErr
YACItemsInit(struct YACItems *);
+// TEXINFO: YACItemsGrow
+// @deftypefun {enum YACErr} YACItemsGrow(struct YACItems *items)
+// Enlarge underlying storage of items, increasing its capacity. If
+// @code{.cap} equals to -1, then nothing will happen and
+// @code{YACErrNoMem} error will be returned. You can use that -1 value
+// to omit attempts to call heap allocation functions.
+// @end deftypefun
enum YACErr
-yacItemsGrow(struct YACItems *);
+YACItemsGrow(struct YACItems *);
+// TEXINFO: YACItemsParse
+// @deftypefun {enum YACErr} YACItemsParse( @
+// struct YACItems *items, @
+// size_t *off, @
+// const unsigned char *buf, @
+// const size_t len)
+// Parse the provided @var{buf} appending newly created linked
+// @ref{YACItem}s in @var{items}. If there is not enough capacity
+// available, then @ref{YACItemsGrow} is automatically called. If
+// @code{.offsets} equal to NULL, then no offsets will be stored.
+//
+// @code{off}set is a position in @var{buf} from which the parsing will
+// be done. Its final value is the position when we stopped.
+// @end deftypefun
enum YACErr
YACItemsParse(
struct YACItems *,
const unsigned char *buf,
const size_t len);
+// TEXINFO: YACItemsEncode
+// @deftypefun bool YACItemsEncode( @
+// const struct YACItems *items, @
+// size_t idx, @
+// size_t *off, @
+// unsigned char *buf, @
+// const size_t cap)
+// Encode the previously filled @var{items} to the @var{buf}. @code{idx}
+// tells what exact item must be encoded. So you can easily encode only
+// the subpart of the @var{items}. @code{off} stored the resulting
+// length of the encoded data.
+// @end deftypefun
bool
YACItemsEncode(
const struct YACItems *,
unsigned char *buf,
const size_t cap);
+// TEXINFO: YACItemsGetByKeyLen
+// @deftypefun size_t YACItemsGetByKeyLen( @
+// const struct YACItems *items, @
+// const size_t itemIdx, @
+// const char *key, @
+// const size_t keyLen)
+// Get the index of the key with @var{key} name of @var{keyLen} length
+// in @var{items}'es element @var{itemIdx}. Returns zero if none found.
+// @end deftypefun
size_t
YACItemsGetByKeyLen(
const struct YACItems *,
const char *key,
const size_t keyLen);
+// TEXINFO: YACItemsGetByKey
+// @deftypefun size_t YACItemsGetByKey( @
+// const struct YACItems *items, @
+// const size_t itemIdx, @
+// const char *key)
+// Get the index of the null-terminated key with @var{key} name in
+// @var{items}'es element @var{itemIdx}. Returns zero if none found.
+// @end deftypefun
size_t
YACItemsGetByKey(const struct YACItems *, const size_t itemIdx, const char *key);
+// TEXINFO: YACItemsGetByKeyAndType
+// @deftypefun size_t YACItemsGetByKeyAndType( @
+// const struct YACItems *items, @
+// const size_t itemIdx, @
+// const char *key, @
+// const enum YACItemType typ)
+// Same as @ref{YACItemsGetByKey}, but also check that value's type is @var{typ}.
+// @end deftypefun
size_t
YACItemsGetByKeyAndType(
const struct YACItems *,
const char *key,
const enum YACItemType typ);
+// TEXINFO: YACStrEqual
+// @deftypefun bool YACStrEqual(const struct YACAtom *atom, const char *s)
+// Returns true if string atom's value equal to null-terminated @var{s}.
+// @end deftypefun
bool
YACStrEqual(const struct YACAtom *, const char *s);
+// TEXINFO: YACListHasOnlyType
+// @deftypefun bool YACListHasOnlyType( @
+// const struct YACItems *items, @
+// const size_t idx, @
+// const enum YACItemType typ)
+// Returns true if @var{idx} list in @var{items} contains only values
+// with the @var{typ} type.
+// @end deftypefun
bool
YACListHasOnlyType(const struct YACItems *, size_t idx, const enum YACItemType typ);
+// TEXINFO: YACMapHasOnlyType
+// @deftypefun bool YACMapHasOnlyType( @
+// const struct YACItems *items, @
+// const size_t idx, @
+// const enum YACItemType typ)
+// Returns true if @var{idx} map in @var{items} contains only values
+// with the @var{typ} type.
+// @end deftypefun
bool
YACMapHasOnlyType(const struct YACItems *, size_t idx, const enum YACItemType typ);
#include "leapsecs.h"
const size_t YACLeapsecsN = 27;
-const int64_t YACLeapsecs1972 = 10;
-int64_t YACLeapsecs[] = {
+const int64_t YACLeapsecs[] = {
1483228800, // 2017-01
1435708800, // 2015-07
1341100800, // 2012-07
#include <stddef.h>
#include <stdint.h>
+// TEXINFO: YACLeapsecs
+// @deftypevar {const int64_t *} YACLeapsecs
+// Leap seconds database. It contains TAI seconds when an additional
+// second was added. @var{YACLeapsecsN} variable holds the length of
+// that list.
+// @end deftypevar
extern const size_t YACLeapsecsN;
-extern const int64_t YACLeapsecs1972;
-extern int64_t YACLeapsecs[];
+extern const int64_t YACLeapsecs[];
#endif // YAC_LEAPSECS_H
#include <stddef.h>
#include <stdint.h>
+// Store big-endian integer of the length @var{len}.
void
yacToBE(unsigned char *buf, const size_t len, const uint64_t v);
Tcl implementation of the YAC encoder.
-* No FLOAT* support. They is stored just as a raw value
+* No FLOAT* support. They can be stored just as a raw value.
tyac is free software: see the file COPYING.LESSER for copying conditions.