diff options
author | bptato <nincsnevem662@gmail.com> | 2023-10-21 20:22:21 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2023-10-21 20:22:21 +0200 |
commit | 69870f3b974e65d61b564b396e01d21cc023e6e9 (patch) | |
tree | f773f5446a2232739c4818080e467851deabb4f8 /src/js | |
parent | f77db0bdefd43ec03d7d26ec7a5d793bece25030 (diff) | |
download | chawan-69870f3b974e65d61b564b396e01d21cc023e6e9.tar.gz |
javascript: add TextEncoder, TextDecoder
Diffstat (limited to 'src/js')
-rw-r--r-- | src/js/arraybuffer.nim | 18 | ||||
-rw-r--r-- | src/js/encoding.nim | 112 | ||||
-rw-r--r-- | src/js/fromjs.nim | 32 | ||||
-rw-r--r-- | src/js/javascript.nim | 1 | ||||
-rw-r--r-- | src/js/opaque.nim | 4 | ||||
-rw-r--r-- | src/js/tojs.nim | 11 |
6 files changed, 178 insertions, 0 deletions
diff --git a/src/js/arraybuffer.nim b/src/js/arraybuffer.nim new file mode 100644 index 00000000..ff9fa24d --- /dev/null +++ b/src/js/arraybuffer.nim @@ -0,0 +1,18 @@ +import bindings/quickjs + +type + JSArrayBuffer* = object + p*: ptr UncheckedArray[uint8] + len*: csize_t + dealloc*: JSFreeArrayBufferDataFunc + + JSArrayBufferView* = object + abuf*: JSArrayBuffer + offset*: csize_t # offset into the buffer + nmemb*: csize_t # number of members + nsize*: csize_t # member size + + JSUint8Array* = object + abuf*: JSArrayBuffer + offset*: csize_t # offset into the buffer + nmemb*: csize_t # number of members diff --git a/src/js/encoding.nim b/src/js/encoding.nim new file mode 100644 index 00000000..31f6f575 --- /dev/null +++ b/src/js/encoding.nim @@ -0,0 +1,112 @@ +import std/streams + +import js/arraybuffer +import js/dict +import js/error +import js/javascript + +import chakasu/charset +import chakasu/decoderstream +import chakasu/encoderstream + +type + TextEncoder = ref object + + TextDecoder = ref object + encoding: Charset + errorMode: DecoderErrorMode + ignoreBOM {.jsget.}: bool + doNotFlush: bool + bomSeen: bool + decoder: DecoderStream + encoder: EncoderStream # to return the string to JS + istream: StringStream + +jsDestructor(TextDecoder) +jsDestructor(TextEncoder) + +type TextDecoderOptions = object of JSDict + fatal: bool + ignoreBOM: bool + +func newTextDecoder(label = "utf-8", options = TextDecoderOptions()): + JSResult[TextDecoder] {.jsctor.} = + let errorMode = if options.fatal: + DECODER_ERROR_MODE_FATAL + else: + DECODER_ERROR_MODE_REPLACEMENT + let encoding = getCharset(label) + if encoding in {CHARSET_UNKNOWN, CHARSET_REPLACEMENT}: + return err(newRangeError("Invalid encoding label")) + return ok(TextDecoder( + errorMode: errorMode, + ignoreBOM: options.ignoreBOM, + encoding: encoding + )) + +type TextDecodeOptions = object of JSDict + stream: bool + +#TODO AllowSharedBufferSource +proc decode(this: TextDecoder, input = none(JSArrayBufferView), + options = TextDecodeOptions()): string {.jsfunc.} = + if not this.doNotFlush: + if this.istream != nil: + this.istream.close() + if this.decoder != nil: + this.decoder.close() + if this.encoder != nil: + this.encoder.close() + this.istream = newStringStream() + this.decoder = newDecoderStream(this.istream, cs = this.encoding, + errormode = this.errorMode) + this.encoder = newEncoderStream(this.decoder, cs = CHARSET_UTF_8) + this.bomSeen = false + if this.doNotFlush != options.stream: + this.doNotFlush = options.stream + this.decoder.setInhibitCheckEnd(options.stream) + if input.isSome: + let input = input.get + let pos = this.istream.getPosition() + #TODO input offset? + this.istream.writeData(input.abuf.p, int(input.abuf.len)) + this.istream.setPosition(pos) + #TODO this should return a JSString, so we do not needlessly re-encode + # the output. (Right now we do, implicitly through toJS.) + return this.encoder.readAll() + +func jencoding(this: TextDecoder): string {.jsfget: "encoding".} = + return $this.encoding + +func fatal(this: TextDecoder): bool {.jsfget.} = + return this.errorMode == DECODER_ERROR_MODE_FATAL + +func newTextEncoder(): TextEncoder {.jsctor.} = + return TextEncoder() + +func jencoding(this: TextEncoder): string {.jsfget: "encoding".} = + return "utf-8" + +proc dealloc_wrap(rt: JSRuntime, opaque, p: pointer) {.cdecl.} = + dealloc(p) + +proc encode(this: TextEncoder, input = ""): JSUint8Array {.jsfunc.} = + # input is already UTF-8 here :P + let buf = cast[ptr UncheckedArray[uint8]](alloc(input.len)) + copyMem(buf, unsafeAddr input[0], input.len) + let abuf = JSArrayBuffer( + p: buf, + len: csize_t(input.len), + dealloc: dealloc_wrap + ) + return JSUint8Array( + abuf: abuf, + offset: 0, + nmemb: csize_t(input.len) + ) + +#TODO encodeInto + +proc addEncodingModule*(ctx: JSContext) = + ctx.registerType(TextDecoder) + ctx.registerType(TextEncoder) diff --git a/src/js/fromjs.nim b/src/js/fromjs.nim index dbc009bc..b2e1cc91 100644 --- a/src/js/fromjs.nim +++ b/src/js/fromjs.nim @@ -5,6 +5,7 @@ import tables import unicode import bindings/quickjs +import js/arraybuffer import js/dict import js/error import js/opaque @@ -416,6 +417,33 @@ proc fromJSDict[T: JSDict](ctx: JSContext, val: JSValue): JSResult[T] = v = ?fromJS[typeof(v)](ctx, esm) return ok(d) +proc fromJSArrayBuffer(ctx: JSContext, val: JSValue): JSResult[JSArrayBuffer] = + var len: csize_t + let p = JS_GetArrayBuffer(ctx, addr len, val) + if p == nil: + return err() + let abuf = JSArrayBuffer( + len: len, + p: cast[ptr UncheckedArray[uint8]](p) + ) + return ok(abuf) + +proc fromJSArrayBufferView(ctx: JSContext, val: JSValue): + JSResult[JSArrayBufferView] = + var offset: csize_t + var nmemb: csize_t + var nsize: csize_t + let jsbuf = JS_GetTypedArrayBuffer(ctx, val, addr offset, addr nmemb, + addr nsize) + let abuf = ?fromJSArrayBuffer(ctx, jsbuf) + let view = JSArrayBufferView( + abuf: abuf, + offset: offset, + nmemb: nmemb, + nsize: nsize + ) + return ok(view) + type FromJSAllowedT = (object and not (Result|Option|Table|JSValue|JSDict)) proc fromJS*[T](ctx: JSContext, val: JSValue): JSResult[T] = @@ -456,6 +484,10 @@ proc fromJS*[T](ctx: JSContext, val: JSValue): JSResult[T] = return fromJSVoid(ctx, val) elif T is JSDict: return fromJSDict[T](ctx, val) + elif T is JSArrayBuffer: + return fromJSArrayBuffer(ctx, val) + elif T is JSArrayBufferView: + return fromJSArrayBufferView(ctx, val) elif compiles(fromJS2(ctx, val, result)): fromJS2(ctx, val, result) else: diff --git a/src/js/javascript.nim b/src/js/javascript.nim index c744345d..996137cd 100644 --- a/src/js/javascript.nim +++ b/src/js/javascript.nim @@ -144,6 +144,7 @@ proc free*(ctx: var JSContext) = JS_FreeValue(ctx, v) JS_FreeValue(ctx, opaque.Array_prototype_values) JS_FreeValue(ctx, opaque.Object_prototype_valueOf) + JS_FreeValue(ctx, opaque.Uint8Array_ctor) for v in opaque.err_ctors: JS_FreeValue(ctx, v) GC_unref(opaque) diff --git a/src/js/opaque.nim b/src/js/opaque.nim index bdc84b5a..30456602 100644 --- a/src/js/opaque.nim +++ b/src/js/opaque.nim @@ -29,6 +29,7 @@ type str_refs*: array[JSStrRefs, JSAtom] Array_prototype_values*: JSValue Object_prototype_valueOf*: JSValue + Uint8Array_ctor*: JSValue err_ctors*: array[JSErrorEnum, JSValue] htmldda*: JSClassID # only one of these exists: document.all. @@ -64,6 +65,9 @@ func newJSContextOpaque*(ctx: JSContext): JSContextOpaque = let objproto = JS_GetClassProto(ctx, JS_CLASS_OBJECT) opaque.Object_prototype_valueOf = JS_GetPropertyStr(ctx, objproto, "valueOf") JS_FreeValue(ctx, objproto) + block: + let u8actor = JS_GetPropertyStr(ctx, global, "Uint8Array") + opaque.Uint8Array_ctor = u8actor for e in JSErrorEnum: let s = $e let err = JS_GetPropertyStr(ctx, global, cstring(s)) diff --git a/src/js/tojs.nim b/src/js/tojs.nim index 89ca4cd4..e614c451 100644 --- a/src/js/tojs.nim +++ b/src/js/tojs.nim @@ -4,6 +4,7 @@ import unicode import bindings/quickjs import io/promise +import js/arraybuffer import js/dict import js/error import js/opaque @@ -259,6 +260,16 @@ proc toJS*(ctx: JSContext, err: JSError): JSValue = proc toJS*(ctx: JSContext, f: JSCFunction): JSValue = return JS_NewCFunction(ctx, f, cstring"", 0) +proc toJS*(ctx: JSContext, abuf: JSArrayBuffer): JSValue = + return JS_NewArrayBuffer(ctx, abuf.p, abuf.len, abuf.dealloc, nil, false) + +proc toJS*(ctx: JSContext, u8a: JSUint8Array): JSValue = + var jsabuf = toJS(ctx, u8a.abuf) + let ctor = ctx.getOpaque().Uint8Array_ctor + let ret = JS_CallConstructor(ctx, ctor, 1, addr jsabuf) + JS_FreeValue(ctx, jsabuf) + return ret + proc toJSP(ctx: JSContext, parent: ref object, child: var object): JSValue = let p = addr child # Save parent as the original ancestor for this tree. |