about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bindings/quickjs.nim11
-rw-r--r--src/html/env.nim6
-rw-r--r--src/js/base64.nim51
-rw-r--r--src/js/fromjs.nim5
-rw-r--r--src/js/strings.nim3
-rw-r--r--src/js/tojs.nim7
-rw-r--r--src/local/client.nim5
7 files changed, 49 insertions, 39 deletions
diff --git a/src/bindings/quickjs.nim b/src/bindings/quickjs.nim
index a401bf1b..f6719e3d 100644
--- a/src/bindings/quickjs.nim
+++ b/src/bindings/quickjs.nim
@@ -90,6 +90,8 @@ type
   JSFreeArrayBufferDataFunc* = proc (rt: JSRuntime,
     opaque, p: pointer) {.cdecl.}
 
+  JSString* {.importc: "JSString*", header: qjsheader.} = distinct pointer
+
   JSPropertyDescriptor* {.importc, header: qjsheader.} = object
     flags*: cint
     value*: JSValue
@@ -264,6 +266,9 @@ const
 const
   JS_PARSE_JSON_EXT* = (1 shl 0)
 
+template JS_VALUE_GET_STRING*(v: untyped): JSString =
+  JSString(JS_VALUE_GET_PTR(v))
+
 template JS_CFUNC_DEF*(n: string, len: uint8, func1: JSCFunction):
     JSCFunctionListEntry =
   JSCFunctionListEntry(name: cstring(n),
@@ -432,6 +437,12 @@ proc JS_ToCStringLen*(ctx: JSContext, plen: ptr csize_t, val1: JSValue): cstring
 proc JS_ToCString*(ctx: JSContext, val1: JSValue): cstring
 proc JS_FreeCString*(ctx: JSContext, `ptr`: cstring)
 
+proc JS_NewNarrowStringLen*(ctx: JSContext, s: cstring, len: csize_t): JSValue
+proc JS_IsStringWideChar*(str: JSString): JS_BOOL
+proc JS_GetNarrowStringBuffer*(str: JSString): ptr UncheckedArray[uint8]
+proc JS_GetWideStringBuffer*(str: JSString): ptr UncheckedArray[uint16]
+proc JS_GetStringLength*(str: JSString): uint32
+
 proc JS_Eval*(ctx: JSContext, input: cstring, input_len: cint, filename: cstring, eval_flags: cint): JSValue
 proc JS_SetInterruptHandler*(rt: JSRuntime, cb: JSInterruptHandler, opaque: pointer)
 proc JS_SetCanBlock*(rt: JSRuntime, can_block: JS_BOOL)
diff --git a/src/html/env.nim b/src/html/env.nim
index d300400b..0543a60c 100644
--- a/src/html/env.nim
+++ b/src/html/env.nim
@@ -1,6 +1,7 @@
 import selectors
 import streams
 
+import bindings/quickjs
 import display/winattrs
 import html/chadombuilder
 import html/dom
@@ -13,6 +14,7 @@ import js/encoding
 import js/error
 import js/intl
 import js/javascript
+import js/strings
 import js/timeout
 import loader/headers
 import loader/loader
@@ -102,10 +104,10 @@ proc setLocation(window: Window, s: string): Err[JSError]
 proc getWindow(window: Window): Window {.jsuffget: "window".} =
   return window
 
-proc atob(window: Window, data: string): DOMResult[string] {.jsfunc.} =
+proc atob(window: Window, data: string): DOMResult[NarrowString] {.jsfunc.} =
   return atob(data)
 
-proc btoa(window: Window, data: string): DOMResult[string] {.jsfunc.} =
+proc btoa(window: Window, data: JSString): DOMResult[string] {.jsfunc.} =
   return btoa(data)
 
 proc addScripting*(window: Window, selector: Selector[int]) =
diff --git a/src/js/base64.nim b/src/js/base64.nim
index 4b8ae540..912843b4 100644
--- a/src/js/base64.nim
+++ b/src/js/base64.nim
@@ -1,48 +1,29 @@
 import std/base64
 
+import bindings/quickjs
 import js/domexception
+import js/javascript
+import js/strings
 import types/opt
 
 # atob and btoa convert Latin-1 to base64 and vice versa. (And throw on
 # anything above latin-1.)
-# We could do this quite efficiently if we had an API for the QuickJS string
-# internal representation. Unfortunately we do not, so we do the following:
-# * atob: decode, convert latin-1 to utf-8, pass to qjs, where it is then
-#   converted to latin-1 again.
-# * btoa: qjs converts its string (either utf-16 or latin-1) to utf-8,
-#   we convert this to latin-1 (or throw), then encode.
-# That is two conversions more than needed (i.e. 0) for each step. We should
-# really write an API for handling QJS strings sometime...
 
-proc atob*(data: string): DOMResult[string] =
+proc atob*(data: string): DOMResult[NarrowString] =
   try:
-    let ds = base64.decode(data)
-    var s = newStringOfCap(ds.len)
-    for c in ds:
-      if uint8(c) <= 0x7F:
-        s &= c
-      else: # latin-1
-        s &= char((uint8(c) shr 6) or 0xC0)
-        s &= char((uint8(c) and 0x3F) or 0x80)
-    return ok(s)
+    let ds = NarrowString(base64.decode(data))
+    return ok(ds)
   except ValueError:
     return err(newDOMException("Invalid character in string",
       "InvalidCharacterError"))
 
-proc btoa*(data: string): DOMResult[string] =
-  var s = newStringOfCap(data.len)
-  var i = 0
-  while i < data.len:
-    let c = data[i]
-    let n = uint8(c)
-    if n <= 0x7F: # ascii
-      s &= c
-      inc i
-    elif n <= 0xC3: # latin-1
-      inc i
-      s &= char((n shl 6) or (uint8(data[i]) and 0x3F))
-      inc i
-    else:
-      return err(newDOMException("Invalid character in string",
-        "InvalidCharacterError"))
-  return ok(base64.encode(s))
+proc btoa*(data: JSString): DOMResult[string] =
+  if JS_IsStringWideChar(data):
+    return err(newDOMException("Invalid character in string",
+      "InvalidCharacterError"))
+  let len = int(JS_GetStringLength(data))
+  if len == 0:
+    return ok("")
+  let buf = JS_GetNarrowStringBuffer(data)
+  let res = base64.encode(toOpenArray(buf, 0, len - 1))
+  return ok(res)
diff --git a/src/js/fromjs.nim b/src/js/fromjs.nim
index b2e1cc91..80fdc1cd 100644
--- a/src/js/fromjs.nim
+++ b/src/js/fromjs.nim
@@ -54,6 +54,9 @@ func fromJSString(ctx: JSContext, val: JSValue): JSResult[string] =
   JS_FreeCString(ctx, outp)
   return ok(ret)
 
+func fromJSString2(ctx: JSContext, val: JSValue): JSResult[JSString] =
+  return ok(JS_VALUE_GET_STRING(val))
+
 func fromJSInt[T: SomeInteger](ctx: JSContext, val: JSValue):
     JSResult[T] =
   if not JS_IsNumber(val):
@@ -449,6 +452,8 @@ type FromJSAllowedT = (object and not (Result|Option|Table|JSValue|JSDict))
 proc fromJS*[T](ctx: JSContext, val: JSValue): JSResult[T] =
   when T is string:
     return fromJSString(ctx, val)
+  elif T is JSString:
+    return fromJSString2(ctx, val)
   elif T is char:
     return fromJSChar(ctx, val)
   elif T is Rune:
diff --git a/src/js/strings.nim b/src/js/strings.nim
new file mode 100644
index 00000000..ab0dd753
--- /dev/null
+++ b/src/js/strings.nim
@@ -0,0 +1,3 @@
+type
+  NarrowString* = distinct string
+  WideString* = distinct seq[uint16]
diff --git a/src/js/tojs.nim b/src/js/tojs.nim
index e614c451..bf2bfe61 100644
--- a/src/js/tojs.nim
+++ b/src/js/tojs.nim
@@ -8,6 +8,7 @@ import js/arraybuffer
 import js/dict
 import js/error
 import js/opaque
+import js/strings
 import js/typeptr
 import types/opt
 
@@ -35,6 +36,9 @@ proc toJS*(ctx: JSContext, promise: EmptyPromise): JSValue
 proc toJS*(ctx: JSContext, obj: ref object): JSValue
 proc toJS*(ctx: JSContext, err: JSError): JSValue
 proc toJS*(ctx: JSContext, f: JSCFunction): JSValue
+proc toJS*(ctx: JSContext, abuf: JSArrayBuffer): JSValue
+proc toJS*(ctx: JSContext, u8a: JSUint8Array): JSValue
+proc toJS*(ctx: JSContext, ns: NarrowString): JSValue
 
 # Convert Nim types to the corresponding JavaScript type, with knowledge of
 # the parent object.
@@ -270,6 +274,9 @@ proc toJS*(ctx: JSContext, u8a: JSUint8Array): JSValue =
   JS_FreeValue(ctx, jsabuf)
   return ret
 
+proc toJS*(ctx: JSContext, ns: NarrowString): JSValue =
+  return JS_NewNarrowStringLen(ctx, cstring(ns), csize_t(string(ns).len))
+
 proc toJSP(ctx: JSContext, parent: ref object, child: var object): JSValue =
   let p = addr child
   # Save parent as the original ancestor for this tree.
diff --git a/src/local/client.nim b/src/local/client.nim
index 9cff0f2f..906c5bee 100644
--- a/src/local/client.nim
+++ b/src/local/client.nim
@@ -34,6 +34,7 @@ import js/fromjs
 import js/intl
 import js/javascript
 import js/module
+import js/strings
 import js/timeout
 import js/tojs
 import loader/headers
@@ -613,10 +614,10 @@ proc jsCollect(client: Client) {.jsfunc.} =
 proc sleep(client: Client, millis: int) {.jsfunc.} =
   sleep millis
 
-proc atob(client: Client, data: string): DOMResult[string] {.jsfunc.} =
+proc atob(client: Client, data: string): DOMResult[NarrowString] {.jsfunc.} =
   return atob(data)
 
-proc btoa(client: Client, data: string): DOMResult[string] {.jsfunc.} =
+proc btoa(client: Client, data: JSString): DOMResult[string] {.jsfunc.} =
   return btoa(data)
 
 func line(client: Client): LineEdit {.jsfget.} =