summaryrefslogtreecommitdiff
path: root/Base64/Base64.lua
blob: 33e78bdaea9033e47e74cb7aa0cad5d4d0b1bad0 (plain)
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
---------------------------------------------------------------------------------------------------
-- -= Base64 =-
---------------------------------------------------------------------------------------------------

-- Make some functions easier to write
local floor = math.floor
local sub = string.sub
local gsub = string.gsub
local rem = table.remove

---------------------------------------------------------------------------------------------------
-- Our base64 value table
local base64 = { ['A']=0,['B']=1,['C']=2,['D']=3,['E']=4,['F']=5,['G']=6,['H']=7,['I']=8,
                ['J']=9,['K']=10,['L']=11,['M']=12,['N']=13,['O']=14,['P']=15,['Q']=16,
                ['R']=17,['S']=18,['T']=19,['U']=20,['V']=21,['W']=22,['X']=23,['Y']=24,
                ['Z']=25,['a']=26,['b']=27,['c']=28,['d']=29,['e']=30,['f']=31,['g']=32,
                ['h']=33,['i']=34,['j']=35,['k']=36,['l']=37,['m']=38,['n']=39,['o']=40,
                ['p']=41,['q']=42,['r']=43,['s']=44,['t']=45,['u']=46,['v']=47,['w']=48,
                ['x']=49,['y']=50,['z']=51,['0']=52,['1']=53,['2']=54,['3']=55,['4']=56,
                ['5']=57,['6']=58,['7']=59,['8']=60,['9']=61,['+']=62,['/']=63,['=']=nil}

---------------------------------------------------------------------------------------------------
-- Decimal values for binary digits
local bin ={}
local mult = 1
for i = 1,40 do
    bin[i] = mult
    mult = mult*2
end

---------------------------------------------------------------------------------------------------
-- A buffer we will use to process the bits
local buffer = 0
local pos = 0
local function clearBuffer()
    buffer = 0
    pos = 1
end

---------------------------------------------------------------------------------------------------
-- Shift all of the bits up in the buffer and put the base64 number on the bottom
local function pushBase64(n)
    if base64[n] == nil then return end
    buffer = buffer * bin[7] + base64[n]
    pos = pos + 6
end

---------------------------------------------------------------------------------------------------
-- Get an int out of the buffer. This is tricky. The byte order is in little endian so we're going
-- to have to isolate and cut the bytes out and then move them around.
local function getInt()
    -- If our buffer isn't filled all the way then fill it with zeros
    while pos < 33 do 
        buffer = buffer * bin[2] 
        pos = pos + 1
    end
    -- Move the buffer position to just below the integer.
    pos = pos - 32
    
    -- Swap the first and forth byte and then the second and third.
    local tmp = floor((buffer%bin[33+pos-1])/bin[25+pos-1]) +
                floor((buffer%bin[25+pos-1])/bin[17+pos-1])*bin[9] +
                floor((buffer%bin[17+pos-1])/bin[9+pos-1])*bin[17] + 
                floor((buffer%bin[9+pos-1])/bin[pos])*bin[25]
    
    -- We've got our integer so let's cut that portion out of the buffer
    buffer = buffer % bin[pos]
    -- Return the int
    return  tmp
end

---------------------------------------------------------------------------------------------------
-- Get a byte out of the buffer
local function getByte()
    -- If our buffer isn't filled all the way then fill it with zeros
    while pos < 9 do
        buffer = buffer * bin[2] 
        pos = pos + 1
    end
    -- Move the buffer position to just below the byte.
    pos = pos - 8
    -- Cut out the byte
    local tmp = floor((buffer%bin[9+pos-1])/bin[pos])
    -- Delete the byte from the buffer
    buffer = buffer % bin[pos]
    -- Return the byte
    return tmp
end

---------------------------------------------------------------------------------------------------
-- Glues together an integer from four bytes. Little endian
local function glueInt(b1, b2, b3, b4)
    return b1%bin[9] + b2%bin[9]*bin[9] + b3%bin[9]*bin[17] + b4%bin[9]*bin[25]
end

---------------------------------------------------------------------------------------------------
-- A Lua set that will filter out characters that aren't in the base64 table
local set = "[^%a%d%+%/%=]"

---------------------------------------------------------------------------------------------------
-- Decodes a base64 string into the given type
local function decode(mode, raw)

    -- Make sure the mode is supported
    assert(mode=="string" or mode=="int" or mode=="byte", "Base64 decode - Invalid mode: " .. mode)

    -- Clear the buffer
    clearBuffer()
    
    -- Filters undefined characters out of the string
    raw = gsub(raw, set, "")
    
    local size = 0          -- Size of the returned type in bits
    local val = {}          -- A table containing the data to be returned
    local raw_pos = 1       -- The position of the progress through the raw base64 string
    local raw_size = #raw   -- The size of the base64 string
    local char = ""         -- The current base64 character to be processed
    
    -- If we're expected to return an int then the bit size is 32, otherwise it's 8
    if mode == "int" then size = 32 else size = 8 end
    
    -- While we still have input
    while raw_pos <= raw_size do
        -- Fill the buffer until we have enough bits
        while pos <= size and raw_pos <= raw_size do
            char = sub(raw,raw_pos,raw_pos)
            pushBase64( char )
            raw_pos = raw_pos + 1
        end
        -- If a nil character is encountered the end the loop
        if char == "=" then break end
        -- Get data from the buffer depending on the type
        if mode == "string" then val[#val+1] = string.char( getByte() ) end
        if mode == "byte" then val[#val+1] = getByte() end
        if mode == "int" then val[#val+1] = getInt() end
    end
    
    if mode == "string" then return table.concat(val) end
    return val
end

---------------------------------------------------------------------------------------------------
-- Encodes a table of ints into a base64 string.
local function encode(ints)

    -- Clear the buffer
    clearBuffer()
    
    local size = 0          -- Size of the returned type in bits
    local val = {}          -- A table containing the data to be returned
    local char = ""         -- The current base64 character to be processed
    
    -- If we're expected to return an int then the bit size is 32, otherwise it's 8
    if mode == "int" then size = 32 else size = 8 end
    
    -- While we still have input
    while raw_pos <= raw_size do
        -- Fill the buffer until we have enough bits
        while pos <= size and raw_pos <= raw_size do
            char = sub(raw,raw_pos,raw_pos)
            pushBase64( char )
            raw_pos = raw_pos + 1
        end
        -- If a nil character is encountered the end the loop
        if char == "=" then break end
        -- Get data from the buffer depending on the type
        if mode == "string" then val[#val+1] = string.char( getByte() ) end
        if mode == "byte" then val[#val+1] = getByte() end
        if mode == "int" then val[#val+1] = getInt() end
    end
    
    if mode == "string" then return table.concat(val) end
    return val
end

---------------------------------------------------------------------------------------------------
-- Returns the functions
return {decode = decode, glueInt = glueInt, base64 = base64}