net: simplify the TcpConn.read_line/0 method, accumulate partially read lines, use a string builder, instead of concatenation
							parent
							
								
									6ea4f361a1
								
							
						
					
					
						commit
						74048e2f17
					
				|  | @ -1,9 +1,12 @@ | |||
| module net | ||||
| 
 | ||||
| import strings | ||||
| 
 | ||||
| const ( | ||||
| 	crlf     = '\r\n' | ||||
| 	msg_peek = 0x02 | ||||
| 	max_read = 400 | ||||
| 	crlf              = '\r\n' | ||||
| 	msg_peek          = 0x02 | ||||
| 	max_read          = 400 | ||||
| 	max_read_line_len = 1048576 | ||||
| ) | ||||
| 
 | ||||
| // get_blocking returns whether the connection is in a blocking state,
 | ||||
|  | @ -39,52 +42,59 @@ pub fn (mut con TcpConn) set_blocking(state bool) ? { | |||
| } | ||||
| 
 | ||||
| // read_line is a *simple*, *non customizable*, blocking line reader.
 | ||||
| // It will *always* return a line, ending with CRLF, or just '', on EOF.
 | ||||
| // It will return a line, ending with LF, or just '', on EOF.
 | ||||
| // NB: if you want more control over the buffer, please use a buffered IO
 | ||||
| // reader instead: `io.new_buffered_reader({reader: io.make_reader(con)})`
 | ||||
| pub fn (mut con TcpConn) read_line() string { | ||||
| 	mut buf := [net.max_read]byte{} // where C.recv will store the network data
 | ||||
| 	mut res := '' // The final result, including the ending \n.
 | ||||
| 	return con.read_line_max(net.max_read_line_len) | ||||
| } | ||||
| 
 | ||||
| // read_line_max is a *simple*, *non customizable*, blocking line reader.
 | ||||
| // It will return a line, ending with LF, '' on EOF.
 | ||||
| // It stops reading, when the result line length exceeds max_line_len.
 | ||||
| [manualfree] | ||||
| pub fn (mut con TcpConn) read_line_max(max_line_len int) string { | ||||
| 	if !con.is_blocking { | ||||
| 		con.set_blocking(true) or {} | ||||
| 	} | ||||
| 	mut buf := [net.max_read]byte{} // where C.recv will store the network data
 | ||||
| 	mut res := strings.new_builder(net.max_read) // The final result, including the ending \n.
 | ||||
| 	defer { | ||||
| 		unsafe { res.free() } | ||||
| 	} | ||||
| 	bstart := unsafe { &buf[0] } | ||||
| 	for { | ||||
| 		mut line := '' // The current line. Can be a partial without \n in it.
 | ||||
| 		n := C.recv(con.sock.handle, &buf[0], net.max_read - 1, net.msg_peek | msg_nosignal) | ||||
| 		if n == -1 { | ||||
| 			return res | ||||
| 		} | ||||
| 		if n == 0 { | ||||
| 			return res | ||||
| 		n := C.recv(con.sock.handle, bstart, net.max_read - 1, net.msg_peek | msg_nosignal) | ||||
| 		if n <= 0 { | ||||
| 			return res.str() | ||||
| 		} | ||||
| 		buf[n] = `\0` | ||||
| 		mut eol_idx := -1 | ||||
| 		mut lend := n | ||||
| 		for i in 0 .. n { | ||||
| 			if int(buf[i]) == `\n` { | ||||
| 			if buf[i] == `\n` { | ||||
| 				eol_idx = i | ||||
| 				// Ensure that tos_clone(buf) later,
 | ||||
| 				// will return *only* the first line (including \n),
 | ||||
| 				// and ignore the rest
 | ||||
| 				buf[i + 1] = `\0` | ||||
| 				lend = i + 1 | ||||
| 				buf[lend] = `\0` | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		line = unsafe { tos_clone(&buf[0]) } | ||||
| 		if eol_idx > 0 { | ||||
| 			// At this point, we are sure that recv returned valid data,
 | ||||
| 			// that contains *at least* one line.
 | ||||
| 			// Ensure that the block till the first \n (including it)
 | ||||
| 			// is removed from the socket's receive queue, so that it does
 | ||||
| 			// not get read again.
 | ||||
| 			C.recv(con.sock.handle, &buf[0], eol_idx + 1, msg_nosignal) | ||||
| 			res += line | ||||
| 			C.recv(con.sock.handle, bstart, lend, msg_nosignal) | ||||
| 			unsafe { res.write_ptr(bstart, lend) } | ||||
| 			break | ||||
| 		} | ||||
| 		// recv returned a buffer without \n in it, just store it for now:
 | ||||
| 		C.recv(con.sock.handle, bstart, n, msg_nosignal) | ||||
| 		unsafe { res.write_ptr(bstart, lend) } | ||||
| 		if res.len > max_line_len { | ||||
| 			break | ||||
| 		} | ||||
| 		// recv returned a buffer without \n in it .
 | ||||
| 		C.recv(con.sock.handle, &buf[0], n, msg_nosignal) | ||||
| 		res += line | ||||
| 		res += net.crlf | ||||
| 		break | ||||
| 	} | ||||
| 	return res | ||||
| 	return res.str() | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue