[m-rev.] for review: add unboxed_readers to the stream module

Julien Fischer jfischer at opturion.com
Sat Apr 18 15:34:40 AEST 2020


For review by anyone.

------------------------

Add unboxed_readers to the stream module.

library/stream.m:
     Add a new type of reader, unboxed_reader.  These provide a 'get'
     operation that does not need to box its output in the non-error case.

library/io.m:
     Add an unboxed_reader instance for text_input_streams and chars.

NEWS:
    Announce the addition.

Julien.

diff --git a/NEWS b/NEWS
index 7032804..bf0d9ef 100644
--- a/NEWS
+++ b/NEWS
@@ -144,6 +144,12 @@ Changes to the Mercury standard library
      - func `keys_as_set/1`
      - pred `keys_as_set/2`

+### Changes to the `stream` module
+
+* We have added a new `reader/4` subclass, `unboxed_reader/4`. This subclass
+  allows readers to provide a `get` operation that avoids boxing non-error
+  outputs.
+
  ### Changes to the `thread.channel` module

  * The following predicate has been deprecated and will be removed in a future
diff --git a/library/io.m b/library/io.m
index f525daf..91c16ff 100644
--- a/library/io.m
+++ b/library/io.m
@@ -1949,6 +1949,7 @@
  :- instance stream.stream(text_input_stream, io).
  :- instance stream.input(text_input_stream, io).
  :- instance stream.reader(text_input_stream, char, io, io.error).
+:- instance stream.unboxed_reader(text_input_stream, char, io, io.error).
  :- instance stream.reader(text_input_stream, line, io, io.error).
  :- instance stream.reader(text_input_stream, text_file, io, io.error).

@@ -2294,7 +2295,16 @@
  [
      ( get(Stream, Result, !IO) :-
          read_char(Stream, Result0, !IO),
-        Result = io.result_to_stream_result(Result0)
+        Result = io.result1_to_stream_result1(Result0)
+    )
+].
+
+:- instance stream.unboxed_reader(input_stream, char, io, io.error)
+    where
+[
+    ( unboxed_get(Stream, Result, Char, !IO) :-
+        read_char_unboxed(Stream, Result0, Char, !IO),
+        Result = io.result0_to_stream_result0(Result0)
      )
  ].

@@ -2336,18 +2346,24 @@
      pred(unget/4) is putback_char
  ].

-:- func result_to_stream_result(io.result(T)) = stream.result(T, io.error).
-
-result_to_stream_result(ok(T)) = ok(T).
-result_to_stream_result(eof) = eof.
-result_to_stream_result(error(Error)) = error(Error).
-
  :- instance stream.line_oriented(input_stream, io) where
  [
      pred(get_line/4) is io.get_line_number,
      pred(set_line/4) is io.set_line_number
  ].

+:- func result1_to_stream_result1(io.result(T)) = stream.result(T, io.error).
+
+result1_to_stream_result1(ok(T)) = ok(T).
+result1_to_stream_result1(eof) = eof.
+result1_to_stream_result1(error(Error)) = error(Error).
+
+:- func result0_to_stream_result0(io.result) = stream.result(io.error).
+
+result0_to_stream_result0(ok) = ok.
+result0_to_stream_result0(eof) = eof.
+result0_to_stream_result0(error(Error)) = error(Error).
+
  %---------------------%
  %
  % Text output streams.
@@ -2458,7 +2474,7 @@ result_to_stream_result(error(Error)) = error(Error).
  [
      ( get(Stream, Result, !IO) :-
          read_byte(Stream, Result0, !IO),
-        Result = result_to_stream_result(Result0)
+        Result = result1_to_stream_result1(Result0)
      )
  ].

@@ -2467,7 +2483,7 @@ result_to_stream_result(error(Error)) = error(Error).
  [
      ( get(Stream, Result, !IO) :-
          read_binary_int8(Stream, Result0, !IO),
-        Result = result_to_stream_result(Result0)
+        Result = result1_to_stream_result1(Result0)
      )
  ].

@@ -2476,7 +2492,7 @@ result_to_stream_result(error(Error)) = error(Error).
  [
      ( get(Stream, Result, !IO) :-
          read_binary_uint8(Stream, Result0, !IO),
-        Result = result_to_stream_result(Result0)
+        Result = result1_to_stream_result1(Result0)
      )
  ].

diff --git a/library/stream.m b/library/stream.m
index 60a599a..6581a2c 100644
--- a/library/stream.m
+++ b/library/stream.m
@@ -110,10 +110,11 @@
      % The get operation should block until the next unit is available,
      % or the end of the stream or an error is detected.
      %
-    % If a call to get/4 returns `eof', all further calls to get/4 or
-    % bulk_get/9 for that stream return `eof'. If a call to get/4
-    % returns `error(...)', all further calls to get/4 or bulk_get/4 for
-    % that stream return an error, although not necessarily the same one.
+    % If a call to get/4 returns `eof', all further calls to get/4,
+    % unboxed_get/5 or bulk_get/9 for that stream return `eof'. If a call to
+    % get/4 returns `error(...)', all further calls to get/4, unboxed_get/5 or
+    % bulk_get/4 for that stream return an error, although not necessarily the
+    % same one.
      %
      % XXX We should provide an interface to allow the user to reset the
      % error status to try again if an error is transient.
@@ -122,6 +123,30 @@
          State::di, State::uo) is det
  ].

+    % An unboxed_reader stream is like a reader stream except that it provides
+    % an interface that avoids a memory allocation when there is no error.
+    %
+:- typeclass unboxed_reader(Stream, Unit, State, Error)
+    <= (input(Stream, State), error(Error), (Stream, Unit -> Error)) where
+[
+    % Get the next unit from the given stream.
+    %
+    % The unboxed_get operation should block until the next unit is available,
+    % or the end of the stream or an error is detected.
+    %
+    % If a call to unboxed_get/5 returns `eof', all further calls to get/4,
+    % unboxed_get/5 or bulk_get/9 for that stream return `eof'. If a call to
+    % unboxed_get/5 returns `error(...)', all further calls to get/4,
+    % unboxed_get/5 or bulk_get/4 for that stream return an error, although not
+    % necessarily the same one.
+    %
+    % XXX We should provide an interface to allow the user to reset the
+    % error status to try again if an error is transient.
+    %
+    pred unboxed_get(Stream::in, result(Error)::out, Unit::out,
+        State::di, State::uo) is det
+].
+
      % A bulk_reader stream is a subclass of specific input stream that can
      % be used to read multiple items of data of a specific type from that
      % input stream into a specified container. For example, binary input
@@ -152,10 +177,10 @@
      % starting at Index will not fit in !Store.
      %
      % If a call to bulk_get/4 returns less than NumItems items, all further
-    % calls to get/4 or bulk_get/4 for that stream return no items. If a call
-    % to bulk_get/9 returns `error(...)', all further calls to get/4 or
-    % bulk_get/9 for that stream return an error, although not necessarily
-    % the same one.
+    % calls to get/4, unboxed_get/5 or bulk_get/4 for that stream return no
+    % items. If a call to bulk_get/9 returns `error(...)', all further calls to
+    % get/4, unboxed_get/5 or bulk_get/9 for that stream return an error,
+    % although not necessarily the same one.
      %
      pred bulk_get(Stream::in, Index::in, int::in,
          Store::bulk_get_di, Store::bulk_get_uo,


More information about the reviews mailing list