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
 | 
					module net
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import strings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	crlf     = '\r\n'
 | 
						crlf              = '\r\n'
 | 
				
			||||||
	msg_peek = 0x02
 | 
						msg_peek          = 0x02
 | 
				
			||||||
	max_read = 400
 | 
						max_read          = 400
 | 
				
			||||||
 | 
						max_read_line_len = 1048576
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// get_blocking returns whether the connection is in a blocking state,
 | 
					// 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.
 | 
					// 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
 | 
					// 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)})`
 | 
					// reader instead: `io.new_buffered_reader({reader: io.make_reader(con)})`
 | 
				
			||||||
pub fn (mut con TcpConn) read_line() string {
 | 
					pub fn (mut con TcpConn) read_line() string {
 | 
				
			||||||
	mut buf := [net.max_read]byte{} // where C.recv will store the network data
 | 
						return con.read_line_max(net.max_read_line_len)
 | 
				
			||||||
	mut res := '' // The final result, including the ending \n.
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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 {
 | 
						if !con.is_blocking {
 | 
				
			||||||
		con.set_blocking(true) or {}
 | 
							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 {
 | 
						for {
 | 
				
			||||||
		mut line := '' // The current line. Can be a partial without \n in it.
 | 
							n := C.recv(con.sock.handle, bstart, net.max_read - 1, net.msg_peek | msg_nosignal)
 | 
				
			||||||
		n := C.recv(con.sock.handle, &buf[0], net.max_read - 1, net.msg_peek | msg_nosignal)
 | 
							if n <= 0 {
 | 
				
			||||||
		if n == -1 {
 | 
								return res.str()
 | 
				
			||||||
			return res
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if n == 0 {
 | 
					 | 
				
			||||||
			return res
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		buf[n] = `\0`
 | 
							buf[n] = `\0`
 | 
				
			||||||
		mut eol_idx := -1
 | 
							mut eol_idx := -1
 | 
				
			||||||
 | 
							mut lend := n
 | 
				
			||||||
		for i in 0 .. n {
 | 
							for i in 0 .. n {
 | 
				
			||||||
			if int(buf[i]) == `\n` {
 | 
								if buf[i] == `\n` {
 | 
				
			||||||
				eol_idx = i
 | 
									eol_idx = i
 | 
				
			||||||
				// Ensure that tos_clone(buf) later,
 | 
									lend = i + 1
 | 
				
			||||||
				// will return *only* the first line (including \n),
 | 
									buf[lend] = `\0`
 | 
				
			||||||
				// and ignore the rest
 | 
					 | 
				
			||||||
				buf[i + 1] = `\0`
 | 
					 | 
				
			||||||
				break
 | 
									break
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		line = unsafe { tos_clone(&buf[0]) }
 | 
					 | 
				
			||||||
		if eol_idx > 0 {
 | 
							if eol_idx > 0 {
 | 
				
			||||||
			// At this point, we are sure that recv returned valid data,
 | 
								// At this point, we are sure that recv returned valid data,
 | 
				
			||||||
			// that contains *at least* one line.
 | 
								// that contains *at least* one line.
 | 
				
			||||||
			// Ensure that the block till the first \n (including it)
 | 
								// Ensure that the block till the first \n (including it)
 | 
				
			||||||
			// is removed from the socket's receive queue, so that it does
 | 
								// is removed from the socket's receive queue, so that it does
 | 
				
			||||||
			// not get read again.
 | 
								// not get read again.
 | 
				
			||||||
			C.recv(con.sock.handle, &buf[0], eol_idx + 1, msg_nosignal)
 | 
								C.recv(con.sock.handle, bstart, lend, msg_nosignal)
 | 
				
			||||||
			res += line
 | 
								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
 | 
								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