Re: small JSON encoder improvement
Jens Thiele <[email protected]> Mon, 30 Sep 2024 18:04:16 +0200
| Newsgroups | gmane.lisp.scheme.gauche |
|---|---|
| Message-ID | <[email protected]> |
just fyi:
testing a native code hack using runtime-compile I can bring it further
down to ~5s:
#!/bin/sh
#| -*- mode: scheme; coding: utf-8; -*-
exec gosh -I. -- $0 "$@"
|#
(use runtime-compile)
(compile-and-load
`((inline-stub
(define-cfn encode-string (s::ScmString* port::ScmPort*) ::void :static :inline
(let* ((body::(const ScmStringBody*) (SCM_STRING_BODY s))
(len::ScmSmallInt (SCM_STRING_BODY_LENGTH body))
(cp::(const char*) (SCM_STRING_BODY_START body))
(ch::ScmChar)
(i::int)
(ESCAPE_BUF_MAX::(const int) 13)
(buf::(.array char (ESCAPE_BUF_MAX)))
)
(SCM_PUTC #\" port)
(for ((set! i 0) (< i len) (post++ i))
(SCM_CHAR_GET cp ch)
(case ch
((#\") (SCM_PUTZ "\\\"" -1 port))
((#\\) (SCM_PUTZ "\\\\" -1 port))
((#\backspace) (SCM_PUTZ "\\b" -1 port))
((#\page) (SCM_PUTZ "\\f" -1 port))
((#\newline) (SCM_PUTZ "\\n" -1 port))
((#\return) (SCM_PUTZ "\\r" -1 port))
((#\tab) (SCM_PUTZ "\\t" -1 port))
((#\delete) (SCM_PUTZ "\\u007f" -1 port))
(else
(cond [(and (or
(<= ch 31)
(>= ch 128))
(< ch #x10000))
(snprintf buf ESCAPE_BUF_MAX "\\u%04x" (cast u_int ch))
(SCM_PUTZ buf 6 port)]
[(>= ch #x10000)
(let* ((code::int (- ch #x10000)))
(snprintf buf
ESCAPE_BUF_MAX
"\\u%04x\\u%04x"
(logior (>> code 10) #xd800)
(logior (logand code #x3ff) #xdc00))
(SCM_PUTZ buf 12 port))]
[else
(SCM_PUTC ch port)])))
(set! cp (+ cp (SCM_CHAR_NBYTES ch)))
)
)
(SCM_PUTC #\" port))
(define-cfn number-to-string (obj::ScmObj) ::ScmObj :static
(let* ([fmt::ScmNumberFormat]
[o::ScmPort* (SCM_PORT (Scm_MakeOutputStringPort TRUE))])
(Scm_NumberFormatInit (& fmt))
(set! (ref fmt radix) 10)
(set! (ref fmt flags) 0)
(set! (ref fmt precision) -1)
(Scm_PrintNumber o obj (& fmt))
(return (Scm_GetOutputString o 0))))
(define-cfn encode-alist (alist port::ScmPort*) ::void :static :inline
;; todo: require proper list?
(SCM_PUTC #\{ port)
(let* ((i::int 0))
(for-each (lambda(kv)
(if (> i 0)
(SCM_PUTC #\, port)
(inc! i))
;; todo: x->string?
(let* ((key (SCM_CAR kv)))
(cond [(SCM_STRINGP key)
(encode-string (SCM_STRING key) port)]
[(SCM_SYMBOLP key)
(encode-string (SCM_STRING (SCM_OBJ (SCM_SYMBOL_NAME key))) port)]
[(SCM_NUMBERP key)
(encode-string (SCM_STRING (number-to-string key)) port)]
[else
(Scm_Error "JSON:can't encode key")]))
(SCM_PUTC #\: port)
(encode (SCM_CDR kv) port))
alist)
(SCM_PUTC #\} port)))
(define-cfn encode-vector (v port::ScmPort*) ::void :static :inline
(SCM_PUTC #\[ port)
(dotimes (i (SCM_VECTOR_SIZE v))
(when (> i 0)
(SCM_PUTC #\, port))
(encode (SCM_VECTOR_ELEMENT v i) port))
(SCM_PUTC #\] port))
(define-cfn encode-number (n port::ScmPort*) ::void :static :inline
(Scm_Write n (SCM_OBJ port) SCM_WRITE_SIMPLE))
(define-cfn encode-boolean (b port::ScmPort*) ::void :static :inline
(if (SCM_TRUEP b)
(SCM_PUTS (SCM_MAKE_STR "true") port)
(SCM_PUTS (SCM_MAKE_STR "false") port)))
(define-cfn encode (obj port::ScmPort*) ::void :static :inline
(cond [(SCM_STRINGP obj)
(encode-string (SCM_STRING obj) port)]
[(SCM_LISTP obj)
(encode-alist obj port)]
[(SCM_VECTORP obj)
(encode-vector obj port)]
[(SCM_NUMBERP obj)
(encode-number obj port)]
[(SCM_BOOLP obj)
(encode-boolean obj port)]
[(SCM_EQ obj (SCM_INTERN "true"))
(SCM_PUTS (SCM_MAKE_STR "true") port)]
[(SCM_EQ obj (SCM_INTERN "false"))
(SCM_PUTS (SCM_MAKE_STR "false") port)]
[else
(Scm_Error "JSON:can't encode obj")]))
(define-cproc json (obj :optional (port::<output-port> (current-output-port)))
(encode obj port))
))
'(json)
:cflags "-O2")
(define (main args)
(json (read))
0)