Skip to content

Instantly share code, notes, and snippets.

@calebsander
Created July 17, 2018 23:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save calebsander/8052bdee96ec711000a95e73bd007f5b to your computer and use it in GitHub Desktop.
Save calebsander/8052bdee96ec711000a95e73bd007f5b to your computer and use it in GitHub Desktop.
base-64 encode and decode in WASM
(module
;; Memory layout
;; 0 - 63: lookup
;; 64 - 186: revLookup
;; 187 - : input, followed by output
(global $INPUT (export "INPUT") i32 (i32.const 187))
(memory (export "memory") 1)
(data (i32.const 0) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
(func (export "init")
(local $i i32)
;; for (i = 0; i < 64; i++)
(loop $initRevLookup
(i32.store8 offset=64 ;; revLookup[lookup[i]] = i
(i32.load8_u (get_local $i))
(get_local $i)
)
(br_if $initRevLookup ;; if (++i < 64) continue
(i32.lt_u
(tee_local $i (i32.add (get_local $i) (i32.const 1)))
(i32.const 64)
)
)
)
;; revLookup['-'] = 62
(i32.store8 offset=64 (i32.const 0x2d) (i32.const 62))
;; revLookup['_'] = 63
(i32.store8 offset=64 (i32.const 0x5f) (i32.const 63))
)
(func $fit (param $end i32)
(local $needed i32)
(set_local $needed ;; needed = (needed >> 16) + (needed % (1 << 16) ? 1 : 0) - memory.size
(i32.sub
(i32.add
(i32.shr_u (get_local $needed) (i32.const 16))
(select
(i32.const 1)
(i32.const 0)
(i32.and (get_local $needed) (i32.const 0xffff))
)
)
(current_memory)
)
)
(if (i32.gt_s (get_local $needed) (i32.const 0)) ;; if (needed > 0) memory.grow(needed)
(drop (grow_memory (get_local $needed)))
)
)
(func (export "fitInput") (param $len i32)
(call $fit (i32.add (get_global $INPUT) (get_local $len)))
)
(func $getPlaceholdersLen (param $len i32) (result i32)
;; input[len - 2] == '='
;; ? 2
;; : (input[len - 1] == '=' ? 1 : 0)
(select
(i32.const 2)
(select
(i32.const 1)
(i32.const 0)
(i32.eq
(i32.load8_u offset=186 (get_local $len))
(i32.const 0x3d)
)
)
(i32.eq
(i32.load8_u offset=185 (get_local $len))
(i32.const 0x3d)
)
)
)
(func (export "getByteArrayOutputLen") (param $len i32) (result i32)
;; return getByteArrayOutputLen(len, getPlaceholdersLen(len))
(call $getByteArrayOutputLen
(get_local $len)
(call $getPlaceholdersLen (get_local $len))
)
)
(func $getByteArrayOutputLen (param $len i32) (param $placeholdersLen i32) (result i32)
(i32.sub ;; return len * 3 / 4 - placeholdersLen
(i32.mul
(i32.shr_u (get_local $len) (i32.const 2))
(i32.const 3)
)
(get_local $placeholdersLen)
)
)
(func (export "toByteArray") (param $len i32) (result i32)
(local $placeholdersLen i32)
(local $output i32)
(local $completeLen i32)
(local $i i32)
(local $curByte i32)
(local $tmp i32)
;; placeholdersLen = getPlaceholdersLen(len)
(set_local $placeholdersLen (call $getPlaceholdersLen (get_local $len)))
(set_local $output ;; output = curByte = INPUT + len
(tee_local $curByte (i32.add (get_global $INPUT) (get_local $len)))
)
(call $fit ;; fit(output + getByteArrayOutputLen(len, placeholdersLen))
(i32.add
(get_local $output)
(call $getByteArrayOutputLen (get_local $len) (get_local $placeholdersLen))
)
)
(set_local $completeLen ;; completeLen = validLen - (placeholdersLen ? 4 : 0)
(i32.sub
;; validLen (== len - placeholdersLen)
(i32.sub (get_local $len) (get_local $placeholdersLen))
(select (i32.const 4) (i32.const 0) (get_local $placeholdersLen))
)
)
;; for (i = 0; i < completeLen; i += 4)
(if (i32.gt_s (get_local $completeLen) (i32.const 0))
(loop $setFullBlocks
;; tmp = revLookup[INPUT[i ]] << 18 |
;; revLookup[INPUT[i + 1]] << 12 |
;; revLookup[INPUT[i + 2]] << 6 |
;; revLookup[INPUT[i + 3]]
(set_local $tmp
(i32.or
(i32.or
(i32.shl
(i32.load8_u offset=64
(i32.load8_u offset=187 (get_local $i))
)
(i32.const 18)
)
(i32.shl
(i32.load8_u offset=64
(i32.load8_u offset=188 (get_local $i))
)
(i32.const 12)
)
)
(i32.or
(i32.shl
(i32.load8_u offset=64
(i32.load8_u offset=189 (get_local $i))
)
(i32.const 6)
)
(i32.load8_u offset=64
(i32.load8_u offset=190 (get_local $i))
)
)
)
)
;; output[curByte ] = tmp >>> 16
;; output[curByte + 1] = tmp >>> 8 & 0xFF
(i32.store16
(get_local $curByte)
(i32.or
(i32.shr_u (get_local $tmp) (i32.const 16))
(i32.and (get_local $tmp) (i32.const 0xff00))
)
)
;; output[curByte + 2] = tmp & 0xFF
(i32.store8 offset=2 (get_local $curByte) (get_local $tmp))
;; curByte += 3
(set_local $curByte (i32.add (get_local $curByte) (i32.const 3)))
(br_if $setFullBlocks ;; if ((i += 4) < completeLen) continue
(i32.lt_u
(tee_local $i (i32.add (get_local $i) (i32.const 4)))
(get_local $completeLen)
)
)
)
)
;; if (!placeholdersLen) return output
(if (i32.eqz (get_local $placeholdersLen)) (return (get_local $output)))
(if (i32.eq (get_local $placeholdersLen) (i32.const 2))
;; if (placeholdersLen === 2)
(then
;; output[curByte] = revLookup[INPUT[i ]] << 2 |
;; revLookup[INPUT[i + 1]] >>> 4
(i32.store8
(get_local $curByte)
(i32.or
(i32.shl
(i32.load8_u offset=64
(i32.load8_u offset=187 (get_local $i))
)
(i32.const 2)
)
(i32.shr_u
(i32.load8_u offset=64
(i32.load8_u offset=188 (get_local $i))
)
(i32.const 4)
)
)
)
)
;; if (placeholdersLen === 1)
(else
;; tmp = revLookup[INPUT[i]] << 10 |
;; revLookup[INPUT[i + 1]] << 4 |
;; revLookup[INPUT[i + 2]] >>> 2
(set_local $tmp
(i32.or
(i32.shl
(i32.load8_u offset=64
(i32.load8_u offset=187 (get_local $i))
)
(i32.const 10)
)
(i32.or
(i32.shl
(i32.load8_u offset=64
(i32.load8_u offset=188 (get_local $i))
)
(i32.const 4)
)
(i32.shr_u
(i32.load8_u offset=64
(i32.load8_u offset=189 (get_local $i))
)
(i32.const 2)
)
)
)
)
;; output[curByte ] = tmp >>> 8
;; output[curByte + 1] = tmp & 0xFF
(i32.store16
(get_local $curByte)
(i32.or
(i32.shr_u (get_local $tmp) (i32.const 8))
(i32.shl (get_local $tmp) (i32.const 8))
)
)
)
)
(get_local $output) ;; return output
)
(func $getExtraBytes (param $len i32) (result i32)
;; return len % 3
(i32.rem_u (get_local $len) (i32.const 3))
)
(func $getTripletLength (param $len i32) (param $extraBytes i32) (result i32)
;; return len - extraBytes
(i32.sub (get_local $len) (get_local $extraBytes))
)
(func $getBase64OutputLen (param $len i32) (param $tripletLength i32) (result i32)
(i32.shl ;; return (tripletLength / 3 + (tripleLength < len)) * 4
(i32.add
(i32.div_u (get_local $tripletLength) (i32.const 3))
(i32.lt_u (get_local $tripletLength) (get_local $len))
)
(i32.const 2)
)
)
(func (export "getBase64OutputLen") (param $len i32) (result i32)
;; return getBase64OutputLen(len, getTripletLength(len, getExtraBytes(len)))
(call $getBase64OutputLen
(get_local $len)
(call $getTripletLength
(get_local $len)
(call $getExtraBytes (get_local $len))
)
)
)
(func (export "fromByteArray") (param $len i32) (result i32)
(local $extraBytes i32)
(local $tripletLength i32)
(local $output i32)
(local $i i32)
(local $curByte i32)
(local $tmp i32)
;; extraBytes = getExtraBytes(len)
(set_local $extraBytes (call $getExtraBytes (get_local $len)))
;; tripleLength = getTripletLength(len, extraBytes)
(set_local $tripletLength
(call $getTripletLength
(get_local $len)
(get_local $extraBytes)
)
)
;; output = INPUT + len
(set_local $output (i32.add (get_global $INPUT) (get_local $len)))
;; if (output % 4) output += 4 - (output % 4)
(set_local $output
(i32.add
(i32.and (get_local $output) (i32.const -4))
(select
(i32.const 4)
(i32.const 0)
(i32.and (get_local $output) (i32.const 3))
)
)
)
;; fit(output + getBase64OutputLen(len, tripletLength))
(call $fit
(i32.add
(get_local $output)
(call $getBase64OutputLen (get_local $len) (get_local $tripletLength))
)
)
;; curByte = output
(set_local $curByte (get_local $output))
;; for (i = 0; i < tripletLength; i += 3)
(if (get_local $tripletLength)
(loop $setFullBlocks
;; tmp = INPUT[i] << 16 | INPUT[i + 1] << 8 | INPUT[i + 2]
(set_local $tmp
(i32.or
(i32.shl
(i32.load8_u offset=187 (get_local $i))
(i32.const 16)
)
(i32.or
(i32.shl
(i32.load8_u offset=188 (get_local $i))
(i32.const 8)
)
(i32.load8_u offset=189 (get_local $i))
)
)
)
;; output[curByte ] = lookup[tmp >>> 18 & 0x3f]
;; output[curByte + 1] = lookup[tmp >>> 12 & 0x3f]
;; output[curByte + 2] = lookup[tmp >>> 6 & 0x3f]
;; output[curByte + 3] = lookup[tmp & 0x3f]
(i32.store align=2
(get_local $curByte)
(i32.or
(i32.or
(i32.load8_u
(i32.and
(i32.shr_u (get_local $tmp) (i32.const 18))
(i32.const 0x3f)
)
)
(i32.shl
(i32.load8_u
(i32.and
(i32.shr_u (get_local $tmp) (i32.const 12))
(i32.const 0x3f)
)
)
(i32.const 8)
)
)
(i32.or
(i32.shl
(i32.load8_u
(i32.and
(i32.shr_u (get_local $tmp) (i32.const 6))
(i32.const 0x3f)
)
)
(i32.const 16)
)
(i32.shl
(i32.load8_u
(i32.and (get_local $tmp) (i32.const 0x3f))
)
(i32.const 24)
)
)
)
)
;; curByte += 4
(set_local $curByte (i32.add (get_local $curByte) (i32.const 4)))
(br_if $setFullBlocks ;; if ((i += 3) < tripletLength) continue
(i32.lt_u
(tee_local $i (i32.add (get_local $i) (i32.const 3)))
(get_local $tripletLength)
)
)
)
)
;; if (!extraBytes) return output
(if (i32.eqz (get_local $extraBytes)) (return (get_local $output)))
(if (i32.eq (get_local $extraBytes) (i32.const 1))
;; if (extraBytes === 1)
(then
;; tmp = INPUT[i]
(set_local $tmp (i32.load8_u offset=187 (get_local $i)))
;; output[curByte ] = lookup[tmp >>> 2 ]
;; output[curByte + 1] = lookup[tmp << 4 & 0x3f]
;; output[curByte + 2] = output[curByte + 3] = '='
(i32.store align=2
(get_local $curByte)
(i32.or
(i32.or
(i32.load8_u (i32.shr_u (get_local $tmp) (i32.const 2)))
(i32.shl
(i32.load8_u
(i32.and
(i32.shl (get_local $tmp) (i32.const 4))
(i32.const 0x3f)
)
)
(i32.const 8)
)
)
(i32.const 0x3d3d0000) ;; '=='
)
)
)
;; if (extraBytes === 2)
(else
(set_local $tmp ;; tmp = INPUT[i] << 8 | INPUT[i + 1]
(i32.or
(i32.shl
(i32.load8_u offset=187 (get_local $i))
(i32.const 8)
)
(i32.load8_u offset=188 (get_local $i))
)
)
;; output[curByte ] = lookup[tmp >>> 10 ]
;; output[curByte + 1] = lookup[tmp >>> 4 & 0x3f]
;; output[curByte + 2] = lookup[tmp << 2 & 0x3f]
;; output[curByte + 3] = '='
(i32.store align=2
(get_local $curByte)
(i32.or
(i32.or
(i32.load8_u (i32.shr_u (get_local $tmp) (i32.const 10)))
(i32.shl
(i32.load8_u
(i32.and
(i32.shr_u (get_local $tmp) (i32.const 4))
(i32.const 0x3f)
)
)
(i32.const 8)
)
)
(i32.or
(i32.shl
(i32.load8_u
(i32.and
(i32.shl (get_local $tmp) (i32.const 2))
(i32.const 0x3f)
)
)
(i32.const 16)
)
(i32.const 0x3d000000) ;; '='
)
)
)
)
)
(get_local $output) ;; return output
)
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment