Skip to content

Instantly share code, notes, and snippets.

@DevAX1T
Forked from metatablecat/b64.lua
Created January 10, 2025 01:53
Show Gist options
  • Select an option

  • Save DevAX1T/a550493165755bf1e1c2bd9f5d322742 to your computer and use it in GitHub Desktop.

Select an option

Save DevAX1T/a550493165755bf1e1c2bd9f5d322742 to your computer and use it in GitHub Desktop.
The fastest luau base64 library in the west. This has been benchmarked within Roblox Luau, results may vary
local SEQ = {
[0] = "A", "B", "C", "D", "E", "F", "G", "H",
"I", "J", "K", "L", "M", "N", "O", "P",
"Q", "R", "S", "T", "U", "V", "W", "X",
"Y", "Z", "a", "b", "c", "d", "e", "f",
"g", "h", "i", "j", "k", "l", "m", "n",
"o", "p", "q", "r", "s", "t", "u", "v",
"w", "x", "y", "z", "0", "1", "2", "3",
"4", "5", "6", "7", "8", "9", "+", "/",
}
local STRING_FAST = {}
local INDEX = {[61] = 0, [65] = 0}
for key, val in ipairs(SEQ) do
-- memoization
INDEX[string.byte(val)] = key
end
-- string.char has a MASSIVE overhead, its faster to precompute
-- the values for performance
for i = 0, 255 do
local c = string.char(i)
STRING_FAST[i] = c
end
local b64 = {}
function b64.encode(str)
local len = string.len(str)
local output = table.create(math.ceil(len/4)*4)
local index = 1
for i = 1, len, 3 do
local b0, b1, b2 = string.byte(str, i, i + 2)
local b = bit32.lshift(b0, 16) + bit32.lshift(b1 or 0, 8) + (b2 or 0)
output[index] = SEQ[bit32.extract(b, 18, 6)]
output[index + 1] = SEQ[bit32.extract(b, 12, 6)]
output[index + 2] = b1 and SEQ[bit32.extract(b, 6, 6)] or "="
output[index + 3] = b2 and SEQ[bit32.band(b, 63)] or "="
index += 4
end
return table.concat(output)
end
function b64.decode(hash)
-- given a 24 bit word (4 6-bit letters), decode 3 bytes from it
local len = string.len(hash)
local output = table.create(len * 0.75)
local index = 1
for i = 1, len, 4 do
local c0, c1, c2, c3 = string.byte(hash, i, i + 3)
local b =
bit32.lshift(INDEX[c0], 18)
+ bit32.lshift(INDEX[c1], 12)
+ bit32.lshift(INDEX[c2], 6)
+ (INDEX[c3])
output[index] = STRING_FAST[bit32.extract(b, 16, 8)]
output[index + 1] = c2 ~= "=" and STRING_FAST[bit32.extract(b, 8, 8)] or "="
output[index + 2] = c3 ~= "=" and STRING_FAST[bit32.band(b, 0xFF)] or "="
index += 3
end
return table.concat(output)
end
return b64

The following benchmark was compared against the Base64 library available here using Boatbomber's plugin

Data was tested with 100,000 char strings

decode encode AGF decode AGF encode
10th % 2.751ms 4.755ms 5.185ms 7.554ms
50th % 2.997ms 5.123ms 7.474ms 9.015ms
90th % 3.482ms 5.864ms 8.847ms 10.030ms
Avg 8.934ms 11.329ms 27.008ms 20.659ms
Min 2.519ms 4.468ms 4.417ms 6.623ms
Max 15.348ms 18.191ms 49.599ms 34.694ms
Average bytes per second 111,931,945.377 88,269,044.046 37,026,066.350 48,405,053.487
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment