Skip to content

Commit 9e1cb03

Browse files
committed
inline header encoding to increase performance
1 parent ea1f8f9 commit 9e1cb03

File tree

2 files changed

+62
-26
lines changed

2 files changed

+62
-26
lines changed

spec/frame_spec.lua

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package.path = package.path..'../src'
22

33
local frame = require'websocket.frame'
4+
local tools = require'websocket.tools'
45

56
local bytes = string.char
67

@@ -18,7 +19,7 @@ describe(
1819
function()
1920
assert.is_same(type(frame),'table')
2021
end)
21-
22+
2223
it(
2324
'provides a decode and a encode function',
2425
function()
@@ -27,7 +28,7 @@ describe(
2728
assert.is.same(type(frame.encode_close),'function')
2829
assert.is.same(type(frame.decode_close),'function')
2930
end)
30-
31+
3132
it(
3233
'provides correct OPCODES',
3334
function()
@@ -38,8 +39,29 @@ describe(
3839
assert.is.same(frame.PING,9)
3940
assert.is.same(frame.PONG,10)
4041
end)
41-
42-
42+
43+
it('.encode_header_small is correct',
44+
function()
45+
local enc = frame.encode_header_small(123,33)
46+
local enc_ref = tools.write_int8(123)..tools.write_int8(33)
47+
assert.is_same(enc, enc_ref)
48+
end)
49+
50+
it('.encode_header_medium is correct',
51+
function()
52+
local enc = frame.encode_header_medium(123,33,555)
53+
local enc_ref = tools.write_int8(123)..tools.write_int8(33)..tools.write_int16(555)
54+
assert.is_same(enc, enc_ref)
55+
end)
56+
57+
it('.encode_header_big is correct',
58+
function()
59+
local enc = frame.encode_header_big(123,33,555,12987)
60+
local enc_ref = tools.write_int8(123)..tools.write_int8(33)..tools.write_int32(555)..tools.write_int32(12987)
61+
assert.is_same(enc, enc_ref)
62+
end)
63+
64+
4365
it(
4466
'RFC: decode a single-frame unmasked text message',
4567
function()
@@ -50,7 +72,7 @@ describe(
5072
assert.is_same(rest,'foo')
5173
assert.is_false(masked)
5274
end)
53-
75+
5476
it(
5577
'RFC: decode a single-frame unmasked text message bytewise and check min length',
5678
function()
@@ -70,7 +92,7 @@ describe(
7092
end
7193
end
7294
end)
73-
95+
7496
it(
7597
'RFC: decode a single-frame masked text message',
7698
function()
@@ -81,7 +103,7 @@ describe(
81103
assert.is_same(rest,'foo')
82104
assert.is_truthy(masked)
83105
end)
84-
106+
85107
it(
86108
'RFC: decode a fragmented test message',
87109
function()
@@ -91,15 +113,15 @@ describe(
91113
assert.is.same(decoded,'Hel')
92114
assert.is_same(rest,'')
93115
assert.is_falsy(masked)
94-
116+
95117
decoded,fin,opcode,rest,masked = frame.decode(lo)
96118
assert.is_true(fin)
97119
assert.is_same(opcode,0x0)
98120
assert.is.same(decoded,'lo')
99121
assert.is_same(rest,'')
100122
assert.is_falsy(masked)
101123
end)
102-
124+
103125
it(
104126
'refuse incomplete unmasked frame',
105127
function()
@@ -108,7 +130,7 @@ describe(
108130
assert.is_same(fin,#hello_unmasked-4)
109131
assert.is_falsy(opcode)
110132
end)
111-
133+
112134
it(
113135
'refuse incomplete masked frame',
114136
function()
@@ -117,7 +139,7 @@ describe(
117139
assert.is_same(fin,#hello_masked-4)
118140
assert.is_falsy(opcode)
119141
end)
120-
142+
121143
it(
122144
'encode single-frame unmasked text',
123145
function()
@@ -135,15 +157,15 @@ describe(
135157
assert.is_same(opcode,frame.TEXT)
136158
assert.is_falsy(masked)
137159
end)
138-
160+
139161
local random_text = function(len)
140162
local chars = {}
141163
for i=1,len do
142164
chars[i] = string.char(math.random(33,126))
143165
end
144166
return table.concat(chars)
145167
end
146-
168+
147169
it(
148170
'encode and decode single-frame of length 127 unmasked text',
149171
function()
@@ -159,7 +181,7 @@ describe(
159181
assert.is_same(rest,'')
160182
assert.is_falsy(masked)
161183
end)
162-
184+
163185
it(
164186
'encode and decode single-frame of length 0xffff-1 unmasked text',
165187
function()
@@ -175,7 +197,7 @@ describe(
175197
assert.is_same(rest,'')
176198
assert.is_falsy(masked)
177199
end)
178-
200+
179201
it(
180202
'encode and decode single-frame of length 0xffff+1 unmasked text',
181203
function()
@@ -189,7 +211,7 @@ describe(
189211
assert.is_true(fin)
190212
assert.is_same(opcode,frame.TEXT)
191213
end)
192-
214+
193215
it(
194216
'encode single-frame masked text',
195217
function()
@@ -201,7 +223,7 @@ describe(
201223
assert.is_same(rest,'')
202224
assert.is_truthy(masked)
203225
end)
204-
226+
205227
it(
206228
'encode fragmented unmasked text',
207229
function()
@@ -210,14 +232,14 @@ describe(
210232
assert.is_falsy(fin)
211233
assert.is_same(opcode,0x1)
212234
assert.is.same(decoded,'Hel')
213-
235+
214236
local lo = frame.encode('lo',frame.CONTINUATION,false)
215237
decoded,fin,opcode = frame.decode(lo)
216238
assert.is_true(fin)
217239
assert.is_same(opcode,0x0)
218240
assert.is.same(decoded,'lo')
219241
end)
220-
242+
221243
it(
222244
'encodes and decodes close packet correctly',
223245
function()
@@ -229,5 +251,5 @@ describe(
229251
assert.is_same(dcode,code)
230252
assert.is_same(dreason,reason)
231253
end)
232-
254+
233255
end)

src/websocket/frame.lua

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ local srep = string.rep
88
local ssub = string.sub
99
local sbyte = string.byte
1010
local schar = string.char
11+
local band = bit.band
12+
local rshift = bit.rshift
1113
local tinsert = table.insert
1214
local tconcat = table.concat
1315
local mmin = math.min
@@ -53,6 +55,18 @@ local xor_mask = function(encoded,mask,payload)
5355
return tconcat(transformed_arr)
5456
end
5557

58+
local encode_header_small = function(header, payload)
59+
return schar(header, payload)
60+
end
61+
62+
local encode_header_medium = function(header, payload, len)
63+
return schar(header, payload, band(rshift(len, 8), 0xFF), band(len, 0xFF))
64+
end
65+
66+
local encode_header_big = function(header, payload, high, low)
67+
return schar(header, payload)..write_int32(high)..write_int32(low)
68+
end
69+
5670
local encode = function(data,opcode,masked,fin)
5771
local header = opcode or 1-- TEXT is default opcode
5872
if fin == nil or fin == true then
@@ -66,18 +80,15 @@ local encode = function(data,opcode,masked,fin)
6680
local chunks = {}
6781
if len < 126 then
6882
payload = bor(payload,len)
69-
tinsert(chunks,write_int8(header,payload))
83+
tinsert(chunks,encode_header_small(header,payload))
7084
elseif len <= 0xffff then
7185
payload = bor(payload,126)
72-
tinsert(chunks,write_int8(header,payload))
73-
tinsert(chunks,write_int16(len))
86+
tinsert(chunks,encode_header_medium(header,payload,len))
7487
elseif len < 2^53 then
7588
local high = mfloor(len/2^32)
7689
local low = len - high*2^32
7790
payload = bor(payload,127)
78-
tinsert(chunks,write_int8(header,payload))
79-
tinsert(chunks,write_int32(high))
80-
tinsert(chunks,write_int32(low))
91+
tinsert(chunks,encode_header_big(header,payload,high,low))
8192
end
8293
if not masked then
8394
tinsert(chunks,data)
@@ -191,6 +202,9 @@ return {
191202
decode = decode,
192203
encode_close = encode_close,
193204
decode_close = decode_close,
205+
encode_header_small = encode_header_small,
206+
encode_header_medium = encode_header_medium,
207+
encode_header_big = encode_header_big,
194208
CONTINUATION = 0,
195209
TEXT = 1,
196210
BINARY = 2,

0 commit comments

Comments
 (0)