about summary refs log tree commit diff stats
path: root/src/js
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2023-10-21 23:34:56 +0200
committerbptato <nincsnevem662@gmail.com>2023-10-21 23:40:24 +0200
commit18008acc141a55449b28c1af487a080c4bbcb355 (patch)
treea81872bfc2e2add0b0c9b6f65f3be15f4d2790c8 /src/js
parent69870f3b974e65d61b564b396e01d21cc023e6e9 (diff)
downloadchawan-18008acc141a55449b28c1af487a080c4bbcb355.tar.gz
base64: reduce pointless re-coding using JSString
We now expose some functions from QuickJS to interact with JavaScript
strings without re-encoding them into UTF-8.
Diffstat (limited to 'src/js')
-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
4 files changed, 31 insertions, 35 deletions
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.