From 7768d1c578e77d8df44314575d93b54b5dcf4c60bd7fca8b40b8fa660c98add5 Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Fri, 27 Dec 2024 15:31:50 +0300 Subject: [PATCH] Unify Go and C printers output --- c/cmd/lib/printai.c | 49 ++++++--- c/cmd/print-items/print-items.c | 83 ++++++-------- c/cmd/print-itered/print-itered.c | 2 +- c/cmd/test-vector/test-vector.c | 177 +++++++++++++++++------------- c/doc/atom.texi | 1 - c/lib/dec.c | 35 +++--- c/lib/dec.h | 6 +- c/lib/enc.c | 22 ---- c/lib/enc.h | 18 --- c/lib/items.c | 13 +-- go/cmd/iter-print/main.go | 119 -------------------- go/cmd/print/colour.go | 35 ++++++ go/cmd/print/main.go | 141 +++++++++++++++++++++++- go/go.mod | 5 + go/go.sum | 4 + 15 files changed, 372 insertions(+), 338 deletions(-) delete mode 100644 go/cmd/iter-print/main.go create mode 100644 go/cmd/print/colour.go diff --git a/c/cmd/lib/printai.c b/c/cmd/lib/printai.c index 7d6ef8a..a591488 100644 --- a/c/cmd/lib/printai.c +++ b/c/cmd/lib/printai.c @@ -9,6 +9,19 @@ #include "hex.h" #include "printai.h" +static void +printDT(const struct tm *tm, const struct timespec *tv, const size_t len) +{ + { + char human[20] = {0}; + strftime(human, sizeof human, "%Y-%m-%d %H:%M:%S", tm); + fputs(human, stdout); + } + if (len == 12) { + fprintf(stdout, ".%09zu", tv->tv_nsec); + } +} + enum KEKSErr PrintTAI64(const unsigned char *buf, const size_t len) { @@ -45,6 +58,16 @@ PrintTAI64(const unsigned char *buf, const size_t len) default: return err; } + time_t t = tv.tv_sec; + struct tm *tm = localtime(&t); + if (tm == NULL) { + hex = HexEnc(buf, len); + fprintf(stdout, "TAI unrepresentable: %s)\n", hex); + free(hex); + return KEKSErrNo; + } + printDT(tm, &tv, len); + fputs(" TAI, ", stdout); err = KEKSTimespecToUTC(&tv); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wswitch-enum" @@ -53,29 +76,21 @@ PrintTAI64(const unsigned char *buf, const size_t len) case KEKSErrNo: break; case KEKSErrTAI64InPast: - hex = HexEnc(buf, len); - fprintf(stdout, "in past: %s)\n", hex); - free(hex); + fputs("UTC in past)", stdout); + return KEKSErrNo; + case KEKSErrTAI64IsLeap: + fputs("leap)", stdout); return KEKSErrNo; default: return err; } - time_t t = tv.tv_sec; - struct tm *tm = localtime(&t); + t = tv.tv_sec; + tm = localtime(&t); if (tm == NULL) { - hex = HexEnc(buf, len); - fprintf(stdout, "unrepresentable: %s)\n", hex); - free(hex); + fputs("UTC unrepresentable)", stdout); return KEKSErrNo; } - { - char human[20] = {0}; - strftime(human, sizeof human, "%Y-%m-%d %H:%M:%S", tm); - fputs(human, stdout); - } - if (len == 12) { - fprintf(stdout, ".%09zu", tv.tv_nsec); - } - fputs(")\n", stdout); + printDT(tm, &tv, len); + fputs(" UTC)\n", stdout); return KEKSErrNo; } diff --git a/c/cmd/print-items/print-items.c b/c/cmd/print-items/print-items.c index 4df2842..379ae23 100644 --- a/c/cmd/print-items/print-items.c +++ b/c/cmd/print-items/print-items.c @@ -33,17 +33,16 @@ #include "../lib/printai.h" #include "../lib/uuid.h" -static const char *ColourRed = "\x1b[31m"; -static const char *ColourGreen = "\x1b[32m"; -static const char *ColourYellow = "\x1b[33m"; -static const char *ColourBlue = "\x1b[34m"; -static const char *ColourMagenta = "\x1b[35m"; -static const char *ColourCyan = "\x1b[36m"; -static const char *ColourWhite = "\x1b[37m"; -static const char *ColourReset = "\x1b[0m"; +static char *ColourRed = "\x1b[31m"; +static char *ColourGreen = "\x1b[32m"; +static char *ColourYellow = "\x1b[33m"; +static char *ColourBlue = "\x1b[34m"; +static char *ColourMagenta = "\x1b[35m"; +static char *ColourCyan = "\x1b[36m"; +static char *ColourWhite = "\x1b[37m"; +static char *ColourReset = "\x1b[0m"; static size_t MaxStrLen = 40; -static bool NoColour = false; static bool NoOffsets = false; static int OffDigits = 0; static char OffFmt[16] = {0}; @@ -82,36 +81,16 @@ printer( // NOLINT(misc-no-recursion) if (NoOffsets) { fputs(" ", stdout); } else { - fprintf( - stdout, - "%s%zd%s ", - NoColour ? "" : ColourBlue, - indent, - NoColour ? "" : ColourReset); - fprintf( - stdout, - OffFmt, - NoColour ? "" : ColourRed, - items->offsets[idx], - NoColour ? "" : ColourReset); + fprintf(stdout, "%s%zd%s ", ColourBlue, indent, ColourReset); + fprintf(stdout, OffFmt, ColourRed, items->offsets[idx], ColourReset); } struct KEKSItem *item = &(items->list[idx]); printIndent(indent); if (inList) { - fprintf( - stdout, - "%s%zu:%s ", - NoColour ? "" : ColourYellow, - listIdx, - NoColour ? "" : ColourReset); + fprintf(stdout, "%s%zu:%s ", ColourYellow, listIdx, ColourReset); } if (mapKey != NULL) { - fprintf( - stdout, - "%s%s:%s ", - NoColour ? "" : ColourGreen, - mapKey, - NoColour ? "" : ColourReset); + fprintf(stdout, "%s%s:%s ", ColourGreen, mapKey, ColourReset); } char *str = NULL; enum KEKSErr err = KEKSErrInvalid; @@ -139,12 +118,7 @@ printer( // NOLINT(misc-no-recursion) fprintf(stdout, "%zd\n", item->atom.v.nint); break; case KEKSItemList: { - fprintf( - stdout, - "[ %s%zd%s\n", - NoColour ? "" : ColourCyan, - item->atom.v.list.len, - NoColour ? "" : ColourReset); + fprintf(stdout, "[ %s%zd%s\n", ColourCyan, item->atom.v.list.len, ColourReset); indent++; idx = item->atom.v.list.head; listIdx = 0; @@ -165,12 +139,7 @@ printer( // NOLINT(misc-no-recursion) break; } case KEKSItemMap: { - fprintf( - stdout, - "{ %s%zd%s\n", - NoColour ? "" : ColourCyan, - item->atom.v.list.len, - NoColour ? "" : ColourReset); + fprintf(stdout, "{ %s%zd%s\n", ColourCyan, item->atom.v.list.len, ColourReset); indent++; idx = item->atom.v.list.head; while (idx != 0) { @@ -197,10 +166,10 @@ printer( // NOLINT(misc-no-recursion) fprintf( stdout, "BLOB[ %s%zu l=%zu%s\n", - NoColour ? "" : ColourCyan, + ColourCyan, item->atom.v.blob.chunks, item->atom.v.blob.chunkLen, - NoColour ? "" : ColourReset); + ColourReset); indent++; idx++; listIdx = 0; @@ -236,9 +205,9 @@ printer( // NOLINT(misc-no-recursion) fprintf( stdout, "%s%zu:%s%s%s\n", - NoColour ? "" : ColourMagenta, + ColourMagenta, item->atom.v.str.len, - NoColour ? "" : ColourReset, + ColourReset, str, (item->atom.v.str.len > MaxStrLen) ? "..." : ""); free(str); @@ -255,8 +224,7 @@ printer( // NOLINT(misc-no-recursion) } case KEKSItemRaw: str = HexEnc(item->atom.v.str.ptr, item->atom.v.str.len); - fprintf( - stdout, "(t=0x%X l=%zu v=%s)\n", item->atom.tag, item->atom.v.str.len, str); + fprintf(stdout, "(l=%zu v=%s)\n", item->atom.v.str.len, str); free(str); break; case KEKSItemEOC: @@ -274,7 +242,6 @@ main(int argc, char **argv) bool noTotals = false; bool onlyTotals = false; ptrdiff_t itemsInitialLen = 2048; - NoColour = getenv("NO_COLOR") != NULL; struct option longopts[] = { {"max-str-len", required_argument, NULL, 'a'}, {"do-encode", no_argument, NULL, 'b'}, @@ -341,6 +308,17 @@ main(int argc, char **argv) usage(); } + if (getenv("NO_COLOR") != NULL) { + ColourRed = ""; + ColourGreen = ""; + ColourYellow = ""; + ColourBlue = ""; + ColourMagenta = ""; + ColourCyan = ""; + ColourWhite = ""; + ColourReset = ""; + } + size_t len = 0; unsigned char *buf = NULL; if (!Mmap(&buf, &len, argv[0])) { @@ -378,6 +356,7 @@ main(int argc, char **argv) return EXIT_FAILURE; } if (!onlyTotals) { + setenv("TZ", "UTC", 1); err = printer(&items, 0, 0, false, 0, NULL); if (err != KEKSErrNo) { fprintf(stderr, "err: %s\n", KEKSErr2Str(err)); diff --git a/c/cmd/print-itered/print-itered.c b/c/cmd/print-itered/print-itered.c index 1cc13ac..80520ef 100644 --- a/c/cmd/print-itered/print-itered.c +++ b/c/cmd/print-itered/print-itered.c @@ -146,7 +146,7 @@ myCb( } case KEKSItemRaw: hex = HexEnc(atom->v.str.ptr, atom->v.str.len); - fprintf(stdout, "(t=0x%X l=%zu v=%s)\n", atom->tag, atom->v.str.len, hex); + fprintf(stdout, "(l=%zu v=%s)\n", atom->v.str.len, hex); free(hex); break; default: diff --git a/c/cmd/test-vector/test-vector.c b/c/cmd/test-vector/test-vector.c index 3eebe6e..790378c 100644 --- a/c/cmd/test-vector/test-vector.c +++ b/c/cmd/test-vector/test-vector.c @@ -59,14 +59,14 @@ main(void) adder(KEKSAtomBinEncode(&Got, buf + Off, len - Off, bin, 63 + 255 + 65535 + 1)); adder(KEKSAtomEOCEncode(&Got, buf + Off, len - Off)); // .str.bin - adder( - KEKSAtomStrEncode(&Got, buf + Off, len - Off, (const unsigned char *)"utf8", 4)); + adder(KEKSAtomStrEncode( + &Got, buf + Off, len - Off, (const unsigned char *)"utf8", 4)); adder(KEKSAtomStrEncode( &Got, buf + Off, len - Off, (const unsigned char *)"привет мир", 19)); adder(KEKSAtomEOCEncode(&Got, buf + Off, len - Off)); // .str - adder( - KEKSAtomStrEncode(&Got, buf + Off, len - Off, (const unsigned char *)"blob", 4)); + adder(KEKSAtomStrEncode( + &Got, buf + Off, len - Off, (const unsigned char *)"blob", 4)); adder(KEKSAtomListEncode(&Got, buf + Off, len - Off)); // .blob adder(KEKSAtomBlobEncode(&Got, buf + Off, len - Off, 12)); // .blob.0 @@ -92,15 +92,15 @@ main(void) adder(KEKSAtomEOCEncode(&Got, buf + Off, len - Off)); // .blob - adder( - KEKSAtomStrEncode(&Got, buf + Off, len - Off, (const unsigned char *)"bool", 4)); + adder(KEKSAtomStrEncode( + &Got, buf + Off, len - Off, (const unsigned char *)"bool", 4)); adder(KEKSAtomListEncode(&Got, buf + Off, len - Off)); // .bool adder(KEKSAtomBoolEncode(&Got, buf + Off, len - Off, true)); adder(KEKSAtomBoolEncode(&Got, buf + Off, len - Off, false)); adder(KEKSAtomEOCEncode(&Got, buf + Off, len - Off)); // .bool - adder( - KEKSAtomStrEncode(&Got, buf + Off, len - Off, (const unsigned char *)"ints", 4)); + adder(KEKSAtomStrEncode( + &Got, buf + Off, len - Off, (const unsigned char *)"ints", 4)); adder(KEKSAtomMapEncode(&Got, buf + Off, len - Off)); // .ints adder( @@ -113,29 +113,38 @@ main(void) adder(KEKSAtomSintEncode(&Got, buf + Off, len - Off, -123)); adder(KEKSAtomSintEncode(&Got, buf + Off, len - Off, -1234)); adder(KEKSAtomSintEncode(&Got, buf + Off, len - Off, -12345678)); - adder(KEKSAtomRawEncode( - &Got, - buf + Off, - len - Off, - KEKSAtomNint, - (const unsigned char *)"\x8A\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", - 11)); - adder(KEKSAtomRawEncode( - &Got, - buf + Off, - len - Off, - KEKSAtomNint, - (const unsigned char - *)"\x91\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", - 18)); - adder(KEKSAtomRawEncode( - &Got, - buf + Off, - len - Off, - KEKSAtomNint, - (const unsigned char - *)"\xBE\x00\xA1\xE5\xA4\x61\x28\x03\x41\x85\x6D\x4A\xD9\x08\xA6\x9E\xA5\xF3\xCC\xC1\x0C\x78\x82\x14\x2B\xB7\xD8\x01\xCC\x38\x0F\x26\xB6\xB4\xD6\x96\x32\x02\x4E\xE5\x21\xF8\xCF\xAF\xB4\x43\xD4\x9A\x2A\x3D\x0C\xC7\x3B\xB4\x75\x7E\x88\x2F\x53\x96\xED\x30\x2B\x41\x82\x10\xD0\xD4\x9D\x71\xBE\x86\xCA\x69\x9C\xF5\xEE\x3B\xD6\xD5\x7E\xD6\x58\xE6\x93\x16\x22\x96\x44\xBA\x65\x0C\x92\xD7\xF0\xD4\xDB\x29\xC3\xAD\x1D\xFA\x99\x79\x16\x6F\x4C\x6E\x79\x56\x1A\x58\xF8\xE2\xC6\x3D\x08\xDF\x4E\x22\x46\xED\x1F\x64\xD2\xD6\x13\xA1\x9D\x8C\x9A\x68\x70\xE6\x18\x8E\x2F\x3A\xD4\x0C\x03\x8F\xDA\x30\x45\x2F\x8D\xDF\xCD\x21\x2A\x6A\x97\x4B\xC2\x5E\xC6\xA0\x56\x4C\x66\xA7\xD2\x87\x50\xFF\x9D\xB4\x58\xB7\x44\x41\xE4\x9E\xE5\xE8\x2D\xBF\x49\x74\xD6\x45\x67\x8E\x0A\xD0\x31\xF9\x7A\xAB\xA8\x55\x45\x1E\xEF\x17\xA8\x9B\x42\x82\x1E\x53\x08\x16\xDD\x57\x93\xA8\x3B\x7A\x82\xE8\xED\xE8\x1E\x7F\x33\x95\x69\x1F\x76\x17\x84\xF8\xBC\x62\x79\x61\xCD\x40\x84\x5E\xE9\x08\xA4\x0B\x9D\x1F\x01\x92\x7B\x38\xEB\x1A\x7D\x4E\xFD\x60\xDB\x09\x44\xF7\xEC\x1B\x83\x2B\x7E\x6E\xB1\x83\x3F\x9A\x35\x15\x76\xAD\x5D\xE5\x71\xFA\xE8\x86\x5D\xA7\x51\x4F\x06\xB0\xFB\xF3\x8C\x1F\x2A\x85\x38\xF5\xD3\x8B\x4E\x18\x00\x1C\xCB\xB9\xDD\xCB\x48\x85\x30\xF6\x08\x6D\x14\x74\x4D\x8B\x56\x72\x16\x6E\x48\xE9\xEF\x93\x77\x25\x75\xDB\x66\xB6\xF2\x57\xC6\xFF\xAD\x6E\x2C\x29\x15\x10\xC5\xED\x02\xE1\xA8\xB2\x4B\x44\xEC\x1E\x2A\x91\x68\x62\x38\xE8\xDE\xFD\x18\xC0\x19\x98\x63\x4A\x50\x76\xA6\xB7\xF8\x5F\xC8\x1A\x1D\x61\xA1\x5B\x2C\x52\x8D\xFA\x08\x2C\xE3\xE3\xE2\xCA\x64\x9A\xC0\x48\x17\xEC\x5C\x12\x3E\x0B\x76\x1A\xB1\x03\xF7\x80\xC0\x14\xF0\x21\xBB\xEB\x7E\xA3\xB8\x6E\x0C\xA1\xC8\x33\xE3\x8E\xF5\xC8\x97\xA6\xD7\xE1\xF4\xA2\x39\x8C\x49\x0B\x3D\x65\xE2\xF4\x5C\x7F\xAE\x40\x2D\x1D\xF1\x69\x8B\x6F\xDD\xB1\x85\x48\x16\x64\x87\x1C\x26\x64\xBF\xD1\x68\x6B\x2B\x33\x72\x78\x3F\x18\x56\xF6\x24\x7A\x3F\x84\x37\xA2\x81\x8F\x68\xB7\xC4\xEA\x13\xA5\xF5\x7B\x73\xC7\x28\x70\xB6\x84\x04\x5F\x14", - 481)); + { + buf[Off] = KEKSAtomNint; + Off++; + size_t l = 11; + memcpy( + buf + Off, + (const unsigned char *)"\x8A\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", + l); + Off += l; + } + { + buf[Off] = KEKSAtomNint; + Off++; + size_t l = 18; + memcpy( + buf + Off, + (const unsigned char + *)"\x91\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + l); + Off += l; + } + { + buf[Off] = KEKSAtomNint; + Off++; + size_t l = 481; + memcpy( + buf + Off, + (const unsigned char + *)"\xBE\x00\xA1\xE5\xA4\x61\x28\x03\x41\x85\x6D\x4A\xD9\x08\xA6\x9E\xA5\xF3\xCC\xC1\x0C\x78\x82\x14\x2B\xB7\xD8\x01\xCC\x38\x0F\x26\xB6\xB4\xD6\x96\x32\x02\x4E\xE5\x21\xF8\xCF\xAF\xB4\x43\xD4\x9A\x2A\x3D\x0C\xC7\x3B\xB4\x75\x7E\x88\x2F\x53\x96\xED\x30\x2B\x41\x82\x10\xD0\xD4\x9D\x71\xBE\x86\xCA\x69\x9C\xF5\xEE\x3B\xD6\xD5\x7E\xD6\x58\xE6\x93\x16\x22\x96\x44\xBA\x65\x0C\x92\xD7\xF0\xD4\xDB\x29\xC3\xAD\x1D\xFA\x99\x79\x16\x6F\x4C\x6E\x79\x56\x1A\x58\xF8\xE2\xC6\x3D\x08\xDF\x4E\x22\x46\xED\x1F\x64\xD2\xD6\x13\xA1\x9D\x8C\x9A\x68\x70\xE6\x18\x8E\x2F\x3A\xD4\x0C\x03\x8F\xDA\x30\x45\x2F\x8D\xDF\xCD\x21\x2A\x6A\x97\x4B\xC2\x5E\xC6\xA0\x56\x4C\x66\xA7\xD2\x87\x50\xFF\x9D\xB4\x58\xB7\x44\x41\xE4\x9E\xE5\xE8\x2D\xBF\x49\x74\xD6\x45\x67\x8E\x0A\xD0\x31\xF9\x7A\xAB\xA8\x55\x45\x1E\xEF\x17\xA8\x9B\x42\x82\x1E\x53\x08\x16\xDD\x57\x93\xA8\x3B\x7A\x82\xE8\xED\xE8\x1E\x7F\x33\x95\x69\x1F\x76\x17\x84\xF8\xBC\x62\x79\x61\xCD\x40\x84\x5E\xE9\x08\xA4\x0B\x9D\x1F\x01\x92\x7B\x38\xEB\x1A\x7D\x4E\xFD\x60\xDB\x09\x44\xF7\xEC\x1B\x83\x2B\x7E\x6E\xB1\x83\x3F\x9A\x35\x15\x76\xAD\x5D\xE5\x71\xFA\xE8\x86\x5D\xA7\x51\x4F\x06\xB0\xFB\xF3\x8C\x1F\x2A\x85\x38\xF5\xD3\x8B\x4E\x18\x00\x1C\xCB\xB9\xDD\xCB\x48\x85\x30\xF6\x08\x6D\x14\x74\x4D\x8B\x56\x72\x16\x6E\x48\xE9\xEF\x93\x77\x25\x75\xDB\x66\xB6\xF2\x57\xC6\xFF\xAD\x6E\x2C\x29\x15\x10\xC5\xED\x02\xE1\xA8\xB2\x4B\x44\xEC\x1E\x2A\x91\x68\x62\x38\xE8\xDE\xFD\x18\xC0\x19\x98\x63\x4A\x50\x76\xA6\xB7\xF8\x5F\xC8\x1A\x1D\x61\xA1\x5B\x2C\x52\x8D\xFA\x08\x2C\xE3\xE3\xE2\xCA\x64\x9A\xC0\x48\x17\xEC\x5C\x12\x3E\x0B\x76\x1A\xB1\x03\xF7\x80\xC0\x14\xF0\x21\xBB\xEB\x7E\xA3\xB8\x6E\x0C\xA1\xC8\x33\xE3\x8E\xF5\xC8\x97\xA6\xD7\xE1\xF4\xA2\x39\x8C\x49\x0B\x3D\x65\xE2\xF4\x5C\x7F\xAE\x40\x2D\x1D\xF1\x69\x8B\x6F\xDD\xB1\x85\x48\x16\x64\x87\x1C\x26\x64\xBF\xD1\x68\x6B\x2B\x33\x72\x78\x3F\x18\x56\xF6\x24\x7A\x3F\x84\x37\xA2\x81\x8F\x68\xB7\xC4\xEA\x13\xA5\xF5\x7B\x73\xC7\x28\x70\xB6\x84\x04\x5F\x14", + l); + Off += l; + } adder(KEKSAtomEOCEncode(&Got, buf + Off, len - Off)); // .ints.neg adder( @@ -148,27 +157,33 @@ main(void) adder(KEKSAtomUintEncode(&Got, buf + Off, len - Off, 123)); adder(KEKSAtomUintEncode(&Got, buf + Off, len - Off, 1234)); adder(KEKSAtomUintEncode(&Got, buf + Off, len - Off, 12345678)); - adder(KEKSAtomRawEncode( - &Got, - buf + Off, - len - Off, - KEKSAtomPint, - (const unsigned char *)"\x8B\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", - 12)); - adder(KEKSAtomRawEncode( - &Got, - buf + Off, - len - Off, - KEKSAtomPint, - (const unsigned char - *)"\x91\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", - 18)); + { + buf[Off] = KEKSAtomPint; + Off++; + size_t l = 12; + memcpy( + buf + Off, + (const unsigned char *)"\x8B\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + l); + Off += l; + } + { + buf[Off] = KEKSAtomPint; + Off++; + size_t l = 18; + memcpy( + buf + Off, + (const unsigned char + *)"\x91\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + l); + Off += l; + } adder(KEKSAtomEOCEncode(&Got, buf + Off, len - Off)); // .ints.pos adder(KEKSAtomEOCEncode(&Got, buf + Off, len - Off)); // .ints - adder( - KEKSAtomStrEncode(&Got, buf + Off, len - Off, (const unsigned char *)"uuid", 4)); + adder(KEKSAtomStrEncode( + &Got, buf + Off, len - Off, (const unsigned char *)"uuid", 4)); adder(KEKSAtomUUIDEncode( &Got, buf + Off, @@ -191,35 +206,41 @@ main(void) assert(KEKSTimespecToTAI64(tai, &ts)); adder(KEKSAtomTAI64Encode(&Got, buf + Off, len - Off, tai, 12)); - adder(KEKSAtomRawEncode( - &Got, - buf + Off, - len - Off, - KEKSAtomTAI64N, - (const unsigned char *)"\x40\x00\x00\x00\x49\x96\x02\xF4\x00\x06\xF8\x55", - 12)); - - adder(KEKSAtomRawEncode( - &Got, - buf + Off, - len - Off, - KEKSAtomTAI64NA, - (const unsigned char - *)"\x40\x00\x00\x00\x49\x96\x02\xF4\x00\x06\xF8\x55\x07\x5B\xCD\x15", - 16)); + { + buf[Off] = KEKSAtomTAI64N; + Off++; + size_t l = 12; + memcpy( + buf + Off, + (const unsigned char + *)"\x40\x00\x00\x00\x49\x96\x02\xF4\x00\x06\xF8\x55", + l); + Off += l; + } + { + buf[Off] = KEKSAtomTAI64NA; + Off++; + size_t l = 16; + memcpy( + buf + Off, + (const unsigned char + *)"\x40\x00\x00\x00\x49\x96\x02\xF4\x00\x06\xF8\x55\x07\x5B\xCD\x15", + l); + Off += l; + } } adder(KEKSAtomEOCEncode(&Got, buf + Off, len - Off)); // .dates adder(KEKSAtomStrEncode( &Got, buf + Off, len - Off, (const unsigned char *)"floats", 6)); adder(KEKSAtomListEncode(&Got, buf + Off, len - Off)); // .floats - adder(KEKSAtomRawEncode( - &Got, - buf + Off, - len - Off, - KEKSAtomFloat32, - (const unsigned char *)"\x01\x02\x03\x04", - 4)); + { + buf[Off] = KEKSAtomFloat32; + Off++; + size_t l = 4; + memcpy(buf + Off, (const unsigned char *)"\x01\x02\x03\x04", l); + Off += l; + } adder(KEKSAtomEOCEncode(&Got, buf + Off, len - Off)); // .floats adder(KEKSAtomStrEncode( @@ -233,13 +254,13 @@ main(void) adder(KEKSAtomBinEncode(&Got, buf + Off, len - Off, NULL, 0)); memset(bin, '\x00', 16); adder(KEKSAtomUUIDEncode(&Got, buf + Off, len - Off, bin)); - adder(KEKSAtomRawEncode( - &Got, - buf + Off, - len - Off, - KEKSAtomTAI64, - (const unsigned char *)"\x00\x00\x00\x00\x00\x00\x00\x00", - 8)); + { + buf[Off] = KEKSAtomTAI64; + Off++; + size_t l = 8; + memcpy(buf + Off, (const unsigned char *)"\x00\x00\x00\x00\x00\x00\x00\x00", l); + Off += l; + } adder(KEKSAtomEOCEncode(&Got, buf + Off, len - Off)); // .empties adder(KEKSAtomEOCEncode(&Got, buf + Off, len - Off)); // . diff --git a/c/doc/atom.texi b/c/doc/atom.texi index 8ae2d42..c502f68 100644 --- a/c/doc/atom.texi +++ b/c/doc/atom.texi @@ -26,4 +26,3 @@ @anchor{KEKSAtomChunkEncode} @DOCSTRING KEKSAtomChunkEncode@ @DOCSTRING KEKSAtomTAI64Encode@ -@DOCSTRING KEKSAtomRawEncode@ diff --git a/c/lib/dec.c b/c/lib/dec.c index 802d437..22d0ae9 100644 --- a/c/lib/dec.c +++ b/c/lib/dec.c @@ -13,6 +13,8 @@ // You should have received a copy of the GNU Lesser General Public // License along with this program. If not, see . +#include +#include #include #include #include @@ -36,12 +38,11 @@ KEKSAtomDecode( // NOLINT(misc-no-recursion) if (len < 1) { return KEKSErrNotEnough; } - atom->tag = buf[0]; + unsigned char tag = buf[0]; - if ((atom->tag & (uint8_t)KEKSAtomStrings) > 0) { - atom->typ = - ((atom->tag & (uint8_t)KEKSAtomIsUTF8) == 0) ? KEKSItemBin : KEKSItemStr; - uint64_t l = atom->tag & (uint8_t)63; + if ((tag & (uint8_t)KEKSAtomStrings) > 0) { + atom->typ = ((tag & (uint8_t)KEKSAtomIsUTF8) == 0) ? KEKSItemBin : KEKSItemStr; + uint64_t l = tag & (uint8_t)63; size_t ll = 0; switch (l) { case 61: @@ -95,7 +96,7 @@ KEKSAtomDecode( // NOLINT(misc-no-recursion) return KEKSErrNo; } - switch (atom->tag) { + switch (tag) { case KEKSAtomEOC: atom->typ = KEKSItemEOC; break; @@ -139,7 +140,7 @@ KEKSAtomDecode( // NOLINT(misc-no-recursion) case KEKSAtomPint: case KEKSAtomNint: { - atom->typ = (atom->tag == KEKSAtomPint) ? KEKSItemPint : KEKSItemNint; + atom->typ = (tag == KEKSAtomPint) ? KEKSItemPint : KEKSItemNint; size_t binGot = 0; struct KEKSAtom bin; memset(&bin, 0, sizeof(struct KEKSAtom)); @@ -167,16 +168,16 @@ KEKSAtomDecode( // NOLINT(misc-no-recursion) } if (bin.v.str.len > 8) { atom->typ = KEKSItemRaw; - atom->v.str.len = binGot; - atom->v.str.ptr = buf + 1; + atom->v.str.len = binGot + 1; + atom->v.str.ptr = buf; return KEKSErrNo; } atom->v.pint = keksFromBE(bin.v.str.ptr, bin.v.str.len); if (atom->typ == KEKSItemNint) { if (atom->v.pint >= ((uint64_t)1 << (uint8_t)63)) { atom->typ = KEKSItemRaw; - atom->v.str.len = binGot; - atom->v.str.ptr = buf + 1; + atom->v.str.len = binGot + 1; + atom->v.str.ptr = buf; } else { atom->v.nint = -1 - (int64_t)(atom->v.pint); } @@ -190,7 +191,7 @@ KEKSAtomDecode( // NOLINT(misc-no-recursion) case KEKSAtomFloat128: case KEKSAtomFloat256: { size_t l = 0; - switch (atom->tag) { + switch (tag) { case KEKSAtomFloat16: l = 2; break; @@ -206,6 +207,8 @@ KEKSAtomDecode( // NOLINT(misc-no-recursion) case KEKSAtomFloat256: l = 32; break; + default: + assert(false); } atom->typ = KEKSItemFloat; (*got) += l; @@ -213,8 +216,8 @@ KEKSAtomDecode( // NOLINT(misc-no-recursion) return KEKSErrNotEnough; } atom->typ = KEKSItemRaw; - atom->v.str.len = l; - atom->v.str.ptr = buf + 1; + atom->v.str.len = l + 1; + atom->v.str.ptr = buf; break; } @@ -222,7 +225,7 @@ KEKSAtomDecode( // NOLINT(misc-no-recursion) case KEKSAtomTAI64N: case KEKSAtomTAI64NA: { size_t l = 0; - switch (atom->tag) { + switch (tag) { case KEKSAtomTAI64: l = 8; break; @@ -232,6 +235,8 @@ KEKSAtomDecode( // NOLINT(misc-no-recursion) case KEKSAtomTAI64NA: l = 16; break; + default: + assert(false); } atom->typ = KEKSItemTAI64; (*got) += l; diff --git a/c/lib/dec.h b/c/lib/dec.h index 64d3cb4..507d48e 100644 --- a/c/lib/dec.h +++ b/c/lib/dec.h @@ -51,9 +51,6 @@ enum KEKSItemType { // @deftp {Data type} {struct KEKSAtom} // Basic unit of the library, describing single TLV-like value. // @table @code -// @item .tag -// Real value of the atom's tag. May be used during debugging or -// when constructing raw value. // @item .typ // High level @ref{KEKSItemType, type} of the atom's value. As a rule // you should look solely on it. @@ -99,8 +96,7 @@ struct KEKSAtom { } str; } v; enum KEKSItemType typ; - unsigned char tag; - char _pad[3]; + char _pad[4]; }; // TEXINFO: KEKSAtomDecode diff --git a/c/lib/enc.c b/c/lib/enc.c index a1a2456..4febb50 100644 --- a/c/lib/enc.c +++ b/c/lib/enc.c @@ -274,25 +274,3 @@ KEKSAtomTAI64Encode( memcpy(buf + 1, src, srcLen); return true; } - -bool -KEKSAtomRawEncode( - size_t *len, - unsigned char *buf, - const size_t cap, - const unsigned char typ, - const unsigned char *src, - const size_t srcLen) -{ - (*len) = 1 + srcLen; - if ((*len) <= srcLen) { - (*len) = 0; - return false; - } - if (cap < (*len)) { - return false; - } - buf[0] = typ; - memcpy(buf + 1, src, srcLen); - return true; -} diff --git a/c/lib/enc.h b/c/lib/enc.h index bb44240..c0bd9ca 100644 --- a/c/lib/enc.h +++ b/c/lib/enc.h @@ -174,22 +174,4 @@ KEKSAtomTAI64Encode( const unsigned char *src, const size_t srcLen); -// TEXINFO: KEKSAtomRawEncode -// @deftypefun bool KEKSAtomRawEncode @ -// (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 -KEKSAtomRawEncode( - size_t *len, - unsigned char *buf, - const size_t cap, - const unsigned char typ, - const unsigned char *src, - const size_t srcLen); - #endif // KEKS_ENC_H diff --git a/c/lib/items.c b/c/lib/items.c index c32574a..1427249 100644 --- a/c/lib/items.c +++ b/c/lib/items.c @@ -422,13 +422,12 @@ KEKSItemsEncode( // NOLINT(misc-no-recursion) &got, buf + *off, cap - (*off), item->atom.v.str.ptr, item->atom.v.str.len); break; case KEKSItemRaw: - ok = KEKSAtomRawEncode( - &got, - buf + *off, - cap - (*off), - item->atom.tag, - item->atom.v.str.ptr, - item->atom.v.str.len); + if ((cap - (*off)) < item->atom.v.str.len) { + return false; + } + memcpy(buf + *off, item->atom.v.str.ptr, item->atom.v.str.len); + ok = true; + (*off) += item->atom.v.str.len; break; default: return false; diff --git a/go/cmd/iter-print/main.go b/go/cmd/iter-print/main.go deleted file mode 100644 index ffce5cb..0000000 --- a/go/cmd/iter-print/main.go +++ /dev/null @@ -1,119 +0,0 @@ -// GoKEKS -- Go KEKS codec implementation -// Copyright (C) 2024-2025 Sergey Matveev -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation, version 3 of the License. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this program. If not, see . - -package main - -import ( - "bufio" - "encoding/hex" - "fmt" - "log" - "os" - "strings" - "time" - - "go.cypherpunks.su/keks" - "go.cypherpunks.su/keks/types" -) - -func prindent(depth int) { - fmt.Print(strings.Repeat(" ", depth)) -} - -func printer(iter *keks.Iterator, count int, inList, inMap bool) { - for i := 0; i < count; i++ { - if !iter.Next() { - panic("unexpected") - } - depth := iter.Depth - prindent(depth) - if inList { - fmt.Printf("%d: ", i) - } else if inMap { - fmt.Print(iter.Str()) - fmt.Print(": ") - if !iter.Next() { - panic("unexpected") - } - } - switch iter.T { - case types.List: - fmt.Printf("[ %d\n", iter.Len()) - printer(iter, iter.Len(), true, false) - prindent(depth) - fmt.Println("]") - case types.Map: - fmt.Printf("{ %d\n", iter.Len()) - printer(iter, iter.Len(), false, true) - prindent(depth) - fmt.Println("}") - - case types.NIL: - fmt.Println("NIL") - case types.Bool: - if iter.Bool() { - fmt.Println("TRUE") - } else { - fmt.Println("FALSE") - } - case types.UUID: - fmt.Println(iter.UUID()) - case types.UInt: - fmt.Println(iter.UInt()) - case types.Int: - fmt.Println(iter.Int()) - case types.BigInt: - fmt.Println(iter.BigInt()) - case types.Blob: - blob := iter.Blob() - fmt.Printf("BLOB[ %d l=%d\n", len(blob.Chunks), blob.ChunkLen) - for i, chunk := range blob.Chunks { - fmt.Print(strings.Repeat(" ", iter.Depth+1)) - fmt.Printf("%d: %d:%s\n", - i, len(chunk), hex.EncodeToString([]byte(chunk))) - } - case types.TAI64: - t := iter.TAI64() - fmt.Printf("%s TAI\n", t.Time().Format(time.DateTime)) - case types.TAI64N: - t := iter.TAI64N() - fmt.Printf("%s TAI\n", t.Time().Format(time.DateTime+".000000000")) - case types.TAI64NA: - t := iter.TAI64NA() - fmt.Printf("TAI64NA[%s]\n", hex.EncodeToString(t[:])) - case types.Bin: - s := iter.Bin() - fmt.Printf("%d:%s\n", len(s), hex.EncodeToString([]byte(s))) - case types.Str: - fmt.Print(`"`) - fmt.Print(iter.Str()) - fmt.Println(`"`) - case types.Raw: - fmt.Printf("RAW[%s]\n", hex.EncodeToString(iter.Raw())) - default: - fmt.Println("???") - } - } -} - -func main() { - ctx := keks.NewDecoderFromReader(bufio.NewReader(os.Stdin), nil) - _, err := ctx.Parse() - if err != nil { - log.Fatal(err) - } - iter := ctx.Iter() - printer(iter, 1, false, false) -} diff --git a/go/cmd/print/colour.go b/go/cmd/print/colour.go new file mode 100644 index 0000000..85ca085 --- /dev/null +++ b/go/cmd/print/colour.go @@ -0,0 +1,35 @@ +package main + +import ( + "bytes" + "os" + + "golang.org/x/term" +) + +var ( + Yellow string + Green string + Blue string + Cyan string + Red string + Magenta string + White string + Reset string +) + +func init() { + if os.Getenv("NO_COLOR") != "" { + return + } + var b bytes.Buffer + t := term.NewTerminal(&b, "") + Yellow = string(t.Escape.Yellow) + Green = string(t.Escape.Green) + Blue = string(t.Escape.Blue) + Cyan = string(t.Escape.Cyan) + Red = string(t.Escape.Red) + Magenta = string(t.Escape.Magenta) + White = string(t.Escape.White) + Reset = string(t.Escape.Reset) +} diff --git a/go/cmd/print/main.go b/go/cmd/print/main.go index 78f4ce9..7b308c3 100644 --- a/go/cmd/print/main.go +++ b/go/cmd/print/main.go @@ -1,19 +1,154 @@ +// GoKEKS -- Go KEKS codec implementation +// Copyright (C) 2024-2025 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, version 3 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this program. If not, see . + package main import ( "bufio" + "encoding/hex" + "flag" "fmt" "log" "os" + "strconv" + "strings" + "time" "go.cypherpunks.su/keks" + "go.cypherpunks.su/keks/types" + "go.cypherpunks.su/tai64n/v4" ) +var MaxStrLen = flag.Uint("max-str-len", 1<<62, "Maximal string length to print") + +func prindent(depth int) { + fmt.Print(strings.Repeat(" ", depth)) +} + +func hexenc(b []byte) string { + return strings.ToUpper(hex.EncodeToString(b)) +} + +func printer(iter *keks.Iterator, count int, inList, inMap bool) { + for i := 0; i < count; i++ { + if !iter.Next() { + panic("unexpected") + } + depth := iter.Depth + fmt.Print(Blue + strconv.Itoa(depth) + Reset) + prindent(depth) + if inList { + fmt.Printf("%s%d:%s ", Yellow, i, Reset) + } else if inMap { + fmt.Printf("%s%s%s: ", Green, iter.Str(), Reset) + if !iter.Next() { + panic("unexpected") + } + } + switch iter.T { + case types.List: + fmt.Printf("[ %s%d%s\n", Cyan, iter.Len(), Reset) + printer(iter, iter.Len(), true, false) + prindent(depth) + fmt.Println(" ]") + case types.Map: + fmt.Printf("{ %s%d%s\n", Cyan, iter.Len(), Reset) + printer(iter, iter.Len(), false, true) + prindent(depth) + fmt.Println(" }") + + case types.NIL: + fmt.Println("NIL") + case types.Bool: + if iter.Bool() { + fmt.Println("TRUE") + } else { + fmt.Println("FALSE") + } + case types.UUID: + fmt.Println(iter.UUID()) + case types.UInt: + fmt.Println(iter.UInt()) + case types.Int: + fmt.Println(iter.Int()) + case types.BigInt: + fmt.Println(iter.BigInt()) + case types.Blob: + blob := iter.Blob() + fmt.Printf("BLOB[ %s%d l=%d%s\n", Cyan, len(blob.Chunks), blob.ChunkLen, Reset) + for i, chunk := range blob.Chunks { + fmt.Print(strings.Repeat(" ", iter.Depth+1)) + fmt.Printf("%d: %d:%s\n", i, len(chunk), hexenc([]byte(chunk))) + } + case types.TAI64: + t := iter.TAI64().Time() + utc, isLeap := tai64n.Leapsecs.Sub(t) + if isLeap { + fmt.Printf("TAI64(%s TAI, leap)\n", t.Format(time.DateTime)) + } else { + fmt.Printf("TAI64(%s TAI, %s UTC)\n", + t.Format(time.DateTime), utc.Format(time.DateTime)) + } + case types.TAI64N: + t := iter.TAI64N().Time() + utc, isLeap := tai64n.Leapsecs.Sub(t) + if isLeap { + fmt.Printf("TAI64N(%s TAI, leap)\n", + t.Format(time.DateTime+".000000000")) + } else { + fmt.Printf("TAI64N(%s TAI, %s UTC)\n", + t.Format(time.DateTime+".000000000"), + utc.Format(time.DateTime+".000000000")) + } + case types.TAI64NA: + t := iter.TAI64NA() + fmt.Printf("TAI64NA(%s)\n", hexenc(t[:])) + case types.Bin: + s := iter.Bin() + var dots string + if len(s) > int(*MaxStrLen) { + s = s[:int(*MaxStrLen)] + dots = "..." + } + fmt.Printf("%s%d%s:%s%s\n", Magenta, len(s), Reset, hexenc(s), dots) + case types.Str: + fmt.Print(`"`) + s := iter.Str() + if len(s) > int(*MaxStrLen) { + fmt.Print(s[:int(*MaxStrLen)]) + fmt.Print("...") + } else { + fmt.Print(s) + } + fmt.Println(`"`) + case types.Raw: + fmt.Printf("(l=%d v=%s)\n", len(iter.Raw()), hexenc(iter.Raw())) + default: + fmt.Println("???") + } + } +} + func main() { - decoder := keks.NewDecoderFromReader(bufio.NewReader(os.Stdin), nil) - v, err := decoder.Decode() + flag.Parse() + ctx := keks.NewDecoderFromReader(bufio.NewReader(os.Stdin), nil) + _, err := ctx.Parse() if err != nil { log.Fatal(err) } - fmt.Printf("%v\n%d bytes\n", v, decoder.Read) + iter := ctx.Iter() + printer(iter, 1, false, false) + fmt.Println(ctx.Read, "bytes") } diff --git a/go/go.mod b/go/go.mod index e1b4cc9..aab0813 100644 --- a/go/go.mod +++ b/go/go.mod @@ -8,3 +8,8 @@ require ( github.com/google/uuid v1.6.0 github.com/mitchellh/mapstructure v1.5.0 ) + +require ( + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect +) diff --git a/go/go.sum b/go/go.sum index 063c682..6790579 100644 --- a/go/go.sum +++ b/go/go.sum @@ -4,3 +4,7 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= go.cypherpunks.su/tai64n/v4 v4.1.0 h1:jW0EyklKXpSy9DSFMcDbu7XuLlMkn6kkpNWiMG6UT5c= go.cypherpunks.su/tai64n/v4 v4.1.0/go.mod h1:/uKUdhLOy8UciRKpapPaFXSOoa/SiXjs3XsDDpAz7OA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= -- 2.48.1