[m-rev.] for review: improvements to net/tcp.m

Peter Wang wangp at students.csse.unimelb.edu.au
Thu Mar 29 12:29:58 AEST 2007


On 28/03/07, Peter Ross <pro at missioncriticalit.com> wrote:
> On Wed, Mar 28, 2007 at 03:50:36PM +1000, Peter Wang wrote:
> > Improvements to the tcp module.
...
> > diff -u -r1.1 tcp.m
> > --- tcp.m     16 Nov 2006 04:01:49 -0000      1.1
> > +++ tcp.m     28 Mar 2007 05:41:41 -0000
> > @@ -57,11 +57,23 @@
> >
> >  :- instance input(tcp, io.state, tcp.error).
> >  :- instance reader(tcp, character, io.state, tcp.error).
> > +:- instance reader(tcp, string, io.state, tcp.error).
> >
> You should state what sort of strings are read?
> Until a new line, for something else.

I've followed Julien's suggestion on this.

>
> Also for me the error returned should also include the string that
> has been read up until the error, when reading a string.

I think this can be added later if required.

> > +].
> > +:- instance reader(tcp, string, io.state, tcp.error) where [
> > +     (get(T, Result, !IO) :-
> > +             tcp__read_line_as_string(T ^ handle, ErrCode, String, !IO),
>
> I'm not sure this code handles the case of reading from a socket the
> following "ABC", eg no newline before the socket closes.

I've replaced my buggy implementation with one based on
io.read_line_as_string_2.  It should be slightly more efficient in the
common case as well.

Interdiff follows.

Thanks.
Peter

diff -u tcp.m tcp.m
--- tcp.m	28 Mar 2007 05:41:41 -0000
+++ tcp.m	29 Mar 2007 02:25:07 -0000
@@ -52,12 +52,14 @@

 :- type error.

+:- type line ---> line(string).
+
 :- instance stream(tcp, io.state).
 :- instance error(tcp.error).

 :- instance input(tcp, io.state, tcp.error).
 :- instance reader(tcp, character, io.state, tcp.error).
-:- instance reader(tcp, string, io.state, tcp.error).
+:- instance reader(tcp, line, io.state, tcp.error).

 :- instance output(tcp, io.state).
 :- instance writer(tcp, character, io.state).
@@ -406,16 +408,16 @@
 		)
 	)
 ].
-:- instance reader(tcp, string, io.state, tcp.error) where [
+:- instance reader(tcp, line, io.state, tcp.error) where [
 	(get(T, Result, !IO) :-
-		tcp__read_line_as_string(T ^ handle, ErrCode, String, !IO),
+		tcp__read_line_as_string_2(T ^ handle, ErrCode, String, !IO),
 		( ErrCode = -1 ->
 			Result = eof
 		; ErrCode = -2 ->
 			get_errno(T ^ handle, Errno, !IO),
 			Result = error(Errno)
 		;
-			Result = ok(String)
+			Result = ok(line(String))
 		)
 	)
 ].
@@ -494,52 +496,73 @@
 	Chr = TCP_get_char(sock);
 }").

-:- pred tcp__read_line_as_string(tcp_handle::in, int::out, string::out,
+    % This implementation is based on io.read_line_as_string_2.
+    %
+:- pred tcp__read_line_as_string_2(tcp_handle::in, int::out, string::out,
 	io::di, io::uo) is det.
 :- pragma foreign_proc("C",
-	tcp__read_line_as_string(TCP::in, ErrCode::out, Str::out,
+	tcp__read_line_as_string_2(TCP::in, Res::out, RetString::out,
 		IO0::di, IO::uo),
 	[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
 "
-	ML_tcp *sock = (ML_tcp *) TCP;
-	size_t	BufLen = 1024;
-	off_t	BufPos = 0;
-	char	*Buf;
-	int	Chr;
-
-	Buf = MR_malloc(BufLen);
-
-	while (1) {
-		Chr = TCP_get_char(sock);
-		if (Chr < 0) {
-			ErrCode = Chr;
-			break;
-		}
-
-		if (BufPos >= BufLen) {
-			BufLen += 1024;
-			Buf = MR_realloc(Buf, BufLen);
-		}
-		Buf[BufPos++] = Chr;
-		if (Chr == '\\n') {
-			ErrCode = 0;
-			break;
-		}
-	}
-
-	if (ErrCode == 0) {
-		if (BufPos >= BufLen) {
-			BufLen += 1;
-			Buf = MR_realloc(Buf, BufLen);
-		}
-		Buf[BufPos] = '\\0';
-		MR_make_aligned_string_copy(Str, Buf);
-	} else {
-		Str = NULL;
-	}
-
-	MR_free(Buf);
-	IO = IO0;
+#define TCP_IO_READ_LINE_GROW(n) ((n) * 3 / 2)
+#define TCP_IO_BYTES_TO_WORDS(n) (((n) + sizeof(MR_Word) - 1) /
sizeof(MR_Word))
+#define TCP_IO_READ_LINE_START   1024
+
+    ML_tcp *sock = (ML_tcp *) TCP;
+    MR_Char initial_read_buffer[TCP_IO_READ_LINE_START];
+    MR_Char *read_buffer = initial_read_buffer;
+    size_t read_buf_size = TCP_IO_READ_LINE_START;
+    size_t i;
+    int char_code = '\\0';
+
+    Res = 0;
+    for (i = 0; char_code != '\\n'; ) {
+        char_code = TCP_get_char(sock);
+        if (char_code == TCP_EOF) {
+            if (i == 0) {
+                Res = -1;
+            }
+            break;
+        }
+        if (char_code == TCP_ERROR) {
+            Res = -2;
+            break;
+        }
+        read_buffer[i++] = char_code;
+        MR_assert(i <= read_buf_size);
+        if (i == read_buf_size) {
+            /* Grow the read buffer */
+            read_buf_size = TCP_IO_READ_LINE_GROW(read_buf_size);
+            if (read_buffer == initial_read_buffer) {
+                read_buffer = MR_NEW_ARRAY(MR_Char, read_buf_size);
+                MR_memcpy(read_buffer, initial_read_buffer,
+                    TCP_IO_READ_LINE_START);
+            } else {
+                read_buffer = MR_RESIZE_ARRAY(read_buffer, MR_Char,
+                    read_buf_size);
+            }
+        }
+    }
+    if (Res == 0) {
+        MR_Word ret_string_word;
+        MR_offset_incr_hp_atomic_msg(ret_string_word,
+            0, TCP_IO_BYTES_TO_WORDS((i + 1) * sizeof(MR_Char)),
+            MR_PROC_LABEL, ""string.string/0"");
+        RetString = (MR_String) ret_string_word;
+        MR_memcpy(RetString, read_buffer, i * sizeof(MR_Char));
+        RetString[i] = '\\0';
+    } else {
+        /*
+        ** We can't just return NULL here, because  otherwise mdb will break
+        ** when it tries to print the string.
+        */
+        RetString = MR_make_string_const("""");
+    }
+    if (read_buffer != initial_read_buffer) {
+        MR_free(read_buffer);
+    }
+    MR_update_io(IO0, IO);
 ").

 :- pred tcp__write_char(tcp_handle::in, char::in, bool::out,
--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to:       mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions:          mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the reviews mailing list