Skip to content

Commit

Permalink
Add json_get_b64() and xb64_decode()
Browse files Browse the repository at this point in the history
  • Loading branch information
cpq committed Feb 10, 2024
1 parent f1ba360 commit 975c5f8
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 5 deletions.
57 changes: 55 additions & 2 deletions str.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ typedef int int32_t;
extern "C" {
#endif

#define ESC(str) fmt_esc, 0, (str)
#define XESC(str) fmt_esc, 0, (str)

// Low level basic functions
size_t xvprintf(void (*)(char, void *), void *, const char *, va_list *);
Expand All @@ -46,14 +46,17 @@ size_t fmt_esc(void (*fn)(char, void *), void *arg, va_list *ap);

// Utility functions
void xhexdump(void (*fn)(char, void *), void *arg, const void *buf, size_t len);
size_t xb64_decode(const char *src, size_t slen, char *dst, size_t dlen);

// JSON parsing API
int json_get(const char *buf, int len, const char *path, int *size);
int json_get_num(const char *buf, int len, const char *path, double *val);
int json_get_bool(const char *buf, int len, const char *path, int *val);
long json_get_long(const char *buf, int len, const char *path, long dflt);
int json_get_str(const char *buf, int len, const char *path, char *dst,
size_t dlen);
long json_get_long(const char *buf, int len, const char *path, long dflt);
int json_get_b64(const char *buf, int len, const char *path, char *dst,
size_t dlen);

#if !defined(STR_API_ONLY)
typedef void (*xout_t)(char, void *); // Output function
Expand Down Expand Up @@ -616,6 +619,47 @@ static int json_unescape(const char *buf, size_t len, char *to, size_t n) {
return (int) j;
}

int xb64_decode_single(int c);
int xb64_decode_single(int c) {
if (c >= 'A' && c <= 'Z') {
return c - 'A';
} else if (c >= 'a' && c <= 'z') {
return c + 26 - 'a';
} else if (c >= '0' && c <= '9') {
return c + 52 - '0';
} else if (c == '+') {
return 62;
} else if (c == '/') {
return 63;
} else if (c == '=') {
return 64;
} else {
return -1;
}
}

size_t xb64_decode(const char *src, size_t slen, char *dst, size_t dlen) {
const char *end = src == NULL ? NULL : src + slen; // Cannot add to NULL
size_t len = 0;
if (dlen < slen / 4 * 3 + 1) goto fail;
while (src != NULL && src + 3 < end) {
int a = xb64_decode_single(src[0]), b = xb64_decode_single(src[1]),
c = xb64_decode_single(src[2]), d = xb64_decode_single(src[3]);
if (a == 64 || a < 0 || b == 64 || b < 0 || c < 0 || d < 0) goto fail;
dst[len++] = (char) ((a << 2) | (b >> 4));
if (src[2] != '=') {
dst[len++] = (char) ((b << 4) | (c >> 2));
if (src[3] != '=') dst[len++] = (char) ((c << 6) | d);
}
src += 4;
}
dst[len] = '\0';
return len;
fail:
if (dlen > 0) dst[0] = '\0';
return 0;
}

int json_get_num(const char *buf, int len, const char *path, double *v) {
int found = 0, n = 0, off = json_get(buf, len, path, &n);
if (off >= 0 && (buf[off] == '-' || (buf[off] >= '0' && buf[off] <= '9'))) {
Expand Down Expand Up @@ -643,6 +687,15 @@ int json_get_str(const char *buf, int len, const char *path, char *dst,
return result;
}

int json_get_b64(const char *buf, int len, const char *path, char *dst,
size_t dlen) {
int result = -1, n = 0, off = json_get(buf, len, path, &n);
if (off >= 0 && n > 1 && buf[off] == '"') {
result = (int) xb64_decode(buf + off + 1, (size_t) (n - 2), dst, dlen);
}
return result;
}

long json_get_long(const char *buf, int len, const char *path, long dflt) {
double v;
if (json_get_num(buf, len, path, &v)) dflt = (long) v;
Expand Down
20 changes: 17 additions & 3 deletions test/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,22 +173,23 @@ static void test_m(void) {
assert(sf("_127.0.0.1_123", "_%M_%d", fmt_ip4, &ip4, 123));
assert(sf("_[164:2100:0:0:0:0:0:0]_123", "_%M_%d", fmt_ip6, ip6, 123));
assert(sf("_01:02:03:04:05:06_123", "_%M_%d", fmt_mac, mac, 123));
assert(sf(esc, "_%M_%d", ESC("a\nb"), 123));
assert(sf(esc, "_%M_%d", XESC("a\nb"), 123));
assert(sf("_eHl6_123", "_%M_%d", fmt_b64, 3, "xyz", 123));
assert(sf(quo, "_%m_%d", fmt_ip4, &ip4, 123));
assert(sf(quo, "_%m_%d", ESC("127.0.0.1"), 123));
assert(sf(quo, "_%m_%d", XESC("127.0.0.1"), 123));

xprintf(out, NULL, "%s: %g\n", "dbl", 1.234); // dbl: 1.234
xprintf(out, NULL, "%.*s\n", 3, "foobar"); // foo
xprintf(out, NULL, "%#04x\n", 11); // 0x0b
xprintf(out, NULL, "%d %5s\n", 7, "pad"); // 7 pad
// JSON: {"value": 1.234}
xprintf(out, NULL, "JSON: {%m: %g}\n", ESC("value"), 1.234);
xprintf(out, NULL, "JSON: {%m: %g}\n", XESC("value"), 1.234);
}

static void test_json(void) {
char buf[100];
const char *s = "{\"a\": -42, \"b\": [\"hi\\t\\u0020\", true, { }, -1.7e-2]}";
const char *s2 = "\"foobar\"";
int ofs, n, b = 0, len = (int) strlen(s);
double d = 0.0;
assert(json_get_long(s, len, "$.a", 0) == -42);
Expand All @@ -201,13 +202,26 @@ static void test_json(void) {
assert((ofs = json_get(s, len, "$.b[2]", &n)) > 0);
assert(n == 3 && s[ofs] == '{' && s[ofs + 2] == '}');
assert(json_get_num(s, len, "$.b[3]", &d) == 1 && d == -0.017);
assert((ofs = json_get(s2, (int) strlen(s2), "$", &n)) == 0);
assert(n == 8);
}

static void test_base64(void) {
char a[100], b[100];
memset(a, ' ', sizeof(a));
memset(b, ' ', sizeof(b));
xsnprintf(a, sizeof(a), "%m", fmt_b64, 2, "hi");
assert(strcmp(a, "\"aGk=\"") == 0);
assert(json_get_b64(a, (int) strlen(a), "$", b, sizeof(b)) == 2);
assert(strcmp(b, "hi") == 0);
}

int main(void) {
test_std();
test_float();
test_m();
test_json();
test_base64();
printf("SUCCESS\n");
return 0;
}

0 comments on commit 975c5f8

Please sign in to comment.