mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Polish redbean serialization
This commit is contained in:
parent
7aafa64ab3
commit
2d1731b995
24 changed files with 828 additions and 158 deletions
63
libc/fmt/formatbinary64.c
Normal file
63
libc/fmt/formatbinary64.c
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*-*- 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/fmt/itoa.h"
|
||||
#include "libc/nexgen32e/bsr.h"
|
||||
|
||||
static inline int PickGoodWidth(unsigned x) {
|
||||
if (x < 16) {
|
||||
if (x < 2) return 0;
|
||||
if (x < 8) return 7;
|
||||
return 15;
|
||||
} else {
|
||||
if (x < 32) return 31;
|
||||
return 63;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts unsigned 64-bit integer to binary string.
|
||||
*
|
||||
* @param p needs at least 67 bytes
|
||||
* @param z is 0 for DIGITS, 1 for 0bDIGITS, 2 for 0bDIGITS if ≠0
|
||||
* @return pointer to nul byte
|
||||
*/
|
||||
char *FormatBinary64(char p[hasatleast 67], uint64_t x, char z) {
|
||||
int i;
|
||||
uint64_t b;
|
||||
if (x) {
|
||||
if (z) {
|
||||
*p++ = '0';
|
||||
*p++ = 'b';
|
||||
}
|
||||
i = PickGoodWidth(bsrl(x));
|
||||
do {
|
||||
b = 1;
|
||||
b <<= i;
|
||||
*p++ = '0' + !!(x & b);
|
||||
} while (i--);
|
||||
} else {
|
||||
if (z == 1) {
|
||||
*p++ = '0';
|
||||
*p++ = 'b';
|
||||
}
|
||||
*p++ = '0';
|
||||
}
|
||||
*p = 0;
|
||||
return p;
|
||||
}
|
63
libc/fmt/formatflex64.c
Normal file
63
libc/fmt/formatflex64.c
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*-*- 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 2022 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/fmt/fmt.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/math.h"
|
||||
|
||||
static inline int CountZeroesHex(uint64_t x) {
|
||||
int n;
|
||||
for (n = 0; x;) {
|
||||
if (!(x & 15)) {
|
||||
++n;
|
||||
}
|
||||
x >>= 4;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static inline int CountZeroesDec(int64_t s) {
|
||||
int n, r;
|
||||
uint64_t x;
|
||||
x = s >= 0 ? s : -(uint64_t)s;
|
||||
for (n = 0; x;) {
|
||||
r = x % 10;
|
||||
x = x / 10;
|
||||
if (!r) ++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats integer using decimal or hexadecimal.
|
||||
*
|
||||
* We choose hex vs. decimal based on whichever one has the most zeroes.
|
||||
* We only bother counting zeroes for numbers outside -256 ≤ 𝑥 ≤ 256.
|
||||
*/
|
||||
char *FormatFlex64(char p[hasatleast 24], int64_t x, char z) {
|
||||
int zhex, zdec;
|
||||
if (-256 <= x && x <= 256) goto UseDecimal;
|
||||
zhex = CountZeroesHex(x) * 16;
|
||||
zdec = CountZeroesDec(x) * 10;
|
||||
if (zdec >= zhex) {
|
||||
UseDecimal:
|
||||
return FormatInt64(p, x);
|
||||
} else {
|
||||
return FormatHex64(p, x, z);
|
||||
}
|
||||
}
|
64
libc/fmt/formathex64.c
Normal file
64
libc/fmt/formathex64.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*-*- 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/fmt/itoa.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nexgen32e/bsr.h"
|
||||
|
||||
static inline int PickGoodWidth(unsigned x, char z) {
|
||||
if (z) {
|
||||
if (x < 16) {
|
||||
if (x < 8) return 8;
|
||||
return 16;
|
||||
} else {
|
||||
if (x < 32) return 32;
|
||||
return 64;
|
||||
}
|
||||
} else {
|
||||
return ROUNDUP(x + 1, 4);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts unsigned 64-bit integer to hex string.
|
||||
*
|
||||
* @param p needs at least 19 bytes
|
||||
* @param z is 0 for DIGITS, 1 for 0bDIGITS, 2 for 0bDIGITS if ≠0
|
||||
* @return pointer to nul byte
|
||||
*/
|
||||
char *FormatHex64(char p[hasatleast 19], uint64_t x, char z) {
|
||||
int i;
|
||||
if (x) {
|
||||
if (z) {
|
||||
*p++ = '0';
|
||||
*p++ = 'x';
|
||||
}
|
||||
i = PickGoodWidth(bsrl(x), z);
|
||||
do {
|
||||
*p++ = "0123456789abcdef"[(x >> (i -= 4)) & 15];
|
||||
} while (i);
|
||||
} else {
|
||||
if (z == 1) {
|
||||
*p++ = '0';
|
||||
*p++ = 'x';
|
||||
}
|
||||
*p++ = '0';
|
||||
}
|
||||
*p = 0;
|
||||
return p;
|
||||
}
|
|
@ -21,7 +21,7 @@
|
|||
/**
|
||||
* Converts unsigned 64-bit integer to octal string.
|
||||
*
|
||||
* @param p needs at least 12 bytes
|
||||
* @param p needs at least 24 bytes
|
||||
* @param z ensures it starts with zero
|
||||
* @return pointer to nul byte
|
||||
*/
|
||||
|
|
|
@ -15,6 +15,9 @@ char *FormatInt64Thousands(char[hasatleast 27], int64_t);
|
|||
char *FormatUint64Thousands(char[hasatleast 27], uint64_t);
|
||||
char *FormatOctal32(char[hasatleast 13], uint32_t, bool);
|
||||
char *FormatOctal64(char[hasatleast 24], uint64_t, bool);
|
||||
char *FormatBinary64(char[hasatleast 67], uint64_t, char);
|
||||
char *FormatHex64(char[hasatleast 19], uint64_t, char);
|
||||
char *FormatFlex64(char[hasatleast 24], int64_t, char);
|
||||
size_t uint64toarray_radix16(uint64_t, char[hasatleast 17]);
|
||||
size_t uint64toarray_fixed16(uint64_t, char[hasatleast 17], uint8_t);
|
||||
size_t uint64toarray_radix8(uint64_t, char[hasatleast 24]);
|
||||
|
|
98
test/libc/fmt/formatbinary64_test.c
Normal file
98
test/libc/fmt/formatbinary64_test.c
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*-*- 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 2022 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/fmt/fmt.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
char buf[67];
|
||||
|
||||
void SetUp(void) {
|
||||
memset(buf, 0x55, sizeof(buf));
|
||||
}
|
||||
|
||||
TEST(FormatBinary64, test1) {
|
||||
EXPECT_EQ(1, FormatBinary64(buf, 0, 2) - buf);
|
||||
EXPECT_STREQ("0", buf);
|
||||
}
|
||||
|
||||
TEST(FormatBinary64, test2) {
|
||||
EXPECT_EQ(1, FormatBinary64(buf, 0, 0) - buf);
|
||||
EXPECT_STREQ("0", buf);
|
||||
EXPECT_EQ(3, FormatBinary64(buf, 0, 1) - buf);
|
||||
EXPECT_STREQ("0b0", buf);
|
||||
}
|
||||
|
||||
TEST(FormatBinary64, test3) {
|
||||
EXPECT_EQ(3, FormatBinary64(buf, 1, 2) - buf);
|
||||
EXPECT_STREQ("0b1", buf);
|
||||
}
|
||||
|
||||
TEST(FormatBinary64, test4) {
|
||||
EXPECT_EQ(1, FormatBinary64(buf, 1, 0) - buf);
|
||||
EXPECT_STREQ("1", buf);
|
||||
}
|
||||
|
||||
TEST(FormatBinary64, test5) {
|
||||
EXPECT_EQ(66, FormatBinary64(buf, 01777777777777777777777UL, 2) - buf);
|
||||
EXPECT_STREQ(
|
||||
"0b1111111111111111111111111111111111111111111111111111111111111111",
|
||||
buf);
|
||||
}
|
||||
|
||||
TEST(FormatBinary64, test6) {
|
||||
EXPECT_EQ(64, FormatBinary64(buf, 01777777777777777777777UL, 0) - buf);
|
||||
EXPECT_STREQ(
|
||||
"1111111111111111111111111111111111111111111111111111111111111111", buf);
|
||||
}
|
||||
|
||||
TEST(FormatBinary64, test7) {
|
||||
EXPECT_EQ(66, FormatBinary64(buf, 0xEBF2AA499B9028EAul, 2) - buf);
|
||||
EXPECT_STREQ(
|
||||
"0b1110101111110010101010100100100110011011100100000010100011101010",
|
||||
buf);
|
||||
}
|
||||
|
||||
TEST(FormatBinary64, test8) {
|
||||
EXPECT_EQ(66, FormatBinary64(buf, 0x00F2AA499B9028EAul, 2) - buf);
|
||||
EXPECT_STREQ(
|
||||
"0b0000000011110010101010100100100110011011100100000010100011101010",
|
||||
buf);
|
||||
}
|
||||
|
||||
TEST(FormatBinary64, testScalesToWordSizes) {
|
||||
EXPECT_EQ(8 + 2, FormatBinary64(buf, 13, 2) - buf);
|
||||
EXPECT_STREQ("0b00001101", buf);
|
||||
EXPECT_EQ(16 + 2, FormatBinary64(buf, 31337, 2) - buf);
|
||||
EXPECT_STREQ("0b0111101001101001", buf);
|
||||
EXPECT_EQ(32 + 2, FormatBinary64(buf, 65536, 2) - buf);
|
||||
EXPECT_STREQ("0b00000000000000010000000000000000", buf);
|
||||
}
|
||||
|
||||
BENCH(FormatBinary64, bench) {
|
||||
EZBENCH2("FormatUint64 tiny", donothing, FormatUint64(buf, 1));
|
||||
EZBENCH2("FormatOctal64 tiny", donothing, FormatOctal64(buf, 1, true));
|
||||
EZBENCH2("FormatBinary64 tiny", donothing, FormatBinary64(buf, 1, 2));
|
||||
EZBENCH2("FormatUint64 big", donothing,
|
||||
FormatUint64(buf, 01777777777777777777777UL));
|
||||
EZBENCH2("FormatOctal64 big", donothing,
|
||||
FormatOctal64(buf, 01777777777777777777777UL, true));
|
||||
EZBENCH2("FormatBinary64 big", donothing,
|
||||
FormatBinary64(buf, 01777777777777777777777UL, 2));
|
||||
}
|
34
test/libc/fmt/formatflex64_test.c
Normal file
34
test/libc/fmt/formatflex64_test.c
Normal 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 2022 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/fmt/fmt.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
char buf[25];
|
||||
|
||||
void SetUp(void) {
|
||||
memset(buf, 0x55, sizeof(buf));
|
||||
}
|
||||
|
||||
TEST(formatflex64, test) {
|
||||
EXPECT_EQ(5, FormatFlex64(buf, 31337, 2) - buf);
|
||||
EXPECT_STREQ("31337", buf);
|
||||
EXPECT_EQ(10, FormatFlex64(buf, 0x80000000, 2) - buf);
|
||||
EXPECT_STREQ("0x80000000", buf);
|
||||
}
|
87
test/libc/fmt/formathex64_test.c
Normal file
87
test/libc/fmt/formathex64_test.c
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*-*- 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 2022 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/fmt/fmt.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
char buf[19];
|
||||
|
||||
void SetUp(void) {
|
||||
memset(buf, 0x55, sizeof(buf));
|
||||
}
|
||||
|
||||
TEST(FormatHex64, test1) {
|
||||
EXPECT_EQ(1, FormatHex64(buf, 0, 2) - buf);
|
||||
EXPECT_STREQ("0", buf);
|
||||
}
|
||||
|
||||
TEST(FormatHex64, test2) {
|
||||
EXPECT_EQ(1, FormatHex64(buf, 0, 0) - buf);
|
||||
EXPECT_STREQ("0", buf);
|
||||
EXPECT_EQ(3, FormatHex64(buf, 0, 1) - buf);
|
||||
EXPECT_STREQ("0x0", buf);
|
||||
}
|
||||
|
||||
TEST(FormatHex64, test3) {
|
||||
EXPECT_EQ(4, FormatHex64(buf, 1, 2) - buf);
|
||||
EXPECT_STREQ("0x01", buf);
|
||||
}
|
||||
|
||||
TEST(FormatHex64, test4) {
|
||||
EXPECT_EQ(1, FormatHex64(buf, 1, 0) - buf);
|
||||
EXPECT_STREQ("1", buf);
|
||||
}
|
||||
|
||||
TEST(FormatHex64, test5) {
|
||||
EXPECT_EQ(18, FormatHex64(buf, 01777777777777777777777UL, 2) - buf);
|
||||
EXPECT_STREQ("0xffffffffffffffff", buf);
|
||||
}
|
||||
|
||||
TEST(FormatHex64, test6) {
|
||||
EXPECT_EQ(16, FormatHex64(buf, 01777777777777777777777UL, 0) - buf);
|
||||
EXPECT_STREQ("ffffffffffffffff", buf);
|
||||
}
|
||||
|
||||
TEST(FormatHex64, test7) {
|
||||
EXPECT_EQ(18, FormatHex64(buf, 0xEBF2AA499B9028EAul, 2) - buf);
|
||||
EXPECT_STREQ("0xebf2aa499b9028ea", buf);
|
||||
}
|
||||
|
||||
TEST(FormatHex64, test8) {
|
||||
EXPECT_EQ(18, FormatHex64(buf, 0x00F2AA499B9028EAul, 2) - buf);
|
||||
EXPECT_STREQ("0x00f2aa499b9028ea", buf);
|
||||
}
|
||||
|
||||
TEST(FormatHex64, testScalesToWordSizes) {
|
||||
EXPECT_EQ(2 + 2, FormatHex64(buf, 13, 2) - buf);
|
||||
EXPECT_STREQ("0x0d", buf);
|
||||
EXPECT_EQ(4 + 2, FormatHex64(buf, 31337, 2) - buf);
|
||||
EXPECT_STREQ("0x7a69", buf);
|
||||
EXPECT_EQ(8 + 2, FormatHex64(buf, 65536, 2) - buf);
|
||||
EXPECT_STREQ("0x00010000", buf);
|
||||
}
|
||||
|
||||
BENCH(FormatHex64, bench) {
|
||||
EZBENCH2("FormatUint64 tiny", donothing, FormatUint64(buf, 1));
|
||||
EZBENCH2("FormatOctal64 tiny", donothing, FormatOctal64(buf, 1, true));
|
||||
EZBENCH2("FormatHex64 tiny", donothing, FormatHex64(buf, 1, 2));
|
||||
EZBENCH2("FormatHex64 big", donothing,
|
||||
FormatHex64(buf, 01777777777777777777777UL, 2));
|
||||
}
|
|
@ -21,7 +21,7 @@
|
|||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
char buf[25];
|
||||
char buf[24];
|
||||
|
||||
void SetUp(void) {
|
||||
memset(buf, 0x55, sizeof(buf));
|
||||
|
|
2
third_party/lua/README.cosmo
vendored
2
third_party/lua/README.cosmo
vendored
|
@ -24,6 +24,8 @@ LOCAL MODIFICATIONS
|
|||
|
||||
Integer literals such as `033` will now be interpreted as octal.
|
||||
|
||||
Integer literals such as `0b10` will now be interpreted as binary.
|
||||
|
||||
The `\e` string literal escape sequence has been added, which is
|
||||
equivalent to `\27` (the Lua version of `\033`) or the ASCII ESC
|
||||
character. It may be used for teletypewriter control like having
|
||||
|
|
2
third_party/lua/cosmo.h
vendored
2
third_party/lua/cosmo.h
vendored
|
@ -15,9 +15,9 @@ int LuaParseUrl(lua_State *);
|
|||
int LuaPushHeader(lua_State *, struct HttpMessage *, char *, int);
|
||||
int LuaPushHeaders(lua_State *, struct HttpMessage *, const char *);
|
||||
void EscapeLuaString(char *, size_t, char **);
|
||||
void LuaPrintStack(lua_State *);
|
||||
void LuaPushLatin1(lua_State *, const char *, size_t);
|
||||
void LuaPushUrlParams(lua_State *, struct UrlParams *);
|
||||
void LuaPrintStack(lua_State *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
101
third_party/lua/lapi.c
vendored
101
third_party/lua/lapi.c
vendored
|
@ -504,26 +504,51 @@ static void *touserdata (const TValue *o) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* lua_touserdata [-0, +0, –]
|
||||
*
|
||||
* If the value at the given index is a full userdata, returns its
|
||||
* memory-block address. If the value is a light userdata, returns its value
|
||||
* (a pointer). Otherwise, returns NULL.
|
||||
*/
|
||||
LUA_API void *lua_touserdata (lua_State *L, int idx) {
|
||||
const TValue *o = index2value(L, idx);
|
||||
return touserdata(o);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* lua_tothread [-0, +0, –]
|
||||
*
|
||||
* Converts the value at the given index to a Lua thread (represented as
|
||||
* lua_State*). This value must be a thread; otherwise, the function returns
|
||||
* NULL.
|
||||
*/
|
||||
LUA_API lua_State *lua_tothread (lua_State *L, int idx) {
|
||||
const TValue *o = index2value(L, idx);
|
||||
return (!ttisthread(o)) ? NULL : thvalue(o);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Returns a pointer to the internal representation of an object.
|
||||
** Note that ANSI C does not allow the conversion of a pointer to
|
||||
** function to a 'void*', so the conversion here goes through
|
||||
** a 'size_t'. (As the returned pointer is only informative, this
|
||||
** conversion should not be a problem.)
|
||||
*/
|
||||
/**
|
||||
* lua_topointer [-0, +0, –]
|
||||
*
|
||||
* Converts the value at the given index to a generic C pointer (void*). The
|
||||
* value can be a userdata, a table, a thread, a string, or a function;
|
||||
* otherwise, lua_topointer returns NULL. Different objects will give
|
||||
* different pointers. There is no way to convert the pointer back to its
|
||||
* original value.
|
||||
*
|
||||
* Typically this function is used only for hashing and debug information.
|
||||
*/
|
||||
LUA_API const void *lua_topointer (lua_State *L, int idx) {
|
||||
/*
|
||||
** Returns a pointer to the internal representation of an object.
|
||||
** Note that ANSI C does not allow the conversion of a pointer to
|
||||
** function to a 'void*', so the conversion here goes through
|
||||
** a 'size_t'. (As the returned pointer is only informative, this
|
||||
** conversion should not be a problem.)
|
||||
*/
|
||||
const TValue *o = index2value(L, idx);
|
||||
switch (ttypetag(o)) {
|
||||
case LUA_VLCF: return cast_voidp(cast_sizet(fvalue(o)));
|
||||
|
@ -881,6 +906,12 @@ static Table *gettable (lua_State *L, int idx) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* lua_rawget [-1, +1, –]
|
||||
*
|
||||
* Similar to lua_gettable, but does a raw access (i.e., without
|
||||
* metamethods).
|
||||
*/
|
||||
LUA_API int lua_rawget (lua_State *L, int idx) {
|
||||
Table *t;
|
||||
const TValue *val;
|
||||
|
@ -901,6 +932,15 @@ LUA_API int lua_rawgeti (lua_State *L, int idx, lua_Integer n) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* lua_rawgetp [-0, +1, –]
|
||||
*
|
||||
* Pushes onto the stack the value t[k], where t is the table at the given
|
||||
* index and k is the pointer p represented as a light userdata. The access
|
||||
* is raw; that is, it does not use the __index metavalue.
|
||||
*
|
||||
* Returns the type of the pushed value.
|
||||
*/
|
||||
LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) {
|
||||
Table *t;
|
||||
TValue k;
|
||||
|
@ -911,6 +951,17 @@ LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* lua_createtable [-0, +1, m]
|
||||
*
|
||||
* Creates a new empty table and pushes it onto the stack. Parameter narr is
|
||||
* a hint for how many elements the table will have as a sequence; parameter
|
||||
* nrec is a hint for how many other elements the table will have. Lua may
|
||||
* use these hints to preallocate memory for the new table. This
|
||||
* preallocation may help performance when you know in advance how many
|
||||
* elements the table will have. Otherwise you can use the function
|
||||
* lua_newtable.
|
||||
*/
|
||||
LUA_API void lua_createtable (lua_State *L, int narray, int nrec) {
|
||||
Table *t;
|
||||
lua_lock(L);
|
||||
|
@ -924,6 +975,15 @@ LUA_API void lua_createtable (lua_State *L, int narray, int nrec) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* lua_getmetatable [-0, +(0|1), –]
|
||||
*
|
||||
* int lua_getmetatable (lua_State *L, int index);
|
||||
*
|
||||
* If the value at the given index has a metatable, the function pushes that
|
||||
* metatable onto the stack and returns 1. Otherwise, the function returns 0
|
||||
* and pushes nothing on the stack.
|
||||
*/
|
||||
LUA_API int lua_getmetatable (lua_State *L, int objindex) {
|
||||
const TValue *obj;
|
||||
Table *mt;
|
||||
|
@ -951,6 +1011,17 @@ LUA_API int lua_getmetatable (lua_State *L, int objindex) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* lua_getiuservalue [-0, +1, –]
|
||||
*
|
||||
* int lua_getiuservalue (lua_State *L, int index, int n);
|
||||
*
|
||||
* Pushes onto the stack the n-th user value associated with the full
|
||||
* userdata at the given index and returns the type of the pushed value.
|
||||
*
|
||||
* If the userdata does not have that value, pushes nil and returns
|
||||
* LUA_TNONE.
|
||||
*/
|
||||
LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) {
|
||||
TValue *o;
|
||||
int t;
|
||||
|
@ -1116,6 +1187,15 @@ LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* lua_setmetatable [-1, +0, –]
|
||||
*
|
||||
* Pops a table or nil from the stack and sets that value as the new
|
||||
* metatable for the value at the given index. (nil means no metatable.)
|
||||
*
|
||||
* (For historical reasons, this function returns an int, which now is always
|
||||
* 1.)
|
||||
*/
|
||||
LUA_API int lua_setmetatable (lua_State *L, int objindex) {
|
||||
TValue *obj;
|
||||
Table *mt;
|
||||
|
@ -1156,6 +1236,13 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* lua_setiuservalue [-1, +0, –]
|
||||
*
|
||||
* Pops a value from the stack and sets it as the new n-th user value
|
||||
* associated to the full userdata at the given index. Returns 0 if the
|
||||
* userdata does not have that value.
|
||||
*/
|
||||
LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) {
|
||||
TValue *o;
|
||||
int res;
|
||||
|
|
6
third_party/lua/lctype.h
vendored
6
third_party/lua/lctype.h
vendored
|
@ -45,6 +45,12 @@
|
|||
('a' <= c_ && c_ <= 'f')); \
|
||||
})
|
||||
|
||||
#define lisbdigit(C) \
|
||||
({ \
|
||||
unsigned char c_ = (C); \
|
||||
'0' <= c_&& c_ <= '1'; \
|
||||
})
|
||||
|
||||
#define lisprint(C) \
|
||||
({ \
|
||||
unsigned char c_ = (C); \
|
||||
|
|
9
third_party/lua/lobject.c
vendored
9
third_party/lua/lobject.c
vendored
|
@ -27,7 +27,6 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#define lobject_c
|
||||
#define LUA_CORE
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "third_party/lua/lctype.h"
|
||||
#include "third_party/lua/ldebug.h"
|
||||
#include "third_party/lua/ldo.h"
|
||||
|
@ -285,6 +284,14 @@ static const char *l_str2int (const char *s, lua_Integer *result) {
|
|||
empty = 0;
|
||||
}
|
||||
}
|
||||
if (s[0] == '0' &&
|
||||
(s[1] == 'b' || s[1] == 'B')) { /* [jart] binary */
|
||||
s += 2; /* skip '0b' */
|
||||
for (; lisbdigit(cast_uchar(*s)); s++) {
|
||||
a = a * 2 + (*s - '0');
|
||||
empty = 0;
|
||||
}
|
||||
}
|
||||
else if (s[0] == '0') { /* [jart] octal is the best radix */
|
||||
for (s += 1; lisdigit(cast_uchar(*s)); s++) {
|
||||
int d = *s - '0';
|
||||
|
|
4
third_party/lua/lrepl.c
vendored
4
third_party/lua/lrepl.c
vendored
|
@ -30,13 +30,13 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/nomultics.internal.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/gc.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "third_party/linenoise/linenoise.h"
|
||||
|
@ -330,7 +330,7 @@ void lua_initrepl(lua_State *L, const char *progname) {
|
|||
prompt = get_prompt(L, 1);
|
||||
if ((g_historypath = linenoiseGetHistoryPath(progname))) {
|
||||
if (linenoiseHistoryLoad(g_historypath) == -1) {
|
||||
kprintf("%r%s: failed to load history: %m%n", g_historypath);
|
||||
fprintf(stderr, "%r%s: failed to load history: %m%n", g_historypath);
|
||||
free(g_historypath);
|
||||
g_historypath = 0;
|
||||
}
|
||||
|
|
139
third_party/lua/luaencodejsondata.c
vendored
139
third_party/lua/luaencodejsondata.c
vendored
|
@ -16,23 +16,37 @@
|
|||
│ 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/fmt/itoa.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/gc.internal.h"
|
||||
#include "libc/stdio/append.internal.h"
|
||||
#include "net/http/escape.h"
|
||||
#include "third_party/lua/cosmo.h"
|
||||
#include "third_party/lua/lauxlib.h"
|
||||
#include "third_party/lua/lua.h"
|
||||
#include "third_party/lua/visitor.h"
|
||||
|
||||
static int LuaEncodeJsonDataImpl(lua_State *L, char **buf, int level,
|
||||
char *numformat, int idx) {
|
||||
char *numformat, int idx,
|
||||
struct LuaVisited *visited) {
|
||||
char *s;
|
||||
bool isarray;
|
||||
size_t tbllen, z;
|
||||
size_t tbllen, i, z;
|
||||
char ibuf[21], fmt[] = "%.14g";
|
||||
if (level > 0) {
|
||||
switch (lua_type(L, idx)) {
|
||||
|
||||
case LUA_TNIL:
|
||||
appendw(buf, READ32LE("null"));
|
||||
return 0;
|
||||
|
||||
case LUA_TBOOLEAN:
|
||||
appendw(buf, lua_toboolean(L, idx) ? READ32LE("true")
|
||||
: READ64LE("false\0\0"));
|
||||
return 0;
|
||||
|
||||
case LUA_TSTRING:
|
||||
s = lua_tolstring(L, idx, &z);
|
||||
s = EscapeJsStringLiteral(s, z, &z);
|
||||
|
@ -41,21 +55,7 @@ static int LuaEncodeJsonDataImpl(lua_State *L, char **buf, int level,
|
|||
appendw(buf, '"');
|
||||
free(s);
|
||||
return 0;
|
||||
case LUA_TNIL:
|
||||
appendw(buf, READ32LE("null"));
|
||||
return 0;
|
||||
case LUA_TFUNCTION:
|
||||
appendf(buf, "\"func@%p\"", lua_touserdata(L, idx));
|
||||
return 0;
|
||||
case LUA_TUSERDATA:
|
||||
appendf(buf, "\"udata@%p\"", lua_touserdata(L, idx));
|
||||
return 0;
|
||||
case LUA_TLIGHTUSERDATA:
|
||||
appendf(buf, "\"light@%p\"", lua_touserdata(L, idx));
|
||||
return 0;
|
||||
case LUA_TTHREAD:
|
||||
appendf(buf, "\"thread@%p\"", lua_touserdata(L, idx));
|
||||
return 0;
|
||||
|
||||
case LUA_TNUMBER:
|
||||
if (lua_isinteger(L, idx)) {
|
||||
appendd(buf, ibuf,
|
||||
|
@ -78,58 +78,64 @@ static int LuaEncodeJsonDataImpl(lua_State *L, char **buf, int level,
|
|||
appendf(buf, fmt, lua_tonumber(L, idx));
|
||||
}
|
||||
return 0;
|
||||
case LUA_TBOOLEAN:
|
||||
appends(buf, lua_toboolean(L, idx) ? "true" : "false");
|
||||
return 0;
|
||||
|
||||
case LUA_TTABLE:
|
||||
tbllen = lua_rawlen(L, idx);
|
||||
// encode tables with numeric indices and empty tables as arrays
|
||||
isarray =
|
||||
tbllen > 0 || // integer keys present
|
||||
(lua_pushnil(L), lua_next(L, -2) == 0) || // no non-integer keys
|
||||
(lua_pop(L, 2), false); // pop key/value pushed by lua_next
|
||||
appendw(buf, isarray ? '[' : '{');
|
||||
if (isarray) {
|
||||
for (size_t i = 1; i <= tbllen; i++) {
|
||||
if (i > 1) appendw(buf, ',');
|
||||
lua_rawgeti(L, -1, i); // table/-2, value/-1
|
||||
LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -1);
|
||||
lua_pop(L, 1);
|
||||
if (LuaPushVisit(visited, lua_topointer(L, idx))) {
|
||||
tbllen = lua_rawlen(L, idx);
|
||||
// encode tables with numeric indices and empty tables as arrays
|
||||
isarray =
|
||||
tbllen > 0 || // integer keys present
|
||||
(lua_pushnil(L), !lua_next(L, -2)) || // no non-integer keys
|
||||
(lua_pop(L, 2), false); // pop key/value pushed by lua_next
|
||||
appendw(buf, isarray ? '[' : '{');
|
||||
if (isarray) {
|
||||
for (i = 1; i <= tbllen; i++) {
|
||||
if (i > 1) appendw(buf, ',');
|
||||
lua_rawgeti(L, -1, i); // table/-2, value/-1
|
||||
LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -1, visited);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
} else {
|
||||
i = 1;
|
||||
lua_pushnil(L); // push the first key
|
||||
while (lua_next(L, -2)) {
|
||||
if (!lua_isstring(L, -2)) {
|
||||
luaL_error(L, "expected number or string as key value");
|
||||
unreachable;
|
||||
}
|
||||
if (i++ > 1) appendw(buf, ',');
|
||||
appendw(buf, '"');
|
||||
if (lua_type(L, -2) == LUA_TSTRING) {
|
||||
s = lua_tolstring(L, -2, &z);
|
||||
s = EscapeJsStringLiteral(s, z, &z);
|
||||
appendd(buf, s, z);
|
||||
free(s);
|
||||
} else {
|
||||
// we'd still prefer to use lua_tostring on a numeric index, but
|
||||
// can't use it in-place, as it breaks lua_next (changes numeric
|
||||
// key to a string)
|
||||
lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1
|
||||
s = lua_tolstring(L, idx, &z);
|
||||
appendd(buf, s, z); // use the copy
|
||||
lua_remove(L, -1); // remove copied key: tab/-3, key/-2, val/-1
|
||||
}
|
||||
appendw(buf, '"' | ':' << 010);
|
||||
LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -1, visited);
|
||||
lua_pop(L, 1); // table/-2, key/-1
|
||||
}
|
||||
// stack: table/-1, as the key was popped by lua_next
|
||||
}
|
||||
appendw(buf, isarray ? ']' : '}');
|
||||
LuaPopVisit(visited);
|
||||
return 0;
|
||||
} else {
|
||||
int i = 1;
|
||||
lua_pushnil(L); // push the first key
|
||||
while (lua_next(L, -2)) {
|
||||
if (!lua_isstring(L, -2)) {
|
||||
luaL_error(L, "expected number or string as key value");
|
||||
unreachable;
|
||||
}
|
||||
if (i++ > 1) appendw(buf, ',');
|
||||
appendw(buf, '"');
|
||||
if (lua_type(L, -2) == LUA_TSTRING) {
|
||||
s = lua_tolstring(L, -2, &z);
|
||||
s = EscapeJsStringLiteral(s, z, &z);
|
||||
appendd(buf, s, z);
|
||||
free(s);
|
||||
} else {
|
||||
// we'd still prefer to use lua_tostring on a numeric index, but
|
||||
// can't use it in-place, as it breaks lua_next (changes numeric
|
||||
// key to a string)
|
||||
lua_pushvalue(L, -2); // table/-4, key/-3, value/-2, key/-1
|
||||
s = lua_tolstring(L, idx, &z);
|
||||
appendd(buf, s, z); // use the copy
|
||||
lua_remove(L, -1); // remove copied key: tab/-3, key/-2, val/-1
|
||||
}
|
||||
appendw(buf, '"' | ':' << 010);
|
||||
LuaEncodeJsonDataImpl(L, buf, level - 1, numformat, -1);
|
||||
lua_pop(L, 1); // table/-2, key/-1
|
||||
}
|
||||
// stack: table/-1, as the key was popped by lua_next
|
||||
// TODO(jart): don't leak memory with longjmp
|
||||
luaL_error(L, "can't serialize cyclic data structure to json");
|
||||
unreachable;
|
||||
}
|
||||
appendw(buf, isarray ? ']' : '}');
|
||||
return 0;
|
||||
|
||||
default:
|
||||
luaL_error(L, "can't serialize value of this type");
|
||||
luaL_error(L, "won't serialize %s to json", luaL_typename(L, idx));
|
||||
unreachable;
|
||||
}
|
||||
} else {
|
||||
|
@ -140,6 +146,9 @@ static int LuaEncodeJsonDataImpl(lua_State *L, char **buf, int level,
|
|||
|
||||
int LuaEncodeJsonData(lua_State *L, char **buf, char *numformat, int idx) {
|
||||
int rc;
|
||||
rc = LuaEncodeJsonDataImpl(L, buf, 64, numformat, idx);
|
||||
struct LuaVisited visited = {0};
|
||||
rc = LuaEncodeJsonDataImpl(L, buf, 64, numformat, idx, &visited);
|
||||
assert(!visited.n);
|
||||
free(visited.p);
|
||||
return rc;
|
||||
}
|
||||
|
|
115
third_party/lua/luaencodeluadata.c
vendored
115
third_party/lua/luaencodeluadata.c
vendored
|
@ -25,39 +25,44 @@
|
|||
#include "libc/x/x.h"
|
||||
#include "third_party/lua/cosmo.h"
|
||||
#include "third_party/lua/lauxlib.h"
|
||||
#include "third_party/lua/lctype.h"
|
||||
#include "third_party/lua/lua.h"
|
||||
#include "third_party/lua/visitor.h"
|
||||
|
||||
struct Visited {
|
||||
int n;
|
||||
void **p;
|
||||
};
|
||||
|
||||
static bool PushVisit(struct Visited *visited, void *p) {
|
||||
int i;
|
||||
for (i = 0; i < visited->n; ++i) {
|
||||
if (visited->p[i] == p) {
|
||||
return false;
|
||||
}
|
||||
static bool IsLuaIdentifier(lua_State *L, int idx) {
|
||||
size_t i, n;
|
||||
const char *p;
|
||||
p = luaL_checklstring(L, idx, &n);
|
||||
if (!lislalpha(p[0])) return false;
|
||||
for (i = 1; i < n; ++i) {
|
||||
if (!lislalnum(p[i])) return false;
|
||||
}
|
||||
visited->p = xrealloc(visited->p, ++visited->n * sizeof(*visited->p));
|
||||
visited->p[visited->n - 1] = p;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void PopVisit(struct Visited *visited) {
|
||||
assert(visited->n > 0);
|
||||
--visited->n;
|
||||
// TODO: Can we be smarter with lua_rawlen?
|
||||
static bool IsLuaArray(lua_State *L) {
|
||||
int i;
|
||||
lua_pushnil(L);
|
||||
for (i = 1; lua_next(L, -2); ++i) {
|
||||
if (!lua_isinteger(L, -2) || lua_tointeger(L, -2) != i) {
|
||||
lua_pop(L, 2);
|
||||
return false;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level,
|
||||
char *numformat, int idx,
|
||||
struct Visited *visited) {
|
||||
struct LuaVisited *visited) {
|
||||
char *s;
|
||||
bool didcomma;
|
||||
bool isarray;
|
||||
lua_Integer i;
|
||||
int ktype, vtype;
|
||||
size_t tbllen, buflen, slen;
|
||||
char ibuf[21], fmt[] = "%.14g";
|
||||
char ibuf[24], fmt[] = "%.14g";
|
||||
if (level > 0) {
|
||||
switch (lua_type(L, idx)) {
|
||||
|
||||
|
@ -71,15 +76,15 @@ static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level,
|
|||
return 0;
|
||||
|
||||
case LUA_TFUNCTION:
|
||||
appendf(buf, "\"func@%p\"", lua_touserdata(L, idx));
|
||||
appendf(buf, "\"func@%p\"", lua_topointer(L, idx));
|
||||
return 0;
|
||||
|
||||
case LUA_TLIGHTUSERDATA:
|
||||
appendf(buf, "\"light@%p\"", lua_touserdata(L, idx));
|
||||
appendf(buf, "\"light@%p\"", lua_topointer(L, idx));
|
||||
return 0;
|
||||
|
||||
case LUA_TTHREAD:
|
||||
appendf(buf, "\"thread@%p\"", lua_touserdata(L, idx));
|
||||
appendf(buf, "\"thread@%p\"", lua_topointer(L, idx));
|
||||
return 0;
|
||||
|
||||
case LUA_TUSERDATA:
|
||||
|
@ -111,7 +116,7 @@ static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level,
|
|||
case LUA_TNUMBER:
|
||||
if (lua_isinteger(L, idx)) {
|
||||
appendd(buf, ibuf,
|
||||
FormatInt64(ibuf, luaL_checkinteger(L, idx)) - ibuf);
|
||||
FormatFlex64(ibuf, luaL_checkinteger(L, idx), 2) - ibuf);
|
||||
} else {
|
||||
// TODO(jart): replace this api
|
||||
while (*numformat == '%' || *numformat == '.' ||
|
||||
|
@ -133,58 +138,52 @@ static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level,
|
|||
return 0;
|
||||
|
||||
case LUA_TBOOLEAN:
|
||||
if (lua_toboolean(L, idx)) {
|
||||
appendw(buf, READ32LE("true"));
|
||||
} else {
|
||||
appendw(buf, READ64LE("false\0\0"));
|
||||
}
|
||||
appendw(buf, lua_toboolean(L, idx) ? READ32LE("true")
|
||||
: READ64LE("false\0\0"));
|
||||
return 0;
|
||||
|
||||
case LUA_TTABLE:
|
||||
i = 0;
|
||||
didcomma = false;
|
||||
appendw(buf, '{');
|
||||
lua_pushvalue(L, idx);
|
||||
lua_pushnil(L); // push the first key
|
||||
while (lua_next(L, -2)) {
|
||||
++i;
|
||||
ktype = lua_type(L, -2);
|
||||
vtype = lua_type(L, -1);
|
||||
if (ktype != LUA_TNUMBER || lua_tointeger(L, -2) != i) {
|
||||
if (PushVisit(visited, lua_touserdata(L, -2))) {
|
||||
if (i > 1) appendw(buf, ',' | ' ' << 8);
|
||||
didcomma = true;
|
||||
if (LuaPushVisit(visited, lua_topointer(L, idx))) {
|
||||
appendw(buf, '{');
|
||||
lua_pushvalue(L, idx); // idx becomes invalid once we change stack
|
||||
isarray = IsLuaArray(L);
|
||||
lua_pushnil(L); // push the first key
|
||||
while (lua_next(L, -2)) {
|
||||
ktype = lua_type(L, -2);
|
||||
vtype = lua_type(L, -1);
|
||||
if (i++ > 0) appendw(buf, ',' | ' ' << 8);
|
||||
if (isarray) {
|
||||
// use {v₁′,v₂′,...} for lua-normal integer keys
|
||||
} else if (ktype == LUA_TSTRING && IsLuaIdentifier(L, -2)) {
|
||||
// use {𝑘=𝑣′} syntax when 𝑘 is legal as a lua identifier
|
||||
s = lua_tolstring(L, -2, &slen);
|
||||
appendd(buf, s, slen);
|
||||
appendw(buf, '=');
|
||||
} else {
|
||||
// use {[𝑘′]=𝑣′} otherwise
|
||||
appendw(buf, '[');
|
||||
LuaEncodeLuaDataImpl(L, buf, level - 1, numformat, -2, visited);
|
||||
appendw(buf, ']' | '=' << 010);
|
||||
PopVisit(visited);
|
||||
} else {
|
||||
// TODO: Strange Lua data structure, do nothing.
|
||||
lua_pop(L, 1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (PushVisit(visited, lua_touserdata(L, -1))) {
|
||||
if (!didcomma && i > 1) appendw(buf, ',' | ' ' << 8);
|
||||
LuaEncodeLuaDataImpl(L, buf, level - 1, numformat, -1, visited);
|
||||
PopVisit(visited);
|
||||
} else {
|
||||
// TODO: Strange Lua data structure, do nothing.
|
||||
lua_pop(L, 1);
|
||||
continue;
|
||||
lua_pop(L, 1); // table/-2, key/-1
|
||||
}
|
||||
lua_pop(L, 1); // table/-2, key/-1
|
||||
lua_pop(L, 1); // table ref
|
||||
LuaPopVisit(visited);
|
||||
appendw(buf, '}');
|
||||
} else {
|
||||
appendf(buf, "\"cyclic@%p\"", lua_topointer(L, idx));
|
||||
}
|
||||
lua_pop(L, 1); // table
|
||||
// stack: table/-1, as the key was popped by lua_next
|
||||
appendw(buf, '}');
|
||||
return 0;
|
||||
|
||||
default:
|
||||
// TODO(jart): don't leak memory with longjmp
|
||||
luaL_error(L, "can't serialize value of this type");
|
||||
unreachable;
|
||||
}
|
||||
} else {
|
||||
// TODO(jart): don't leak memory with longjmp
|
||||
luaL_error(L, "too many nested tables");
|
||||
unreachable;
|
||||
}
|
||||
|
@ -192,7 +191,7 @@ static int LuaEncodeLuaDataImpl(lua_State *L, char **buf, int level,
|
|||
|
||||
int LuaEncodeLuaData(lua_State *L, char **buf, char *numformat, int idx) {
|
||||
int rc;
|
||||
struct Visited visited = {0};
|
||||
struct LuaVisited visited = {0};
|
||||
rc = LuaEncodeLuaDataImpl(L, buf, 64, numformat, idx, &visited);
|
||||
assert(!visited.n);
|
||||
free(visited.p);
|
||||
|
|
38
third_party/lua/visitor.c
vendored
Normal file
38
third_party/lua/visitor.c
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*-*- 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 2022 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/assert.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "third_party/lua/visitor.h"
|
||||
|
||||
bool LuaPushVisit(struct LuaVisited *visited, const void *p) {
|
||||
int i;
|
||||
for (i = 0; i < visited->n; ++i) {
|
||||
if (visited->p[i] == p) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
visited->p = xrealloc(visited->p, ++visited->n * sizeof(*visited->p));
|
||||
visited->p[visited->n - 1] = p;
|
||||
return true;
|
||||
}
|
||||
|
||||
void LuaPopVisit(struct LuaVisited *visited) {
|
||||
assert(visited->n > 0);
|
||||
--visited->n;
|
||||
}
|
16
third_party/lua/visitor.h
vendored
Normal file
16
third_party/lua/visitor.h
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef COSMOPOLITAN_THIRD_PARTY_LUA_VISITOR_H_
|
||||
#define COSMOPOLITAN_THIRD_PARTY_LUA_VISITOR_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct LuaVisited {
|
||||
int n;
|
||||
const void **p;
|
||||
};
|
||||
|
||||
bool LuaPushVisit(struct LuaVisited *, const void *);
|
||||
void LuaPopVisit(struct LuaVisited *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_THIRD_PARTY_LUA_VISITOR_H_ */
|
|
@ -464,6 +464,7 @@ int main(int argc, char *argv[]) {
|
|||
/* __log_level = kLogDebug; */
|
||||
GetOpts(argc, argv);
|
||||
for (i = 3; i < 16; ++i) close(i);
|
||||
errno = 0;
|
||||
// poll()'ing /dev/null stdin file descriptor on xnu returns POLLNVAL?!
|
||||
if (IsWindows()) {
|
||||
CHECK_EQ(3, (g_bogusfd = open("/dev/null", O_RDONLY | O_CLOEXEC)));
|
||||
|
|
|
@ -357,6 +357,10 @@ LUA ENHANCEMENTS
|
|||
`0644 == 420` is the case in redbean, whereas in upstream Lua
|
||||
`0644 == 644` would be the case.
|
||||
|
||||
- redbean supports binary (base 2) integer literals. For example
|
||||
`0b1010 == 10` is the case in redbean, whereas in upstream Lua
|
||||
`0b1010` would result in an error.
|
||||
|
||||
- redbean supports the GNU syntax for the ASCII ESC character in
|
||||
string literals. For example, `"\e"` is the same as `"\x1b"`.
|
||||
|
||||
|
@ -1284,6 +1288,31 @@ FUNCTIONS
|
|||
possibly because your system is under load and the benchmark was
|
||||
preempted by the operating system, or moved to a different core.
|
||||
|
||||
oct(int)
|
||||
└─→ str
|
||||
|
||||
Formats string as octal integer literal string. If the provided
|
||||
value is zero, the result will be `"0"`. Otherwise the resulting
|
||||
value will be the zero-prefixed octal string. The result is
|
||||
currently modulo 2^64. Negative numbers are converted to unsigned.
|
||||
|
||||
hex(int)
|
||||
└─→ str
|
||||
|
||||
Formats string as hexadecimal integer literal string. If the
|
||||
provided value is zero, the result will be `"0"`. Otherwise the
|
||||
resulting value will be the `"0x"`-prefixed hex string. The result
|
||||
is currently modulo 2^64. Negative numbers are converted to
|
||||
unsigned.
|
||||
|
||||
bin(int)
|
||||
└─→ str
|
||||
|
||||
Formats string as binary integer literal string. If the provided
|
||||
value is zero, the result will be `"0"`. Otherwise the resulting
|
||||
value will be the `"0b"`-prefixed binary str. The result is
|
||||
currently modulo 2^64. Negative numbers are converted to unsigned.
|
||||
|
||||
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
@ -1745,6 +1774,27 @@ UNIX MODULE
|
|||
end
|
||||
end
|
||||
|
||||
unix.WIFEXITED(wstatus:int)
|
||||
└─→ bool
|
||||
|
||||
Returns true if process exited cleanly.
|
||||
|
||||
unix.WEXITSTATUS(wstatus:int)
|
||||
└─→ exitcode:uint8
|
||||
|
||||
Returns code passed to exit() assuming `WIFEXITED(wstatus)` is true.
|
||||
|
||||
unix.WIFSIGNALED(wstatus:int)
|
||||
└─→ bool
|
||||
|
||||
Returns true if process terminated due to a signal.
|
||||
|
||||
unix.WTERMSIG(wstatus:int)
|
||||
└─→ sig:uint8
|
||||
|
||||
Returns signal that caused process to terminate assuming
|
||||
`WIFSIGNALED(wstatus)` is true.
|
||||
|
||||
unix.getpid()
|
||||
└─→ pid:int
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "libc/bits/popcnt.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/rusage.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
|
@ -60,6 +61,33 @@ static int Rdpid(void) {
|
|||
return rdpid();
|
||||
}
|
||||
|
||||
int LuaHex(lua_State *L) {
|
||||
char b[19];
|
||||
uint64_t x;
|
||||
x = luaL_checkinteger(L, 1);
|
||||
FormatHex64(b, x, true);
|
||||
lua_pushstring(L, b);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int LuaOct(lua_State *L) {
|
||||
char b[24];
|
||||
uint64_t x;
|
||||
x = luaL_checkinteger(L, 1);
|
||||
FormatOctal64(b, x, true);
|
||||
lua_pushstring(L, b);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int LuaBin(lua_State *L) {
|
||||
char b[67];
|
||||
uint64_t x;
|
||||
x = luaL_checkinteger(L, 1);
|
||||
FormatBinary64(b, x, 2);
|
||||
lua_pushstring(L, b);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int LuaGetTime(lua_State *L) {
|
||||
lua_pushnumber(L, nowl());
|
||||
return 1;
|
||||
|
@ -258,7 +286,7 @@ int LuaPopcnt(lua_State *L) {
|
|||
int LuaBsr(lua_State *L) {
|
||||
long x;
|
||||
if ((x = luaL_checkinteger(L, 1))) {
|
||||
lua_pushinteger(L, bsr(x));
|
||||
lua_pushinteger(L, bsrl(x));
|
||||
return 1;
|
||||
} else {
|
||||
luaL_argerror(L, 1, "zero");
|
||||
|
@ -269,7 +297,7 @@ int LuaBsr(lua_State *L) {
|
|||
int LuaBsf(lua_State *L) {
|
||||
long x;
|
||||
if ((x = luaL_checkinteger(L, 1))) {
|
||||
lua_pushinteger(L, bsf(x));
|
||||
lua_pushinteger(L, bsfl(x));
|
||||
return 1;
|
||||
} else {
|
||||
luaL_argerror(L, 1, "zero");
|
||||
|
|
|
@ -12,6 +12,7 @@ int luaopen_argon2(lua_State *);
|
|||
int luaopen_lsqlite3(lua_State *);
|
||||
|
||||
int LuaBenchmark(lua_State *);
|
||||
int LuaBin(lua_State *);
|
||||
int LuaBsf(lua_State *);
|
||||
int LuaBsr(lua_State *);
|
||||
int LuaCategorizeIp(lua_State *);
|
||||
|
@ -45,6 +46,7 @@ int LuaGetMonospaceWidth(lua_State *);
|
|||
int LuaGetRandomBytes(lua_State *);
|
||||
int LuaGetTime(lua_State *);
|
||||
int LuaHasControlCodes(lua_State *);
|
||||
int LuaHex(lua_State *);
|
||||
int LuaIndentLines(lua_State *);
|
||||
int LuaIsAcceptableHost(lua_State *);
|
||||
int LuaIsAcceptablePath(lua_State *);
|
||||
|
@ -58,6 +60,7 @@ int LuaIsValidHttpToken(lua_State *);
|
|||
int LuaLemur64(lua_State *);
|
||||
int LuaMd5(lua_State *);
|
||||
int LuaMeasureEntropy(lua_State *);
|
||||
int LuaOct(lua_State *);
|
||||
int LuaParseHost(lua_State *);
|
||||
int LuaParseHttpDateTime(lua_State *);
|
||||
int LuaParseIp(lua_State *);
|
||||
|
|
|
@ -192,6 +192,22 @@ STATIC_YOINK("zip_uri_support");
|
|||
#define HeaderEqualCase(H, S) \
|
||||
SlicesEqualCase(S, strlen(S), HeaderData(H), HeaderLength(H))
|
||||
|
||||
#define TRACE_BEGIN \
|
||||
do { \
|
||||
if (!IsTiny()) { \
|
||||
if (funtrace) ++g_ftrace; \
|
||||
if (systrace) ++__strace; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TRACE_END \
|
||||
do { \
|
||||
if (!IsTiny()) { \
|
||||
if (funtrace) --g_ftrace; \
|
||||
if (systrace) --__strace; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// letters not used: EIJNOQWXYnoqwxy
|
||||
// digits not used: 0123456789
|
||||
// puncts not used: !"#$%&'()*+,-./;<=>@[\]^_`{|}~
|
||||
|
@ -5066,6 +5082,9 @@ static const luaL_Reg kLuaFuncs[] = {
|
|||
{"Underlong", LuaUnderlong}, //
|
||||
{"VisualizeControlCodes", LuaVisualizeControlCodes}, //
|
||||
{"Write", LuaWrite}, //
|
||||
{"bin", LuaBin}, //
|
||||
{"hex", LuaHex}, //
|
||||
{"oct", LuaOct}, //
|
||||
#ifndef UNSECURE
|
||||
{"Fetch", LuaFetch}, //
|
||||
{"EvadeDragnetSurveillance", LuaEvadeDragnetSurveillance}, //
|
||||
|
@ -5196,7 +5215,6 @@ static void LuaPrint(lua_State *L) {
|
|||
static void LuaInterpreter(lua_State *L) {
|
||||
int i, n, sig, status;
|
||||
const char *script;
|
||||
if (funtrace) ftrace_install();
|
||||
if (optind < __argc) {
|
||||
script = __argv[optind];
|
||||
if (!strcmp(script, "-")) script = 0;
|
||||
|
@ -5206,11 +5224,9 @@ static void LuaInterpreter(lua_State *L) {
|
|||
luaL_checkstack(L, n + 3, "too many script args");
|
||||
for (i = 1; i <= n; i++) lua_rawgeti(L, -i, i);
|
||||
lua_remove(L, -i); // remove arg table from stack
|
||||
if (funtrace) ++g_ftrace;
|
||||
if (systrace) ++__strace;
|
||||
TRACE_BEGIN;
|
||||
status = lua_runchunk(L, n, LUA_MULTRET);
|
||||
if (systrace) --__strace;
|
||||
if (funtrace) --g_ftrace;
|
||||
TRACE_END;
|
||||
}
|
||||
lua_report(L, status);
|
||||
} else {
|
||||
|
@ -5233,9 +5249,9 @@ static void LuaInterpreter(lua_State *L) {
|
|||
exit(1);
|
||||
}
|
||||
if (status == LUA_OK) {
|
||||
if (funtrace) ++g_ftrace;
|
||||
TRACE_BEGIN;
|
||||
status = lua_runchunk(GL, 0, LUA_MULTRET);
|
||||
if (funtrace) --g_ftrace;
|
||||
TRACE_END;
|
||||
}
|
||||
if (status == LUA_OK) {
|
||||
LuaPrint(GL);
|
||||
|
@ -6365,19 +6381,10 @@ static int HandleConnection(size_t i) {
|
|||
meltdown = false;
|
||||
__isworker = true;
|
||||
connectionclose = false;
|
||||
if (!IsTiny()) {
|
||||
if (systrace) {
|
||||
__strace = 1;
|
||||
__kbirth = rdtsc();
|
||||
}
|
||||
if (funtrace) {
|
||||
if (ftrace_install() != -1) {
|
||||
g_ftrace = 1;
|
||||
} else {
|
||||
WARNF("ftrace failed to install %m");
|
||||
}
|
||||
}
|
||||
if (!IsTiny() && systrace) {
|
||||
__kbirth = rdtsc();
|
||||
}
|
||||
TRACE_BEGIN;
|
||||
if (sandboxed) {
|
||||
CHECK_NE(-1, EnableSandbox());
|
||||
}
|
||||
|
@ -6850,7 +6857,6 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
CASE('S', ++sandboxed);
|
||||
CASE('v', ++__log_level);
|
||||
CASE('s', --__log_level);
|
||||
CASE('f', funtrace = true);
|
||||
CASE('Z', systrace = true);
|
||||
CASE('b', logbodies = true);
|
||||
CASE('z', printport = true);
|
||||
|
@ -6889,6 +6895,12 @@ static void GetOpts(int argc, char *argv[]) {
|
|||
CASE('C', ProgramFile(optarg, ProgramCertificate));
|
||||
CASE('K', ProgramFile(optarg, ProgramPrivateKey));
|
||||
#endif
|
||||
case 'f':
|
||||
funtrace = true;
|
||||
if (ftrace_install() == -1) {
|
||||
WARNF("ftrace failed to install %m");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
PrintUsage(2, EX_USAGE);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue