1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
import chagashi/charset
import chagashi/decoder
import monoucha/javascript
import monoucha/jserror
import monoucha/jstypes
import monoucha/quickjs
import types/opt
type
JSTextEncoder = ref object
JSTextDecoder = ref object
encoding: Charset
ignoreBOM {.jsget.}: bool
errorMode: DecoderErrorMode
stream: bool
bomSeen: bool
tdctx: TextDecoderContext
jsDestructor(JSTextDecoder)
jsDestructor(JSTextEncoder)
type TextDecoderOptions = object of JSDict
fatal {.jsdefault.}: bool
ignoreBOM {.jsdefault.}: bool
func newJSTextDecoder(label = "utf-8"; options = TextDecoderOptions()):
JSResult[JSTextDecoder] {.jsctor.} =
let encoding = getCharset(label)
if encoding in {CHARSET_UNKNOWN, CHARSET_REPLACEMENT}:
return errRangeError("Invalid encoding label")
let errorMode = if options.fatal: demFatal else: demReplacement
return ok(JSTextDecoder(
ignoreBOM: options.ignoreBOM,
errorMode: errorMode,
tdctx: initTextDecoderContext(encoding, errorMode),
encoding: encoding
))
func fatal(this: JSTextDecoder): bool {.jsfget.} =
return this.errorMode == demFatal
type TextDecodeOptions = object of JSDict
stream {.jsdefault.}: bool
#TODO AllowSharedBufferSource
proc decode(ctx: JSContext; this: JSTextDecoder;
input = none(JSArrayBufferView); options = TextDecodeOptions()): JSValue
{.jsfunc.} =
if not this.stream:
this.tdctx = initTextDecoderContext(this.encoding, this.errorMode)
this.bomSeen = false
this.stream = options.stream
if input.isSome:
let input = input.get
let H = int(input.abuf.len) - 1
var oq = ""
let stream = this.stream
for chunk in this.tdctx.decode(input.abuf.p.toOpenArray(0, H), not stream):
oq &= chunk
if this.tdctx.failed:
this.tdctx.failed = false
return JS_ThrowTypeError(ctx, "failed to decode string")
return JS_NewStringLen(ctx, cstring(oq), csize_t(oq.len))
return JS_NewString(ctx, "")
func jencoding(this: JSTextDecoder): string {.jsfget: "encoding".} =
return $this.encoding
func newTextEncoder(): JSTextEncoder {.jsctor.} =
return JSTextEncoder()
func jencoding(this: JSTextEncoder): string {.jsfget: "encoding".} =
return "utf-8"
proc dealloc_wrap(rt: JSRuntime; opaque, p: pointer) {.cdecl.} =
dealloc(p)
proc encode(this: JSTextEncoder; input = ""): JSUint8Array {.jsfunc.} =
# we have to validate input first :/
#TODO it is possible to do less copies here...
var input = input.toValidUTF8()
let buf = cast[ptr UncheckedArray[uint8]](alloc(input.len))
copyMem(buf, addr 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(JSTextDecoder, name = "TextDecoder")
ctx.registerType(JSTextEncoder, name = "TextEncoder")
|