Add function for creating hex string literals

This commit is contained in:
Justine Tunney 2021-08-07 07:05:19 -07:00
parent aeeb851422
commit 1f766a332f
14 changed files with 250 additions and 112 deletions

View file

@ -39,15 +39,17 @@
* @param l is byte length of `s`
* @return bytes appended (always `l`) or -1 if `ENOMEM`
* @see appendz(b).i to get buffer length
* @note 20% faster than appends()
*/
ssize_t appendd(char **b, const void *s, size_t l) {
char *p;
size_t n;
struct appendz z;
assert(b);
z = appendz((p = *b));
if (ROUNDUP(z.i + l + 1, 8) + W > z.n) {
n = ROUNDUP(z.i + l + 1, 8) + W;
if (n > z.n) {
if (!z.n) z.n = W * 2;
while (ROUNDUP(z.i + l + 1, 8) + W > z.n) z.n += z.n >> 1;
while (n > z.n) z.n += z.n >> 1;
z.n = ROUNDUP(z.n, W);
if ((p = realloc(p, z.n))) {
z.n = malloc_usable_size(p);
@ -59,9 +61,7 @@ ssize_t appendd(char **b, const void *s, size_t l) {
}
*(char *)mempcpy(p + z.i, s, l) = 0;
z.i += l;
if (!IsTiny() && W == 8) {
z.i |= (size_t)APPEND_COOKIE << 48;
}
if (!IsTiny() && W == 8) z.i |= (size_t)APPEND_COOKIE << 48;
*(size_t *)(p + z.n - W) = z.i;
return l;
}

View file

@ -27,6 +27,7 @@
*
* @return bytes appended or -1 if `ENOMEM`
* @see appendz(b).i to get buffer length
* @note O(1) amortized buffer growth
*/
ssize_t(appendf)(char **b, const char *fmt, ...) {
int n;

View file

@ -20,6 +20,7 @@
#include "libc/dce.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/bsr.h"
#include "libc/stdio/append.internal.h"
#include "libc/str/str.h"
@ -37,35 +38,39 @@
* free(b);
*
* If `i` is greater than the current length then the extra bytes are
* filled with NUL characters.
* filled with NUL characters. If `i` is less than the current length
* then memory is released to the system.
*
* The resulting buffer is guarranteed to be NUL-terminated, i.e.
* `!b[appendz(b).i]` will be the case.
* `!b[appendz(b).i]` will be the case even if both params are 0.
*
* @return `i` or -1 if `ENOMEM`
* @see appendz(b).i to get buffer length
*/
ssize_t appendr(char **b, size_t i) {
char *p;
size_t n;
struct appendz z;
assert(b);
z = appendz((p = *b));
z.n = ROUNDUP(i + 1, 8) + W;
if ((p = realloc(p, z.n))) {
z.n = malloc_usable_size(p);
assert(!(z.n & (W - 1)));
*b = p;
} else {
return -1;
if (i != z.i || !p) {
n = ROUNDUP(i + 1, 8) + W;
if (n > z.n || bsrl(n) < bsrl(z.n)) {
if ((p = realloc(p, n))) {
n = malloc_usable_size(p);
assert(!(n & (W - 1)));
*b = p;
} else {
return -1;
}
}
if (i > z.i) {
memset(p + z.i, 0, i - z.i + 1);
} else {
p[i] = 0;
}
*(size_t *)(p + n - W) =
i | (!IsTiny() && W == 8 ? (size_t)APPEND_COOKIE << 48 : 0);
}
if (i > z.i) {
memset(p, z.i, i - z.i);
}
z.i = i;
p[z.i] = 0;
if (!IsTiny() && W == 8) {
z.i |= (size_t)APPEND_COOKIE << 48;
}
*(size_t *)(p + z.n - W) = z.i;
return i;
}

View file

@ -31,6 +31,7 @@
*
* @return bytes appended (always `strlen(s)`) or -1 if `ENOMEM`
* @see appendz(b).i to get buffer length
* @note 2x faster than appendf()
*/
ssize_t appends(char **b, const char *s) {
return appendd(b, s, strlen(s));

View file

@ -16,18 +16,23 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/macros.internal.h"
#include "libc/nexgen32e/bsr.h"
#include "libc/stdio/append.internal.h"
#define W sizeof(size_t)
/**
* Appends character or word-encoded string to buffer.
*
* Up to eight characters can be appended. For example:
*
* appendw(&s, 'h'|'i'<<8);
* appendw(&s, READ64LE("hi\0\0\0\0\0"));
*
* Is equivalent to:
* Are equivalent to:
*
* appends(&s, "hi");
*
@ -37,15 +42,40 @@
*
* Will append a single NUL character.
*
* This function is slightly faster than appendd() and appends(). Its
* main advantage is it improves code size in functions that call it.
*
* The resulting buffer is guaranteed to be NUL-terminated, i.e.
* `!b[appendz(b).i]` will be the case.
*
* @return bytes appended or -1 if `ENOMEM`
* @see appendz(b).i to get buffer length
*/
ssize_t appendw(char **b, uint64_t w) {
char t[8];
unsigned n = 1;
WRITE64LE(t, w);
if (w) n += bsrl(w) >> 3;
return appendd(b, t, n);
size_t n;
unsigned l;
char *p, *q;
struct appendz z;
z = appendz((p = *b));
l = w ? (bsrl(w) >> 3) + 1 : 1;
n = ROUNDUP(z.i + 8 + 1, 8) + W;
if (n > z.n) {
if (!z.n) z.n = W * 2;
while (n > z.n) z.n += z.n >> 1;
z.n = ROUNDUP(z.n, W);
if ((p = realloc(p, z.n))) {
z.n = malloc_usable_size(p);
assert(!(z.n & (W - 1)));
*b = p;
} else {
return -1;
}
}
q = p + z.i;
WRITE64LE(q, w);
q[8] = 0;
z.i += l;
if (!IsTiny() && W == 8) z.i |= (size_t)APPEND_COOKIE << 48;
*(size_t *)(p + z.n - W) = z.i;
return l;
}

View file

@ -26,6 +26,7 @@
/**
* Returns size of append buffer.
*
* @param p must be 0 or have been allocated by an append*() function
* @return i is number of bytes stored in buffer
* @return n is number of bytes in allocation
*/
@ -36,6 +37,12 @@ struct appendz appendz(char *p) {
assert(z.n >= W * 2 && !(z.n & (W - 1)));
z.i = *(size_t *)(p + z.n - W);
if (!IsTiny() && W == 8) {
/*
* This check should fail if an append*() function was passed a
* pointer that was allocated manually by malloc(). Append ptrs
* can be free()'d safely, but they need to be allocated by the
* append library, because we write a special value to the end.
*/
assert((z.i >> 48) == APPEND_COOKIE);
z.i &= 0x0000ffffffffffff;
}

52
libc/stdio/dumphexc.c Normal file
View file

@ -0,0 +1,52 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
#include "libc/stdio/append.internal.h"
#include "libc/stdio/hex.internal.h"
#include "libc/str/str.h"
/**
* Turns data into "\x00..." string literal.
*/
char *DumpHexc(const char *p, size_t n, size_t *z) {
long o;
int i, m;
char A[128], *q, *s = 0;
if (n == -1) n = p ? strlen(p) : 0;
appendw(&s, '"' | '\\' << 8 | '\n' << 16);
for (o = 0; (m = MIN(16, n)); p += m, n -= m) {
q = A;
for (i = 0; i < m; ++i) {
*q++ = '\\';
*q++ = 'x';
*q++ = "0123456789abcdef"[(p[i] & 0xF0) >> 4];
*q++ = "0123456789abcdef"[(p[i] & 0x0F) >> 0];
}
if (o) appendw(&s, '\\' | '\n' << 8);
appendd(&s, A, q - A);
o += m;
}
if (appendw(&s, '"') != -1) {
if (z) *z = appendz(s).i;
return s;
} else {
free(s);
return 0;
}
}

11
libc/stdio/hex.internal.h Normal file
View file

@ -0,0 +1,11 @@
#ifndef COSMOPOLITAN_LIBC_STDIO_HEX_INTERNAL_H_
#define COSMOPOLITAN_LIBC_STDIO_HEX_INTERNAL_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
char *DumpHex(const char *, size_t, size_t *);
char *DumpHexc(const char *, size_t, size_t *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_STDIO_HEX_INTERNAL_H_ */

View file

@ -57,7 +57,7 @@ o/$(MODE)/libc/stdio/fputc.o: \
o//libc/stdio/appendw.o: \
OVERRIDE_CFLAGS += \
-O2
-Os
LIBC_STDIO_LIBS = $(foreach x,$(LIBC_STDIO_ARTIFACTS),$($(x)))
LIBC_STDIO_SRCS = $(foreach x,$(LIBC_STDIO_ARTIFACTS),$($(x)_SRCS))

View file

@ -30,14 +30,16 @@
ssize_t(vappendf)(char **b, const char *f, va_list v) {
char *p;
int r, s;
size_t n;
va_list w;
struct appendz z;
z = appendz((p = *b));
va_copy(w, v);
if ((r = (vsnprintf)(p + z.i, z.n ? z.n - W - z.i : 0, f, v)) >= 0) {
if (ROUNDUP(z.i + r + 1, 8) + W > z.n) {
n = ROUNDUP(z.i + r + 1, 8) + W;
if (n > z.n) {
if (!z.n) z.n = W * 2;
while (ROUNDUP(z.i + r + 1, 8) + W > z.n) z.n += z.n >> 1;
while (n > z.n) z.n += z.n >> 1;
z.n = ROUNDUP(z.n, W);
if ((p = realloc(p, z.n))) {
z.n = malloc_usable_size(p);

View file

@ -1,27 +1,28 @@
#ifndef COSMOPOLITAN_LIBC_BENCH_H_
#define COSMOPOLITAN_LIBC_BENCH_H_
#include "libc/bits/safemacros.internal.h"
#include "libc/nexgen32e/bench.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/**
* @fileoverview Microbenchmarking tools.
* @fileoverview Microbenchmarking Toolz.
*/
#ifndef BENCHLOOP
#define BENCHLOOP(START, STOP, N, INIT, EXPR) \
({ \
unsigned long Iter, Count; \
double Average, Sample, Time1, Time2; \
for (Average = 1, Iter = 1, Count = (N); Iter < Count; ++Iter) { \
INIT; \
Time1 = START(); \
EXPR; \
Time2 = STOP(); \
Sample = Time2 - Time1; \
Average += 1. / Iter * (Sample - Average); \
} \
Average; \
#define BENCHLOOP(START, STOP, N, INIT, EXPR) \
({ \
unsigned long Iter, Count; \
uint64_t Time1, Time2; \
double Average; \
for (Average = 1, Iter = 1, Count = (N); Iter < Count; ++Iter) { \
INIT; \
Time1 = START(); \
EXPR; \
Time2 = STOP(); \
Average += 1. / Iter * (unsignedsubtract(Time2, Time1) - Average); \
} \
Average; \
})
#endif /* BENCHLOOP */

View file

@ -0,0 +1,34 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/stdio/hex.internal.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/hyperion.h"
#include "libc/testlib/testlib.h"
TEST(DumpHexc, test) {
EXPECT_STREQ("\"\\\n\
\\x68\\x65\\x6c\\x6c\\x6f\\xe2\\x86\\x92\\x0a\\x01\\x02\\x74\\x68\\x65\\x65\\x72\\\n\
\\x68\\x75\\x72\\x63\\x65\\x6f\\x61\\x68\\x72\\x63\\x75\\x6f\\x65\\x61\\x75\\x68\\\n\
\\x63\\x72\"",
DumpHexc("hello→\n\1\2theerhurceoahrcuoeauhcr", -1, 0));
}
BENCH(DumpHexc, bench) {
EZBENCH2("dumphexc", donothing, free(DumpHexc(kHyperion, kHyperionSize, 0)));
}

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/bits.h"
#include "libc/stdio/append.internal.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
@ -87,8 +88,7 @@ TEST(appendw, test) {
EXPECT_EQ(0, b[0]);
EXPECT_EQ('h', b[1]);
EXPECT_EQ(0, b[2]);
ASSERT_NE(-1, appendw(&b, 'e' | 'l' << 8 | 'l' << 16 | 'o' << 24 |
(uint64_t)'!' << 32));
ASSERT_NE(-1, appendw(&b, READ64LE("ello!\0\0")));
EXPECT_EQ(7, appendz(b).i);
EXPECT_EQ(0, b[0]);
EXPECT_EQ('h', b[1]);
@ -101,7 +101,7 @@ TEST(appendw, test) {
free(b);
}
TEST(appendr, test) {
TEST(appendr, testShrink_nulTerminates) {
char *b = 0;
ASSERT_NE(-1, appends(&b, "hello"));
EXPECT_EQ(5, appendz(b).i);
@ -121,15 +121,54 @@ TEST(appendr, test) {
free(b);
}
TEST(appendr, testExtend_zeroFills) {
char *b = 0;
ASSERT_NE(-1, appends(&b, "hello"));
EXPECT_EQ(5, appendz(b).i);
ASSERT_NE(-1, appendr(&b, 20));
EXPECT_EQ(20, appendz(b).i);
ASSERT_BINEQ(u"hello                ", b);
ASSERT_NE(-1, appendr(&b, 20));
EXPECT_EQ(20, appendz(b).i);
ASSERT_BINEQ(u"hello                ", b);
free(b);
}
TEST(appendr, testAbsent_allocatesNul) {
char *b = 0;
ASSERT_NE(-1, appendr(&b, 0));
EXPECT_EQ(0, appendz(b).i);
ASSERT_BINEQ(u" ", b);
free(b);
}
TEST(appendd, testMemFail_doesntInitializePointer) {
char *b = 0;
ASSERT_EQ(-1, appendd(&b, 0, -1ull >> 8));
EXPECT_EQ(0, b);
}
TEST(appendd, testMemFail_doesntFreeExistingAllocation) {
char *b = 0;
ASSERT_NE(-1, appends(&b, "hello"));
EXPECT_STREQ("hello", b);
ASSERT_EQ(-1, appendd(&b, 0, -1ull >> 8));
EXPECT_STREQ("hello", b);
free(b);
}
BENCH(vappendf, bench) {
const char t[] = {0};
char *b = 0;
EZBENCH2("appendf", donothing, appendf(&b, "1"));
EZBENCH2("appendf", donothing, appendf(&b, "hello"));
free(b), b = 0;
EZBENCH2("appends", donothing, appends(&b, "1"));
EZBENCH2("appends", donothing, appends(&b, "hello"));
free(b), b = 0;
EZBENCH2("appendw", donothing, appendw(&b, 'B'));
EZBENCH2("appendw", donothing,
appendw(&b, 'h' | 'e' << 8 | 'l' << 16 | 'l' << 24 |
(uint64_t)'o' << 32));
free(b), b = 0;
EZBENCH2("appendd", donothing, appendd(&b, t, 1));
EZBENCH2("appendd", donothing, appendd(&b, "hello", 5));
EZBENCH2("appendr", donothing, appendr(&b, 0));
free(b), b = 0;
}

View file

@ -18,75 +18,30 @@
*/
#include "libc/fmt/fmt.h"
#include "libc/macros.internal.h"
#include "libc/stdio/append.internal.h"
#include "libc/stdio/hex.internal.h"
#include "libc/stdio/stdio.h"
struct Append {
size_t i, n;
char *p;
};
int AppendFmt(struct Append *b, const char *fmt, ...) {
int n;
char *p;
va_list va, vb;
va_start(va, fmt);
va_copy(vb, va);
n = vsnprintf(b->p + b->i, b->n - b->i, fmt, va);
if (n >= b->n - b->i) {
do {
if (b->n) {
b->n += b->n >> 1; /* this is the important line */
} else {
b->n = 16;
}
} while (b->i + n + 1 > b->n);
b->p = realloc(b->p, b->n);
vsnprintf(b->p + b->i, b->n - b->i, fmt, vb);
}
va_end(vb);
va_end(va);
b->i += n;
return n;
}
char *DumpHexc(const char *data, size_t size, size_t *z) {
long o;
int b, i, n;
char A[128], *p;
struct Append buf;
if (size == -1) size = data ? strlen(data) : 0;
buf.i = 0;
buf.n = 256;
buf.p = calloc(1, 256);
AppendFmt(&buf, "\"\\\n");
for (b = o = 0; (n = MIN(16, size)); data += n, size -= n) {
p = A;
for (i = 0; i < n; ++i) {
*p++ = '\\';
*p++ = 'x';
*p++ = "0123456789abcdef"[(data[i] & 0xF0) >> 4];
*p++ = "0123456789abcdef"[(data[i] & 0x0F) >> 0];
}
if (o) AppendFmt(&buf, "\\\n");
AppendFmt(&buf, "%.*s", p - A, A);
o += n;
}
AppendFmt(&buf, "\"");
if (z) *z = buf.i;
return buf.p;
}
/**
* @fileoverview Hex String Literal Converter, e.g.
*
* $ echo hello | o/tool/viz/dumphexc.com
* "\
* \x68\x65\x6c\x6c\x6f\x0a"
*/
int main(int argc, char *argv[]) {
char *p;
size_t n, g;
char *b = 0;
char buf[512];
struct Append b = {0};
while ((g = fread(buf, 1, sizeof(buf), stdin))) {
AppendFmt(&b, "%.*s", g, buf);
appendd(&b, buf, g);
}
if (!ferror(stdin)) {
p = DumpHexc(b.p, b.i, &n);
p = DumpHexc(b, appendz(b).i, &n);
fwrite(p, 1, n, stdout);
free(p);
}
printf("\n");
return ferror(stdin) || ferror(stdout) ? 1 : 0;