diff options
author | bptato <nincsnevem662@gmail.com> | 2024-04-25 20:31:46 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-04-25 20:39:01 +0200 |
commit | a42f96ee6287c714ce90206009f20494995940db (patch) | |
tree | a7d76007e2360c84c0b309d8aa934c186851dbe6 /src | |
parent | 91707a9b2ee3ceee6bdd13f5262dcc1dd675fbc7 (diff) | |
download | chawan-a42f96ee6287c714ce90206009f20494995940db.tar.gz |
data: replace std/base64 with atob
std's version is known to be broken on versions we still support, and it makes no sense to use different decoders anyway. (This does introduce a bit of a dependency hell, because js/base64 depends on js/javascript which tries to bring in the entire QuickJS runtime. So we move that out into twtstr, and manually convert a Result[string, string] to DOMException in js/base64.)
Diffstat (limited to 'src')
-rw-r--r-- | src/js/base64.nim | 68 | ||||
-rw-r--r-- | src/utils/twtstr.nim | 63 |
2 files changed, 67 insertions, 64 deletions
diff --git a/src/js/base64.nim b/src/js/base64.nim index 9d1d35e5..a1693aa3 100644 --- a/src/js/base64.nim +++ b/src/js/base64.nim @@ -8,71 +8,11 @@ import utils/twtstr # atob and btoa convert Latin-1 to base64 and vice versa. (And throw on # anything above latin-1.) -func atob(c: char): uint8 {.inline.} = - # see RFC 4648 table - if c in AsciiUpperAlpha: - return uint8(c) - uint8('A') - if c in AsciiLowerAlpha: - return uint8(c) - uint8('a') + 26 - if c in AsciiDigit: - return uint8(c) - uint8('0') + 52 - if c == '+': - return 62 - if c == '/': - return 63 - return uint8.high - proc atob*(data: string): DOMResult[NarrowString] = - var outs = newStringOfCap(data.len div 4 * 3) - var buf: array[4, uint8] - var i = 0 - var j = 0 - var pad = 0 - while true: - i = data.skipBlanks(i) - if i >= data.len: - break - if data[i] == '=': - i = data.skipBlanks(i + 1) - inc pad - break - buf[j] = atob(data[i]) - if buf[j] == uint8.high: - return errDOMException("Invalid character in encoded string", - "InvalidCharacterError") - if j == 3: - let ob1 = (buf[0] shl 2) or (buf[1] shr 4) # 6 bits of b0 | 2 bits of b1 - let ob2 = (buf[1] shl 4) or (buf[2] shr 2) # 4 bits of b1 | 4 bits of b2 - let ob3 = (buf[2] shl 6) or buf[3] # 2 bits of b2 | 6 bits of b3 - outs &= char(ob1) - outs &= char(ob2) - outs &= char(ob3) - j = 0 - else: - inc j - inc i - if i < data.len: - if i < data.len and data[i] == '=': - inc pad - inc i - i = data.skipBlanks(i) - if pad > 0 and j + pad != 4: - return errDOMException("Too much padding", "InvalidCharacterError") - if i < data.len: - return errDOMException("Invalid character after encoded string", - "InvalidCharacterError") - if j == 3: - let ob1 = (buf[0] shl 2) or (buf[1] shr 4) # 6 bits of b0 | 2 bits of b1 - let ob2 = (buf[1] shl 4) or (buf[2] shr 2) # 4 bits of b1 | 4 bits of b2 - outs &= char(ob1) - outs &= char(ob2) - elif j == 2: - let ob1 = (buf[0] shl 2) or (buf[1] shr 4) # 6 bits of b0 | 2 bits of b1 - outs &= char(ob1) - elif j != 0: - return errDOMException("Incorrect number of characters in encoded string", - "InvalidCharacterError") - return ok(NarrowString(outs)) + let r = atob0(data) + if r.isNone: + return err(newDOMException(r.error, "InvalidCharacterError")) + return ok(NarrowString(r.get)) const AMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" diff --git a/src/utils/twtstr.nim b/src/utils/twtstr.nim index 66a211b9..6cab9ac7 100644 --- a/src/utils/twtstr.nim +++ b/src/utils/twtstr.nim @@ -711,3 +711,66 @@ proc getContentTypeAttr*(contentType, attrname: string): string = else: s &= c return s + +func atob(c: char): uint8 {.inline.} = + # see RFC 4648 table + if c in AsciiUpperAlpha: + return uint8(c) - uint8('A') + if c in AsciiLowerAlpha: + return uint8(c) - uint8('a') + 26 + if c in AsciiDigit: + return uint8(c) - uint8('0') + 52 + if c == '+': + return 62 + if c == '/': + return 63 + return uint8.high + +func atob0*(data: string): Result[string, string] = + var outs = newStringOfCap(data.len div 4 * 3) + var buf: array[4, uint8] + var i = 0 + var j = 0 + var pad = 0 + while true: + i = data.skipBlanks(i) + if i >= data.len: + break + if data[i] == '=': + i = data.skipBlanks(i + 1) + inc pad + break + buf[j] = atob(data[i]) + if buf[j] == uint8.high: + return err("Invalid character in encoded string") + if j == 3: + let ob1 = (buf[0] shl 2) or (buf[1] shr 4) # 6 bits of b0 | 2 bits of b1 + let ob2 = (buf[1] shl 4) or (buf[2] shr 2) # 4 bits of b1 | 4 bits of b2 + let ob3 = (buf[2] shl 6) or buf[3] # 2 bits of b2 | 6 bits of b3 + outs &= char(ob1) + outs &= char(ob2) + outs &= char(ob3) + j = 0 + else: + inc j + inc i + if i < data.len: + if i < data.len and data[i] == '=': + inc pad + inc i + i = data.skipBlanks(i) + if pad > 0 and j + pad != 4: + return err("Too much padding") + if i < data.len: + return err("Invalid character after encoded string") + if j == 3: + let ob1 = (buf[0] shl 2) or (buf[1] shr 4) # 6 bits of b0 | 2 bits of b1 + let ob2 = (buf[1] shl 4) or (buf[2] shr 2) # 4 bits of b1 | 4 bits of b2 + outs &= char(ob1) + outs &= char(ob2) + elif j == 2: + let ob1 = (buf[0] shl 2) or (buf[1] shr 4) # 6 bits of b0 | 2 bits of b1 + outs &= char(ob1) + elif j != 0: + return err("Incorrect number of characters in encoded string") + return ok(outs) |