about summary refs log tree commit diff stats
path: root/src/js/base64.nim
blob: a1693aa3ac292c4287d590cdd4ab359bca4a8b44 (plain) (blame)
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
import bindings/quickjs
import js/domexception
import js/javascript
import js/jstypes
import types/opt
import utils/twtstr

# atob and btoa convert Latin-1 to base64 and vice versa. (And throw on
# anything above latin-1.)

proc atob*(data: string): DOMResult[NarrowString] =
  let r = atob0(data)
  if r.isNone:
    return err(newDOMException(r.error, "InvalidCharacterError"))
  return ok(NarrowString(r.get))

const AMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

func btoa*(data: openArray[uint8]): string =
  if data.len == 0:
    return ""
  var L = data.len div 3 * 4
  if (let rem = data.len mod 3; rem) > 0:
    L += 3 - rem
  var s = newStringOfCap(L)
  var i = 0
  let endw = data.len - 2
  while i < endw:
    let n = uint32(data[i]) shl 16 or
      uint32(data[i + 1]) shl 8 or
      uint32(data[i + 2])
    i += 3
    s &= AMap[n shr 18 and 0x3F]
    s &= AMap[n shr 12 and 0x3F]
    s &= AMap[n shr 6 and 0x3F]
    s &= AMap[n and 0x3F]
  if i < data.len:
    let b1 = uint32(data[i])
    inc i
    if i < data.len:
      let b2 = uint32(data[i])
      s &= AMap[b1 shr 2]                      # 6 bits of b1
      s &= AMap[b1 shl 4 and 0x3F or b2 shr 4] # 2 bits of b1 | 4 bits of b2
      s &= AMap[b2 shl 2 and 0x3F]             # 4 bits of b2
    else:
      s &= AMap[b1 shr 2]          # 6 bits of b1
      s &= AMap[b1 shl 4 and 0x3F] # 2 bits of b1
      s &= '='
    s &= '='
  return s

func btoa*(data: string): string =
  return btoa(data.toOpenArrayByte(0, data.len - 1))

proc btoa*(ctx: JSContext; data: JSValue): DOMResult[string] =
  let data = JS_ToString(ctx, data)
  if JS_IsException(data):
    return err()
  assert JS_IsString(data)
  if JS_IsStringWideChar(data):
    JS_FreeValue(ctx, data)
    return errDOMException("Invalid character in string",
      "InvalidCharacterError")
  let len = int(JS_GetStringLength(data))
  if len == 0:
    JS_FreeValue(ctx, data)
    return ok("")
  let buf = JS_GetNarrowStringBuffer(data)
  let res = btoa(buf.toOpenArray(0, len - 1))
  JS_FreeValue(ctx, data)
  return ok(res)