@@ -7,8 +7,8 @@ const RecvError = public.IO.RecvError;
77const SendError = public .IO .SendError ;
88const TimeoutError = public .IO .TimeoutError ;
99
10+ const MsgBuffer = @import ("msg.zig" ).MsgBuffer ;
1011const Browser = @import ("browser/browser.zig" ).Browser ;
11-
1212const cdp = @import ("cdp/cdp.zig" );
1313
1414const NoError = error {NoError };
@@ -91,157 +91,6 @@ pub const Cmd = struct {
9191 }
9292};
9393
94- /// MsgBuffer return messages from a raw text read stream,
95- /// according to the following format `<msg_size>:<msg>`.
96- /// It handles both:
97- /// - combined messages in one read
98- /// - single message in several read (multipart)
99- /// It is safe (and good practice) to reuse the same MsgBuffer
100- /// on several reads of the same stream.
101- const MsgBuffer = struct {
102- size : usize = 0 ,
103- buf : []u8 ,
104- pos : usize = 0 ,
105-
106- fn init (alloc : std.mem.Allocator , size : usize ) std.mem.Allocator.Error ! MsgBuffer {
107- const buf = try alloc .alloc (u8 , size );
108- return .{ .buf = buf };
109- }
110-
111- fn deinit (self : MsgBuffer , alloc : std.mem.Allocator ) void {
112- alloc .free (self .buf );
113- }
114-
115- fn isFinished (self : * MsgBuffer ) bool {
116- return self .pos >= self .size ;
117- }
118-
119- fn isEmpty (self : MsgBuffer ) bool {
120- return self .size == 0 and self .pos == 0 ;
121- }
122-
123- fn reset (self : * MsgBuffer ) void {
124- self .size = 0 ;
125- self .pos = 0 ;
126- }
127-
128- // read input
129- // - `do_func` is a callback to execute on each message of the input
130- // - `data` is a arbitrary payload that will be passed to the callback along with
131- // the message itself
132- fn read (
133- self : * MsgBuffer ,
134- alloc : std.mem.Allocator ,
135- input : []const u8 ,
136- data : anytype ,
137- comptime do_func : fn (data : @TypeOf (data ), msg : []const u8 ) anyerror ! void ,
138- ) ! void {
139- var _input = input ; // make input writable
140-
141- while (true ) {
142- var msg : []const u8 = undefined ;
143-
144- // msg size
145- var msg_size : usize = undefined ;
146- if (self .isEmpty ()) {
147- // parse msg size metadata
148- const size_pos = std .mem .indexOfScalar (u8 , _input , ':' ).? ;
149- const size_str = _input [0.. size_pos ];
150- msg_size = try std .fmt .parseInt (u32 , size_str , 10 );
151- _input = _input [size_pos + 1 .. ];
152- } else {
153- msg_size = self .size ;
154- }
155-
156- // multipart
157- const is_multipart = ! self .isEmpty () or _input .len < msg_size ;
158- if (is_multipart ) {
159-
160- // set msg size on empty MsgBuffer
161- if (self .isEmpty ()) {
162- self .size = msg_size ;
163- }
164-
165- // get the new position of the cursor
166- const new_pos = self .pos + _input .len ;
167-
168- // check if the current input can fit in MsgBuffer
169- if (new_pos > self .buf .len ) {
170- // max_size is the max between msg size and current new cursor position
171- const max_size = @max (self .size , new_pos );
172- // resize the MsgBuffer to fit
173- self .buf = try alloc .realloc (self .buf , max_size );
174- }
175-
176- // copy the current input into MsgBuffer
177- @memcpy (self .buf [self .pos .. new_pos ], _input [0.. ]);
178-
179- // set the new cursor position
180- self .pos = new_pos ;
181-
182- // if multipart is not finished, go fetch the next input
183- if (! self .isFinished ()) return ;
184-
185- // otherwhise multipart is finished, use its buffer as input
186- _input = self .buf [0.. self .pos ];
187- self .reset ();
188- }
189-
190- // handle several JSON msg in 1 read
191- const is_combined = _input .len > msg_size ;
192- msg = _input [0.. msg_size ];
193- std .log .debug ("msg: {s}" , .{msg [0.. @min (MaxStdOutSize , msg_size )]});
194- if (is_combined ) {
195- _input = _input [msg_size .. ];
196- }
197-
198- try @call (.auto , do_func , .{ data , msg });
199-
200- if (! is_combined ) break ;
201- }
202- }
203- };
204-
205- fn doTest (nb : * u8 , _ : []const u8 ) anyerror ! void {
206- nb .* += 1 ;
207- }
208-
209- test "MsgBuffer" {
210- const Case = struct {
211- input : []const u8 ,
212- nb : u8 ,
213- };
214- const alloc = std .testing .allocator ;
215- const cases = [_ ]Case {
216- // simple
217- .{ .input = "2:ok" , .nb = 1 },
218- // combined
219- .{ .input = "2:ok3:foo7:bar2:ok" , .nb = 3 }, // "bar2:ok" is a message, no need to escape "2:" here
220- // multipart
221- .{ .input = "9:multi" , .nb = 0 },
222- .{ .input = "part" , .nb = 1 },
223- // multipart & combined
224- .{ .input = "9:multi" , .nb = 0 },
225- .{ .input = "part2:ok" , .nb = 2 },
226- // several multipart
227- .{ .input = "23:multi" , .nb = 0 },
228- .{ .input = "several" , .nb = 0 },
229- .{ .input = "complex" , .nb = 0 },
230- .{ .input = "part" , .nb = 1 },
231- // combined & multipart
232- .{ .input = "2:ok9:multi" , .nb = 1 },
233- .{ .input = "part" , .nb = 1 },
234- };
235- var nb : u8 = undefined ;
236- var msg_buf = try MsgBuffer .init (alloc , 10 );
237- defer msg_buf .deinit (alloc );
238- for (cases ) | case | {
239- nb = 0 ;
240- try msg_buf .read (alloc , case .input , & nb , doTest );
241- try std .testing .expect (nb == case .nb );
242- }
243- }
244-
24594// I/O Send
24695// --------
24796
0 commit comments