Repository navigation
Expand file tree
/
Copy pathpb.lua
More file actions
124 lines (110 loc) · 3.95 KB
/
Copy pathpb.lua
File metadata and controls
124 lines (110 loc) · 3.95 KB
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
-- PB8/PB16 encoders
-- Copyright 2018, 2019 Damian Yerrick
--
-- This Lua port is an altered version of the original Python encoders.
--
-- This software is provided 'as-is', without any express or implied
-- warranty. In no event will the authors be held liable for any damages
-- arising from the use of this software.
--
-- Permission is granted to anyone to use this software for any purpose,
-- including commercial applications, and to alter it and redistribute it
-- freely, subject to the following restrictions:
--
-- 1. The origin of this software must not be misrepresented; you must not
-- claim that you wrote the original software. If you use this software
-- in a product, an acknowledgment in the product documentation would be
-- appreciated but is not required.
-- 2. Altered source versions must be plainly marked as such, and must not be
-- misrepresented as being the original software.
-- 3. This notice may not be removed or altered from any source distribution.
local pb = {}
local function input_bytes(data)
local bytes = {}
if type(data) == "string" then
for i = 1, #data do bytes[i] = string.byte(data, i) end
else
assert(type(data) == "table", "PB input must be a string or byte table")
for i = 1, #data do bytes[i] = data[i] end
end
for i, value in ipairs(bytes) do
assert(type(value) == "number" and value % 1 == 0
and value >= 0 and value <= 0xff,
"invalid PB input byte at index " .. i)
end
return bytes
end
local function read_file(filename)
local file = assert(io.open(filename, "rb"))
local data = assert(file:read("*all"))
file:close()
return data
end
-- Encode bytes as PB8. Each packet expands to eight bytes and starts with an
-- MSB-first control byte: 1 repeats the previous byte and 0 reads a literal.
function pb.pb8(data)
local input = input_bytes(data)
local output = {}
local previous = 0
for first = 1, #input, 8 do
local chunk = {}
for i = first, math.min(first + 7, #input) do
chunk[#chunk + 1] = input[i]
end
while #chunk < 8 do chunk[#chunk + 1] = chunk[#chunk] end
local control_index = #output + 1
output[control_index] = 0
for i, value in ipairs(chunk) do
if value == previous then
output[control_index] = output[control_index] | (0x80 >> (i - 1))
else
output[#output + 1] = value
previous = value
end
end
end
return output
end
-- Encode bytes as PB16. This is PB8 over two interleaved streams, so a set
-- control bit repeats the byte two positions back. Partial packets preserve
-- the two streams and are padded to exactly eight uncompressed bytes.
function pb.pb16(data)
local input = input_bytes(data)
local output = {}
local previous = { 0, 0 }
for first = 1, #input, 8 do
local chunk = {}
for i = first, math.min(first + 7, #input) do
chunk[#chunk + 1] = input[i]
end
if #chunk == 1 then
chunk[2] = previous[2]
elseif #chunk % 2 ~= 0 then
chunk[#chunk + 1] = chunk[#chunk - 1]
end
local plane0, plane1 = chunk[#chunk - 1], chunk[#chunk]
while #chunk < 8 do
chunk[#chunk + 1] = plane0
chunk[#chunk + 1] = plane1
end
local control_index = #output + 1
output[control_index] = 0
for i, value in ipairs(chunk) do
local stream = ((i - 1) % 2) + 1
if value == previous[stream] then
output[control_index] = output[control_index] | (0x80 >> (i - 1))
else
output[#output + 1] = value
previous[stream] = value
end
end
end
return output
end
function pb.pb8_file(filename)
return pb.pb8(read_file(filename))
end
function pb.pb16_file(filename)
return pb.pb16(read_file(filename))
end
return pb