dectai.* contains converter from TAI64 to UTC.
leapsecs.* contains the leap seconds database itself.
iter.* contains helpers that may pass over the iterables.
+pool.* 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
-/print
+/print-itered
+/print-pooled
/test-vector
-redo-ifchange print test-vector
+redo-ifchange print-itered print-pooled test-vector
#!/bin/sh -e
-exec rm -f print test-vector
+exec rm -f print-itered print-pooled test-vector
--- /dev/null
+#include <stdlib.h>
+
+static const size_t maxStrLen = 40;
+
+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;
+}
#include <yac/dec.h>
#include <yac/dectai.h>
+#include <yac/err.h>
#include <yac/iter.h>
-static const size_t maxStrLen = 40;
-
-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;
-}
+#include "hex.c.in"
struct CbState {
ptrdiff_t indent;
struct CbState *state = (struct CbState *)(cbState);
if ((atom->typ) == YACItemEOC) {
state->indent--;
- if (state->indent < 0) {
- return YACErrUnexpectedEOC;
- }
+ assert(state->indent >= 0);
}
printf("%04zd ", *off);
for (ptrdiff_t i = 0; i < state->indent; i++) {
free(hex);
break;
default:
- fprintf(stdout, "unknown atom\n");
+ fprintf(stderr, "unknown atom\n");
+ return EXIT_FAILURE;
}
return YACErrNo;
}
--- /dev/null
+// cyac -- C YAC encoder implementation
+// Copyright (C) 2024 Sergey Matveev <stargrave@stargrave.org>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation, version 3 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include <assert.h>
+#include <errno.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/err.h>
+#include <yac/pool.h>
+
+#include "hex.c.in"
+
+static void
+printIndent(const ptrdiff_t indent)
+{
+ for (ptrdiff_t i = 0; i < indent; i++) {
+ fputs(" ", stdout);
+ }
+}
+
+static enum YACErr
+printer(
+ const struct YACItemPool *pool,
+ ptrdiff_t idx,
+ ptrdiff_t indent,
+ ptrdiff_t listIdx,
+ const char *mapKey)
+{
+ struct YACItem *item = &(pool->list[idx]);
+ printf("%04zd ", item->off);
+ if (item->atom.typ == YACItemEOC) {
+ indent--;
+ assert(indent >= 0);
+ }
+ printIndent(indent);
+ if (listIdx >= 0) {
+ fprintf(stdout, "%zu: ", listIdx);
+ }
+ if (mapKey != NULL) {
+ fprintf(stdout, "%s: ", mapKey);
+ }
+ char *str = NULL;
+ enum YACErr err = YACErrInvalid;
+ switch (item->atom.typ) {
+ 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",
+ item->atom.val.buf[0],
+ item->atom.val.buf[1],
+ item->atom.val.buf[2],
+ item->atom.val.buf[3],
+ item->atom.val.buf[4],
+ item->atom.val.buf[5],
+ item->atom.val.buf[6],
+ item->atom.val.buf[7],
+ item->atom.val.buf[8],
+ item->atom.val.buf[9],
+ item->atom.val.buf[10],
+ item->atom.val.buf[11],
+ item->atom.val.buf[12],
+ item->atom.val.buf[13],
+ item->atom.val.buf[14],
+ item->atom.val.buf[15]);
+ break;
+ case YACItemUint:
+ fprintf(stdout, "%zu\n", item->atom.val.uint);
+ break;
+ case YACItemSint:
+ fprintf(stdout, "%zd\n", item->atom.val.sint);
+ break;
+ case YACItemList: {
+ printf("[ %zd\n", item->atom.len);
+ indent++;
+ idx = item->atom.val.first;
+ listIdx = 0;
+ while (idx != -1) {
+ err = printer(pool, idx, indent, listIdx, NULL);
+ if (err != YACErrNo) {
+ return err;
+ }
+ idx = pool->list[idx].next;
+ listIdx++;
+ }
+ fputs(" ", stdout);
+ indent--;
+ printIndent(indent);
+ fputs("]\n", stdout);
+ break;
+ }
+ case YACItemMap: {
+ printf("{ %zd\n", item->atom.len);
+ indent++;
+ idx = item->atom.val.first;
+ while (idx != -1) {
+ str = strndup(
+ (const char *)(pool->list[idx].atom.val.buf), pool->list[idx].atom.len);
+ idx = pool->list[idx].next;
+ err = printer(pool, idx, indent, -1, str);
+ free(str);
+ if (err != YACErrNo) {
+ return err;
+ }
+ idx = pool->list[idx].next;
+ }
+ fputs(" ", stdout);
+ indent--;
+ printIndent(indent);
+ fputs("}\n", stdout);
+ break;
+ }
+ case YACItemBlob:
+ printf("BLOB[ %zu l=%zu\n", item->atom.len, item->atom.val.uint);
+ indent++;
+ idx++;
+ listIdx = 0;
+ while (idx != -1) {
+ err = printer(pool, idx, indent, listIdx, NULL);
+ if (err != YACErrNo) {
+ return err;
+ }
+ idx = pool->list[idx].next;
+ listIdx++;
+ }
+ fputs(" ", stdout);
+ indent--;
+ printIndent(indent);
+ fputs("]\n", stdout);
+ break;
+ case YACItemFloat:
+ fputs("FLOAT: TODO\n", stdout);
+ break;
+ case YACItemTAI64: {
+ if ((item->atom.len) == 16) {
+ str = HexEnc(item->atom.val.buf, item->atom.len);
+ fprintf(stdout, "TAI64NA(%s)\n", str);
+ free(str);
+ break;
+ }
+ switch (item->atom.len) {
+ case 8:
+ fputs("TAI64(", stdout);
+ break;
+ case 12:
+ fputs("TAI64N(", stdout);
+ break;
+ }
+ struct timeval tv;
+ err = YACTAI64ToTimeval(&tv, item->atom.val.buf, item->atom.len);
+ if (err == YACErrTAI64BadNsec) {
+ str = HexEnc(item->atom.val.buf, item->atom.len);
+ fprintf(stdout, "unrepresentable: %s)\n", str);
+ free(str);
+ break;
+ }
+ if (err != YACErrNo) {
+ return err;
+ }
+ time_t t = tv.tv_sec;
+ struct tm *tm = localtime(&t);
+ if (tm == NULL) {
+ str = HexEnc(item->atom.val.buf, item->atom.len);
+ fprintf(stdout, "unrepresentable: %s)\n", str);
+ free(str);
+ break;
+ }
+ char human[20] = {0};
+ strftime(human, sizeof human, "%Y-%m-%d %H:%M:%S", tm);
+ fputs(human, stdout);
+ if ((item->atom.len) == 12) {
+ fprintf(stdout, ".%zu", tv.tv_usec);
+ }
+ fputs(")\n", stdout);
+ break;
+ }
+ case YACItemBin: {
+ const size_t l = (item->atom.len > maxStrLen) ? maxStrLen : item->atom.len;
+ str = HexEnc(item->atom.val.buf, l);
+ fprintf(
+ stdout,
+ "%zu:%s%s\n",
+ item->atom.len,
+ str,
+ (item->atom.len > maxStrLen) ? "..." : "");
+ free(str);
+ break;
+ }
+ case YACItemStr: {
+ const size_t l = (item->atom.len > maxStrLen) ? maxStrLen : item->atom.len;
+ str = strndup((const char *)item->atom.val.buf, l);
+ fprintf(stdout, "\"%s%s\"\n", str, (item->atom.len > maxStrLen) ? "..." : "");
+ free(str);
+ break;
+ }
+ case YACItemRaw:
+ str = HexEnc(item->atom.val.buf, item->atom.len);
+ fprintf(stdout, "(t=0x%X l=%zu v=%s)\n", item->atom.tag, item->atom.len, str);
+ free(str);
+ break;
+ case YACItemEOC:
+ default:
+ fprintf(stderr, "unknown atom\n");
+ return EXIT_FAILURE;
+ }
+ 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);
+ if (fd == -1) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+ struct stat sb;
+ memset(&sb, 0, sizeof(struct stat));
+ if (fstat(fd, &sb) != 0) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ return EXIT_FAILURE;
+ }
+ len = (size_t)sb.st_size;
+ buf = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
+ }
+
+ struct YACItemPool pool;
+ YACItemPoolInit(&pool);
+ ptrdiff_t off = 0;
+ enum YACErr err = YACItemPoolParse(&pool, &off, buf, len);
+ if (err != YACErrNo) {
+ fprintf(stderr, "err: %d\n", err);
+ return EXIT_FAILURE;
+ }
+ err = printer(&pool, 0, 0, -1, NULL);
+ if (err != YACErrNo) {
+ fprintf(stderr, "err: %d\n", err);
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
#include "atoms.h"
#include "dec.h"
+#include "err.h"
#include "frombe.h"
#include "utf8.h"
return YACErrNotEnough;
}
if (buf[1] == 0) {
- return YACErrIntZeroByte;
+ return YACErrIntNonMinimal;
}
if (l > 8) {
atom->typ = YACItemRaw;
#include <stddef.h>
#include <stdint.h>
+#include "err.h"
+
enum YACItemType {
YACItemEOC = 0,
YACItemNIL = 1,
YACItemFalse,
YACItemTrue,
- YACItemUUID, // atom.val.buf
- YACItemUint, // atom.val.uint
- YACItemSint, // atom.val.sint
+ YACItemUUID,
+ YACItemUint,
+ YACItemSint,
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
- 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
- YACErrLenTooBig, // string length >1<<60
- 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
+ YACItemFloat,
+ YACItemTAI64,
+ YACItemBin,
+ YACItemStr,
+ 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:
+// @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{.val.buf} contains the 16-byte UUID value.
+// @item YACItemUint
+// @code{.val.uint} contains positive integer's value.
+// @item YACItemSint
+// @code{.val.sint} contains negative integer's value.
+// @item YACItemList
+// No additional fields are used, if parsed through
+// @code{YACAtomDecode()}.
+// If parsed through the @code{YACItemPoolParse()}, then
+// @code{.len} contains the number of elements in a list,
+// @code{.val.first} is the pool index of the first element.
+// It equals to -1, if list is empty.
+// @item YACItemMap
+// No additional fields are used, if parsed through
+// @code{YACAtomDecode()}.
+// If parsed through the @code{YACItemPoolParse()}, then
+// @code{.len} contains the number of elements in a map,
+// @code{.val.first} is the 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{.val.first} equals to -1, if map is empty.
+// @item YACItemBlob
+// @code{.val.uint} contains the length of the chunk.
+// If parsed through the @code{YACItemPoolParse()}, then
+// @code{.len} contains the number of chunks, including the
+// terminating binary string, that may be empty.
+// @code{.val.first} is the pool index of the first chunk.
+// @item YACItemFloat
+// @code{.val.TODO} contains float's value.
+// @item YACItemTAI64
+// @code{.len} contains the length of the TAI64, that is either 8,
+// 12, or 16 bytes long. @code{.val.buf} points to the value itself.
+// @item YACItemBin
+// @code{.len} contains the length of the string.
+// @code{.val.buf} points to the value itself.
+// @item YACItemStr
+// @code{.len} contains the length of the string.
+// @code{.val.buf} points to the value itself.
+// @item YACItemRaw
+// @code{.tag} is the raw value's tag, its first byte.
+// @code{.len} contains the length of its value.
+// @code{.val.buf} points to its value.
+// @end table
+// @end deftypevar
struct YACAtom {
- ptrdiff_t off; // length of the whole atom
- size_t len; // length of the strings, TAI64, raw values
+ ptrdiff_t off;
+ size_t len;
union {
- uint64_t uint; // unsigned integer's value, blob's chunk len
- int64_t sint; // negative signed integer's value
- const unsigned char *buf; // strings, TAI64, UUID value
+ uint64_t uint;
+ int64_t sint;
+ const unsigned char *buf;
+ ptrdiff_t first;
} val;
- enum YACItemType typ; // type of the item, consolidated
- unsigned char tag; // real type of the atom
+ enum YACItemType typ;
+ unsigned char tag;
char _pad[3];
};
#include <stdint.h>
#include <sys/time.h>
-#include "dec.h"
#include "dectai.h"
+#include "err.h"
#include "frombe.h"
#include "leapsecs.h"
#include <stddef.h>
#include <sys/time.h>
-#include "dec.h"
+#include "err.h"
enum YACErr
YACTAI64ToTimeval(struct timeval *tv, const unsigned char *buf, const size_t len);
-redo-ifchange $2.c $2.h conf/cc conf/cflags
+redo-ifchange $2.c $(./h-extract.pl $2.[ch]) conf/cc conf/cflags
read CC <conf/cc
CFLAGS=$(cat conf/cflags)
$CC $CFLAGS -c -o $3 $2.c
--- /dev/null
+#ifndef YAC_ERR_H
+#define YAC_ERR_H
+
+// @deftypevar enum YACErr
+// @table @code
+// @item YACErrInvalid
+// Unset error, must be never met.
+// @item YACErrNo
+// No error, everything is good.
+// @item YACErrNotEnough
+// Not enough data. Atom's @code{.off} must contain how much.
+// @item YACErrUnknownType, // unknown atom's type
+// Unknown atom's type.
+// @item YACErrLenNonMinimal
+// Non minimal string's length encoding.
+// @item YACErrLenTooBig,
+// Too long string (>1<<60), can not be decoded.
+// @item YACErrBadUTF8
+// Invalid UTF-8 codepoint or zero byte met.
+// @item YACErrIntNonMinimal
+// Non minimal integer encoding.
+// @item YACErrBlobBadLen
+// Blob with invalid chunk length.
+// @item YACErrBlobBadAtom
+// Blob contains unexpected atom.
+// @item YACErrBlobBadTerm
+// Blob contains invalid terminator.
+// @item YACErrTAI64TooBig
+// Too large TAI64 value, out-of-bounds.
+// @item YACErrTAI64BadNsec
+// Invalid TAI64 nanoseconds value.
+// @item YACErrTAI64BadAsec
+// Invalid TAI64 attoseconds value.
+// @item YACErrMapBadKey
+// Either bad type of map's key, or it is empty.
+// @item YACErrMapNoVal
+// Missing value in a map.
+// @item YACErrMapUnordered
+// Unordered map keys.
+// @item YACErrNoMem
+// Not enough memory for allocation.
+// @end table
+// @end deftypevar
+enum YACErr {
+ YACErrInvalid = 0,
+ YACErrNo = 1,
+ YACErrNotEnough,
+ YACErrUnknownType,
+ YACErrLenNonMinimal,
+ YACErrLenTooBig,
+ YACErrBadUTF8,
+ YACErrIntZeroByte,
+ YACErrIntNonMinimal,
+ YACErrBlobBadLen,
+ YACErrBlobBadAtom,
+ YACErrBlobBadTerm,
+ YACErrTAI64TooBig,
+ YACErrTAI64BadNsec,
+ YACErrTAI64BadAsec,
+ YACErrMapBadKey,
+ YACErrMapNoVal,
+ YACErrMapUnordered,
+ YACErrNoMem,
+};
+
+#endif // YAC_ERR_H
--- /dev/null
+#!/usr/bin/env perl
+# Extract all locally included header files (not <>-ones, but "")
+
+# hack, to badly exit if there is unexistent file
+$SIG{__WARN__} = sub { die @_ };
+
+map { $inc{$_} = 1 } @ARGV;
+while (<>) {
+ /^#include "([^\/]+)"$/ and ($1 !~ /\.in$/) and $inc{$1} = 1;
+};
+print join " ", sort keys %inc;
#include <string.h>
#include "dec.h"
+#include "err.h"
#include "iter.h"
enum YACErr
switch (atom->typ) {
case YACItemNIL:
if (((ptrdiff_t)len - *off) <= (ptrdiff_t)chunkLen) {
- return YACErrBlobShortChunk;
+ atom->off = (ptrdiff_t)chunkLen;
+ return YACErrNotEnough;
}
atom->typ = YACItemBin;
atom->val.buf = buf + *off;
#include <stddef.h>
#include "dec.h"
+#include "err.h"
typedef enum YACErr (*YACIterCb)(
const unsigned char *key,
frombe.o
iter.o
leapsecs.o
+pool.o
tobe.o
utf8.o
--- /dev/null
+// cyac -- C YAC encoder implementation
+// Copyright (C) 2024 Sergey Matveev <stargrave@stargrave.org>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation, version 3 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dec.h"
+#include "err.h"
+#include "pool.h"
+
+static const ptrdiff_t yacPoolChunkLen = 32;
+
+enum YACErr
+YACItemPoolInit(struct YACItemPool *pool)
+{
+ pool->len = 0;
+ pool->cap = yacPoolChunkLen;
+ pool->list = calloc((size_t)(pool->cap), sizeof(struct YACItem));
+ if (pool->list == NULL) {
+ return YACErrNoMem;
+ }
+ return YACErrNo;
+}
+
+enum YACErr
+YACItemPoolGrow(struct YACItemPool *pool)
+{
+ pool->cap += yacPoolChunkLen;
+ if (pool->cap <= 0) {
+ return YACErrNoMem;
+ }
+ const size_t size = (size_t)(pool->cap) * sizeof(struct YACItem);
+ pool->list = realloc(pool->list, size);
+ if (pool->list == NULL) {
+ return YACErrNoMem;
+ }
+ const size_t begin = (size_t)(pool->len) * sizeof(struct YACItem);
+ memset((unsigned char *)(pool->list) + begin, 0, size - begin);
+ return YACErrNo;
+}
+
+static enum YACErr
+yacItemPoolAdd(
+ struct YACItemPool *pool,
+ ptrdiff_t *off,
+ const unsigned char *buf,
+ const size_t len)
+{
+ enum YACErr err = YACErrInvalid;
+ if (pool->len == pool->cap) {
+ err = YACItemPoolGrow(pool);
+ if (err != YACErrNo) {
+ return err;
+ }
+ }
+ struct YACItem *item = &(pool->list[pool->len]);
+ item->next = -1;
+ item->off = *off;
+ err = YACAtomDecode(&(item->atom), buf + *off, len - (size_t)(*off));
+ if (err != YACErrNo) {
+ return err;
+ }
+ (*off) += item->atom.off;
+ pool->len++;
+ if (pool->len <= 0) {
+ return YACErrNoMem;
+ }
+ return YACErrNo;
+}
+
+enum YACErr
+YACItemPoolParse(
+ struct YACItemPool *pool,
+ ptrdiff_t *off,
+ const unsigned char *buf,
+ const size_t len)
+{
+ ptrdiff_t item = pool->len;
+ enum YACErr err = yacItemPoolAdd(pool, off, buf, len);
+ if (err != YACErrNo) {
+ return err;
+ }
+ switch (pool->list[item].atom.typ) {
+ case YACItemList: {
+ pool->list[item].atom.val.first = item + 1;
+ pool->list[item].atom.len = 0;
+ ptrdiff_t prev = -1;
+ ptrdiff_t cur = -1;
+ ptrdiff_t idx = item;
+ for (;;) {
+ err = YACItemPoolParse(pool, off, buf, len);
+ if (err != YACErrNo) {
+ return err;
+ }
+ cur = idx + 1;
+ if (pool->list[cur].atom.typ == YACItemEOC) {
+ if (pool->list[item].atom.len == 0) {
+ pool->list[item].atom.val.first = -1;
+ }
+ return YACErrNo;
+ }
+ if (prev != -1) {
+ pool->list[prev].next = cur;
+ }
+ prev = cur;
+ idx = (pool->len) - 1;
+ pool->list[item].atom.len++;
+ }
+ }
+ case YACItemMap: {
+ pool->list[item].atom.val.first = item + 1;
+ pool->list[item].atom.len = 0;
+ ptrdiff_t idx = item;
+ ptrdiff_t prev = -1;
+ ptrdiff_t cur = -1;
+ size_t prevKeyLen = 0;
+ const unsigned char *prevKey = NULL;
+ for (;;) {
+ err = YACItemPoolParse(pool, off, buf, len);
+ if (err != YACErrNo) {
+ return err;
+ }
+ cur = idx + 1;
+ switch (pool->list[cur].atom.typ) {
+ case YACItemEOC:
+ if (pool->list[item].atom.len == 0) {
+ pool->list[item].atom.val.first = -1;
+ }
+ return YACErrNo;
+ case YACItemStr:
+ break;
+ case YACItemNIL:
+ case YACItemFalse:
+ case YACItemTrue:
+ case YACItemUUID:
+ case YACItemUint:
+ case YACItemSint:
+ case YACItemList:
+ case YACItemMap:
+ case YACItemBlob:
+ case YACItemFloat:
+ case YACItemTAI64:
+ case YACItemBin:
+ case YACItemRaw:
+ default:
+ return YACErrMapBadKey;
+ }
+ if (pool->list[cur].atom.len == 0) {
+ return YACErrMapBadKey;
+ }
+ if (pool->list[cur].atom.len < prevKeyLen) {
+ return YACErrMapUnordered;
+ }
+ if ((pool->list[cur].atom.len == prevKeyLen) &&
+ (memcmp(prevKey, pool->list[cur].atom.val.buf, prevKeyLen) >= 0)) {
+ return YACErrMapUnordered;
+ }
+ prevKeyLen = pool->list[cur].atom.len;
+ prevKey = pool->list[cur].atom.val.buf;
+ if (prev != -1) {
+ pool->list[prev].next = cur;
+ }
+ prev = cur;
+ idx = (pool->len) - 1;
+ err = YACItemPoolParse(pool, off, buf, len);
+ if (err != YACErrNo) {
+ return err;
+ }
+ cur = idx + 1;
+ if (pool->list[cur].atom.typ == YACItemEOC) {
+ return YACErrMapNoVal;
+ }
+ pool->list[prev].next = cur;
+ prev = cur;
+ idx = (pool->len) - 1;
+ pool->list[item].atom.len++;
+ }
+ }
+ case YACItemBlob: {
+ pool->list[item].atom.len = 0;
+ const ptrdiff_t chunkLen = (ptrdiff_t)(pool->list[item].atom.val.uint);
+ ptrdiff_t idx = item;
+ ptrdiff_t prev = -1;
+ ptrdiff_t cur = -1;
+ bool eoc = false;
+ while (!eoc) {
+ err = YACItemPoolParse(pool, off, buf, len);
+ if (err != YACErrNo) {
+ return err;
+ }
+ cur = idx + 1;
+ struct YACAtom *atom = &(pool->list[cur].atom);
+ switch (atom->typ) {
+ case YACItemNIL:
+ if (((ptrdiff_t)len - *off) <= chunkLen) {
+ atom->off = chunkLen;
+ return YACErrNotEnough;
+ }
+ atom->typ = YACItemBin;
+ atom->val.buf = buf + *off;
+ atom->len = (size_t)chunkLen;
+ (*off) += chunkLen;
+ break;
+ case YACItemBin:
+ if ((ptrdiff_t)(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 YACItemRaw:
+ default:
+ return YACErrBlobBadAtom;
+ }
+ if (prev != -1) {
+ pool->list[prev].next = cur;
+ }
+ prev = cur;
+ idx = (pool->len) - 1;
+ pool->list[item].atom.len++;
+ }
+ break;
+ }
+ case YACItemEOC:
+ case YACItemNIL:
+ case YACItemFalse:
+ case YACItemTrue:
+ case YACItemUUID:
+ case YACItemUint:
+ case YACItemSint:
+ case YACItemFloat:
+ case YACItemTAI64:
+ case YACItemBin:
+ case YACItemStr:
+ case YACItemRaw:
+ default:
+ break;
+ }
+ return YACErrNo;
+}
--- /dev/null
+#ifndef YAC_POOL_H
+#define YAC_POOL_H
+
+#include <stddef.h>
+
+#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.
+//
+// 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 those containers.
+// @end deftypevar
+struct YACItem {
+ ptrdiff_t next;
+ ptrdiff_t off;
+ struct YACAtom atom;
+};
+
+// @deftypevar struct YACItemPool
+// Pool contains concatenated @code{YACItem}s. Item's @{.next} can be
+// used as an index in that pool: @code{pool->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
+struct YACItemPool {
+ struct YACItem *list;
+ ptrdiff_t len;
+ ptrdiff_t cap;
+};
+
+enum YACErr
+YACItemPoolInit(struct YACItemPool *);
+
+enum YACErr
+YACItemPoolGrow(struct YACItemPool *);
+
+enum YACErr
+YACItemPoolParse(
+ struct YACItemPool *,
+ ptrdiff_t *off,
+ const unsigned char *buf,
+ const size_t len);
+
+#endif // YAC_POOL_H