protocol-hpack 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +6 -0
- data/.gitignore +12 -0
- data/.gitmodules +3 -0
- data/.rspec +3 -0
- data/.travis.yml +23 -0
- data/Gemfile +10 -0
- data/README.md +81 -0
- data/Rakefile +8 -0
- data/http-hpack.gemspec +23 -0
- data/lib/protocol/hpack.rb +24 -0
- data/lib/protocol/hpack/compressor.rb +193 -0
- data/lib/protocol/hpack/context.rb +337 -0
- data/lib/protocol/hpack/decompressor.rb +154 -0
- data/lib/protocol/hpack/huffman.rb +343 -0
- data/lib/protocol/hpack/huffman/machine.rb +290 -0
- data/lib/protocol/hpack/version.rb +25 -0
- data/tasks/huffman.rake +9 -0
- data/tasks/huffman.rb +181 -0
- metadata +116 -0
@@ -0,0 +1,154 @@
|
|
1
|
+
# Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
# Copyrigh, 2013, by Ilya Grigorik.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
|
22
|
+
require_relative 'context'
|
23
|
+
require_relative 'huffman'
|
24
|
+
|
25
|
+
module Protocol
|
26
|
+
module HPACK
|
27
|
+
# Responsible for decoding received headers and maintaining compression
|
28
|
+
# context of the opposing peer. Decompressor must be initialized with
|
29
|
+
# appropriate starting context based on local role: client or server.
|
30
|
+
class Decompressor
|
31
|
+
def initialize(buffer, context = Context.new)
|
32
|
+
@buffer = buffer
|
33
|
+
@context = context
|
34
|
+
@offset = 0
|
35
|
+
end
|
36
|
+
|
37
|
+
attr :buffer
|
38
|
+
attr :context
|
39
|
+
attr :offset
|
40
|
+
|
41
|
+
def end?
|
42
|
+
@offset >= @buffer.bytesize
|
43
|
+
end
|
44
|
+
|
45
|
+
def read_byte
|
46
|
+
if byte = @buffer.getbyte(@offset)
|
47
|
+
@offset += 1
|
48
|
+
end
|
49
|
+
|
50
|
+
return byte
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def peek_byte
|
55
|
+
@buffer.getbyte(@offset)
|
56
|
+
end
|
57
|
+
|
58
|
+
def read_bytes(length)
|
59
|
+
slice = @buffer.byteslice(@offset, length)
|
60
|
+
|
61
|
+
@offset += length
|
62
|
+
|
63
|
+
return slice
|
64
|
+
end
|
65
|
+
|
66
|
+
# Decodes integer value from provided buffer.
|
67
|
+
#
|
68
|
+
# @param bits [Integer] number of available bits
|
69
|
+
# @return [Integer]
|
70
|
+
def read_integer(bits)
|
71
|
+
limit = 2**bits - 1
|
72
|
+
value = !bits.zero? ? (read_byte & limit) : 0
|
73
|
+
|
74
|
+
shift = 0
|
75
|
+
|
76
|
+
while byte = read_byte
|
77
|
+
value += ((byte & 127) << shift)
|
78
|
+
shift += 7
|
79
|
+
|
80
|
+
break if (byte & 128).zero?
|
81
|
+
end if (value == limit)
|
82
|
+
|
83
|
+
return value
|
84
|
+
end
|
85
|
+
|
86
|
+
# Decodes string value from provided buffer.
|
87
|
+
#
|
88
|
+
# @return [String] UTF-8 encoded string
|
89
|
+
# @raise [CompressionError] when input is malformed
|
90
|
+
def read_string
|
91
|
+
huffman = (peek_byte & 0x80) == 0x80
|
92
|
+
|
93
|
+
length = read_integer(7)
|
94
|
+
string = read_bytes(length)
|
95
|
+
|
96
|
+
raise CompressionError, "Invalid string length, got #{string.bytesize}, expecting #{length}!" unless string.bytesize == length
|
97
|
+
|
98
|
+
string = Huffman.new.decode(string) if huffman
|
99
|
+
|
100
|
+
return string.force_encoding(Encoding::UTF_8)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Decodes header command from provided buffer.
|
104
|
+
#
|
105
|
+
# @param buffer [Buffer]
|
106
|
+
# @return [Hash] command
|
107
|
+
def read_header
|
108
|
+
pattern = peek_byte
|
109
|
+
|
110
|
+
header = {}
|
111
|
+
header[:type], type = HEADER_REPRESENTATION.find do |_t, desc|
|
112
|
+
mask = (pattern >> desc[:prefix]) << desc[:prefix]
|
113
|
+
mask == desc[:pattern]
|
114
|
+
end
|
115
|
+
|
116
|
+
raise CompressionError unless header[:type]
|
117
|
+
|
118
|
+
header[:name] = read_integer(type[:prefix])
|
119
|
+
|
120
|
+
case header[:type]
|
121
|
+
when :indexed
|
122
|
+
raise CompressionError if header[:name].zero?
|
123
|
+
header[:name] -= 1
|
124
|
+
when :changetablesize
|
125
|
+
header[:value] = header[:name]
|
126
|
+
else
|
127
|
+
if (header[:name]).zero?
|
128
|
+
header[:name] = read_string
|
129
|
+
else
|
130
|
+
header[:name] -= 1
|
131
|
+
end
|
132
|
+
|
133
|
+
header[:value] = read_string
|
134
|
+
end
|
135
|
+
|
136
|
+
return header
|
137
|
+
end
|
138
|
+
|
139
|
+
# Decodes and processes header commands within provided buffer.
|
140
|
+
#
|
141
|
+
# @param buffer [Buffer]
|
142
|
+
# @return [Array] +[[name, value], ...]+
|
143
|
+
def decode(list = [])
|
144
|
+
while !end?
|
145
|
+
if pair = @context.decode(read_header)
|
146
|
+
list << pair
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
return list
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,343 @@
|
|
1
|
+
# Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
# Copyrigh, 2013, by Ilya Grigorik.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
|
22
|
+
require_relative 'huffman/machine'
|
23
|
+
|
24
|
+
module Protocol
|
25
|
+
module HPACK
|
26
|
+
class CompressionError < RuntimeError
|
27
|
+
end
|
28
|
+
|
29
|
+
# Implementation of huffman encoding for HPACK
|
30
|
+
#
|
31
|
+
# - http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-10
|
32
|
+
class Huffman
|
33
|
+
BITS_AT_ONCE = 4
|
34
|
+
EOS = 256
|
35
|
+
|
36
|
+
# Encodes provided value via huffman encoding.
|
37
|
+
# Length is not encoded in this method.
|
38
|
+
#
|
39
|
+
# @param str [String]
|
40
|
+
# @return [String] binary string
|
41
|
+
def encode(str)
|
42
|
+
bitstring = str.each_byte.map {|chr| ENCODE_TABLE[chr]}.join
|
43
|
+
bitstring << '1' * ((8 - bitstring.size) % 8)
|
44
|
+
[bitstring].pack('B*')
|
45
|
+
end
|
46
|
+
|
47
|
+
# Decodes provided Huffman coded string.
|
48
|
+
#
|
49
|
+
# @param buf [Buffer]
|
50
|
+
# @return [String] binary string
|
51
|
+
# @raise [CompressionError] when Huffman coded string is malformed
|
52
|
+
def decode(buf)
|
53
|
+
emit = ''
|
54
|
+
state = 0 # start state
|
55
|
+
|
56
|
+
mask = (1 << BITS_AT_ONCE) - 1
|
57
|
+
buf.each_byte do |chr|
|
58
|
+
(8 / BITS_AT_ONCE - 1).downto(0) do |shift|
|
59
|
+
branch = (chr >> (shift * BITS_AT_ONCE)) & mask
|
60
|
+
# MACHINE[state] = [final, [transitions]]
|
61
|
+
# [final] unfinished bits so far are prefix of the EOS code.
|
62
|
+
# Each transition is [emit, next]
|
63
|
+
# [emit] character to be emitted on this transition, empty string, or EOS.
|
64
|
+
# [next] next state number.
|
65
|
+
trans = MACHINE[state][branch]
|
66
|
+
raise CompressionError, 'Huffman decode error (EOS found)' if trans.first == EOS
|
67
|
+
emit << trans.first.chr if trans.first
|
68
|
+
state = trans.last
|
69
|
+
end
|
70
|
+
end
|
71
|
+
# Check whether partial input is correctly filled
|
72
|
+
unless state <= MAX_FINAL_STATE
|
73
|
+
raise CompressionError, 'Huffman decode error (EOS invalid)'
|
74
|
+
end
|
75
|
+
emit.force_encoding(Encoding::BINARY)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Huffman table as specified in
|
79
|
+
# - http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-10#appendix-B
|
80
|
+
CODES = [
|
81
|
+
[0x1ff8, 13],
|
82
|
+
[0x7fffd8, 23],
|
83
|
+
[0xfffffe2, 28],
|
84
|
+
[0xfffffe3, 28],
|
85
|
+
[0xfffffe4, 28],
|
86
|
+
[0xfffffe5, 28],
|
87
|
+
[0xfffffe6, 28],
|
88
|
+
[0xfffffe7, 28],
|
89
|
+
[0xfffffe8, 28],
|
90
|
+
[0xffffea, 24],
|
91
|
+
[0x3ffffffc, 30],
|
92
|
+
[0xfffffe9, 28],
|
93
|
+
[0xfffffea, 28],
|
94
|
+
[0x3ffffffd, 30],
|
95
|
+
[0xfffffeb, 28],
|
96
|
+
[0xfffffec, 28],
|
97
|
+
[0xfffffed, 28],
|
98
|
+
[0xfffffee, 28],
|
99
|
+
[0xfffffef, 28],
|
100
|
+
[0xffffff0, 28],
|
101
|
+
[0xffffff1, 28],
|
102
|
+
[0xffffff2, 28],
|
103
|
+
[0x3ffffffe, 30],
|
104
|
+
[0xffffff3, 28],
|
105
|
+
[0xffffff4, 28],
|
106
|
+
[0xffffff5, 28],
|
107
|
+
[0xffffff6, 28],
|
108
|
+
[0xffffff7, 28],
|
109
|
+
[0xffffff8, 28],
|
110
|
+
[0xffffff9, 28],
|
111
|
+
[0xffffffa, 28],
|
112
|
+
[0xffffffb, 28],
|
113
|
+
[0x14, 6],
|
114
|
+
[0x3f8, 10],
|
115
|
+
[0x3f9, 10],
|
116
|
+
[0xffa, 12],
|
117
|
+
[0x1ff9, 13],
|
118
|
+
[0x15, 6],
|
119
|
+
[0xf8, 8],
|
120
|
+
[0x7fa, 11],
|
121
|
+
[0x3fa, 10],
|
122
|
+
[0x3fb, 10],
|
123
|
+
[0xf9, 8],
|
124
|
+
[0x7fb, 11],
|
125
|
+
[0xfa, 8],
|
126
|
+
[0x16, 6],
|
127
|
+
[0x17, 6],
|
128
|
+
[0x18, 6],
|
129
|
+
[0x0, 5],
|
130
|
+
[0x1, 5],
|
131
|
+
[0x2, 5],
|
132
|
+
[0x19, 6],
|
133
|
+
[0x1a, 6],
|
134
|
+
[0x1b, 6],
|
135
|
+
[0x1c, 6],
|
136
|
+
[0x1d, 6],
|
137
|
+
[0x1e, 6],
|
138
|
+
[0x1f, 6],
|
139
|
+
[0x5c, 7],
|
140
|
+
[0xfb, 8],
|
141
|
+
[0x7ffc, 15],
|
142
|
+
[0x20, 6],
|
143
|
+
[0xffb, 12],
|
144
|
+
[0x3fc, 10],
|
145
|
+
[0x1ffa, 13],
|
146
|
+
[0x21, 6],
|
147
|
+
[0x5d, 7],
|
148
|
+
[0x5e, 7],
|
149
|
+
[0x5f, 7],
|
150
|
+
[0x60, 7],
|
151
|
+
[0x61, 7],
|
152
|
+
[0x62, 7],
|
153
|
+
[0x63, 7],
|
154
|
+
[0x64, 7],
|
155
|
+
[0x65, 7],
|
156
|
+
[0x66, 7],
|
157
|
+
[0x67, 7],
|
158
|
+
[0x68, 7],
|
159
|
+
[0x69, 7],
|
160
|
+
[0x6a, 7],
|
161
|
+
[0x6b, 7],
|
162
|
+
[0x6c, 7],
|
163
|
+
[0x6d, 7],
|
164
|
+
[0x6e, 7],
|
165
|
+
[0x6f, 7],
|
166
|
+
[0x70, 7],
|
167
|
+
[0x71, 7],
|
168
|
+
[0x72, 7],
|
169
|
+
[0xfc, 8],
|
170
|
+
[0x73, 7],
|
171
|
+
[0xfd, 8],
|
172
|
+
[0x1ffb, 13],
|
173
|
+
[0x7fff0, 19],
|
174
|
+
[0x1ffc, 13],
|
175
|
+
[0x3ffc, 14],
|
176
|
+
[0x22, 6],
|
177
|
+
[0x7ffd, 15],
|
178
|
+
[0x3, 5],
|
179
|
+
[0x23, 6],
|
180
|
+
[0x4, 5],
|
181
|
+
[0x24, 6],
|
182
|
+
[0x5, 5],
|
183
|
+
[0x25, 6],
|
184
|
+
[0x26, 6],
|
185
|
+
[0x27, 6],
|
186
|
+
[0x6, 5],
|
187
|
+
[0x74, 7],
|
188
|
+
[0x75, 7],
|
189
|
+
[0x28, 6],
|
190
|
+
[0x29, 6],
|
191
|
+
[0x2a, 6],
|
192
|
+
[0x7, 5],
|
193
|
+
[0x2b, 6],
|
194
|
+
[0x76, 7],
|
195
|
+
[0x2c, 6],
|
196
|
+
[0x8, 5],
|
197
|
+
[0x9, 5],
|
198
|
+
[0x2d, 6],
|
199
|
+
[0x77, 7],
|
200
|
+
[0x78, 7],
|
201
|
+
[0x79, 7],
|
202
|
+
[0x7a, 7],
|
203
|
+
[0x7b, 7],
|
204
|
+
[0x7ffe, 15],
|
205
|
+
[0x7fc, 11],
|
206
|
+
[0x3ffd, 14],
|
207
|
+
[0x1ffd, 13],
|
208
|
+
[0xffffffc, 28],
|
209
|
+
[0xfffe6, 20],
|
210
|
+
[0x3fffd2, 22],
|
211
|
+
[0xfffe7, 20],
|
212
|
+
[0xfffe8, 20],
|
213
|
+
[0x3fffd3, 22],
|
214
|
+
[0x3fffd4, 22],
|
215
|
+
[0x3fffd5, 22],
|
216
|
+
[0x7fffd9, 23],
|
217
|
+
[0x3fffd6, 22],
|
218
|
+
[0x7fffda, 23],
|
219
|
+
[0x7fffdb, 23],
|
220
|
+
[0x7fffdc, 23],
|
221
|
+
[0x7fffdd, 23],
|
222
|
+
[0x7fffde, 23],
|
223
|
+
[0xffffeb, 24],
|
224
|
+
[0x7fffdf, 23],
|
225
|
+
[0xffffec, 24],
|
226
|
+
[0xffffed, 24],
|
227
|
+
[0x3fffd7, 22],
|
228
|
+
[0x7fffe0, 23],
|
229
|
+
[0xffffee, 24],
|
230
|
+
[0x7fffe1, 23],
|
231
|
+
[0x7fffe2, 23],
|
232
|
+
[0x7fffe3, 23],
|
233
|
+
[0x7fffe4, 23],
|
234
|
+
[0x1fffdc, 21],
|
235
|
+
[0x3fffd8, 22],
|
236
|
+
[0x7fffe5, 23],
|
237
|
+
[0x3fffd9, 22],
|
238
|
+
[0x7fffe6, 23],
|
239
|
+
[0x7fffe7, 23],
|
240
|
+
[0xffffef, 24],
|
241
|
+
[0x3fffda, 22],
|
242
|
+
[0x1fffdd, 21],
|
243
|
+
[0xfffe9, 20],
|
244
|
+
[0x3fffdb, 22],
|
245
|
+
[0x3fffdc, 22],
|
246
|
+
[0x7fffe8, 23],
|
247
|
+
[0x7fffe9, 23],
|
248
|
+
[0x1fffde, 21],
|
249
|
+
[0x7fffea, 23],
|
250
|
+
[0x3fffdd, 22],
|
251
|
+
[0x3fffde, 22],
|
252
|
+
[0xfffff0, 24],
|
253
|
+
[0x1fffdf, 21],
|
254
|
+
[0x3fffdf, 22],
|
255
|
+
[0x7fffeb, 23],
|
256
|
+
[0x7fffec, 23],
|
257
|
+
[0x1fffe0, 21],
|
258
|
+
[0x1fffe1, 21],
|
259
|
+
[0x3fffe0, 22],
|
260
|
+
[0x1fffe2, 21],
|
261
|
+
[0x7fffed, 23],
|
262
|
+
[0x3fffe1, 22],
|
263
|
+
[0x7fffee, 23],
|
264
|
+
[0x7fffef, 23],
|
265
|
+
[0xfffea, 20],
|
266
|
+
[0x3fffe2, 22],
|
267
|
+
[0x3fffe3, 22],
|
268
|
+
[0x3fffe4, 22],
|
269
|
+
[0x7ffff0, 23],
|
270
|
+
[0x3fffe5, 22],
|
271
|
+
[0x3fffe6, 22],
|
272
|
+
[0x7ffff1, 23],
|
273
|
+
[0x3ffffe0, 26],
|
274
|
+
[0x3ffffe1, 26],
|
275
|
+
[0xfffeb, 20],
|
276
|
+
[0x7fff1, 19],
|
277
|
+
[0x3fffe7, 22],
|
278
|
+
[0x7ffff2, 23],
|
279
|
+
[0x3fffe8, 22],
|
280
|
+
[0x1ffffec, 25],
|
281
|
+
[0x3ffffe2, 26],
|
282
|
+
[0x3ffffe3, 26],
|
283
|
+
[0x3ffffe4, 26],
|
284
|
+
[0x7ffffde, 27],
|
285
|
+
[0x7ffffdf, 27],
|
286
|
+
[0x3ffffe5, 26],
|
287
|
+
[0xfffff1, 24],
|
288
|
+
[0x1ffffed, 25],
|
289
|
+
[0x7fff2, 19],
|
290
|
+
[0x1fffe3, 21],
|
291
|
+
[0x3ffffe6, 26],
|
292
|
+
[0x7ffffe0, 27],
|
293
|
+
[0x7ffffe1, 27],
|
294
|
+
[0x3ffffe7, 26],
|
295
|
+
[0x7ffffe2, 27],
|
296
|
+
[0xfffff2, 24],
|
297
|
+
[0x1fffe4, 21],
|
298
|
+
[0x1fffe5, 21],
|
299
|
+
[0x3ffffe8, 26],
|
300
|
+
[0x3ffffe9, 26],
|
301
|
+
[0xffffffd, 28],
|
302
|
+
[0x7ffffe3, 27],
|
303
|
+
[0x7ffffe4, 27],
|
304
|
+
[0x7ffffe5, 27],
|
305
|
+
[0xfffec, 20],
|
306
|
+
[0xfffff3, 24],
|
307
|
+
[0xfffed, 20],
|
308
|
+
[0x1fffe6, 21],
|
309
|
+
[0x3fffe9, 22],
|
310
|
+
[0x1fffe7, 21],
|
311
|
+
[0x1fffe8, 21],
|
312
|
+
[0x7ffff3, 23],
|
313
|
+
[0x3fffea, 22],
|
314
|
+
[0x3fffeb, 22],
|
315
|
+
[0x1ffffee, 25],
|
316
|
+
[0x1ffffef, 25],
|
317
|
+
[0xfffff4, 24],
|
318
|
+
[0xfffff5, 24],
|
319
|
+
[0x3ffffea, 26],
|
320
|
+
[0x7ffff4, 23],
|
321
|
+
[0x3ffffeb, 26],
|
322
|
+
[0x7ffffe6, 27],
|
323
|
+
[0x3ffffec, 26],
|
324
|
+
[0x3ffffed, 26],
|
325
|
+
[0x7ffffe7, 27],
|
326
|
+
[0x7ffffe8, 27],
|
327
|
+
[0x7ffffe9, 27],
|
328
|
+
[0x7ffffea, 27],
|
329
|
+
[0x7ffffeb, 27],
|
330
|
+
[0xffffffe, 28],
|
331
|
+
[0x7ffffec, 27],
|
332
|
+
[0x7ffffed, 27],
|
333
|
+
[0x7ffffee, 27],
|
334
|
+
[0x7ffffef, 27],
|
335
|
+
[0x7fffff0, 27],
|
336
|
+
[0x3ffffee, 26],
|
337
|
+
[0x3fffffff, 30],
|
338
|
+
].each(&:freeze).freeze
|
339
|
+
|
340
|
+
ENCODE_TABLE = CODES.map {|c, l| [c].pack('N').unpack('B*').first[-l..-1]}.each(&:freeze).freeze
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|