Skip to content

Commit

Permalink
Add custom printf version
Browse files Browse the repository at this point in the history
- add `js_snprintf`, `js_printf`... to handle extra conversions:
  - support for wxx length modifier
  - support for `%b` and `%B`
  - `%oa` and `%#oa` to convert `JSAtom` values
  - `%ps` to convert `JSString` values
- add `dbuf_vprintf_fun` replaceable `dbuf_printf` handler
- change `JS_DumpString` to `JS_FormatString` to convert `JSSAtom` to quoted strings
- change `JS_AtomGetStrRT` to `JS_FormatAtom` to convert `JSAtom` to strings
- change `JS_AtomGetStr` to return direct string pointer for builtin atoms
- remove `print_atom`
- use custom conversions for trace messages and error messages
- add support for `%b`, `%B` and `w` length modifier in `std.printf`
- remove error handlers: `JS_ThrowTypeErrorAtom` and `JS_ThrowSyntaxErrorAtom`
  • Loading branch information
chqrlie committed Apr 20, 2024
1 parent 83726bb commit ead95a7
Show file tree
Hide file tree
Showing 6 changed files with 904 additions and 393 deletions.
48 changes: 31 additions & 17 deletions cutils.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,29 +178,43 @@ int dbuf_putstr(DynBuf *s, const char *str)
return dbuf_put(s, (const uint8_t *)str, strlen(str));
}

int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,
const char *fmt, ...)
static int dbuf_vprintf_default(DynBuf *s, const char *fmt, va_list ap)
{
va_list arg;
size_t avail = s->allocated_size - s->size;
int len;

va_copy(arg, ap);
len = vsnprintf((char *)(s->buf + s->size), avail, fmt, arg);
va_end(arg);

if (len >= 0) {
if ((size_t)len >= avail) {
if (dbuf_realloc(s, s->size + len + 1))
return -1;
avail = s->allocated_size - s->size;
va_copy(arg, ap);
vsnprintf((char *)(s->buf + s->size), avail, fmt, arg);
va_end(arg);
}
s->size += len;
}
return len;
}

/* replaceable formatter */
int (*dbuf_vprintf_fun)(DynBuf *s, const char *fmt, va_list ap) = dbuf_vprintf_default;

__attribute__((format(printf, 2, 3)))
int dbuf_printf(DynBuf *s, const char *fmt, ...)
{
va_list ap;
char buf[128];
int len;

va_start(ap, fmt);
len = vsnprintf(buf, sizeof(buf), fmt, ap);
len = (*dbuf_vprintf_fun)(s, fmt, ap);
va_end(ap);
if (len < sizeof(buf)) {
/* fast case */
return dbuf_put(s, (uint8_t *)buf, len);
} else {
if (dbuf_realloc(s, s->size + len + 1))
return -1;
va_start(ap, fmt);
vsnprintf((char *)(s->buf + s->size), s->allocated_size - s->size,
fmt, ap);
va_end(ap);
s->size += len;
}
return 0;
return len;
}

void dbuf_free(DynBuf *s)
Expand Down
6 changes: 4 additions & 2 deletions cutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#ifndef CUTILS_H
#define CUTILS_H

#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
Expand Down Expand Up @@ -376,8 +377,9 @@ static inline int dbuf_put_u64(DynBuf *s, uint64_t val)
{
return dbuf_put(s, (uint8_t *)&val, 8);
}
int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,
FORMAT_STRING(const char *fmt), ...);
__attribute__((format(printf, 2, 3)))
int dbuf_printf(DynBuf *s, FORMAT_STRING(const char *fmt), ...);
extern int (*dbuf_vprintf_fun)(DynBuf *s, const char *fmt, va_list ap);
void dbuf_free(DynBuf *s);
static inline BOOL dbuf_error(DynBuf *s) {
return s->error;
Expand Down
76 changes: 48 additions & 28 deletions quickjs-libc.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,6 @@ static JSValue js_printf_internal(JSContext *ctx,
int64_t int64_arg;
double double_arg;
const char *string_arg;
/* Use indirect call to dbuf_printf to prevent gcc warning */
int (*dbuf_printf_fun)(DynBuf *s, const char *fmt, ...) = (void*)dbuf_printf;

js_std_dbuf_init(ctx, &dbuf);

Expand Down Expand Up @@ -225,7 +223,9 @@ static JSValue js_printf_internal(JSContext *ctx,
goto missing;
if (JS_ToInt32(ctx, &int32_arg, argv[i++]))
goto fail;
q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg);
if (q > fmtbuf + sizeof(fmtbuf) - 11)
goto invalid;
q += i32toa(q, int32_arg);
fmt++;
} else {
while (my_isdigit(*fmt)) {
Expand All @@ -243,7 +243,9 @@ static JSValue js_printf_internal(JSContext *ctx,
goto missing;
if (JS_ToInt32(ctx, &int32_arg, argv[i++]))
goto fail;
q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg);
if (q > fmtbuf + sizeof(fmtbuf) - 11)
goto invalid;
q += i32toa(q, int32_arg);
fmt++;
} else {
while (my_isdigit(*fmt)) {
Expand All @@ -254,10 +256,33 @@ static JSValue js_printf_internal(JSContext *ctx,
}
}

/* we only support the "l" modifier for 64 bit numbers */
mod = ' ';
if (*fmt == 'l') {
mod = *fmt++;
/* we only support the "l" modifier for 64 bit numbers
and the w# length modifier with a bitlength of 1 to 64
*/
// XXX: should use value changing conversions
mod = *fmt;
if (mod == 'w') {
int bitwidth;
if (q >= fmtbuf + sizeof(fmtbuf) - 4)
goto invalid;
*q++ = *fmt++;
if (!(*fmt >= '1' && *fmt <= '9'))
goto invalid;
bitwidth = *fmt - '0';
*q++ = *fmt++;
if (*fmt >= '0' && *fmt <= '9') {
bitwidth = bitwidth * 10 + *fmt - '0';
*q++ = *fmt++;
}
if (bitwidth > 32)
mod = 'l';
} else
if (mod == 'l') {
fmt++;
if (q >= fmtbuf + sizeof(fmtbuf) - 3)
goto invalid;
*q++ = 'l';
*q++ = 'l';
}

/* type */
Expand Down Expand Up @@ -285,10 +310,14 @@ static JSValue js_printf_internal(JSContext *ctx,
if ((unsigned)int32_arg > 0x10FFFF)
int32_arg = 0xFFFD;
/* ignore conversion flags, width and precision */
// XXX: Hash modifier could output pretty Unicode character
// XXX: `l` length modifier is implicit
len = unicode_to_utf8(cbuf, int32_arg);
dbuf_put(&dbuf, cbuf, len);
break;

case 'b':
case 'B':
case 'd':
case 'i':
case 'o':
Expand All @@ -297,40 +326,29 @@ static JSValue js_printf_internal(JSContext *ctx,
case 'X':
if (i >= argc)
goto missing;
// XXX: should handle BigInt values
if (JS_ToInt64Ext(ctx, &int64_arg, argv[i++]))
goto fail;
if (mod == 'l') {
/* 64 bit number */
#if defined(_WIN32)
if (q >= fmtbuf + sizeof(fmtbuf) - 3)
goto invalid;
q[2] = q[-1];
q[-1] = 'I';
q[0] = '6';
q[1] = '4';
q[3] = '\0';
dbuf_printf_fun(&dbuf, fmtbuf, (int64_t)int64_arg);
#else
if (q >= fmtbuf + sizeof(fmtbuf) - 2)
goto invalid;
q[1] = q[-1];
q[-1] = q[0] = 'l';
q[2] = '\0';
dbuf_printf_fun(&dbuf, fmtbuf, (long long)int64_arg);
#endif
dbuf_printf(&dbuf, fmtbuf, (int64_t)int64_arg);
} else {
dbuf_printf_fun(&dbuf, fmtbuf, (int)int64_arg);
dbuf_printf(&dbuf, fmtbuf, (int)int64_arg);
}
break;

case 's':
if (i >= argc)
goto missing;
/* XXX: handle strings containing null characters */
// XXX: # could output encoded string
// XXX: null values should output as `<null>`
// XXX: undefined values should output as `<undefined>`
// XXX: `l` length modifier is implicit
string_arg = JS_ToCString(ctx, argv[i++]);
if (!string_arg)
goto fail;
dbuf_printf_fun(&dbuf, fmtbuf, string_arg);
dbuf_printf(&dbuf, fmtbuf, string_arg);
JS_FreeCString(ctx, string_arg);
break;

Expand All @@ -346,10 +364,12 @@ static JSValue js_printf_internal(JSContext *ctx,
goto missing;
if (JS_ToFloat64(ctx, &double_arg, argv[i++]))
goto fail;
dbuf_printf_fun(&dbuf, fmtbuf, double_arg);
dbuf_printf(&dbuf, fmtbuf, double_arg);
break;

case '%':
if (q != fmtbuf + 2) // accept only %%
goto invalid;
dbuf_putc(&dbuf, '%');
break;

Expand Down
Loading

0 comments on commit ead95a7

Please sign in to comment.