Skip to content

Instantly share code, notes, and snippets.

@nberlette
Created March 14, 2025 09:14
Show Gist options
  • Save nberlette/8e27170c1515ba31f5b978cc6be62c87 to your computer and use it in GitHub Desktop.
Save nberlette/8e27170c1515ba31f5b978cc6be62c87 to your computer and use it in GitHub Desktop.
WebAssembly (WAT) implementation of atob / btoa
(module
;; Memory: 1 page = 64 KiB
(memory $memory 1)
;; Exported error flag (0 = no error, 1 = error)
(global $error_flag (mut i32) (i32.const 0))
;; Base64 encoding table
(data (i32.const 0) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
;; Base64 decoding table
(data (i32.const 64) "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3e\xff\xff\xff\x3f"
"\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\xff\xff\xff\x00\xff\xff"
"\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e"
"\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\xff\xff\xff\xff\xff"
"\xff\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28"
"\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\xff\xff\xff\xff\xff")
(export "memory" (memory $memory))
(export "error_flag" (global $error_flag))
(export "btoa" (func $btoa))
(export "atob" (func $atob))
;; Encode (btoa)
(func $btoa
(param $input i32)
(param $len i32)
(result i32)
(local $i i32)
(local $o i32)
(local $buffer i32)
(local $char i32)
(global.set $error_flag (i32.const 0))
(local.set $o (i32.const 1024))
(block $exit
(loop $encode
(br_if $exit (i32.ge_u (local.get $i) (local.get $len)))
(local.set $buffer (i32.const 0))
(local.set $buffer (i32.shl (i32.load8_u (i32.add (local.get $input) (local.get $i))) (i32.const 16)))
(local.set $i (i32.add (local.get $i) (i32.const 1)))
(if (i32.lt_u (local.get $i) (local.get $len))
(then
(local.set $buffer (i32.or (local.get $buffer) (i32.shl (i32.load8_u (i32.add (local.get $input) (local.get $i))) (i32.const 8))))
(local.set $i (i32.add (local.get $i) (i32.const 1)))
)
)
(if (i32.lt_u (local.get $i) (local.get $len))
(then
(local.set $buffer (i32.or (local.get $buffer) (i32.load8_u (i32.add (local.get $input) (local.get $i)))))
(local.set $i (i32.add (local.get $i) (i32.const 1)))
)
)
(local.set $char (i32.and (i32.shr_u (local.get $buffer) (i32.const 18)) (i32.const 63)))
(i32.store8 (local.get $o) (i32.load8_u (i32.add (i32.const 0) (local.get $char))))
(local.set $o (i32.add (local.get $o) (i32.const 1)))
(local.set $char (i32.and (i32.shr_u (local.get $buffer) (i32.const 12)) (i32.const 63)))
(i32.store8 (local.get $o) (i32.load8_u (i32.add (i32.const 0) (local.get $char))))
(local.set $o (i32.add (local.get $o) (i32.const 1)))
(local.set $char (i32.and (i32.shr_u (local.get $buffer) (i32.const 6)) (i32.const 63)))
(i32.store8 (local.get $o) (i32.load8_u (i32.add (i32.const 0) (local.get $char))))
(local.set $o (i32.add (local.get $o) (i32.const 1)))
(local.set $char (i32.and (local.get $buffer) (i32.const 63)))
(i32.store8 (local.get $o) (i32.load8_u (i32.add (i32.const 0) (local.get $char))))
(local.set $o (i32.add (local.get $o) (i32.const 1)))
(br $encode)
)
)
(local.get $o)
)
;; Decode (atob)
(func $atob
(param $input i32)
(param $len i32)
(result i32)
(local $i i32)
(local $o i32)
(local $buffer i32)
(local $valid i32)
(global.set $error_flag (i32.const 0))
(local.set $o (i32.const 1024))
(block $exit
(loop $decode
(br_if $exit (i32.ge_u (local.get $i) (local.get $len)))
(local.set $valid (i32.load8_u (i32.add (i32.const 64) (i32.load8_u (i32.add (local.get $input) (local.get $i))))))
(if (i32.eq (local.get $valid) (i32.const 255))
(then
(global.set $error_flag (i32.const 1))
(br $exit)
)
)
(local.set $buffer (i32.shl (local.get $buffer) (i32.const 6)))
(local.set $buffer (i32.or (local.get $buffer) (local.get $valid)))
(local.set $i (i32.add (local.get $i) (i32.const 1)))
(if (i32.eq (i32.and (local.get $i) (i32.const 3)) (i32.const 0))
(then
(i32.store8 (local.get $o) (i32.shr_u (local.get $buffer) (i32.const 16)))
(local.set $o (i32.add (local.get $o) (i32.const 1)))
(i32.store8 (local.get $o) (i32.and (i32.shr_u (local.get $buffer) (i32.const 8)) (i32.const 255)))
(local.set $o (i32.add (local.get $o) (i32.const 1)))
(i32.store8 (local.get $o) (i32.and (local.get $buffer) (i32.const 255)))
(local.set $o (i32.add (local.get $o) (i32.const 1)))
)
)
(br $decode)
)
)
(local.get $o)
)
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment