18
18
19
19
const std = @import ("std" );
20
20
21
- /// MsgBuffer returns messages from a raw text read stream,
22
- /// according to the following format `<msg_size>:<msg>`.
21
+ pub const MsgSize = 16 * 1204 ; // 16KB
22
+ pub const HeaderSize = 2 ;
23
+ pub const MaxSize = HeaderSize + MsgSize ;
24
+
25
+ pub const Msg = struct {
26
+ pub fn getSize (data : []const u8 ) usize {
27
+ return std .mem .readInt (u16 , data [0.. HeaderSize ], .little );
28
+ }
29
+
30
+ pub fn setSize (len : usize , header : * [2 ]u8 ) void {
31
+ std .mem .writeInt (u16 , header , @intCast (len ), .little );
32
+ }
33
+ };
34
+
35
+ /// Buffer returns messages from a raw text read stream,
36
+ /// with the message size being encoded on the 2 first bytes (little endian)
23
37
/// It handles both:
24
38
/// - combined messages in one read
25
39
/// - single message in several reads (multipart)
26
- /// It's safe (and a good practice) to reuse the same MsgBuffer
40
+ /// It's safe (and a good practice) to reuse the same Buffer
27
41
/// on several reads of the same stream.
28
- pub const MsgBuffer = struct {
29
- size : usize = 0 ,
42
+ pub const Buffer = struct {
30
43
buf : []u8 ,
44
+ size : usize = 0 ,
31
45
pos : usize = 0 ,
32
46
33
- const MaxSize = 1024 * 1024 ; // 1MB
34
-
35
- pub fn init (alloc : std.mem.Allocator , size : usize ) std.mem.Allocator.Error ! MsgBuffer {
36
- const buf = try alloc .alloc (u8 , size );
37
- return .{ .buf = buf };
38
- }
39
-
40
- pub fn deinit (self : MsgBuffer , alloc : std.mem.Allocator ) void {
41
- alloc .free (self .buf );
42
- }
43
-
44
- fn isFinished (self : * MsgBuffer ) bool {
47
+ fn isFinished (self : * const Buffer ) bool {
45
48
return self .pos >= self .size ;
46
49
}
47
50
48
- fn isEmpty (self : MsgBuffer ) bool {
51
+ fn isEmpty (self : * const Buffer ) bool {
49
52
return self .size == 0 and self .pos == 0 ;
50
53
}
51
54
52
- fn reset (self : * MsgBuffer ) void {
55
+ fn reset (self : * Buffer ) void {
53
56
self .size = 0 ;
54
57
self .pos = 0 ;
55
58
}
56
59
57
60
// read input
58
- pub fn read (self : * MsgBuffer , alloc : std.mem.Allocator , input : []const u8 ) ! struct {
61
+ pub fn read (self : * Buffer , input : []const u8 ) ! struct {
59
62
msg : []const u8 ,
60
63
left : []const u8 ,
61
64
} {
@@ -64,11 +67,9 @@ pub const MsgBuffer = struct {
64
67
// msg size
65
68
var msg_size : usize = undefined ;
66
69
if (self .isEmpty ()) {
67
- // parse msg size metadata
68
- const size_pos = std .mem .indexOfScalar (u8 , _input , ':' ) orelse return error .InputWithoutSize ;
69
- const size_str = _input [0.. size_pos ];
70
- msg_size = try std .fmt .parseInt (u32 , size_str , 10 );
71
- _input = _input [size_pos + 1 .. ];
70
+ // decode msg size header
71
+ msg_size = Msg .getSize (_input );
72
+ _input = _input [HeaderSize .. ];
72
73
} else {
73
74
msg_size = self .size ;
74
75
}
@@ -77,7 +78,7 @@ pub const MsgBuffer = struct {
77
78
const is_multipart = ! self .isEmpty () or _input .len < msg_size ;
78
79
if (is_multipart ) {
79
80
80
- // set msg size on empty MsgBuffer
81
+ // set msg size on empty Buffer
81
82
if (self .isEmpty ()) {
82
83
self .size = msg_size ;
83
84
}
@@ -90,18 +91,7 @@ pub const MsgBuffer = struct {
90
91
return error .MsgTooBig ;
91
92
}
92
93
93
- // check if the current input can fit in MsgBuffer
94
- if (new_pos > self .buf .len ) {
95
- // we want to realloc at least:
96
- // - a size big enough to fit the entire input (ie. new_pos)
97
- // - a size big enough (ie. current msg size + starting buffer size)
98
- // to avoid multiple reallocation
99
- const new_size = @max (self .buf .len + self .size , new_pos );
100
- // resize the MsgBuffer to fit
101
- self .buf = try alloc .realloc (self .buf , new_size );
102
- }
103
-
104
- // copy the current input into MsgBuffer
94
+ // copy the current input into Buffer
105
95
// NOTE: we could use @memcpy but it's not Thread-safe (alias problem)
106
96
// see https://www.openmymind.net/Zigs-memcpy-copyForwards-and-copyBackwards/
107
97
// Intead we just use std.mem.copyForwards
@@ -123,47 +113,45 @@ pub const MsgBuffer = struct {
123
113
}
124
114
};
125
115
126
- fn doTest (nb : * u8 ) void {
127
- nb .* += 1 ;
128
- }
129
-
130
- test "MsgBuffer" {
116
+ test "Buffer" {
131
117
const Case = struct {
132
118
input : []const u8 ,
133
119
nb : u8 ,
134
120
};
135
- const alloc = std . testing . allocator ;
121
+
136
122
const cases = [_ ]Case {
137
123
// simple
138
- .{ .input = "2: ok" , .nb = 1 },
124
+ .{ .input = .{ 2 , 0 } ++ " ok" , .nb = 1 },
139
125
// combined
140
- .{ .input = "2:ok3:foo7:bar2:ok" , . nb = 3 }, // "bar2: ok" is a message, no need to escape "2:" here
126
+ .{ .input = .{ 2 , 0 } ++ " ok" ++ .{ 3 , 0 } ++ "foo" , . nb = 2 },
141
127
// multipart
142
- .{ .input = "9: multi" , .nb = 0 },
128
+ .{ .input = .{ 9 , 0 } ++ " multi" , .nb = 0 },
143
129
.{ .input = "part" , .nb = 1 },
144
130
// multipart & combined
145
- .{ .input = "9: multi" , .nb = 0 },
146
- .{ .input = "part2: ok" , .nb = 2 },
131
+ .{ .input = .{ 9 , 0 } ++ " multi" , .nb = 0 },
132
+ .{ .input = "part" ++ .{ 2 , 0 } ++ " ok" , .nb = 2 },
147
133
// multipart & combined with other multipart
148
- .{ .input = "9: multi" , .nb = 0 },
149
- .{ .input = "part8: co" , .nb = 1 },
134
+ .{ .input = .{ 9 , 0 } ++ " multi" , .nb = 0 },
135
+ .{ .input = "part" ++ .{ 8 , 0 } ++ " co" , .nb = 1 },
150
136
.{ .input = "mbined" , .nb = 1 },
151
137
// several multipart
152
- .{ .input = "23: multi" , .nb = 0 },
138
+ .{ .input = .{ 23 , 0 } ++ " multi" , .nb = 0 },
153
139
.{ .input = "several" , .nb = 0 },
154
140
.{ .input = "complex" , .nb = 0 },
155
141
.{ .input = "part" , .nb = 1 },
156
142
// combined & multipart
157
- .{ .input = "2:ok9: multi" , .nb = 1 },
143
+ .{ .input = .{ 2 , 0 } ++ "ok" ++ .{ 9 , 0 } ++ " multi" , .nb = 1 },
158
144
.{ .input = "part" , .nb = 1 },
159
145
};
160
- var msg_buf = try MsgBuffer .init (alloc , 10 );
161
- defer msg_buf .deinit (alloc );
146
+
147
+ var b : [MaxSize ]u8 = undefined ;
148
+ var buf = Buffer { .buf = & b };
149
+
162
150
for (cases ) | case | {
163
151
var nb : u8 = 0 ;
164
- var input : [] const u8 = case .input ;
152
+ var input = case .input ;
165
153
while (input .len > 0 ) {
166
- const parts = msg_buf .read (alloc , input ) catch | err | {
154
+ const parts = buf .read (input ) catch | err | {
167
155
if (err == error .MsgMultipart ) break ; // go to the next case input
168
156
return err ;
169
157
};
0 commit comments