[m-rev.] for review: reading multibyte integers from binary file streams

Julien Fischer jfischer at opturion.com
Tue Jan 15 18:43:00 AEDT 2019


Hi Zoltan,

My responses to your review are below; there are relative and full diffs 
attached.

On Wed, 9 Jan 2019, Zoltan Somogyi wrote:

> On Tue, 8 Jan 2019 04:02:56 +0000 (UTC), Julien Fischer <jfischer at opturion.com> wrote:
>>> I will have a look if you send me the diff as an attachment, though
>>> someone else should check the Java and C# versions.
>>
>> Attached.
>
> And the review is attached to this email.

> > diff --git a/library/io.m b/library/io.m
> > index d407c09..4b852c7 100644
> > --- a/library/io.m
> > +++ b/library/io.m
> > @@ -108,6 +108,16 @@
> >      ;       eof
> >      ;       error(io.error).
> > 
> > +    % maybe_incomplete_result is used for multibyte values read from a binary
> > +    % stream where it is possible for the final element in the stream to be
> > +    % incomplete.
> > +    %
> > +:- type maybe_incomplete_result(T)
> > +    --->    ok(T)
> > +    ;       eof
> > +    ;       incomplete(list(uint8))
> > +    ;       error(io.error).
> > +
> 
> I would add a sentence to explain what incomplete and its argument mean.

Done.

> >  :- pred read_binary_uint8(io.binary_input_stream::in, io.result(uint8)::out,
> >      io::di, io::uo) is det.
> > 
> > +    % The following predicates read multibyte integer values from the current
> > +    % binary input stream or from the specified input stream.
> > +    %
> > +    % These names of these predicates have the form:
> 
> *The* names of these predicates ...

Fixed.

> > +    %    read_binary_<TYPE><SUFFIX>
> > +    %
> > +    % where <TYPE> is the name of one of the Mercury multibyte fixed size
> > +    % integer types. <SUFFIX> is optional and specifies the byte order in
> > +    % which the integer value is read from the stream.  It may be one of:
> 
> "in which the integer value is read from the stream" is a bit ambiguous;
> it can be read as "the byte order used in the input stream", which I think
> is the meaning you intend, but it could also be read as "the byte order of
> the machine doing the reading". I would reword to eliminate any possibility
> of misreading.

Done.

> > @@ -2132,6 +2256,19 @@ using System.Security.Principal;
> >  :- pragma foreign_export_enum("Java", result_code/0,
> >      [prefix("ML_RESULT_CODE_"), uppercase]).
> > 
> > +:- type maybe_incomplete_result_code
> > +    --->    ok
> > +    ;       eof
> > +    ;       incomplete
> > +    ;       error.
> 
> These function symbol names are *seriously* overloaded. We can't easily do
> anything about the ones in exported types, 
> but I would add a disambiguating prefix to these.

I've added the prefix "mirc_" to them (and simplified their foreign names
accordingly).  (I'll add a prefix to the constructors of the existing
result_code/0 type separately.)

> > +%---------------------%
> > +
> > +read_binary_int16(Result, !IO) :-
> > +    binary_input_stream(Stream, !IO),
> > +    read_binary_int16(Stream, Result, !IO).
> > +
> > +read_binary_int16(Stream, Result, !IO) :-
> > +    ( if native_byte_order_is_big_endian then
> > +        read_binary_int16_be(Stream, Result, !IO)
> > +    else
> > +        read_binary_int16_le(Stream, Result, !IO)
> > +    ).
> > +
> > +:- pred native_byte_order_is_big_endian is semidet.
> > +
> > +:- pragma foreign_proc("C",
> > +    native_byte_order_is_big_endian,
> > +    [promise_pure, will_not_call_mercury, thread_safe],
> > +"
> > +    #if defined(MR_BIG_ENDIAN)
> > +        SUCCESS_INDICATOR = MR_TRUE;
> > +    #else
> > +        SUCCESS_INDICATOR = MR_FALSE;
> > +    #endif
> > +").
> > +
> > +:- pragma foreign_proc("C#",
> > +    native_byte_order_is_big_endian,
> > +    [promise_pure, will_not_call_mercury, thread_safe],
> > +"
> > +    SUCCESS_INDICATOR = !(BitConverter.IsLittleEndian);
> > +").
> > +
> > +:- pragma foreign_proc("Java",
> > +    native_byte_order_is_big_endian,
> > +    [promise_pure, will_not_call_mercury, thread_safe],
> > +"
> > +    SUCCESS_INDICATOR =
> > +        (java.nio.ByteOrder.nativeOrder() == java.nio.ByteOrder.BIG_ENDIAN);
> > +").
> > +
> > +native_byte_order_is_big_endian :-
> > +    sorry($module,
> > +        "native_byte_order_is_big_endian/0 NYI for Erlang").
> 
> Since the helper pred native_byte_order_is_big_endian is not specific
> to reading in int16s, I would put its declaration and implementation
> at the bottom of your additions, or at the bottom of the whole module.
> You want the 16. 32 and 64 bit code to be as similar as possible to each
> other; including this predicate that all of them need with one of them
> is an unnecessary non-similarity.

Done.

> > +%---------------------%
> > +
> > +read_binary_int16_le(Result, !IO) :-
> > +    binary_input_stream(Stream, !IO),
> > +    read_binary_int16_le(Stream, Result, !IO).
> > +
> > +read_binary_int16_le(binary_input_stream(Stream), Result, !IO) :-
> > +    do_read_binary_uint16_le(Stream, Result0, UInt16, IncompleteBytes,
> > +        Error, !IO),
> > +    (
> > +        Result0 = ok,
> > +        Int16 = cast_from_uint16(UInt16),
> > +        Result = ok(Int16)
> > +    ;
> > +        Result0 = eof,
> > +        Result = eof
> > +    ;
> > +        Result0 = incomplete,
> > +        Result = incomplete(IncompleteBytes)
> > +    ;
> > +        Result0 = error,
> > +        make_err_msg(Error, "read failed: ", Msg),
> > +        Result = error(io_error(Msg))
> > +    ).
> 
> We used to have code in the compiler where X0 and X were of different types,
> as here. I often found that they made the code harder to read, and fixed that
> by renaming. I would s/Result0/ResultCode/ here and in every other similar
> situation in your diff.

Done.

> > +read_binary_int16_be(Result, !IO) :-
> > +    binary_input_stream(Stream, !IO),
> > +    read_binary_int16_be(Stream, Result, !IO).
> > +
> > +read_binary_int16_be(binary_input_stream(Stream), Result, !IO) :-
> > +    do_read_binary_uint16_le(Stream, Result0, UInt16LE, IncompleteBytes,
> > +        Error, !IO),
> > +    (
> > +        Result0 = ok,
> > +        UInt16BE = reverse_bytes(UInt16LE),
> > +        Int16 = cast_from_uint16(UInt16BE),
> > +        Result = ok(Int16)
> 
> Doing the conversion from byte sequence to an N bit int one way, then
> reversing the bytes in the N bit int, is slower than building the N bit int
> the right way in the first place. It does require fewer lines of code.
> Is that your reason for choosing this approach?

I've altered this below.  For the C grade specifically, we are ultimately
calling fread(), so the bytes will be read into the buffer in one direction
(i.e. in some case we will need to reverse them).

> > +:- pred do_read_binary_uint16_le(stream::in, maybe_incomplete_result_code::out,
> > +    uint16::out, list(uint8)::out, system_error::out, io::di, io::uo) is det.
> 
> I presume the reason you return unsigned here is that the caller will
> reshuffle the bytes, and keeping the data as unsigned until the last moment
> reduces the chance of bugs that cause sign extension to copy the most
> significant bit of the wrong byte. I would add a comment to this predicate,
> and its mirrors for other values of N, saying this.

No, it was simply to avoid inflating the number of foreign_procs; we have
versions of reverse_bytes that work on signed integers so problems
caused by sign extensions shouldn't be an issue for that operation.

> > +:- pragma foreign_proc("C",
> > +    do_read_binary_uint16_le(Stream::in, Result::out, UInt16::out,
> > +        IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
> > +    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
> > +"
> > +    unsigned char buffer[2];
> > +    size_t nread = MR_READ(*Stream, buffer, 2);
> > +    IncompleteBytes = MR_list_empty();
> > +
> > +    if (nread < 2) {
> > +        UInt16 = 0;
> > +        if (MR_FERROR(*Stream)) {
> > +            Result = ML_MAYBE_INCOMPLETE_RESULT_CODE_ERROR,
> > +            Error = errno;
> > +        } else if (nread > 0) {
> > +            int i;
> > +            Result = ML_MAYBE_INCOMPLETE_RESULT_CODE_INCOMPLETE;
> > +            IncompleteBytes = MR_list_cons(buffer[0], IncompleteBytes);
> > +            Error = 0;
> > +        } else {
> > +            Result = ML_MAYBE_INCOMPLETE_RESULT_CODE_EOF;
> > +            Error = 0;
> > +        }
> > +    } else {
> > +        Result = ML_MAYBE_INCOMPLETE_RESULT_CODE_OK;
> > +        #if defined(MR_BIG_ENDIAN)
> > +            ((unsigned char *) &UInt16)[0] = buffer[1];
> > +            ((unsigned char *) &UInt16)[1] = buffer[0];
> > +        #else
> > +            UInt16 = *((uint16_t *) buffer);
> > +        #endif
> > +        Error = 0;
> > +    }
> > +").
> 
> Both paths through the code for the OK case work only on machines with
> 8 bit bytes. That is ok, since we only support those machines.

GCC and clang both only support machines with 8-bit bytes, nevermind us ;-)
(MSVC is almost certainly the same.)

> However, the code of the MR_BIG_ENDIAN case should be replaced with
> a call to the existing macro MR_uint16_reverse_bytes.

I forgot it was there!

> Not only does it work on non-8-bit-byte machines, but it should be faster,
> since it does not require any load or store operations.

It definitely won't work on non-8-bit-byte machines in some cases since the
fallback definitions it uses in the absence of any C compiler intrinsic
functions *do* assume 8-bit bytes.

> The code would also be shorter, and would explicitly document your intent.

Done.

> Since your approach makes e.g. read_uint16_be use do_read_binary_uint16_le,
> do_read_binary_uint16_le should defined not just after read_uint16_le
> but also after read_uint16_be as well.

I've changed this so that read_binary_uint16_{be,le} both call
do_read_binary_uint16 and that as an extra argument specifying the endianess of
the values in the stream.  Similarly for the other sizes and signednesses.

> What I would do write for C is a pragma foreign_code that
> 
> - defines a macro for building big endian N byte uints from an N element
>   array of bytes, where N is 2, 4 or 8;
> 
> - defines a little endian version of that same macro,
> 
> - defines a macro for the body of do_read_binary_uintN_EN that invokes
>   placeholder macros (named maybe ML_build_int) for the OK case.
> 
> Then the definition of each of do_read_binary_uintN_EN would just plug
> one of the first two sets of macros into an invocation of the third.

Done.

> The runtime uses this technique extensively to minimize unnecessary code
> duplication when defining multiple related pieces of code. I don't know
> whether C# or Java have any equivalent mechanism.

There's no preprocessor or templating if that's what you mean; there
are other approachs that might work but they would be less efficient.

> > +            UInt32 = (uint) (buffer[3] << 24 | buffer[2] << 16 |
> > +                buffer[1] << 8 | buffer[0]);
> 
> > +            UInt32 =
> > +                (buffer[3] & 0xff) << 24 |
> > +                (buffer[2] & 0xff) << 16 |
> > +                (buffer[1] & 0xff) << 8  |
> > +                (buffer[0] & 0xff);
> > +        }
> 
> Any reason why the C# and Java versions are laid out differently?

No, I've adjusted them to be consistent.

> Any reason why the C code uses a completely different approach?

The C versions are taking advantage of the fact that in some cases we can just
read the integer value directly from the byte buffer.  Doing the same in the
other languages is more expensive that what we do here.

> > diff --git a/tests/hard_coded/read_binary_int16.exp b/tests/hard_coded/read_binary_int16.exp
> > index e69de29..c54276a 100644
> > --- a/tests/hard_coded/read_binary_int16.exp
> > +++ b/tests/hard_coded/read_binary_int16.exp

...

> I would restructure the tests so that each input is reported on only once,
> like this:
> 
> Input: [1u8, 0u8] (LE: 1) (BE: 256)
> Big endian result:    256i16
> Little endian result: 1i16
> Native result:        1i16
> 
> It would also make the tests faster, since each test file contents would be
> created just once, not three times.

I'll do that as a separate change.

...

> > +            io.remove_file(test_file, _, !IO)
> > +        ;
> > +            OpenInResult = error(IO_Error),
> > +            io.format("I/O ERROR: %s\n", [s(io.error_message(IO_Error))],
> > +                !IO)
> > +        )
> > +    ;
> > +        OpenOutResult = error(IO_Error),
> > +        io.format("I/O ERROR: %s\n", [s(io.error_message(IO_Error))], !IO)
> > +    ).
> 
> This code closes the binary output file, but not the binary input file.
> 
> The same comments apply to the other test cases as well.

Fixed.

Julien.
-------------- next part --------------
diff --git a/library/io.m b/library/io.m
index 4b852c7..58b4dbb 100644
--- a/library/io.m
+++ b/library/io.m
@@ -2,7 +2,7 @@
 % vim: ft=mercury ts=4 sw=4 et
 %---------------------------------------------------------------------------%
 % Copyright (C) 1993-2012 The University of Melbourne.
-% Copyright (C) 2013-2018 The Mercury team.
+% Copyright (C) 2013-2019 The Mercury team.
 % This file is distributed under the terms specified in COPYING.LIB.
 %---------------------------------------------------------------------------%
 %
@@ -108,9 +108,12 @@
     ;       eof
     ;       error(io.error).
 
-    % maybe_incomplete_result is used for multibyte values read from a binary
-    % stream where it is possible for the final element in the stream to be
-    % incomplete.
+    % maybe_incomplete_result is returned when reading multibyte values from a
+    % binary stream. `incomplete(Bytes)' is returned when at least one byte of
+    % a value has already been read but there are insufficient bytes
+    % remaining the stream to complete the value. In that case, `Bytes' will
+    % contain the bytes that have already been read from the stream, in the
+    % order in which they were read.
     %
 :- type maybe_incomplete_result(T)
     --->    ok(T)
@@ -883,15 +886,16 @@
     io::di, io::uo) is det.
 
     % The following predicates read multibyte integer values from the current
-    % binary input stream or from the specified input stream.
+    % binary input stream or from the specified binary input stream.
     %
-    % These names of these predicates have the form:
+    % The names of these predicates have the form:
     %
     %    read_binary_<TYPE><SUFFIX>
     %
     % where <TYPE> is the name of one of the Mercury multibyte fixed size
-    % integer types. <SUFFIX> is optional and specifies the byte order in
-    % which the integer value is read from the stream.  It may be one of:
+    % integer types. <SUFFIX> is optional and specifies what order the
+    % bytes in input stream that make up the multibyte integer occur
+    % in. It may be one of:
     %
     %     no suffix - native byte order of the underlying platform.
     %     "_le"     - little endian byte order.
@@ -2257,17 +2261,17 @@ using System.Security.Principal;
     [prefix("ML_RESULT_CODE_"), uppercase]).
 
 :- type maybe_incomplete_result_code
-    --->    ok
-    ;       eof
-    ;       incomplete
-    ;       error.
+    --->    mirc_ok
+    ;       mirc_eof
+    ;       mirc_incomplete
+    ;       mirc_error.
 
 :- pragma foreign_export_enum("C", maybe_incomplete_result_code/0,
-    [prefix("ML_MAYBE_INCOMPLETE_RESULT_CODE_"), uppercase]).
+    [prefix("ML_"), uppercase]).
 :- pragma foreign_export_enum("C#", maybe_incomplete_result_code/0,
-    [prefix("ML_MAYBE_INCOMPLETE_RESULT_CODE_"), uppercase]).
+    [prefix("ML_"), uppercase]).
 :- pragma foreign_export_enum("Java", maybe_incomplete_result_code/0,
-    [prefix("ML_MAYBE_INCOMPLETE_RESULT_CODE_"), uppercase]).
+    [prefix("ML_"), uppercase]).
 
     % Reads a character (code point) from specified stream. This may
     % involve converting external character encodings into Mercury's internal
@@ -2533,38 +2537,6 @@ read_binary_int16(Stream, Result, !IO) :-
         read_binary_int16_le(Stream, Result, !IO)
     ).
 
-:- pred native_byte_order_is_big_endian is semidet.
-
-:- pragma foreign_proc("C",
-    native_byte_order_is_big_endian,
-    [promise_pure, will_not_call_mercury, thread_safe],
-"
-    #if defined(MR_BIG_ENDIAN)
-        SUCCESS_INDICATOR = MR_TRUE;
-    #else
-        SUCCESS_INDICATOR = MR_FALSE;
-    #endif
-").
-
-:- pragma foreign_proc("C#",
-    native_byte_order_is_big_endian,
-    [promise_pure, will_not_call_mercury, thread_safe],
-"
-    SUCCESS_INDICATOR = !(BitConverter.IsLittleEndian);
-").
-
-:- pragma foreign_proc("Java",
-    native_byte_order_is_big_endian,
-    [promise_pure, will_not_call_mercury, thread_safe],
-"
-    SUCCESS_INDICATOR =
-        (java.nio.ByteOrder.nativeOrder() == java.nio.ByteOrder.BIG_ENDIAN);
-").
-
-native_byte_order_is_big_endian :-
-    sorry($module,
-        "native_byte_order_is_big_endian/0 NYI for Erlang").
-
 %---------------------%
 
 read_binary_int16_le(Result, !IO) :-
@@ -2572,20 +2544,21 @@ read_binary_int16_le(Result, !IO) :-
     read_binary_int16_le(Stream, Result, !IO).
 
 read_binary_int16_le(binary_input_stream(Stream), Result, !IO) :-
-    do_read_binary_uint16_le(Stream, Result0, UInt16, IncompleteBytes,
+    do_read_binary_uint16(Stream, little_endian, ResultCode, UInt16,
+        IncompleteBytes,
         Error, !IO),
     (
-        Result0 = ok,
+        ResultCode = mirc_ok,
         Int16 = cast_from_uint16(UInt16),
         Result = ok(Int16)
     ;
-        Result0 = eof,
+        ResultCode = mirc_eof,
         Result = eof
     ;
-        Result0 = incomplete,
+        ResultCode = mirc_incomplete,
         Result = incomplete(IncompleteBytes)
     ;
-        Result0 = error,
+        ResultCode = mirc_error,
         make_err_msg(Error, "read failed: ", Msg),
         Result = error(io_error(Msg))
     ).
@@ -2597,21 +2570,20 @@ read_binary_int16_be(Result, !IO) :-
     read_binary_int16_be(Stream, Result, !IO).
 
 read_binary_int16_be(binary_input_stream(Stream), Result, !IO) :-
-    do_read_binary_uint16_le(Stream, Result0, UInt16LE, IncompleteBytes,
-        Error, !IO),
+    do_read_binary_uint16(Stream, big_endian, ResultCode, UInt16,
+        IncompleteBytes, Error, !IO),
     (
-        Result0 = ok,
-        UInt16BE = reverse_bytes(UInt16LE),
-        Int16 = cast_from_uint16(UInt16BE),
+        ResultCode = mirc_ok,
+        Int16 = cast_from_uint16(UInt16),
         Result = ok(Int16)
     ;
-        Result0 = eof,
+        ResultCode = mirc_eof,
         Result = eof
     ;
-        Result0 = incomplete,
+        ResultCode = mirc_incomplete,
         Result = incomplete(IncompleteBytes)
     ;
-        Result0 = error,
+        ResultCode = mirc_error,
         make_err_msg(Error, "read failed: ", Msg),
         Result = error(io_error(Msg))
     ).
@@ -2636,64 +2608,64 @@ read_binary_uint16_le(Result, !IO) :-
     read_binary_uint16_le(Stream, Result, !IO).
 
 read_binary_uint16_le(binary_input_stream(Stream), Result, !IO) :-
-    do_read_binary_uint16_le(Stream, Result0, UInt16, IncompleteBytes,
-        Error, !IO),
+    do_read_binary_uint16(Stream, little_endian, ResultCode, UInt16,
+        IncompleteBytes, Error, !IO),
     (
-        Result0 = ok,
+        ResultCode = mirc_ok,
         Result = ok(UInt16)
     ;
-        Result0 = eof,
+        ResultCode = mirc_eof,
         Result = eof
     ;
-        Result0 = incomplete,
+        ResultCode = mirc_incomplete,
         Result = incomplete(IncompleteBytes)
     ;
-        Result0 = error,
+        ResultCode = mirc_error,
         make_err_msg(Error, "read failed: ", Msg),
         Result = error(io_error(Msg))
     ).
 
-:- pred do_read_binary_uint16_le(stream::in, maybe_incomplete_result_code::out,
-    uint16::out, list(uint8)::out, system_error::out, io::di, io::uo) is det.
+%---------------------%
+
+read_binary_uint16_be(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_uint16_be(Stream, Result, !IO).
+
+read_binary_uint16_be(binary_input_stream(Stream), Result, !IO) :-
+    do_read_binary_uint16(Stream, big_endian, ResultCode, UInt16,
+        IncompleteBytes, Error, !IO),
+    (
+        ResultCode = mirc_ok,
+        Result = ok(UInt16)
+    ;
+        ResultCode = mirc_eof,
+        Result = eof
+    ;
+        ResultCode = mirc_incomplete,
+        Result = incomplete(IncompleteBytes)
+    ;
+        ResultCode = mirc_error,
+        make_err_msg(Error, "read failed: ", Msg),
+        Result = error(io_error(Msg))
+    ).
+
+:- pred do_read_binary_uint16(stream::in, byte_order::in,
+    maybe_incomplete_result_code::out, uint16::out, list(uint8)::out,
+    system_error::out, io::di, io::uo) is det.
 
 :- pragma foreign_proc("C",
-    do_read_binary_uint16_le(Stream::in, Result::out, UInt16::out,
-        IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    do_read_binary_uint16(Stream::in, ByteOrder::in, ResultCode::out,
+        UInt16::out, IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        tabled_for_io],
 "
-    unsigned char buffer[2];
-    size_t nread = MR_READ(*Stream, buffer, 2);
-    IncompleteBytes = MR_list_empty();
-
-    if (nread < 2) {
-        UInt16 = 0;
-        if (MR_FERROR(*Stream)) {
-            Result = ML_MAYBE_INCOMPLETE_RESULT_CODE_ERROR,
-            Error = errno;
-        } else if (nread > 0) {
-            int i;
-            Result = ML_MAYBE_INCOMPLETE_RESULT_CODE_INCOMPLETE;
-            IncompleteBytes = MR_list_cons(buffer[0], IncompleteBytes);
-            Error = 0;
-        } else {
-            Result = ML_MAYBE_INCOMPLETE_RESULT_CODE_EOF;
-            Error = 0;
-        }
-    } else {
-        Result = ML_MAYBE_INCOMPLETE_RESULT_CODE_OK;
-        #if defined(MR_BIG_ENDIAN)
-            ((unsigned char *) &UInt16)[0] = buffer[1];
-            ((unsigned char *) &UInt16)[1] = buffer[0];
-        #else
-            UInt16 = *((uint16_t *) buffer);
-        #endif
-        Error = 0;
-    }
+    ML_do_read_binary_uintN(2, 16, Stream, ByteOrder, ResultCode, UInt16,
+        IncompleteBytes, Error);
 ").
 
 :- pragma foreign_proc("C#",
-    do_read_binary_uint16_le(Stream::in, Result::out, UInt16::out,
-        IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
+    do_read_binary_uint16(Stream::in, ByteOrder::in, Result::out,
+        UInt16::out, IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
     [will_not_call_mercury, promise_pure, thread_safe],
 "
     byte[] buffer = new byte[2];
@@ -2719,25 +2691,29 @@ read_binary_uint16_le(binary_input_stream(Stream), Result, !IO) :-
         }
         if (nread < 2) {
             if (nread > 0) {
-                Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_INCOMPLETE;
+                Result = io.ML_MIRC_INCOMPLETE;
                 IncompleteBytes = list.cons(buffer[0], IncompleteBytes);
             } else {
-                Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_EOF;
+                Result = io.ML_MIRC_EOF;
             }
         } else {
-            Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_OK;
-            UInt16 = (ushort) (buffer[1] << 8 | (buffer[0] & 0x00ff));
+            Result = io.ML_MIRC_OK;
+            if (ByteOrder == io.ML_LITTLE_ENDIAN) {
+                UInt16 = (ushort) (buffer[1] << 8 | (buffer[0] & 0x00ff));
+            } else {
+                UInt16 = (ushort) (buffer[0] << 8 | (buffer[1] & 0x00ff));
+            }
         }
         Error = null;
     } catch (System.Exception e) {
-        Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_ERROR;
+        Result = io.ML_MIRC_ERROR;
         Error = e;
     }
 ").
 
 :- pragma foreign_proc("Java",
-    do_read_binary_uint16_le(Stream::in, Result::out, UInt16::out,
-        IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
+    do_read_binary_uint16(Stream::in, ByteOrder::in, Result::out,
+        UInt16::out, IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
     [will_not_call_mercury, promise_pure, thread_safe],
 "
     byte[] buffer = new byte[2];
@@ -2756,49 +2732,28 @@ read_binary_uint16_le(binary_input_stream(Stream), Result, !IO) :-
         }
         if (nread < 2) {
             if (nread > 0) {
-                Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_INCOMPLETE;
+                Result = io.ML_MIRC_INCOMPLETE;
                 IncompleteBytes = list.cons(buffer[0], IncompleteBytes);
             } else {
-                Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_EOF;
+                Result = io.ML_MIRC_EOF;
             }
         } else {
-            Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_OK;
-            UInt16 = (short) (buffer[1] << 8 | (buffer[0] & 0x00ff));
+            Result = io.ML_MIRC_OK;
+            if (ByteOrder == io.ML_LITTLE_ENDIAN) {
+                UInt16 = (short) (buffer[1] << 8 | (buffer[0] & 0x00ff));
+            } else {
+                UInt16 = (short) (buffer[0] << 8 | (buffer[1] & 0x00ff));
+            }
         }
         Error = null;
     } catch (java.lang.Exception e) {
-        Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_ERROR;
+        Result = io.ML_MIRC_ERROR;
         Error = e;
     }
 ").
 
-do_read_binary_uint16_le(_, _, _, _, _, _, _) :-
-    sorry($module, "do_read_binary_uint16_le NYI for Erlang").
-
-%---------------------%
-
-read_binary_uint16_be(Result, !IO) :-
-    binary_input_stream(Stream, !IO),
-    read_binary_uint16_be(Stream, Result, !IO).
-
-read_binary_uint16_be(binary_input_stream(Stream), Result, !IO) :-
-    do_read_binary_uint16_le(Stream, Result0, UInt16LE, IncompleteBytes,
-        Error, !IO),
-    (
-        Result0 = ok,
-        UInt16BE = uint16.reverse_bytes(UInt16LE),
-        Result = ok(UInt16BE)
-    ;
-        Result0 = eof,
-        Result = eof
-    ;
-        Result0 = incomplete,
-        Result = incomplete(IncompleteBytes)
-    ;
-        Result0 = error,
-        make_err_msg(Error, "read failed: ", Msg),
-        Result = error(io_error(Msg))
-    ).
+do_read_binary_uint16(_, _, _, _, _, _, _, _) :-
+    sorry($module, "do_read_binary_uint16 NYI for Erlang").
 
 %---------------------%
 
@@ -2820,20 +2775,20 @@ read_binary_int32_le(Result, !IO) :-
     read_binary_int32_le(Stream, Result, !IO).
 
 read_binary_int32_le(binary_input_stream(Stream), Result, !IO) :-
-    do_read_binary_uint32_le(Stream, Result0, UInt32, IncompleteBytes,
-        Error, !IO),
+    do_read_binary_uint32(Stream, little_endian, ResultCode, UInt32,
+        IncompleteBytes, Error, !IO),
     (
-        Result0 = ok,
+        ResultCode = mirc_ok,
         Int32 = cast_from_uint32(UInt32),
         Result = ok(Int32)
     ;
-        Result0 = eof,
+        ResultCode = mirc_eof,
         Result = eof
     ;
-        Result0 = incomplete,
+        ResultCode = mirc_incomplete,
         Result = incomplete(IncompleteBytes)
     ;
-        Result0 = error,
+        ResultCode = mirc_error,
         make_err_msg(Error, "read failed: ", Msg),
         Result = error(io_error(Msg))
     ).
@@ -2845,21 +2800,20 @@ read_binary_int32_be(Result, !IO) :-
     read_binary_int32_be(Stream, Result, !IO).
 
 read_binary_int32_be(binary_input_stream(Stream), Result, !IO) :-
-    do_read_binary_uint32_le(Stream, Result0, UInt32LE, IncompleteBytes,
-        Error, !IO),
+    do_read_binary_uint32(Stream, big_endian, ResultCode, UInt32,
+        IncompleteBytes, Error, !IO),
     (
-        Result0 = ok,
-        UInt32BE = reverse_bytes(UInt32LE),
-        Int32 = cast_from_uint32(UInt32BE),
+        ResultCode = mirc_ok,
+        Int32 = cast_from_uint32(UInt32),
         Result = ok(Int32)
     ;
-        Result0 = eof,
+        ResultCode = mirc_eof,
         Result = eof
     ;
-        Result0 = incomplete,
+        ResultCode = mirc_incomplete,
         Result = incomplete(IncompleteBytes)
     ;
-        Result0 = error,
+        ResultCode = mirc_error,
         make_err_msg(Error, "read failed: ", Msg),
         Result = error(io_error(Msg))
     ).
@@ -2884,68 +2838,66 @@ read_binary_uint32_le(Result, !IO) :-
     read_binary_uint32_le(Stream, Result, !IO).
 
 read_binary_uint32_le(binary_input_stream(Stream), Result, !IO) :-
-    do_read_binary_uint32_le(Stream, Result0, UInt32, IncompleteBytes,
-        Error, !IO),
+    do_read_binary_uint32(Stream, little_endian, ResultCode, UInt32,
+        IncompleteBytes, Error, !IO),
     (
-        Result0 = ok,
+        ResultCode = mirc_ok,
         Result = ok(UInt32)
     ;
-        Result0 = eof,
+        ResultCode = mirc_eof,
         Result = eof
     ;
-        Result0 = incomplete,
+        ResultCode = mirc_incomplete,
         Result = incomplete(IncompleteBytes)
     ;
-        Result0 = error,
+        ResultCode = mirc_error,
         make_err_msg(Error, "read failed: ", Msg),
         Result = error(io_error(Msg))
     ).
 
-:- pred do_read_binary_uint32_le(stream::in, maybe_incomplete_result_code::out,
-    uint32::out, list(uint8)::out, system_error::out, io::di, io::uo) is det.
+%---------------------%
+
+read_binary_uint32_be(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_uint32_be(Stream, Result, !IO).
+
+read_binary_uint32_be(binary_input_stream(Stream), Result, !IO) :-
+    do_read_binary_uint32(Stream, big_endian, ResultCode, UInt32,
+        IncompleteBytes, Error, !IO),
+    (
+        ResultCode = mirc_ok,
+        Result = ok(UInt32)
+    ;
+        ResultCode = mirc_eof,
+        Result = eof
+    ;
+        ResultCode = mirc_incomplete,
+        Result = incomplete(IncompleteBytes)
+    ;
+        ResultCode = mirc_error,
+        make_err_msg(Error, "read failed: ", Msg),
+        Result = error(io_error(Msg))
+    ).
+
+%---------------------%
+
+:- pred do_read_binary_uint32(stream::in, byte_order::in,
+    maybe_incomplete_result_code::out, uint32::out, list(uint8)::out,
+    system_error::out, io::di, io::uo) is det.
 
 :- pragma foreign_proc("C",
-    do_read_binary_uint32_le(Stream::in, Result::out, UInt32::out,
-        IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    do_read_binary_uint32(Stream::in, ByteOrder::in, ResultCode::out,
+        UInt32::out, IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        tabled_for_io],
 "
-    unsigned char buffer[4];
-    size_t nread = MR_READ(*Stream, buffer, 4);
-    IncompleteBytes = MR_list_empty();
-
-    if (nread < 4) {
-        UInt32 = 0;
-        if (MR_FERROR(*Stream)) {
-            Result = ML_MAYBE_INCOMPLETE_RESULT_CODE_ERROR,
-            Error = errno;
-        } else if (nread > 0) {
-            int i;
-            Result = ML_MAYBE_INCOMPLETE_RESULT_CODE_INCOMPLETE;
-            for (i = nread - 1; i >= 0; i--) {
-                IncompleteBytes = MR_list_cons(buffer[i], IncompleteBytes);
-            }
-            Error = 0;
-        } else {
-            Result = ML_MAYBE_INCOMPLETE_RESULT_CODE_EOF;
-            Error = 0;
-        }
-    } else {
-        Result = ML_MAYBE_INCOMPLETE_RESULT_CODE_OK;
-        #if defined(MR_BIG_ENDIAN)
-            ((unsigned char *) &UInt32)[0] = buffer[3];
-            ((unsigned char *) &UInt32)[1] = buffer[2];
-            ((unsigned char *) &UInt32)[2] = buffer[1];
-            ((unsigned char *) &UInt32)[3] = buffer[0];
-        #else
-            UInt32 = *((uint32_t *) buffer);
-        #endif
-        Error = 0;
-    }
+    ML_do_read_binary_uintN(4, 32, Stream, ByteOrder, ResultCode, UInt32,
+        IncompleteBytes, Error);
 ").
 
 :- pragma foreign_proc("C#",
-    do_read_binary_uint32_le(Stream::in, Result::out, UInt32::out,
-        IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
+    do_read_binary_uint32(Stream::in, ByteOrder::in, ResultCode::out,
+        UInt32::out, IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
     [will_not_call_mercury, promise_pure, thread_safe],
 "
     byte[] buffer = new byte[4];
@@ -2971,28 +2923,39 @@ read_binary_uint32_le(binary_input_stream(Stream), Result, !IO) :-
         }
         if (nread < 4) {
             if (nread > 0) {
-                Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_INCOMPLETE;
+                ResultCode = io.ML_MIRC_INCOMPLETE;
                 for (int i = nread - 1; i >= 0; i--) {
                     IncompleteBytes = list.cons(buffer[i], IncompleteBytes);
                 }
             } else {
-                Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_EOF;
+                ResultCode = io.ML_MIRC_EOF;
             }
         } else {
-            Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_OK;
-            UInt32 = (uint) (buffer[3] << 24 | buffer[2] << 16 |
-                buffer[1] << 8 | buffer[0]);
+            ResultCode = io.ML_MIRC_OK;
+            if (ByteOrder == io.ML_LITTLE_ENDIAN) {
+                UInt32 = (uint) (
+                    buffer[3] << 24 |
+                    buffer[2] << 16 |
+                    buffer[1] << 8  |
+                    buffer[0]);
+            } else {
+                UInt32 = (uint) (
+                    buffer[0] << 24 |
+                    buffer[1] << 16 |
+                    buffer[2] << 8  |
+                    buffer[3]);
+            }
         }
         Error = null;
     } catch (System.Exception e) {
-        Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_ERROR;
+        ResultCode = io.ML_MIRC_ERROR;
         Error = e;
     }
 ").
 
 :- pragma foreign_proc("Java",
-    do_read_binary_uint32_le(Stream::in, Result::out, UInt32::out,
-        IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
+    do_read_binary_uint32(Stream::in, ByteOrder::in, ResultCode::out,
+        UInt32::out, IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
     [will_not_call_mercury, promise_pure, thread_safe],
 "
     byte[] buffer = new byte[4];
@@ -3011,55 +2974,38 @@ read_binary_uint32_le(binary_input_stream(Stream), Result, !IO) :-
         }
         if (nread < 4) {
             if (nread > 0) {
-                Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_INCOMPLETE;
+                ResultCode = io.ML_MIRC_INCOMPLETE;
                 for (int i = nread - 1; i >= 0; i--) {
                     IncompleteBytes = list.cons(buffer[i], IncompleteBytes);
                 }
             } else {
-                Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_EOF;
+                ResultCode = io.ML_MIRC_EOF;
             }
         } else {
-            Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_OK;
-            UInt32 =
-                (buffer[3] & 0xff) << 24 |
-                (buffer[2] & 0xff) << 16 |
-                (buffer[1] & 0xff) << 8  |
-                (buffer[0] & 0xff);
+            ResultCode = io.ML_MIRC_OK;
+            if (ByteOrder == io.ML_LITTLE_ENDIAN) {
+                UInt32 =
+                    (buffer[3] & 0xff) << 24 |
+                    (buffer[2] & 0xff) << 16 |
+                    (buffer[1] & 0xff) << 8  |
+                    (buffer[0] & 0xff);
+            } else {
+                UInt32 =
+                    (buffer[0] & 0xff) << 24 |
+                    (buffer[1] & 0xff) << 16 |
+                    (buffer[2] & 0xff) << 8  |
+                    (buffer[3] & 0xff);
+            }
         }
         Error = null;
     } catch (java.lang.Exception e) {
-        Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_ERROR;
+        ResultCode = io.ML_MIRC_ERROR;
         Error = e;
     }
 ").
 
-do_read_binary_uint32_le(_, _, _, _, _, _, _) :-
-    sorry($module, "do_read_binary_uint32_le NYI for Erlang").
-
-%---------------------%
-
-read_binary_uint32_be(Result, !IO) :-
-    binary_input_stream(Stream, !IO),
-    read_binary_uint32_be(Stream, Result, !IO).
-
-read_binary_uint32_be(binary_input_stream(Stream), Result, !IO) :-
-    do_read_binary_uint32_le(Stream, Result0, UInt32LE, IncompleteBytes,
-        Error, !IO),
-    (
-        Result0 = ok,
-        UInt32BE = uint32.reverse_bytes(UInt32LE),
-        Result = ok(UInt32BE)
-    ;
-        Result0 = eof,
-        Result = eof
-    ;
-        Result0 = incomplete,
-        Result = incomplete(IncompleteBytes)
-    ;
-        Result0 = error,
-        make_err_msg(Error, "read failed: ", Msg),
-        Result = error(io_error(Msg))
-    ).
+do_read_binary_uint32(_, _, _, _, _, _, _, _) :-
+    sorry($module, "do_read_binary_uint32 NYI for Erlang").
 
 %---------------------%
 
@@ -3081,20 +3027,20 @@ read_binary_int64_le(Result, !IO) :-
     read_binary_int64_le(Stream, Result, !IO).
 
 read_binary_int64_le(binary_input_stream(Stream), Result, !IO) :-
-    do_read_binary_uint64_le(Stream, Result0, UInt64, IncompleteBytes,
-        Error, !IO),
+    do_read_binary_uint64(Stream, little_endian, ResultCode, UInt64,
+        IncompleteBytes, Error, !IO),
     (
-        Result0 = ok,
+        ResultCode = mirc_ok,
         Int64 = cast_from_uint64(UInt64),
         Result = ok(Int64)
     ;
-        Result0 = eof,
+        ResultCode = mirc_eof,
         Result = eof
     ;
-        Result0 = incomplete,
+        ResultCode = mirc_incomplete,
         Result = incomplete(IncompleteBytes)
     ;
-        Result0 = error,
+        ResultCode = mirc_error,
         make_err_msg(Error, "read failed: ", Msg),
         Result = error(io_error(Msg))
     ).
@@ -3106,21 +3052,20 @@ read_binary_int64_be(Result, !IO) :-
     read_binary_int64_be(Stream, Result, !IO).
 
 read_binary_int64_be(binary_input_stream(Stream), Result, !IO) :-
-    do_read_binary_uint64_le(Stream, Result0, UInt64LE, IncompleteBytes,
-        Error, !IO),
+    do_read_binary_uint64(Stream, big_endian, ResultCode, UInt64,
+        IncompleteBytes, Error, !IO),
     (
-        Result0 = ok,
-        UInt64BE = reverse_bytes(UInt64LE),
-        Int64 = cast_from_uint64(UInt64BE),
+        ResultCode = mirc_ok,
+        Int64 = cast_from_uint64(UInt64),
         Result = ok(Int64)
     ;
-        Result0 = eof,
+        ResultCode = mirc_eof,
         Result = eof
     ;
-        Result0 = incomplete,
+        ResultCode = mirc_incomplete,
         Result = incomplete(IncompleteBytes)
     ;
-        Result0 = error,
+        ResultCode = mirc_error,
         make_err_msg(Error, "read failed: ", Msg),
         Result = error(io_error(Msg))
     ).
@@ -3145,72 +3090,66 @@ read_binary_uint64_le(Result, !IO) :-
     read_binary_uint64_le(Stream, Result, !IO).
 
 read_binary_uint64_le(binary_input_stream(Stream), Result, !IO) :-
-    do_read_binary_uint64_le(Stream, Result0, UInt64, IncompleteBytes,
-        Error, !IO),
+    do_read_binary_uint64(Stream, little_endian, ResultCode, UInt64,
+        IncompleteBytes, Error, !IO),
     (
-        Result0 = ok,
+        ResultCode = mirc_ok,
         Result = ok(UInt64)
     ;
-        Result0 = eof,
+        ResultCode = mirc_eof,
         Result = eof
     ;
-        Result0 = incomplete,
+        ResultCode = mirc_incomplete,
         Result = incomplete(IncompleteBytes)
     ;
-        Result0 = error,
+        ResultCode = mirc_error,
         make_err_msg(Error, "read failed: ", Msg),
         Result = error(io_error(Msg))
     ).
 
-:- pred do_read_binary_uint64_le(stream::in, maybe_incomplete_result_code::out,
-    uint64::out, list(uint8)::out, system_error::out, io::di, io::uo) is det.
+%---------------------%
+
+read_binary_uint64_be(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_uint64_be(Stream, Result, !IO).
+
+read_binary_uint64_be(binary_input_stream(Stream), Result, !IO) :-
+    do_read_binary_uint64(Stream, big_endian, ResultCode, UInt64,
+        IncompleteBytes, Error, !IO),
+    (
+        ResultCode = mirc_ok,
+        Result = ok(UInt64)
+    ;
+        ResultCode = mirc_eof,
+        Result = eof
+    ;
+        ResultCode = mirc_incomplete,
+        Result = incomplete(IncompleteBytes)
+    ;
+        ResultCode = mirc_error,
+        make_err_msg(Error, "read failed: ", Msg),
+        Result = error(io_error(Msg))
+    ).
+
+%---------------------%
+
+:- pred do_read_binary_uint64(stream::in, byte_order::in,
+    maybe_incomplete_result_code::out, uint64::out, list(uint8)::out,
+    system_error::out, io::di, io::uo) is det.
 
 :- pragma foreign_proc("C",
-    do_read_binary_uint64_le(Stream::in, Result::out, UInt64::out,
-        IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    do_read_binary_uint64(Stream::in, ByteOrder::in, ResultCode::out,
+        UInt64::out, IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        tabled_for_io],
 "
-    unsigned char buffer[8];
-    size_t nread = MR_READ(*Stream, buffer, 8);
-    IncompleteBytes = MR_list_empty();
-
-    if (nread < 8) {
-        UInt64 = 0;
-        if (MR_FERROR(*Stream)) {
-            Result = ML_MAYBE_INCOMPLETE_RESULT_CODE_ERROR,
-            Error = errno;
-        } else if (nread > 0) {
-            int i;
-            Result = ML_MAYBE_INCOMPLETE_RESULT_CODE_INCOMPLETE;
-            for (i = nread - 1; i >= 0; i--) {
-                IncompleteBytes = MR_list_cons(buffer[i], IncompleteBytes);
-            }
-            Error = 0;
-        } else {
-            Result = ML_MAYBE_INCOMPLETE_RESULT_CODE_EOF;
-            Error = 0;
-        }
-    } else {
-        Result = ML_MAYBE_INCOMPLETE_RESULT_CODE_OK;
-        #if defined(MR_BIG_ENDIAN)
-            ((unsigned char *) &UInt64)[0] = buffer[7];
-            ((unsigned char *) &UInt64)[1] = buffer[6];
-            ((unsigned char *) &UInt64)[2] = buffer[5];
-            ((unsigned char *) &UInt64)[3] = buffer[3];
-            ((unsigned char *) &UInt64)[4] = buffer[3];
-            ((unsigned char *) &UInt64)[5] = buffer[2];
-            ((unsigned char *) &UInt64)[6] = buffer[1];
-            ((unsigned char *) &UInt64)[7] = buffer[0];
-        #else
-            UInt64 = *((uint64_t *) buffer);
-        #endif
-        Error = 0;
-    }
+    ML_do_read_binary_uintN(8, 64, Stream, ByteOrder, ResultCode, UInt64,
+        IncompleteBytes, Error);
 ").
 
 :- pragma foreign_proc("C#",
-    do_read_binary_uint64_le(Stream::in, Result::out, UInt64::out,
-        IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
+    do_read_binary_uint64(Stream::in, ByteOrder::in, ResultCode::out,
+        UInt64::out, IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
     [will_not_call_mercury, promise_pure, thread_safe],
 "
     byte[] buffer = new byte[8];
@@ -3236,35 +3175,47 @@ read_binary_uint64_le(binary_input_stream(Stream), Result, !IO) :-
         }
         if (nread < 8) {
             if (nread > 0) {
-                Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_INCOMPLETE;
+                ResultCode = io.ML_MIRC_INCOMPLETE;
                 for (int i = nread - 1; i >=0; i--) {
                     IncompleteBytes = list.cons(buffer[i], IncompleteBytes);
                 }
             } else {
-                Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_EOF;
+                ResultCode = io.ML_MIRC_EOF;
             }
         } else {
-            Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_OK;
-            UInt64 = (ulong) (
-                (ulong) buffer[7] << 56 |
-                (ulong) buffer[6] << 48 |
-                (ulong) buffer[5] << 40 |
-                (ulong) buffer[4] << 32 |
-                (ulong) buffer[3] << 24 |
-                (ulong) buffer[2] << 16 |
-                (ulong) buffer[1] << 8  |
-                (ulong) buffer[0]);
+            ResultCode = io.ML_MIRC_OK;
+            if (ByteOrder == io.ML_LITTLE_ENDIAN) {
+                UInt64 = (ulong) (
+                    (ulong) buffer[7] << 56 |
+                    (ulong) buffer[6] << 48 |
+                    (ulong) buffer[5] << 40 |
+                    (ulong) buffer[4] << 32 |
+                    (ulong) buffer[3] << 24 |
+                    (ulong) buffer[2] << 16 |
+                    (ulong) buffer[1] << 8  |
+                    (ulong) buffer[0]);
+            } else {
+                UInt64 = (ulong) (
+                    (ulong) buffer[0] << 56 |
+                    (ulong) buffer[1] << 48 |
+                    (ulong) buffer[2] << 40 |
+                    (ulong) buffer[3] << 32 |
+                    (ulong) buffer[4] << 24 |
+                    (ulong) buffer[5] << 16 |
+                    (ulong) buffer[6] << 8  |
+                    (ulong) buffer[7]);
+            }
         }
         Error = null;
     } catch (System.Exception e) {
-        Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_ERROR;
+        ResultCode = io.ML_MIRC_ERROR;
         Error = e;
     }
 ").
 
 :- pragma foreign_proc("Java",
-    do_read_binary_uint64_le(Stream::in, Result::out, UInt64::out,
-        IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
+    do_read_binary_uint64(Stream::in, ByteOrder::in, ResultCode::out,
+        UInt64::out, IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
     [will_not_call_mercury, promise_pure, thread_safe],
 "
     byte[] buffer = new byte[8];
@@ -3283,59 +3234,195 @@ read_binary_uint64_le(binary_input_stream(Stream), Result, !IO) :-
         }
         if (nread < 8) {
             if (nread > 0) {
-                Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_INCOMPLETE;
+                ResultCode = io.ML_MIRC_INCOMPLETE;
                 for (int i = nread - 1; i >= 0; i--) {
                     IncompleteBytes = list.cons(buffer[i], IncompleteBytes);
                 }
             } else {
-                Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_EOF;
+                ResultCode = io.ML_MIRC_EOF;
             }
         } else {
-            Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_OK;
-            UInt64 =
-                (long) (buffer[7] & 0xff) << 56 |
-                (long) (buffer[6] & 0xff) << 48 |
-                (long) (buffer[5] & 0xff) << 40 |
-                (long) (buffer[4] & 0xff) << 32 |
-                (long) (buffer[3] & 0xff) << 24 |
-                (long) (buffer[2] & 0xff) << 16 |
-                (long) (buffer[1] & 0xff) << 8  |
-                (long) (buffer[0] & 0xff);
+            ResultCode = io.ML_MIRC_OK;
+            if (ByteOrder == io.ML_LITTLE_ENDIAN) {
+                UInt64 =
+                    (long) (buffer[7] & 0xff) << 56 |
+                    (long) (buffer[6] & 0xff) << 48 |
+                    (long) (buffer[5] & 0xff) << 40 |
+                    (long) (buffer[4] & 0xff) << 32 |
+                    (long) (buffer[3] & 0xff) << 24 |
+                    (long) (buffer[2] & 0xff) << 16 |
+                    (long) (buffer[1] & 0xff) << 8  |
+                    (long) (buffer[0] & 0xff);
+            } else {
+                UInt64 =
+                    (long) (buffer[0] & 0xff) << 56 |
+                    (long) (buffer[1] & 0xff) << 48 |
+                    (long) (buffer[2] & 0xff) << 40 |
+                    (long) (buffer[3] & 0xff) << 32 |
+                    (long) (buffer[4] & 0xff) << 24 |
+                    (long) (buffer[5] & 0xff) << 16 |
+                    (long) (buffer[6] & 0xff) << 8  |
+                    (long) (buffer[7] & 0xff);
+            }
         }
         Error = null;
     } catch (java.lang.Exception e) {
-        Result = io.ML_MAYBE_INCOMPLETE_RESULT_CODE_ERROR;
+        ResultCode = io.ML_MIRC_ERROR;
         Error = e;
     }
 ").
 
-do_read_binary_uint64_le(_, _, _, _, _, _, _) :-
+do_read_binary_uint64(_, _, _, _, _, _, _, _) :-
     sorry($module, "do_read_binary_uint64_le NYI for Erlang").
 
 %---------------------%
 
-read_binary_uint64_be(Result, !IO) :-
-    binary_input_stream(Stream, !IO),
-    read_binary_uint64_be(Stream, Result, !IO).
+:- type byte_order
+    --->    big_endian
+    ;       little_endian.
 
-read_binary_uint64_be(binary_input_stream(Stream), Result, !IO) :-
-    do_read_binary_uint64_le(Stream, Result0, UInt64LE, IncompleteBytes,
-        Error, !IO),
-    (
-        Result0 = ok,
-        UInt64BE = uint64.reverse_bytes(UInt64LE),
-        Result = ok(UInt64BE)
-    ;
-        Result0 = eof,
-        Result = eof
-    ;
-        Result0 = incomplete,
-        Result = incomplete(IncompleteBytes)
-    ;
-        Result0 = error,
-        make_err_msg(Error, "read failed: ", Msg),
-        Result = error(io_error(Msg))
-    ).
+:- pragma foreign_export_enum("C", byte_order/0,
+    [prefix("ML_"), uppercase]).
+:- pragma foreign_export_enum("Java", byte_order/0,
+    [prefix("ML_"), uppercase]).
+:- pragma foreign_export_enum("C#", byte_order/0,
+    [prefix("ML_"), uppercase]).
+
+:- pred native_byte_order_is_big_endian is semidet.
+
+:- pragma foreign_proc("C",
+    native_byte_order_is_big_endian,
+    [promise_pure, will_not_call_mercury, thread_safe],
+"
+    #if defined(MR_BIG_ENDIAN)
+        SUCCESS_INDICATOR = MR_TRUE;
+    #else
+        SUCCESS_INDICATOR = MR_FALSE;
+    #endif
+").
+
+:- pragma foreign_proc("C#",
+    native_byte_order_is_big_endian,
+    [promise_pure, will_not_call_mercury, thread_safe],
+"
+    SUCCESS_INDICATOR = !(BitConverter.IsLittleEndian);
+").
+
+:- pragma foreign_proc("Java",
+    native_byte_order_is_big_endian,
+    [promise_pure, will_not_call_mercury, thread_safe],
+"
+    SUCCESS_INDICATOR =
+        (java.nio.ByteOrder.nativeOrder() == java.nio.ByteOrder.BIG_ENDIAN);
+").
+
+native_byte_order_is_big_endian :-
+    sorry($module,
+        "native_byte_order_is_big_endian/0 NYI for Erlang").
+
+%---------------------%
+%
+% C implementation of reading multibyte integers from binary streams.
+%
+
+:- pragma foreign_decl("C", "
+
+// ML_N_BIT_INT_T(n) expands to the name of an n-bit unsigned integer type in
+// C.
+//
+#define ML_N_BIT_INT_T(n) \
+    MR_PASTE3(uint, n, _t)
+
+// ML_REVERSE_BYTES_FUNC(n) expands to the name a function exported by the
+// Mercury runtime that can be used to reverse the bytes in an n-bit
+// unsigned integer.
+//
+#define ML_REVERSE_BYTES_FUNC(n) \
+    MR_PASTE3(MR_uint, n, _reverse_bytes)
+
+// ML_build_uintN(int n, MR_Word byte_order, unsigned char *buffer,
+//     uintN_t value):
+//
+// Build an n-bit unsigned integer using the bytes stored in the array
+// 'buffer'.  The order of the bytes in the buffer are given by 'byte_order'.
+// The result is assigned to the lvalue 'value'
+//
+// We have two definitions of this macro, one for big-endian machines
+// and one for little-endian machines.
+//
+#if defined(MR_BIG_ENDIAN)
+#define ML_build_uintN(n, byte_order, buffer, value)                 \
+    do {                                                             \
+        if (byte_order == ML_LITTLE_ENDIAN) {                        \
+            value = ML_REVERSE_BYTES_FUNC(n)(                        \
+                *((ML_N_BIT_INT_T(n) *) buffer));                    \
+        } else {                                                     \
+            value = *((ML_N_BIT_INT_T(n) *) buffer);                 \
+        }                                                            \
+    } while (0)
+#else
+#define ML_build_uintN(n, byte_order, buffer, value)                 \
+    do {                                                             \
+        if (byte_order == ML_LITTLE_ENDIAN) {                        \
+            value = *((ML_N_BIT_INT_T(n) *) buffer);                 \
+        } else {                                                     \
+            value = ML_REVERSE_BYTES_FUNC(n)(                        \
+                *((ML_N_BIT_INT_T(n) *) buffer));                    \
+        }                                                            \
+    } while (0)
+#endif
+
+// ML_do_read_binary_uintN(int nbytes, int nbits, MR_Word stream,
+//     MR_Word byte_order, MR_Word result_code, MR_Word result_value,
+//     MR_Word result_incomplete, MR_Word result_error):
+//
+// This macro implements the do_read_binary_uint{16 32,64}/8 predicates.
+// It expands to code for reading an 'nbits'-bit ('nbytes'-byte) unsigned
+// integer from the binary stream 'stream', with the bytes in the stream
+// being in 'byte_order' order.
+//
+// The result is returned as follows:
+//
+// 'result_code' is set the status code (maybe_incomplete_result_code/0)
+// for the read.
+// 'result_value' is the value of the integer read on a successful read
+// or zero otherwise.
+// 'result_incomplete' is the list of bytes read so far for an incomplete
+// read or the empty list otherwise.
+// 'result_error' is the errno if an I/O error occurs or zero otherwise.
+//
+#define ML_do_read_binary_uintN(nbytes, nbits, stream, byte_order,           \
+       result_code, result_value, result_incomplete, result_error)           \
+    do {                                                                     \
+        unsigned char buffer[nbytes];                                        \
+        size_t nread = MR_READ(*stream, buffer, nbytes);                     \
+        result_incomplete = MR_list_empty();                                 \
+                                                                             \
+        if (nread < nbytes) {                                                \
+            result_value = 0;                                                \
+            if (MR_FERROR(*Stream)) {                                        \
+                result_code = ML_MIRC_ERROR,                                 \
+                result_error = errno;                                        \
+            } else if (nread > 0) {                                          \
+                int i;                                                       \
+                result_code = ML_MIRC_INCOMPLETE;                            \
+                for (i = nread - 1; i >= 0; i--) {                           \
+                    result_incomplete =                                      \
+                        MR_list_cons(buffer[i],                              \
+                        result_incomplete);                                  \
+                }                                                            \
+                result_error = 0;                                            \
+            } else {                                                         \
+                result_code = ML_MIRC_EOF;                                   \
+                result_error = 0;                                            \
+            }                                                                \
+        } else {                                                             \
+            result_code = ML_MIRC_OK;                                        \
+            ML_build_uintN(nbits, byte_order, buffer, result_value);         \
+            result_error = 0;                                                \
+        }                                                                    \
+    } while (0)
+").
 
 %---------------------%
 
@@ -4024,6 +4111,7 @@ binary_input_stream_file_size(binary_input_stream(Stream), Size, !IO) :-
 #endif
 #include ""mercury_types.h""            // for MR_Integer
 #include ""mercury_library_types.h""    // for MercuryFilePtr
+#include ""mercury_int.h""              // for MR_*_reverse_bytes
 ").
 
 :- pragma foreign_proc("C",
diff --git a/tests/hard_coded/read_binary_int16.m b/tests/hard_coded/read_binary_int16.m
index 171475b..bdbe134 100644
--- a/tests/hard_coded/read_binary_int16.m
+++ b/tests/hard_coded/read_binary_int16.m
@@ -68,6 +68,7 @@ run_test(ByteOrder, TestBytes, !IO) :-
                 ByteOrder = native,
                 read_binary_int16(InFile, ReadResult, !IO)
             ),
+            io.close_binary_input(InFile, !IO),
             (
                 ReadResult = ok(ResultUInt16),
                 io.write_string("Result: ", !IO),
diff --git a/tests/hard_coded/read_binary_int32.m b/tests/hard_coded/read_binary_int32.m
index cecad92..d750498 100644
--- a/tests/hard_coded/read_binary_int32.m
+++ b/tests/hard_coded/read_binary_int32.m
@@ -63,6 +63,7 @@ run_test(ByteOrder, TestBytes, !IO) :-
                 ByteOrder = native,
                 read_binary_int32(InFile, ReadResult, !IO)
             ),
+            io.close_binary_input(InFile, !IO),
             (
                 ReadResult = ok(ResultUInt16),
                 io.write_string("Result: ", !IO),
diff --git a/tests/hard_coded/read_binary_int64.m b/tests/hard_coded/read_binary_int64.m
index fdeb287..87cbc63 100644
--- a/tests/hard_coded/read_binary_int64.m
+++ b/tests/hard_coded/read_binary_int64.m
@@ -65,6 +65,7 @@ run_test(ByteOrder, TestBytes, !IO) :-
                 ByteOrder = native,
                 read_binary_int64(InFile, ReadResult, !IO)
             ),
+            io.close_binary_input(InFile, !IO),
             (
                 ReadResult = ok(ResultUInt16),
                 io.write_string("Result: ", !IO),
diff --git a/tests/hard_coded/read_binary_uint16.m b/tests/hard_coded/read_binary_uint16.m
index 8a0ec6e..3b2a38e 100644
--- a/tests/hard_coded/read_binary_uint16.m
+++ b/tests/hard_coded/read_binary_uint16.m
@@ -68,6 +68,7 @@ run_test(ByteOrder, TestBytes, !IO) :-
                 ByteOrder = native,
                 read_binary_uint16(InFile, ReadResult, !IO)
             ),
+            io.close_binary_input(InFile, !IO),
             (
                 ReadResult = ok(ResultUInt16),
                 io.write_string("Result: ", !IO),
diff --git a/tests/hard_coded/read_binary_uint32.m b/tests/hard_coded/read_binary_uint32.m
index c400c12..a7c7801 100644
--- a/tests/hard_coded/read_binary_uint32.m
+++ b/tests/hard_coded/read_binary_uint32.m
@@ -63,6 +63,7 @@ run_test(ByteOrder, TestBytes, !IO) :-
                 ByteOrder = native,
                 read_binary_uint32(InFile, ReadResult, !IO)
             ),
+            io.close_binary_input(InFile, !IO),
             (
                 ReadResult = ok(ResultUInt16),
                 io.write_string("Result: ", !IO),
diff --git a/tests/hard_coded/read_binary_uint64.m b/tests/hard_coded/read_binary_uint64.m
index 165b5c9..e742472 100644
--- a/tests/hard_coded/read_binary_uint64.m
+++ b/tests/hard_coded/read_binary_uint64.m
@@ -65,6 +65,7 @@ run_test(ByteOrder, TestBytes, !IO) :-
                 ByteOrder = native,
                 read_binary_uint64(InFile, ReadResult, !IO)
             ),
+            io.close_binary_input(InFile, !IO),
             (
                 ReadResult = ok(ResultUInt16),
                 io.write_string("Result: ", !IO),
-------------- next part --------------
diff --git a/library/io.m b/library/io.m
index d407c09..58b4dbb 100644
--- a/library/io.m
+++ b/library/io.m
@@ -2,7 +2,7 @@
 % vim: ft=mercury ts=4 sw=4 et
 %---------------------------------------------------------------------------%
 % Copyright (C) 1993-2012 The University of Melbourne.
-% Copyright (C) 2013-2018 The Mercury team.
+% Copyright (C) 2013-2019 The Mercury team.
 % This file is distributed under the terms specified in COPYING.LIB.
 %---------------------------------------------------------------------------%
 %
@@ -108,6 +108,19 @@
     ;       eof
     ;       error(io.error).
 
+    % maybe_incomplete_result is returned when reading multibyte values from a
+    % binary stream. `incomplete(Bytes)' is returned when at least one byte of
+    % a value has already been read but there are insufficient bytes
+    % remaining the stream to complete the value. In that case, `Bytes' will
+    % contain the bytes that have already been read from the stream, in the
+    % order in which they were read.
+    %
+:- type maybe_incomplete_result(T)
+    --->    ok(T)
+    ;       eof
+    ;       incomplete(list(uint8))
+    ;       error(io.error).
+
 :- type read_result(T)
     --->    ok(T)
     ;       eof
@@ -872,6 +885,118 @@
 :- pred read_binary_uint8(io.binary_input_stream::in, io.result(uint8)::out,
     io::di, io::uo) is det.
 
+    % The following predicates read multibyte integer values from the current
+    % binary input stream or from the specified binary input stream.
+    %
+    % The names of these predicates have the form:
+    %
+    %    read_binary_<TYPE><SUFFIX>
+    %
+    % where <TYPE> is the name of one of the Mercury multibyte fixed size
+    % integer types. <SUFFIX> is optional and specifies what order the
+    % bytes in input stream that make up the multibyte integer occur
+    % in. It may be one of:
+    %
+    %     no suffix - native byte order of the underlying platform.
+    %     "_le"     - little endian byte order.
+    %     "_be"     - big endian byte order.
+    %
+:- pred read_binary_int16(maybe_incomplete_result(int16)::out,
+    io::di, io::uo) is det.
+:- pred read_binary_int16(io.binary_input_stream::in,
+    maybe_incomplete_result(int16)::out, io::di, io::uo) is det.
+
+:- pred read_binary_int16_le(maybe_incomplete_result(int16)::out,
+    io::di, io::uo) is det.
+:- pred read_binary_int16_le(io.binary_input_stream::in,
+    maybe_incomplete_result(int16)::out, io::di, io::uo) is det.
+
+:- pred read_binary_int16_be(maybe_incomplete_result(int16)::out,
+    io::di, io::uo) is det.
+:- pred read_binary_int16_be(io.binary_input_stream::in,
+    maybe_incomplete_result(int16)::out, io::di, io::uo) is det.
+
+:- pred read_binary_uint16(maybe_incomplete_result(uint16)::out,
+    io::di, io::uo) is det.
+:- pred read_binary_uint16(io.binary_input_stream::in,
+    maybe_incomplete_result(uint16)::out, io::di, io::uo) is det.
+
+:- pred read_binary_uint16_le(maybe_incomplete_result(uint16)::out,
+    io::di, io::uo) is det.
+:- pred read_binary_uint16_le(io.binary_input_stream::in,
+    maybe_incomplete_result(uint16)::out, io::di, io::uo) is det.
+
+:- pred read_binary_uint16_be(maybe_incomplete_result(uint16)::out,
+    io::di, io::uo) is det.
+:- pred read_binary_uint16_be(io.binary_input_stream::in,
+    maybe_incomplete_result(uint16)::out, io::di, io::uo) is det.
+
+%---------------------%
+
+:- pred read_binary_int32(maybe_incomplete_result(int32)::out,
+    io::di, io::uo) is det.
+:- pred read_binary_int32(io.binary_input_stream::in,
+    maybe_incomplete_result(int32)::out, io::di, io::uo) is det.
+
+:- pred read_binary_int32_le(maybe_incomplete_result(int32)::out,
+    io::di, io::uo) is det.
+:- pred read_binary_int32_le(io.binary_input_stream::in,
+    maybe_incomplete_result(int32)::out, io::di, io::uo) is det.
+
+:- pred read_binary_int32_be(maybe_incomplete_result(int32)::out,
+    io::di, io::uo) is det.
+:- pred read_binary_int32_be(io.binary_input_stream::in,
+    maybe_incomplete_result(int32)::out, io::di, io::uo) is det.
+
+:- pred read_binary_uint32(maybe_incomplete_result(uint32)::out,
+    io::di, io::uo) is det.
+:- pred read_binary_uint32(io.binary_input_stream::in,
+    maybe_incomplete_result(uint32)::out, io::di, io::uo) is det.
+
+:- pred read_binary_uint32_le(maybe_incomplete_result(uint32)::out,
+    io::di, io::uo) is det.
+:- pred read_binary_uint32_le(io.binary_input_stream::in,
+    maybe_incomplete_result(uint32)::out, io::di, io::uo) is det.
+
+:- pred read_binary_uint32_be(maybe_incomplete_result(uint32)::out,
+    io::di, io::uo) is det.
+:- pred read_binary_uint32_be(io.binary_input_stream::in,
+    maybe_incomplete_result(uint32)::out, io::di, io::uo) is det.
+
+%---------------------%
+
+:- pred read_binary_int64(maybe_incomplete_result(int64)::out,
+    io::di, io::uo) is det.
+:- pred read_binary_int64(io.binary_input_stream::in,
+    maybe_incomplete_result(int64)::out, io::di, io::uo) is det.
+
+:- pred read_binary_int64_le(maybe_incomplete_result(int64)::out,
+    io::di, io::uo) is det.
+:- pred read_binary_int64_le(io.binary_input_stream::in,
+    maybe_incomplete_result(int64)::out, io::di, io::uo) is det.
+
+:- pred read_binary_int64_be(maybe_incomplete_result(int64)::out,
+    io::di, io::uo) is det.
+:- pred read_binary_int64_be(io.binary_input_stream::in,
+    maybe_incomplete_result(int64)::out, io::di, io::uo) is det.
+
+:- pred read_binary_uint64(maybe_incomplete_result(uint64)::out,
+    io::di, io::uo) is det.
+:- pred read_binary_uint64(io.binary_input_stream::in,
+    maybe_incomplete_result(uint64)::out, io::di, io::uo) is det.
+
+:- pred read_binary_uint64_le(maybe_incomplete_result(uint64)::out,
+    io::di, io::uo) is det.
+:- pred read_binary_uint64_le(io.binary_input_stream::in,
+    maybe_incomplete_result(uint64)::out, io::di, io::uo) is det.
+
+:- pred read_binary_uint64_be(maybe_incomplete_result(uint64)::out,
+    io::di, io::uo) is det.
+:- pred read_binary_uint64_be(io.binary_input_stream::in,
+    maybe_incomplete_result(uint64)::out, io::di, io::uo) is det.
+
+%---------------------%
+
     % Fill a bitmap from the current binary input stream
     % or from the specified binary input stream.
     % Return the number of bytes read. On end-of-file, the number of
@@ -1977,6 +2102,9 @@
 :- import_module exception.
 :- import_module int.
 :- import_module int8.
+:- import_module int16.
+:- import_module int32.
+:- import_module int64.
 :- import_module parser.
 :- import_module require.
 :- import_module stream.string_writer.
@@ -2132,6 +2260,19 @@ using System.Security.Principal;
 :- pragma foreign_export_enum("Java", result_code/0,
     [prefix("ML_RESULT_CODE_"), uppercase]).
 
+:- type maybe_incomplete_result_code
+    --->    mirc_ok
+    ;       mirc_eof
+    ;       mirc_incomplete
+    ;       mirc_error.
+
+:- pragma foreign_export_enum("C", maybe_incomplete_result_code/0,
+    [prefix("ML_"), uppercase]).
+:- pragma foreign_export_enum("C#", maybe_incomplete_result_code/0,
+    [prefix("ML_"), uppercase]).
+:- pragma foreign_export_enum("Java", maybe_incomplete_result_code/0,
+    [prefix("ML_"), uppercase]).
+
     % Reads a character (code point) from specified stream. This may
     % involve converting external character encodings into Mercury's internal
     % character representation and (for text streams) converting OS line
@@ -2383,6 +2524,908 @@ read_binary_uint8(binary_input_stream(Stream), Result, !IO) :-
         Result = error(io_error(Msg))
     ).
 
+%---------------------%
+
+read_binary_int16(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_int16(Stream, Result, !IO).
+
+read_binary_int16(Stream, Result, !IO) :-
+    ( if native_byte_order_is_big_endian then
+        read_binary_int16_be(Stream, Result, !IO)
+    else
+        read_binary_int16_le(Stream, Result, !IO)
+    ).
+
+%---------------------%
+
+read_binary_int16_le(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_int16_le(Stream, Result, !IO).
+
+read_binary_int16_le(binary_input_stream(Stream), Result, !IO) :-
+    do_read_binary_uint16(Stream, little_endian, ResultCode, UInt16,
+        IncompleteBytes,
+        Error, !IO),
+    (
+        ResultCode = mirc_ok,
+        Int16 = cast_from_uint16(UInt16),
+        Result = ok(Int16)
+    ;
+        ResultCode = mirc_eof,
+        Result = eof
+    ;
+        ResultCode = mirc_incomplete,
+        Result = incomplete(IncompleteBytes)
+    ;
+        ResultCode = mirc_error,
+        make_err_msg(Error, "read failed: ", Msg),
+        Result = error(io_error(Msg))
+    ).
+
+%---------------------%
+
+read_binary_int16_be(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_int16_be(Stream, Result, !IO).
+
+read_binary_int16_be(binary_input_stream(Stream), Result, !IO) :-
+    do_read_binary_uint16(Stream, big_endian, ResultCode, UInt16,
+        IncompleteBytes, Error, !IO),
+    (
+        ResultCode = mirc_ok,
+        Int16 = cast_from_uint16(UInt16),
+        Result = ok(Int16)
+    ;
+        ResultCode = mirc_eof,
+        Result = eof
+    ;
+        ResultCode = mirc_incomplete,
+        Result = incomplete(IncompleteBytes)
+    ;
+        ResultCode = mirc_error,
+        make_err_msg(Error, "read failed: ", Msg),
+        Result = error(io_error(Msg))
+    ).
+
+%---------------------%
+
+read_binary_uint16(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_uint16(Stream, Result, !IO).
+
+read_binary_uint16(Stream, Result, !IO) :-
+    ( if native_byte_order_is_big_endian then
+        read_binary_uint16_be(Stream, Result, !IO)
+    else
+        read_binary_uint16_le(Stream, Result, !IO)
+    ).
+
+%---------------------%
+
+read_binary_uint16_le(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_uint16_le(Stream, Result, !IO).
+
+read_binary_uint16_le(binary_input_stream(Stream), Result, !IO) :-
+    do_read_binary_uint16(Stream, little_endian, ResultCode, UInt16,
+        IncompleteBytes, Error, !IO),
+    (
+        ResultCode = mirc_ok,
+        Result = ok(UInt16)
+    ;
+        ResultCode = mirc_eof,
+        Result = eof
+    ;
+        ResultCode = mirc_incomplete,
+        Result = incomplete(IncompleteBytes)
+    ;
+        ResultCode = mirc_error,
+        make_err_msg(Error, "read failed: ", Msg),
+        Result = error(io_error(Msg))
+    ).
+
+%---------------------%
+
+read_binary_uint16_be(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_uint16_be(Stream, Result, !IO).
+
+read_binary_uint16_be(binary_input_stream(Stream), Result, !IO) :-
+    do_read_binary_uint16(Stream, big_endian, ResultCode, UInt16,
+        IncompleteBytes, Error, !IO),
+    (
+        ResultCode = mirc_ok,
+        Result = ok(UInt16)
+    ;
+        ResultCode = mirc_eof,
+        Result = eof
+    ;
+        ResultCode = mirc_incomplete,
+        Result = incomplete(IncompleteBytes)
+    ;
+        ResultCode = mirc_error,
+        make_err_msg(Error, "read failed: ", Msg),
+        Result = error(io_error(Msg))
+    ).
+
+:- pred do_read_binary_uint16(stream::in, byte_order::in,
+    maybe_incomplete_result_code::out, uint16::out, list(uint8)::out,
+    system_error::out, io::di, io::uo) is det.
+
+:- pragma foreign_proc("C",
+    do_read_binary_uint16(Stream::in, ByteOrder::in, ResultCode::out,
+        UInt16::out, IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        tabled_for_io],
+"
+    ML_do_read_binary_uintN(2, 16, Stream, ByteOrder, ResultCode, UInt16,
+        IncompleteBytes, Error);
+").
+
+:- pragma foreign_proc("C#",
+    do_read_binary_uint16(Stream::in, ByteOrder::in, Result::out,
+        UInt16::out, IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    byte[] buffer = new byte[2];
+    io.MR_MercuryFileStruct mf = Stream;
+    UInt16 = 0;
+    IncompleteBytes = list.empty_list();
+
+    int nread = 0;
+
+    if (mf.putback != -1) {
+        buffer[nread] = (byte) mf.putback;
+        nread++;
+        mf.putback = -1;
+    }
+
+    try {
+        for ( ; nread < 2; nread++) {
+            int b = mf.stream.ReadByte();
+            if (b == -1) {
+                break;
+            }
+            buffer[nread] = (byte) b;
+        }
+        if (nread < 2) {
+            if (nread > 0) {
+                Result = io.ML_MIRC_INCOMPLETE;
+                IncompleteBytes = list.cons(buffer[0], IncompleteBytes);
+            } else {
+                Result = io.ML_MIRC_EOF;
+            }
+        } else {
+            Result = io.ML_MIRC_OK;
+            if (ByteOrder == io.ML_LITTLE_ENDIAN) {
+                UInt16 = (ushort) (buffer[1] << 8 | (buffer[0] & 0x00ff));
+            } else {
+                UInt16 = (ushort) (buffer[0] << 8 | (buffer[1] & 0x00ff));
+            }
+        }
+        Error = null;
+    } catch (System.Exception e) {
+        Result = io.ML_MIRC_ERROR;
+        Error = e;
+    }
+").
+
+:- pragma foreign_proc("Java",
+    do_read_binary_uint16(Stream::in, ByteOrder::in, Result::out,
+        UInt16::out, IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    byte[] buffer = new byte[2];
+    MR_BinaryInputFile mf = (MR_BinaryInputFile) Stream;
+    UInt16 = 0;
+    IncompleteBytes = list.empty_list();
+
+    try {
+        int nread;
+        for (nread = 0; nread < 2; nread++) {
+            int next = mf.read_byte();
+            if (next == -1) {
+                break;
+            }
+            buffer[nread] = (byte) next;
+        }
+        if (nread < 2) {
+            if (nread > 0) {
+                Result = io.ML_MIRC_INCOMPLETE;
+                IncompleteBytes = list.cons(buffer[0], IncompleteBytes);
+            } else {
+                Result = io.ML_MIRC_EOF;
+            }
+        } else {
+            Result = io.ML_MIRC_OK;
+            if (ByteOrder == io.ML_LITTLE_ENDIAN) {
+                UInt16 = (short) (buffer[1] << 8 | (buffer[0] & 0x00ff));
+            } else {
+                UInt16 = (short) (buffer[0] << 8 | (buffer[1] & 0x00ff));
+            }
+        }
+        Error = null;
+    } catch (java.lang.Exception e) {
+        Result = io.ML_MIRC_ERROR;
+        Error = e;
+    }
+").
+
+do_read_binary_uint16(_, _, _, _, _, _, _, _) :-
+    sorry($module, "do_read_binary_uint16 NYI for Erlang").
+
+%---------------------%
+
+read_binary_int32(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_int32(Stream, Result, !IO).
+
+read_binary_int32(Stream, Result, !IO) :-
+    ( if native_byte_order_is_big_endian then
+        read_binary_int32_be(Stream, Result, !IO)
+    else
+        read_binary_int32_le(Stream, Result, !IO)
+    ).
+
+%---------------------%
+
+read_binary_int32_le(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_int32_le(Stream, Result, !IO).
+
+read_binary_int32_le(binary_input_stream(Stream), Result, !IO) :-
+    do_read_binary_uint32(Stream, little_endian, ResultCode, UInt32,
+        IncompleteBytes, Error, !IO),
+    (
+        ResultCode = mirc_ok,
+        Int32 = cast_from_uint32(UInt32),
+        Result = ok(Int32)
+    ;
+        ResultCode = mirc_eof,
+        Result = eof
+    ;
+        ResultCode = mirc_incomplete,
+        Result = incomplete(IncompleteBytes)
+    ;
+        ResultCode = mirc_error,
+        make_err_msg(Error, "read failed: ", Msg),
+        Result = error(io_error(Msg))
+    ).
+
+%---------------------%
+
+read_binary_int32_be(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_int32_be(Stream, Result, !IO).
+
+read_binary_int32_be(binary_input_stream(Stream), Result, !IO) :-
+    do_read_binary_uint32(Stream, big_endian, ResultCode, UInt32,
+        IncompleteBytes, Error, !IO),
+    (
+        ResultCode = mirc_ok,
+        Int32 = cast_from_uint32(UInt32),
+        Result = ok(Int32)
+    ;
+        ResultCode = mirc_eof,
+        Result = eof
+    ;
+        ResultCode = mirc_incomplete,
+        Result = incomplete(IncompleteBytes)
+    ;
+        ResultCode = mirc_error,
+        make_err_msg(Error, "read failed: ", Msg),
+        Result = error(io_error(Msg))
+    ).
+
+%---------------------%
+
+read_binary_uint32(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_uint32(Stream, Result, !IO).
+
+read_binary_uint32(Stream, Result, !IO) :-
+    ( if native_byte_order_is_big_endian then
+        read_binary_uint32_be(Stream, Result, !IO)
+    else
+        read_binary_uint32_le(Stream, Result, !IO)
+    ).
+
+%---------------------%
+
+read_binary_uint32_le(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_uint32_le(Stream, Result, !IO).
+
+read_binary_uint32_le(binary_input_stream(Stream), Result, !IO) :-
+    do_read_binary_uint32(Stream, little_endian, ResultCode, UInt32,
+        IncompleteBytes, Error, !IO),
+    (
+        ResultCode = mirc_ok,
+        Result = ok(UInt32)
+    ;
+        ResultCode = mirc_eof,
+        Result = eof
+    ;
+        ResultCode = mirc_incomplete,
+        Result = incomplete(IncompleteBytes)
+    ;
+        ResultCode = mirc_error,
+        make_err_msg(Error, "read failed: ", Msg),
+        Result = error(io_error(Msg))
+    ).
+
+%---------------------%
+
+read_binary_uint32_be(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_uint32_be(Stream, Result, !IO).
+
+read_binary_uint32_be(binary_input_stream(Stream), Result, !IO) :-
+    do_read_binary_uint32(Stream, big_endian, ResultCode, UInt32,
+        IncompleteBytes, Error, !IO),
+    (
+        ResultCode = mirc_ok,
+        Result = ok(UInt32)
+    ;
+        ResultCode = mirc_eof,
+        Result = eof
+    ;
+        ResultCode = mirc_incomplete,
+        Result = incomplete(IncompleteBytes)
+    ;
+        ResultCode = mirc_error,
+        make_err_msg(Error, "read failed: ", Msg),
+        Result = error(io_error(Msg))
+    ).
+
+%---------------------%
+
+:- pred do_read_binary_uint32(stream::in, byte_order::in,
+    maybe_incomplete_result_code::out, uint32::out, list(uint8)::out,
+    system_error::out, io::di, io::uo) is det.
+
+:- pragma foreign_proc("C",
+    do_read_binary_uint32(Stream::in, ByteOrder::in, ResultCode::out,
+        UInt32::out, IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        tabled_for_io],
+"
+    ML_do_read_binary_uintN(4, 32, Stream, ByteOrder, ResultCode, UInt32,
+        IncompleteBytes, Error);
+").
+
+:- pragma foreign_proc("C#",
+    do_read_binary_uint32(Stream::in, ByteOrder::in, ResultCode::out,
+        UInt32::out, IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    byte[] buffer = new byte[4];
+    io.MR_MercuryFileStruct mf = Stream;
+    UInt32 = 0;
+    IncompleteBytes = list.empty_list();
+
+    int nread = 0;
+
+    if (mf.putback != -1) {
+        buffer[nread] = (byte) mf.putback;
+        nread++;
+        mf.putback = -1;
+    }
+
+    try {
+        for ( ; nread < 4; nread++) {
+            int b = mf.stream.ReadByte();
+            if (b == -1) {
+                break;
+            }
+            buffer[nread] = (byte) b;
+        }
+        if (nread < 4) {
+            if (nread > 0) {
+                ResultCode = io.ML_MIRC_INCOMPLETE;
+                for (int i = nread - 1; i >= 0; i--) {
+                    IncompleteBytes = list.cons(buffer[i], IncompleteBytes);
+                }
+            } else {
+                ResultCode = io.ML_MIRC_EOF;
+            }
+        } else {
+            ResultCode = io.ML_MIRC_OK;
+            if (ByteOrder == io.ML_LITTLE_ENDIAN) {
+                UInt32 = (uint) (
+                    buffer[3] << 24 |
+                    buffer[2] << 16 |
+                    buffer[1] << 8  |
+                    buffer[0]);
+            } else {
+                UInt32 = (uint) (
+                    buffer[0] << 24 |
+                    buffer[1] << 16 |
+                    buffer[2] << 8  |
+                    buffer[3]);
+            }
+        }
+        Error = null;
+    } catch (System.Exception e) {
+        ResultCode = io.ML_MIRC_ERROR;
+        Error = e;
+    }
+").
+
+:- pragma foreign_proc("Java",
+    do_read_binary_uint32(Stream::in, ByteOrder::in, ResultCode::out,
+        UInt32::out, IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    byte[] buffer = new byte[4];
+    MR_BinaryInputFile mf = (MR_BinaryInputFile) Stream;
+    UInt32 = 0;
+    IncompleteBytes = list.empty_list();
+
+    try {
+        int nread;
+        for (nread = 0; nread < 4; nread++) {
+            int next = mf.read_byte();
+            if (next == -1) {
+                break;
+            }
+            buffer[nread] = (byte) next;
+        }
+        if (nread < 4) {
+            if (nread > 0) {
+                ResultCode = io.ML_MIRC_INCOMPLETE;
+                for (int i = nread - 1; i >= 0; i--) {
+                    IncompleteBytes = list.cons(buffer[i], IncompleteBytes);
+                }
+            } else {
+                ResultCode = io.ML_MIRC_EOF;
+            }
+        } else {
+            ResultCode = io.ML_MIRC_OK;
+            if (ByteOrder == io.ML_LITTLE_ENDIAN) {
+                UInt32 =
+                    (buffer[3] & 0xff) << 24 |
+                    (buffer[2] & 0xff) << 16 |
+                    (buffer[1] & 0xff) << 8  |
+                    (buffer[0] & 0xff);
+            } else {
+                UInt32 =
+                    (buffer[0] & 0xff) << 24 |
+                    (buffer[1] & 0xff) << 16 |
+                    (buffer[2] & 0xff) << 8  |
+                    (buffer[3] & 0xff);
+            }
+        }
+        Error = null;
+    } catch (java.lang.Exception e) {
+        ResultCode = io.ML_MIRC_ERROR;
+        Error = e;
+    }
+").
+
+do_read_binary_uint32(_, _, _, _, _, _, _, _) :-
+    sorry($module, "do_read_binary_uint32 NYI for Erlang").
+
+%---------------------%
+
+read_binary_int64(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_int64(Stream, Result, !IO).
+
+read_binary_int64(Stream, Result, !IO) :-
+    ( if native_byte_order_is_big_endian then
+        read_binary_int64_be(Stream, Result, !IO)
+    else
+        read_binary_int64_le(Stream, Result, !IO)
+    ).
+
+%---------------------%
+
+read_binary_int64_le(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_int64_le(Stream, Result, !IO).
+
+read_binary_int64_le(binary_input_stream(Stream), Result, !IO) :-
+    do_read_binary_uint64(Stream, little_endian, ResultCode, UInt64,
+        IncompleteBytes, Error, !IO),
+    (
+        ResultCode = mirc_ok,
+        Int64 = cast_from_uint64(UInt64),
+        Result = ok(Int64)
+    ;
+        ResultCode = mirc_eof,
+        Result = eof
+    ;
+        ResultCode = mirc_incomplete,
+        Result = incomplete(IncompleteBytes)
+    ;
+        ResultCode = mirc_error,
+        make_err_msg(Error, "read failed: ", Msg),
+        Result = error(io_error(Msg))
+    ).
+
+%---------------------%
+
+read_binary_int64_be(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_int64_be(Stream, Result, !IO).
+
+read_binary_int64_be(binary_input_stream(Stream), Result, !IO) :-
+    do_read_binary_uint64(Stream, big_endian, ResultCode, UInt64,
+        IncompleteBytes, Error, !IO),
+    (
+        ResultCode = mirc_ok,
+        Int64 = cast_from_uint64(UInt64),
+        Result = ok(Int64)
+    ;
+        ResultCode = mirc_eof,
+        Result = eof
+    ;
+        ResultCode = mirc_incomplete,
+        Result = incomplete(IncompleteBytes)
+    ;
+        ResultCode = mirc_error,
+        make_err_msg(Error, "read failed: ", Msg),
+        Result = error(io_error(Msg))
+    ).
+
+%---------------------%
+
+read_binary_uint64(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_uint64(Stream, Result, !IO).
+
+read_binary_uint64(Stream, Result, !IO) :-
+    ( if native_byte_order_is_big_endian then
+        read_binary_uint64_be(Stream, Result, !IO)
+    else
+        read_binary_uint64_le(Stream, Result, !IO)
+    ).
+
+%---------------------%
+
+read_binary_uint64_le(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_uint64_le(Stream, Result, !IO).
+
+read_binary_uint64_le(binary_input_stream(Stream), Result, !IO) :-
+    do_read_binary_uint64(Stream, little_endian, ResultCode, UInt64,
+        IncompleteBytes, Error, !IO),
+    (
+        ResultCode = mirc_ok,
+        Result = ok(UInt64)
+    ;
+        ResultCode = mirc_eof,
+        Result = eof
+    ;
+        ResultCode = mirc_incomplete,
+        Result = incomplete(IncompleteBytes)
+    ;
+        ResultCode = mirc_error,
+        make_err_msg(Error, "read failed: ", Msg),
+        Result = error(io_error(Msg))
+    ).
+
+%---------------------%
+
+read_binary_uint64_be(Result, !IO) :-
+    binary_input_stream(Stream, !IO),
+    read_binary_uint64_be(Stream, Result, !IO).
+
+read_binary_uint64_be(binary_input_stream(Stream), Result, !IO) :-
+    do_read_binary_uint64(Stream, big_endian, ResultCode, UInt64,
+        IncompleteBytes, Error, !IO),
+    (
+        ResultCode = mirc_ok,
+        Result = ok(UInt64)
+    ;
+        ResultCode = mirc_eof,
+        Result = eof
+    ;
+        ResultCode = mirc_incomplete,
+        Result = incomplete(IncompleteBytes)
+    ;
+        ResultCode = mirc_error,
+        make_err_msg(Error, "read failed: ", Msg),
+        Result = error(io_error(Msg))
+    ).
+
+%---------------------%
+
+:- pred do_read_binary_uint64(stream::in, byte_order::in,
+    maybe_incomplete_result_code::out, uint64::out, list(uint8)::out,
+    system_error::out, io::di, io::uo) is det.
+
+:- pragma foreign_proc("C",
+    do_read_binary_uint64(Stream::in, ByteOrder::in, ResultCode::out,
+        UInt64::out, IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        tabled_for_io],
+"
+    ML_do_read_binary_uintN(8, 64, Stream, ByteOrder, ResultCode, UInt64,
+        IncompleteBytes, Error);
+").
+
+:- pragma foreign_proc("C#",
+    do_read_binary_uint64(Stream::in, ByteOrder::in, ResultCode::out,
+        UInt64::out, IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    byte[] buffer = new byte[8];
+    io.MR_MercuryFileStruct mf = Stream;
+    UInt64 = 0;
+    IncompleteBytes = list.empty_list();
+
+    int nread = 0;
+
+    if (mf.putback != -1) {
+        buffer[nread] = (byte) mf.putback;
+        nread++;
+        mf.putback = -1;
+    }
+
+    try {
+        for ( ; nread < 8; nread++) {
+            int b = mf.stream.ReadByte();
+            if (b == -1) {
+                break;
+            }
+            buffer[nread] = (byte) b;
+        }
+        if (nread < 8) {
+            if (nread > 0) {
+                ResultCode = io.ML_MIRC_INCOMPLETE;
+                for (int i = nread - 1; i >=0; i--) {
+                    IncompleteBytes = list.cons(buffer[i], IncompleteBytes);
+                }
+            } else {
+                ResultCode = io.ML_MIRC_EOF;
+            }
+        } else {
+            ResultCode = io.ML_MIRC_OK;
+            if (ByteOrder == io.ML_LITTLE_ENDIAN) {
+                UInt64 = (ulong) (
+                    (ulong) buffer[7] << 56 |
+                    (ulong) buffer[6] << 48 |
+                    (ulong) buffer[5] << 40 |
+                    (ulong) buffer[4] << 32 |
+                    (ulong) buffer[3] << 24 |
+                    (ulong) buffer[2] << 16 |
+                    (ulong) buffer[1] << 8  |
+                    (ulong) buffer[0]);
+            } else {
+                UInt64 = (ulong) (
+                    (ulong) buffer[0] << 56 |
+                    (ulong) buffer[1] << 48 |
+                    (ulong) buffer[2] << 40 |
+                    (ulong) buffer[3] << 32 |
+                    (ulong) buffer[4] << 24 |
+                    (ulong) buffer[5] << 16 |
+                    (ulong) buffer[6] << 8  |
+                    (ulong) buffer[7]);
+            }
+        }
+        Error = null;
+    } catch (System.Exception e) {
+        ResultCode = io.ML_MIRC_ERROR;
+        Error = e;
+    }
+").
+
+:- pragma foreign_proc("Java",
+    do_read_binary_uint64(Stream::in, ByteOrder::in, ResultCode::out,
+        UInt64::out, IncompleteBytes::out, Error::out, _IO0::di, _IO::uo),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    byte[] buffer = new byte[8];
+    MR_BinaryInputFile mf = (MR_BinaryInputFile) Stream;
+    UInt64 = 0;
+    IncompleteBytes = list.empty_list();
+
+    try {
+        int nread;
+        for (nread = 0; nread < 8; nread++) {
+            int next = mf.read_byte();
+            if (next == -1) {
+                break;
+            }
+            buffer[nread] = (byte) next;
+        }
+        if (nread < 8) {
+            if (nread > 0) {
+                ResultCode = io.ML_MIRC_INCOMPLETE;
+                for (int i = nread - 1; i >= 0; i--) {
+                    IncompleteBytes = list.cons(buffer[i], IncompleteBytes);
+                }
+            } else {
+                ResultCode = io.ML_MIRC_EOF;
+            }
+        } else {
+            ResultCode = io.ML_MIRC_OK;
+            if (ByteOrder == io.ML_LITTLE_ENDIAN) {
+                UInt64 =
+                    (long) (buffer[7] & 0xff) << 56 |
+                    (long) (buffer[6] & 0xff) << 48 |
+                    (long) (buffer[5] & 0xff) << 40 |
+                    (long) (buffer[4] & 0xff) << 32 |
+                    (long) (buffer[3] & 0xff) << 24 |
+                    (long) (buffer[2] & 0xff) << 16 |
+                    (long) (buffer[1] & 0xff) << 8  |
+                    (long) (buffer[0] & 0xff);
+            } else {
+                UInt64 =
+                    (long) (buffer[0] & 0xff) << 56 |
+                    (long) (buffer[1] & 0xff) << 48 |
+                    (long) (buffer[2] & 0xff) << 40 |
+                    (long) (buffer[3] & 0xff) << 32 |
+                    (long) (buffer[4] & 0xff) << 24 |
+                    (long) (buffer[5] & 0xff) << 16 |
+                    (long) (buffer[6] & 0xff) << 8  |
+                    (long) (buffer[7] & 0xff);
+            }
+        }
+        Error = null;
+    } catch (java.lang.Exception e) {
+        ResultCode = io.ML_MIRC_ERROR;
+        Error = e;
+    }
+").
+
+do_read_binary_uint64(_, _, _, _, _, _, _, _) :-
+    sorry($module, "do_read_binary_uint64_le NYI for Erlang").
+
+%---------------------%
+
+:- type byte_order
+    --->    big_endian
+    ;       little_endian.
+
+:- pragma foreign_export_enum("C", byte_order/0,
+    [prefix("ML_"), uppercase]).
+:- pragma foreign_export_enum("Java", byte_order/0,
+    [prefix("ML_"), uppercase]).
+:- pragma foreign_export_enum("C#", byte_order/0,
+    [prefix("ML_"), uppercase]).
+
+:- pred native_byte_order_is_big_endian is semidet.
+
+:- pragma foreign_proc("C",
+    native_byte_order_is_big_endian,
+    [promise_pure, will_not_call_mercury, thread_safe],
+"
+    #if defined(MR_BIG_ENDIAN)
+        SUCCESS_INDICATOR = MR_TRUE;
+    #else
+        SUCCESS_INDICATOR = MR_FALSE;
+    #endif
+").
+
+:- pragma foreign_proc("C#",
+    native_byte_order_is_big_endian,
+    [promise_pure, will_not_call_mercury, thread_safe],
+"
+    SUCCESS_INDICATOR = !(BitConverter.IsLittleEndian);
+").
+
+:- pragma foreign_proc("Java",
+    native_byte_order_is_big_endian,
+    [promise_pure, will_not_call_mercury, thread_safe],
+"
+    SUCCESS_INDICATOR =
+        (java.nio.ByteOrder.nativeOrder() == java.nio.ByteOrder.BIG_ENDIAN);
+").
+
+native_byte_order_is_big_endian :-
+    sorry($module,
+        "native_byte_order_is_big_endian/0 NYI for Erlang").
+
+%---------------------%
+%
+% C implementation of reading multibyte integers from binary streams.
+%
+
+:- pragma foreign_decl("C", "
+
+// ML_N_BIT_INT_T(n) expands to the name of an n-bit unsigned integer type in
+// C.
+//
+#define ML_N_BIT_INT_T(n) \
+    MR_PASTE3(uint, n, _t)
+
+// ML_REVERSE_BYTES_FUNC(n) expands to the name a function exported by the
+// Mercury runtime that can be used to reverse the bytes in an n-bit
+// unsigned integer.
+//
+#define ML_REVERSE_BYTES_FUNC(n) \
+    MR_PASTE3(MR_uint, n, _reverse_bytes)
+
+// ML_build_uintN(int n, MR_Word byte_order, unsigned char *buffer,
+//     uintN_t value):
+//
+// Build an n-bit unsigned integer using the bytes stored in the array
+// 'buffer'.  The order of the bytes in the buffer are given by 'byte_order'.
+// The result is assigned to the lvalue 'value'
+//
+// We have two definitions of this macro, one for big-endian machines
+// and one for little-endian machines.
+//
+#if defined(MR_BIG_ENDIAN)
+#define ML_build_uintN(n, byte_order, buffer, value)                 \
+    do {                                                             \
+        if (byte_order == ML_LITTLE_ENDIAN) {                        \
+            value = ML_REVERSE_BYTES_FUNC(n)(                        \
+                *((ML_N_BIT_INT_T(n) *) buffer));                    \
+        } else {                                                     \
+            value = *((ML_N_BIT_INT_T(n) *) buffer);                 \
+        }                                                            \
+    } while (0)
+#else
+#define ML_build_uintN(n, byte_order, buffer, value)                 \
+    do {                                                             \
+        if (byte_order == ML_LITTLE_ENDIAN) {                        \
+            value = *((ML_N_BIT_INT_T(n) *) buffer);                 \
+        } else {                                                     \
+            value = ML_REVERSE_BYTES_FUNC(n)(                        \
+                *((ML_N_BIT_INT_T(n) *) buffer));                    \
+        }                                                            \
+    } while (0)
+#endif
+
+// ML_do_read_binary_uintN(int nbytes, int nbits, MR_Word stream,
+//     MR_Word byte_order, MR_Word result_code, MR_Word result_value,
+//     MR_Word result_incomplete, MR_Word result_error):
+//
+// This macro implements the do_read_binary_uint{16 32,64}/8 predicates.
+// It expands to code for reading an 'nbits'-bit ('nbytes'-byte) unsigned
+// integer from the binary stream 'stream', with the bytes in the stream
+// being in 'byte_order' order.
+//
+// The result is returned as follows:
+//
+// 'result_code' is set the status code (maybe_incomplete_result_code/0)
+// for the read.
+// 'result_value' is the value of the integer read on a successful read
+// or zero otherwise.
+// 'result_incomplete' is the list of bytes read so far for an incomplete
+// read or the empty list otherwise.
+// 'result_error' is the errno if an I/O error occurs or zero otherwise.
+//
+#define ML_do_read_binary_uintN(nbytes, nbits, stream, byte_order,           \
+       result_code, result_value, result_incomplete, result_error)           \
+    do {                                                                     \
+        unsigned char buffer[nbytes];                                        \
+        size_t nread = MR_READ(*stream, buffer, nbytes);                     \
+        result_incomplete = MR_list_empty();                                 \
+                                                                             \
+        if (nread < nbytes) {                                                \
+            result_value = 0;                                                \
+            if (MR_FERROR(*Stream)) {                                        \
+                result_code = ML_MIRC_ERROR,                                 \
+                result_error = errno;                                        \
+            } else if (nread > 0) {                                          \
+                int i;                                                       \
+                result_code = ML_MIRC_INCOMPLETE;                            \
+                for (i = nread - 1; i >= 0; i--) {                           \
+                    result_incomplete =                                      \
+                        MR_list_cons(buffer[i],                              \
+                        result_incomplete);                                  \
+                }                                                            \
+                result_error = 0;                                            \
+            } else {                                                         \
+                result_code = ML_MIRC_EOF;                                   \
+                result_error = 0;                                            \
+            }                                                                \
+        } else {                                                             \
+            result_code = ML_MIRC_OK;                                        \
+            ML_build_uintN(nbits, byte_order, buffer, result_value);         \
+            result_error = 0;                                                \
+        }                                                                    \
+    } while (0)
+").
+
+%---------------------%
+
 read_bitmap(!Bitmap, BytesRead, Result, !IO) :-
     binary_input_stream(Stream, !IO),
     read_bitmap(Stream, !Bitmap, BytesRead, Result, !IO).
@@ -3068,6 +4111,7 @@ binary_input_stream_file_size(binary_input_stream(Stream), Size, !IO) :-
 #endif
 #include ""mercury_types.h""            // for MR_Integer
 #include ""mercury_library_types.h""    // for MercuryFilePtr
+#include ""mercury_int.h""              // for MR_*_reverse_bytes
 ").
 
 :- pragma foreign_proc("C",
diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile
index 78f7ef5..d99efb8 100644
--- a/tests/hard_coded/Mmakefile
+++ b/tests/hard_coded/Mmakefile
@@ -303,6 +303,12 @@ ORDINARY_PROGS = \
 	random_permutation \
 	random_simple \
 	rational_test \
+	read_binary_int16 \
+	read_binary_int32 \
+	read_binary_int64 \
+	read_binary_uint16 \
+	read_binary_uint32 \
+	read_binary_uint64 \
 	read_min_int \
 	recursive_main \
 	redoip_clobber \
diff --git a/tests/hard_coded/read_binary_int16.exp b/tests/hard_coded/read_binary_int16.exp
new file mode 100644
index 0000000..c54276a
--- /dev/null
+++ b/tests/hard_coded/read_binary_int16.exp
@@ -0,0 +1,36 @@
+================
+Input: []
+Result: EOF (read big-endian)
+================
+Input: [0u8]
+Result: Incomplete ([0u8]) (read big-endian)
+================
+Input: [1u8, 0u8] (LE: 1) (BE: 256)
+Result: 256i16 (read big-endian)
+================
+Input: [255u8, 0u8] (LE: 255) (BE: -256)
+Result: -256i16 (read big-endian)
+================
+Input: []
+Result: EOF (read little-endian)
+================
+Input: [0u8]
+Result: Incomplete ([0u8]) (read little-endian)
+================
+Input: [1u8, 0u8] (LE: 1) (BE: 256)
+Result: 1i16 (read little-endian)
+================
+Input: [255u8, 0u8] (LE: 255) (BE: -256)
+Result: 255i16 (read little-endian)
+================
+Input: []
+Result: EOF (read native byte order)
+================
+Input: [0u8]
+Result: Incomplete ([0u8]) (read native byte order)
+================
+Input: [1u8, 0u8] (LE: 1) (BE: 256)
+Result: 1i16 (read native byte order)
+================
+Input: [255u8, 0u8] (LE: 255) (BE: -256)
+Result: 255i16 (read native byte order)
diff --git a/tests/hard_coded/read_binary_int16.m b/tests/hard_coded/read_binary_int16.m
new file mode 100644
index 0000000..bdbe134
--- /dev/null
+++ b/tests/hard_coded/read_binary_int16.m
@@ -0,0 +1,144 @@
+%---------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et
+%---------------------------------------------------------------------------%
+%
+% Test reading of binary int16s.
+%
+%---------------------------------------------------------------------------%
+
+:- module read_binary_int16.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module list.
+:- import_module string.
+:- import_module int16.
+
+%---------------------------------------------------------------------------%
+
+main(!IO) :-
+    list.foldl(run_test(big_endian), test_cases, !IO),
+    list.foldl(run_test(little_endian), test_cases, !IO),
+    list.foldl(run_test(native), test_cases, !IO).
+
+:- pred run_test(byte_order::in, test_case::in, io::di, io::uo) is det.
+
+run_test(ByteOrder, TestBytes, !IO) :-
+    io.remove_file(test_file, _, !IO),
+    io.write_string("================\n", !IO),
+    io.open_binary_output(test_file, OpenOutResult, !IO),
+    (
+        OpenOutResult = ok(OutFile),
+        io.write_string("Input: ", !IO),
+        io.write(TestBytes, !IO),
+        (
+            ( TestBytes = []
+            ; TestBytes = [_]
+            ; TestBytes = [_, _, _ | _]
+            ),
+            io.nl(!IO)
+        ;
+            TestBytes = [Byte1, Byte2],
+            io.write_string(" (LE: ", !IO),
+            io.write_int16(from_bytes_le(Byte1, Byte2), !IO),
+            io.write_string(") (BE: ", !IO),
+            io.write_int16(from_bytes_be(Byte1, Byte2), !IO),
+            io.write_string(")\n", !IO)
+        ),
+        list.foldl(write_binary_uint8(OutFile), TestBytes, !IO),
+        io.close_binary_output(OutFile, !IO),
+        io.open_binary_input(test_file, OpenInResult, !IO),
+        (
+            OpenInResult = ok(InFile),
+            (
+                ByteOrder = big_endian,
+                read_binary_int16_be(InFile, ReadResult, !IO)
+            ;
+                ByteOrder = little_endian,
+                read_binary_int16_le(InFile, ReadResult, !IO)
+            ;
+                ByteOrder = native,
+                read_binary_int16(InFile, ReadResult, !IO)
+            ),
+            io.close_binary_input(InFile, !IO),
+            (
+                ReadResult = ok(ResultUInt16),
+                io.write_string("Result: ", !IO),
+                io.write(ResultUInt16, !IO),
+                io.write_string(" (", !IO),
+                describe_byte_order(ByteOrder, !IO),
+                io.write_string(")\n", !IO)
+            ;
+                ReadResult = eof,
+                io.write_string("Result: EOF (", !IO),
+                describe_byte_order(ByteOrder, !IO),
+                io.write_string(")\n", !IO)
+            ;
+                ReadResult = incomplete(Bytes),
+                io.format("Result: Incomplete (%s) (", [s(string(Bytes))],
+                    !IO),
+                describe_byte_order(ByteOrder, !IO),
+                io.write_string(")\n", !IO)
+            ;
+                ReadResult = error(IO_Error),
+                io.format("Result: Error (%s)\n", [s(io.error_message(IO_Error))],
+                    !IO)
+            ),
+            io.remove_file(test_file, _, !IO)
+        ;
+            OpenInResult = error(IO_Error),
+            io.format("I/O ERROR: %s\n", [s(io.error_message(IO_Error))],
+                !IO)
+        )
+    ;
+        OpenOutResult = error(IO_Error),
+        io.format("I/O ERROR: %s\n", [s(io.error_message(IO_Error))], !IO)
+    ).
+
+:- pred describe_byte_order(byte_order::in, io::di, io::uo) is det.
+
+describe_byte_order(ByteOrder, !IO) :-
+    (
+        ByteOrder = big_endian,
+        io.write_string("read big-endian", !IO)
+    ;
+        ByteOrder = little_endian,
+        io.write_string("read little-endian", !IO)
+    ;
+        ByteOrder = native,
+        io.write_string("read native byte order", !IO)
+    ).
+
+:- func test_file = string.
+
+test_file = "read_binary_int16.bin".
+
+%---------------------------------------------------------------------------%
+
+:- type byte_order
+    --->    big_endian
+    ;       little_endian
+    ;       native.
+
+:- type test_case == list(uint8).
+
+:- func test_cases = list(test_case).
+
+test_cases = [
+    [],
+    [0u8],
+    [1u8, 0u8],
+    [0xffu8, 0u8]
+].
+
+%---------------------------------------------------------------------------%
+:- end_module read_binary_int16.
+%---------------------------------------------------------------------------%
diff --git a/tests/hard_coded/read_binary_int32.exp b/tests/hard_coded/read_binary_int32.exp
new file mode 100644
index 0000000..26cf1b9
--- /dev/null
+++ b/tests/hard_coded/read_binary_int32.exp
@@ -0,0 +1,63 @@
+================
+Input: []
+Result: EOF (read big-endian)
+================
+Input: [1u8]
+Result: Incomplete ([1u8]) (read big-endian)
+================
+Input: [1u8, 2u8]
+Result: Incomplete ([1u8, 2u8]) (read big-endian)
+================
+Input: [1u8, 2u8, 3u8]
+Result: Incomplete ([1u8, 2u8, 3u8]) (read big-endian)
+================
+Input: [0u8, 0u8, 0u8, 1u8] (LE: 16777216) (BE: 1)
+Result: 1i32 (read big-endian)
+================
+Input: [255u8, 0u8, 0u8, 0u8] (LE: 255) (BE: -16777216)
+Result: -16777216i32 (read big-endian)
+================
+Input: [1u8, 1u8, 0u8, 0u8] (LE: 257) (BE: 16842752)
+Result: 16842752i32 (read big-endian)
+================
+Input: []
+Result: EOF (read little-endian)
+================
+Input: [1u8]
+Result: Incomplete ([1u8]) (read little-endian)
+================
+Input: [1u8, 2u8]
+Result: Incomplete ([1u8, 2u8]) (read little-endian)
+================
+Input: [1u8, 2u8, 3u8]
+Result: Incomplete ([1u8, 2u8, 3u8]) (read little-endian)
+================
+Input: [0u8, 0u8, 0u8, 1u8] (LE: 16777216) (BE: 1)
+Result: 16777216i32 (read little-endian)
+================
+Input: [255u8, 0u8, 0u8, 0u8] (LE: 255) (BE: -16777216)
+Result: 255i32 (read little-endian)
+================
+Input: [1u8, 1u8, 0u8, 0u8] (LE: 257) (BE: 16842752)
+Result: 257i32 (read little-endian)
+================
+Input: []
+Result: EOF (read native byte order)
+================
+Input: [1u8]
+Result: Incomplete ([1u8]) (read native byte order)
+================
+Input: [1u8, 2u8]
+Result: Incomplete ([1u8, 2u8]) (read native byte order)
+================
+Input: [1u8, 2u8, 3u8]
+Result: Incomplete ([1u8, 2u8, 3u8]) (read native byte order)
+================
+Input: [0u8, 0u8, 0u8, 1u8] (LE: 16777216) (BE: 1)
+Result: 16777216i32 (read native byte order)
+================
+Input: [255u8, 0u8, 0u8, 0u8] (LE: 255) (BE: -16777216)
+Result: 255i32 (read native byte order)
+================
+Input: [1u8, 1u8, 0u8, 0u8] (LE: 257) (BE: 16842752)
+Result: 257i32 (read native byte order)
diff --git a/tests/hard_coded/read_binary_int32.m b/tests/hard_coded/read_binary_int32.m
new file mode 100644
index 0000000..d750498
--- /dev/null
+++ b/tests/hard_coded/read_binary_int32.m
@@ -0,0 +1,142 @@
+%---------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et
+%---------------------------------------------------------------------------%
+%
+% Test reading of binary int32s.
+%
+%---------------------------------------------------------------------------%
+
+:- module read_binary_int32.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module list.
+:- import_module string.
+:- import_module int32.
+
+%---------------------------------------------------------------------------%
+
+main(!IO) :-
+    list.foldl(run_test(big_endian), test_cases, !IO),
+    list.foldl(run_test(little_endian), test_cases, !IO),
+    list.foldl(run_test(native), test_cases, !IO).
+
+:- pred run_test(byte_order::in, test_case::in, io::di, io::uo) is det.
+
+run_test(ByteOrder, TestBytes, !IO) :-
+    io.remove_file(test_file, _, !IO),
+    io.write_string("================\n", !IO),
+    io.open_binary_output(test_file, OpenOutResult, !IO),
+    (
+        OpenOutResult = ok(OutFile),
+        io.write_string("Input: ", !IO),
+        io.write(TestBytes, !IO),
+        ( if TestBytes = [Byte1, Byte2, Byte3, Byte4] then
+            io.write_string(" (LE: ", !IO),
+            io.write_int32(from_bytes_le(Byte1, Byte2, Byte3, Byte4), !IO),
+            io.write_string(") (BE: ", !IO),
+            io.write_int32(from_bytes_be(Byte1, Byte2, Byte3, Byte4), !IO),
+            io.write_string(")\n", !IO)
+        else
+            io.nl(!IO)
+        ),
+        list.foldl(write_binary_uint8(OutFile), TestBytes, !IO),
+        io.close_binary_output(OutFile, !IO),
+        io.open_binary_input(test_file, OpenInResult, !IO),
+        (
+            OpenInResult = ok(InFile),
+            (
+                ByteOrder = big_endian,
+                read_binary_int32_be(InFile, ReadResult, !IO)
+            ;
+                ByteOrder = little_endian,
+                read_binary_int32_le(InFile, ReadResult, !IO)
+            ;
+                ByteOrder = native,
+                read_binary_int32(InFile, ReadResult, !IO)
+            ),
+            io.close_binary_input(InFile, !IO),
+            (
+                ReadResult = ok(ResultUInt16),
+                io.write_string("Result: ", !IO),
+                io.write(ResultUInt16, !IO),
+                io.write_string(" (", !IO),
+                describe_byte_order(ByteOrder, !IO),
+                io.write_string(")\n", !IO)
+            ;
+                ReadResult = eof,
+                io.write_string("Result: EOF (", !IO),
+                describe_byte_order(ByteOrder, !IO),
+                io.write_string(")\n", !IO)
+            ;
+                ReadResult = incomplete(Bytes),
+                io.format("Result: Incomplete (%s) (", [s(string(Bytes))],
+                    !IO),
+                describe_byte_order(ByteOrder, !IO),
+                io.write_string(")\n", !IO)
+            ;
+                ReadResult = error(IO_Error),
+                io.format("Result: Error (%s)\n", [s(io.error_message(IO_Error))],
+                    !IO)
+            ),
+            io.remove_file(test_file, _, !IO)
+        ;
+            OpenInResult = error(IO_Error),
+            io.format("I/O ERROR: %s\n", [s(io.error_message(IO_Error))],
+                !IO)
+        )
+    ;
+        OpenOutResult = error(IO_Error),
+        io.format("I/O ERROR: %s\n", [s(io.error_message(IO_Error))], !IO)
+    ).
+
+:- pred describe_byte_order(byte_order::in, io::di, io::uo) is det.
+
+describe_byte_order(ByteOrder, !IO) :-
+    (
+        ByteOrder = big_endian,
+        io.write_string("read big-endian", !IO)
+    ;
+        ByteOrder = little_endian,
+        io.write_string("read little-endian", !IO)
+    ;
+        ByteOrder = native,
+        io.write_string("read native byte order", !IO)
+    ).
+
+:- func test_file = string.
+
+test_file = "read_binary_int32.bin".
+
+%---------------------------------------------------------------------------%
+
+:- type byte_order
+    --->    big_endian
+    ;       little_endian
+    ;       native.
+
+:- type test_case == list(uint8).
+
+:- func test_cases = list(test_case).
+
+test_cases = [
+    [],
+    [1u8],
+    [1u8, 2u8],
+    [1u8, 2u8, 3u8],
+    [0u8, 0u8, 0u8, 1u8],
+    [0xffu8, 0u8, 0u8, 0u8],
+    [1u8, 1u8, 0u8, 0u8]
+].
+
+%---------------------------------------------------------------------------%
+:- end_module read_binary_int32.
+%---------------------------------------------------------------------------%
diff --git a/tests/hard_coded/read_binary_int64.exp b/tests/hard_coded/read_binary_int64.exp
new file mode 100644
index 0000000..e975eab
--- /dev/null
+++ b/tests/hard_coded/read_binary_int64.exp
@@ -0,0 +1,108 @@
+================
+Input: []
+Result: EOF (read big-endian)
+================
+Input: [1u8]
+Result: Incomplete ([1u8]) (read big-endian)
+================
+Input: [1u8, 2u8]
+Result: Incomplete ([1u8, 2u8]) (read big-endian)
+================
+Input: [1u8, 2u8, 3u8]
+Result: Incomplete ([1u8, 2u8, 3u8]) (read big-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8]) (read big-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8]) (read big-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8, 6u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8, 6u8]) (read big-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8, 7u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8, 7u8]) (read big-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8, 7u8, 8u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8, 7u8, 8u8]) (read big-endian)
+================
+Input: [0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8] (LE: 72057594037927936) (BE: 1)
+Result: 1i64 (read big-endian)
+================
+Input: [255u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] (LE: 255) (BE: -72057594037927936)
+Result: -72057594037927936i64 (read big-endian)
+================
+Input: [1u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] (LE: 257) (BE: 72339069014638592)
+Result: 72339069014638592i64 (read big-endian)
+================
+Input: []
+Result: EOF (read little-endian)
+================
+Input: [1u8]
+Result: Incomplete ([1u8]) (read little-endian)
+================
+Input: [1u8, 2u8]
+Result: Incomplete ([1u8, 2u8]) (read little-endian)
+================
+Input: [1u8, 2u8, 3u8]
+Result: Incomplete ([1u8, 2u8, 3u8]) (read little-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8]) (read little-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8]) (read little-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8, 6u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8, 6u8]) (read little-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8, 7u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8, 7u8]) (read little-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8, 7u8, 8u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8, 7u8, 8u8]) (read little-endian)
+================
+Input: [0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8] (LE: 72057594037927936) (BE: 1)
+Result: 72057594037927936i64 (read little-endian)
+================
+Input: [255u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] (LE: 255) (BE: -72057594037927936)
+Result: 255i64 (read little-endian)
+================
+Input: [1u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] (LE: 257) (BE: 72339069014638592)
+Result: 257i64 (read little-endian)
+================
+Input: []
+Result: EOF (read native byte order)
+================
+Input: [1u8]
+Result: Incomplete ([1u8]) (read native byte order)
+================
+Input: [1u8, 2u8]
+Result: Incomplete ([1u8, 2u8]) (read native byte order)
+================
+Input: [1u8, 2u8, 3u8]
+Result: Incomplete ([1u8, 2u8, 3u8]) (read native byte order)
+================
+Input: [1u8, 2u8, 3u8, 4u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8]) (read native byte order)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8]) (read native byte order)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8, 6u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8, 6u8]) (read native byte order)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8, 7u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8, 7u8]) (read native byte order)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8, 7u8, 8u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8, 7u8, 8u8]) (read native byte order)
+================
+Input: [0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8] (LE: 72057594037927936) (BE: 1)
+Result: 72057594037927936i64 (read native byte order)
+================
+Input: [255u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] (LE: 255) (BE: -72057594037927936)
+Result: 255i64 (read native byte order)
+================
+Input: [1u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] (LE: 257) (BE: 72339069014638592)
+Result: 257i64 (read native byte order)
diff --git a/tests/hard_coded/read_binary_int64.m b/tests/hard_coded/read_binary_int64.m
new file mode 100644
index 0000000..87cbc63
--- /dev/null
+++ b/tests/hard_coded/read_binary_int64.m
@@ -0,0 +1,149 @@
+%---------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et
+%---------------------------------------------------------------------------%
+%
+% Test reading of binary int64s.
+%
+%---------------------------------------------------------------------------%
+
+:- module read_binary_int64.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module list.
+:- import_module string.
+:- import_module int64.
+
+%---------------------------------------------------------------------------%
+
+main(!IO) :-
+    list.foldl(run_test(big_endian), test_cases, !IO),
+    list.foldl(run_test(little_endian), test_cases, !IO),
+    list.foldl(run_test(native), test_cases, !IO).
+
+:- pred run_test(byte_order::in, test_case::in, io::di, io::uo) is det.
+
+run_test(ByteOrder, TestBytes, !IO) :-
+    io.remove_file(test_file, _, !IO),
+    io.write_string("================\n", !IO),
+    io.open_binary_output(test_file, OpenOutResult, !IO),
+    (
+        OpenOutResult = ok(OutFile),
+        io.write_string("Input: ", !IO),
+        io.write(TestBytes, !IO),
+        ( if
+            TestBytes = [Byte1, Byte2, Byte3, Byte4, Byte5, Byte6, Byte7, Byte8]
+        then
+            io.write_string(" (LE: ", !IO),
+            io.write_int64(from_bytes_le(Byte1, Byte2, Byte3, Byte4, Byte5, Byte6, Byte7, Byte8), !IO),
+            io.write_string(") (BE: ", !IO),
+            io.write_int64(from_bytes_be(Byte1, Byte2, Byte3, Byte4, Byte5, Byte6, Byte7, Byte8), !IO),
+            io.write_string(")\n", !IO)
+        else
+            io.nl(!IO)
+        ),
+        list.foldl(write_binary_uint8(OutFile), TestBytes, !IO),
+        io.close_binary_output(OutFile, !IO),
+        io.open_binary_input(test_file, OpenInResult, !IO),
+        (
+            OpenInResult = ok(InFile),
+            (
+                ByteOrder = big_endian,
+                read_binary_int64_be(InFile, ReadResult, !IO)
+            ;
+                ByteOrder = little_endian,
+                read_binary_int64_le(InFile, ReadResult, !IO)
+            ;
+                ByteOrder = native,
+                read_binary_int64(InFile, ReadResult, !IO)
+            ),
+            io.close_binary_input(InFile, !IO),
+            (
+                ReadResult = ok(ResultUInt16),
+                io.write_string("Result: ", !IO),
+                io.write(ResultUInt16, !IO),
+                io.write_string(" (", !IO),
+                describe_byte_order(ByteOrder, !IO),
+                io.write_string(")\n", !IO)
+            ;
+                ReadResult = eof,
+                io.write_string("Result: EOF (", !IO),
+                describe_byte_order(ByteOrder, !IO),
+                io.write_string(")\n", !IO)
+            ;
+                ReadResult = incomplete(Bytes),
+                io.format("Result: Incomplete (%s) (", [s(string(Bytes))],
+                    !IO),
+                describe_byte_order(ByteOrder, !IO),
+                io.write_string(")\n", !IO)
+            ;
+                ReadResult = error(IO_Error),
+                io.format("Result: Error (%s)\n", [s(io.error_message(IO_Error))],
+                    !IO)
+            ),
+            io.remove_file(test_file, _, !IO)
+        ;
+            OpenInResult = error(IO_Error),
+            io.format("I/O ERROR: %s\n", [s(io.error_message(IO_Error))],
+                !IO)
+        )
+    ;
+        OpenOutResult = error(IO_Error),
+        io.format("I/O ERROR: %s\n", [s(io.error_message(IO_Error))], !IO)
+    ).
+
+:- pred describe_byte_order(byte_order::in, io::di, io::uo) is det.
+
+describe_byte_order(ByteOrder, !IO) :-
+    (
+        ByteOrder = big_endian,
+        io.write_string("read big-endian", !IO)
+    ;
+        ByteOrder = little_endian,
+        io.write_string("read little-endian", !IO)
+    ;
+        ByteOrder = native,
+        io.write_string("read native byte order", !IO)
+    ).
+
+:- func test_file = string.
+
+test_file = "read_binary_int64.bin".
+
+%---------------------------------------------------------------------------%
+
+:- type byte_order
+    --->    big_endian
+    ;       little_endian
+    ;       native.
+
+:- type test_case == list(uint8).
+
+:- func test_cases = list(test_case).
+
+test_cases = [
+    [],
+    [1u8],
+    [1u8, 2u8],
+    [1u8, 2u8, 3u8],
+    [1u8, 2u8, 3u8, 4u8],
+    [1u8, 2u8, 3u8, 4u8, 5u8],
+    [1u8, 2u8, 3u8, 4u8, 5u8, 6u8],
+    [1u8, 2u8, 3u8, 4u8, 5u8, 7u8],
+    [1u8, 2u8, 3u8, 4u8, 5u8, 7u8, 8u8],
+    [0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8],
+    [0xffu8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8],
+    [1u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8]
+].
+
+%---------------------------------------------------------------------------%
+:- end_module read_binary_int64.
+%---------------------------------------------------------------------------%
diff --git a/tests/hard_coded/read_binary_uint16.exp b/tests/hard_coded/read_binary_uint16.exp
new file mode 100644
index 0000000..658cef5
--- /dev/null
+++ b/tests/hard_coded/read_binary_uint16.exp
@@ -0,0 +1,36 @@
+================
+Input: []
+Result: EOF (read big-endian)
+================
+Input: [0u8]
+Result: Incomplete ([0u8]) (read big-endian)
+================
+Input: [1u8, 0u8] (LE: 1) (BE: 256)
+Result: 256u16 (read big-endian)
+================
+Input: [255u8, 0u8] (LE: 255) (BE: 65280)
+Result: 65280u16 (read big-endian)
+================
+Input: []
+Result: EOF (read little-endian)
+================
+Input: [0u8]
+Result: Incomplete ([0u8]) (read little-endian)
+================
+Input: [1u8, 0u8] (LE: 1) (BE: 256)
+Result: 1u16 (read little-endian)
+================
+Input: [255u8, 0u8] (LE: 255) (BE: 65280)
+Result: 255u16 (read little-endian)
+================
+Input: []
+Result: EOF (read native byte order)
+================
+Input: [0u8]
+Result: Incomplete ([0u8]) (read native byte order)
+================
+Input: [1u8, 0u8] (LE: 1) (BE: 256)
+Result: 1u16 (read native byte order)
+================
+Input: [255u8, 0u8] (LE: 255) (BE: 65280)
+Result: 255u16 (read native byte order)
diff --git a/tests/hard_coded/read_binary_uint16.m b/tests/hard_coded/read_binary_uint16.m
new file mode 100644
index 0000000..3b2a38e
--- /dev/null
+++ b/tests/hard_coded/read_binary_uint16.m
@@ -0,0 +1,144 @@
+%---------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et
+%---------------------------------------------------------------------------%
+%
+% Test reading of binary uint16s.
+%
+%---------------------------------------------------------------------------%
+
+:- module read_binary_uint16.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module list.
+:- import_module string.
+:- import_module uint16.
+
+%---------------------------------------------------------------------------%
+
+main(!IO) :-
+    list.foldl(run_test(big_endian), test_cases, !IO),
+    list.foldl(run_test(little_endian), test_cases, !IO),
+    list.foldl(run_test(native), test_cases, !IO).
+
+:- pred run_test(byte_order::in, test_case::in, io::di, io::uo) is det.
+
+run_test(ByteOrder, TestBytes, !IO) :-
+    io.remove_file(test_file, _, !IO),
+    io.write_string("================\n", !IO),
+    io.open_binary_output(test_file, OpenOutResult, !IO),
+    (
+        OpenOutResult = ok(OutFile),
+        io.write_string("Input: ", !IO),
+        io.write(TestBytes, !IO),
+        (
+            ( TestBytes = []
+            ; TestBytes = [_]
+            ; TestBytes = [_, _, _ | _]
+            ),
+            io.nl(!IO)
+        ;
+            TestBytes = [Byte1, Byte2],
+            io.write_string(" (LE: ", !IO),
+            io.write_uint16(from_bytes_le(Byte1, Byte2), !IO),
+            io.write_string(") (BE: ", !IO),
+            io.write_uint16(from_bytes_be(Byte1, Byte2), !IO),
+            io.write_string(")\n", !IO)
+        ),
+        list.foldl(write_binary_uint8(OutFile), TestBytes, !IO),
+        io.close_binary_output(OutFile, !IO),
+        io.open_binary_input(test_file, OpenInResult, !IO),
+        (
+            OpenInResult = ok(InFile),
+            (
+                ByteOrder = big_endian,
+                read_binary_uint16_be(InFile, ReadResult, !IO)
+            ;
+                ByteOrder = little_endian,
+                read_binary_uint16_le(InFile, ReadResult, !IO)
+            ;
+                ByteOrder = native,
+                read_binary_uint16(InFile, ReadResult, !IO)
+            ),
+            io.close_binary_input(InFile, !IO),
+            (
+                ReadResult = ok(ResultUInt16),
+                io.write_string("Result: ", !IO),
+                io.write(ResultUInt16, !IO),
+                io.write_string(" (", !IO),
+                describe_byte_order(ByteOrder, !IO),
+                io.write_string(")\n", !IO)
+            ;
+                ReadResult = eof,
+                io.write_string("Result: EOF (", !IO),
+                describe_byte_order(ByteOrder, !IO),
+                io.write_string(")\n", !IO)
+            ;
+                ReadResult = incomplete(Bytes),
+                io.format("Result: Incomplete (%s) (", [s(string(Bytes))],
+                    !IO),
+                describe_byte_order(ByteOrder, !IO),
+                io.write_string(")\n", !IO)
+            ;
+                ReadResult = error(IO_Error),
+                io.format("Result: Error (%s)\n", [s(io.error_message(IO_Error))],
+                    !IO)
+            ),
+            io.remove_file(test_file, _, !IO)
+        ;
+            OpenInResult = error(IO_Error),
+            io.format("I/O ERROR: %s\n", [s(io.error_message(IO_Error))],
+                !IO)
+        )
+    ;
+        OpenOutResult = error(IO_Error),
+        io.format("I/O ERROR: %s\n", [s(io.error_message(IO_Error))], !IO)
+    ).
+
+:- pred describe_byte_order(byte_order::in, io::di, io::uo) is det.
+
+describe_byte_order(ByteOrder, !IO) :-
+    (
+        ByteOrder = big_endian,
+        io.write_string("read big-endian", !IO)
+    ;
+        ByteOrder = little_endian,
+        io.write_string("read little-endian", !IO)
+    ;
+        ByteOrder = native,
+        io.write_string("read native byte order", !IO)
+    ).
+
+:- func test_file = string.
+
+test_file = "read_binary_uint16.bin".
+
+%---------------------------------------------------------------------------%
+
+:- type byte_order
+    --->    big_endian
+    ;       little_endian
+    ;       native.
+
+:- type test_case == list(uint8).
+
+:- func test_cases = list(test_case).
+
+test_cases = [
+    [],
+    [0u8],
+    [1u8, 0u8],
+    [0xffu8, 0u8]
+].
+
+%---------------------------------------------------------------------------%
+:- end_module read_binary_uint16.
+%---------------------------------------------------------------------------%
diff --git a/tests/hard_coded/read_binary_uint32.exp b/tests/hard_coded/read_binary_uint32.exp
new file mode 100644
index 0000000..7225201
--- /dev/null
+++ b/tests/hard_coded/read_binary_uint32.exp
@@ -0,0 +1,63 @@
+================
+Input: []
+Result: EOF (read big-endian)
+================
+Input: [1u8]
+Result: Incomplete ([1u8]) (read big-endian)
+================
+Input: [1u8, 2u8]
+Result: Incomplete ([1u8, 2u8]) (read big-endian)
+================
+Input: [1u8, 2u8, 3u8]
+Result: Incomplete ([1u8, 2u8, 3u8]) (read big-endian)
+================
+Input: [0u8, 0u8, 0u8, 1u8] (LE: 16777216) (BE: 1)
+Result: 1u32 (read big-endian)
+================
+Input: [255u8, 0u8, 0u8, 0u8] (LE: 255) (BE: 4278190080)
+Result: 4278190080u32 (read big-endian)
+================
+Input: [1u8, 1u8, 0u8, 0u8] (LE: 257) (BE: 16842752)
+Result: 16842752u32 (read big-endian)
+================
+Input: []
+Result: EOF (read little-endian)
+================
+Input: [1u8]
+Result: Incomplete ([1u8]) (read little-endian)
+================
+Input: [1u8, 2u8]
+Result: Incomplete ([1u8, 2u8]) (read little-endian)
+================
+Input: [1u8, 2u8, 3u8]
+Result: Incomplete ([1u8, 2u8, 3u8]) (read little-endian)
+================
+Input: [0u8, 0u8, 0u8, 1u8] (LE: 16777216) (BE: 1)
+Result: 16777216u32 (read little-endian)
+================
+Input: [255u8, 0u8, 0u8, 0u8] (LE: 255) (BE: 4278190080)
+Result: 255u32 (read little-endian)
+================
+Input: [1u8, 1u8, 0u8, 0u8] (LE: 257) (BE: 16842752)
+Result: 257u32 (read little-endian)
+================
+Input: []
+Result: EOF (read native byte order)
+================
+Input: [1u8]
+Result: Incomplete ([1u8]) (read native byte order)
+================
+Input: [1u8, 2u8]
+Result: Incomplete ([1u8, 2u8]) (read native byte order)
+================
+Input: [1u8, 2u8, 3u8]
+Result: Incomplete ([1u8, 2u8, 3u8]) (read native byte order)
+================
+Input: [0u8, 0u8, 0u8, 1u8] (LE: 16777216) (BE: 1)
+Result: 16777216u32 (read native byte order)
+================
+Input: [255u8, 0u8, 0u8, 0u8] (LE: 255) (BE: 4278190080)
+Result: 255u32 (read native byte order)
+================
+Input: [1u8, 1u8, 0u8, 0u8] (LE: 257) (BE: 16842752)
+Result: 257u32 (read native byte order)
diff --git a/tests/hard_coded/read_binary_uint32.m b/tests/hard_coded/read_binary_uint32.m
new file mode 100644
index 0000000..a7c7801
--- /dev/null
+++ b/tests/hard_coded/read_binary_uint32.m
@@ -0,0 +1,142 @@
+%---------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et
+%---------------------------------------------------------------------------%
+%
+% Test reading of binary uint32s.
+%
+%---------------------------------------------------------------------------%
+
+:- module read_binary_uint32.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module list.
+:- import_module string.
+:- import_module uint32.
+
+%---------------------------------------------------------------------------%
+
+main(!IO) :-
+    list.foldl(run_test(big_endian), test_cases, !IO),
+    list.foldl(run_test(little_endian), test_cases, !IO),
+    list.foldl(run_test(native), test_cases, !IO).
+
+:- pred run_test(byte_order::in, test_case::in, io::di, io::uo) is det.
+
+run_test(ByteOrder, TestBytes, !IO) :-
+    io.remove_file(test_file, _, !IO),
+    io.write_string("================\n", !IO),
+    io.open_binary_output(test_file, OpenOutResult, !IO),
+    (
+        OpenOutResult = ok(OutFile),
+        io.write_string("Input: ", !IO),
+        io.write(TestBytes, !IO),
+        ( if TestBytes = [Byte1, Byte2, Byte3, Byte4] then
+            io.write_string(" (LE: ", !IO),
+            io.write_uint32(from_bytes_le(Byte1, Byte2, Byte3, Byte4), !IO),
+            io.write_string(") (BE: ", !IO),
+            io.write_uint32(from_bytes_be(Byte1, Byte2, Byte3, Byte4), !IO),
+            io.write_string(")\n", !IO)
+        else
+            io.nl(!IO)
+        ),
+        list.foldl(write_binary_uint8(OutFile), TestBytes, !IO),
+        io.close_binary_output(OutFile, !IO),
+        io.open_binary_input(test_file, OpenInResult, !IO),
+        (
+            OpenInResult = ok(InFile),
+            (
+                ByteOrder = big_endian,
+                read_binary_uint32_be(InFile, ReadResult, !IO)
+            ;
+                ByteOrder = little_endian,
+                read_binary_uint32_le(InFile, ReadResult, !IO)
+            ;
+                ByteOrder = native,
+                read_binary_uint32(InFile, ReadResult, !IO)
+            ),
+            io.close_binary_input(InFile, !IO),
+            (
+                ReadResult = ok(ResultUInt16),
+                io.write_string("Result: ", !IO),
+                io.write(ResultUInt16, !IO),
+                io.write_string(" (", !IO),
+                describe_byte_order(ByteOrder, !IO),
+                io.write_string(")\n", !IO)
+            ;
+                ReadResult = eof,
+                io.write_string("Result: EOF (", !IO),
+                describe_byte_order(ByteOrder, !IO),
+                io.write_string(")\n", !IO)
+            ;
+                ReadResult = incomplete(Bytes),
+                io.format("Result: Incomplete (%s) (", [s(string(Bytes))],
+                    !IO),
+                describe_byte_order(ByteOrder, !IO),
+                io.write_string(")\n", !IO)
+            ;
+                ReadResult = error(IO_Error),
+                io.format("Result: Error (%s)\n", [s(io.error_message(IO_Error))],
+                    !IO)
+            ),
+            io.remove_file(test_file, _, !IO)
+        ;
+            OpenInResult = error(IO_Error),
+            io.format("I/O ERROR: %s\n", [s(io.error_message(IO_Error))],
+                !IO)
+        )
+    ;
+        OpenOutResult = error(IO_Error),
+        io.format("I/O ERROR: %s\n", [s(io.error_message(IO_Error))], !IO)
+    ).
+
+:- pred describe_byte_order(byte_order::in, io::di, io::uo) is det.
+
+describe_byte_order(ByteOrder, !IO) :-
+    (
+        ByteOrder = big_endian,
+        io.write_string("read big-endian", !IO)
+    ;
+        ByteOrder = little_endian,
+        io.write_string("read little-endian", !IO)
+    ;
+        ByteOrder = native,
+        io.write_string("read native byte order", !IO)
+    ).
+
+:- func test_file = string.
+
+test_file = "read_binary_uint32.bin".
+
+%---------------------------------------------------------------------------%
+
+:- type byte_order
+    --->    big_endian
+    ;       little_endian
+    ;       native.
+
+:- type test_case == list(uint8).
+
+:- func test_cases = list(test_case).
+
+test_cases = [
+    [],
+    [1u8],
+    [1u8, 2u8],
+    [1u8, 2u8, 3u8],
+    [0u8, 0u8, 0u8, 1u8],
+    [0xffu8, 0u8, 0u8, 0u8],
+    [1u8, 1u8, 0u8, 0u8]
+].
+
+%---------------------------------------------------------------------------%
+:- end_module read_binary_uint32.
+%---------------------------------------------------------------------------%
diff --git a/tests/hard_coded/read_binary_uint64.exp b/tests/hard_coded/read_binary_uint64.exp
new file mode 100644
index 0000000..6144024
--- /dev/null
+++ b/tests/hard_coded/read_binary_uint64.exp
@@ -0,0 +1,108 @@
+================
+Input: []
+Result: EOF (read big-endian)
+================
+Input: [1u8]
+Result: Incomplete ([1u8]) (read big-endian)
+================
+Input: [1u8, 2u8]
+Result: Incomplete ([1u8, 2u8]) (read big-endian)
+================
+Input: [1u8, 2u8, 3u8]
+Result: Incomplete ([1u8, 2u8, 3u8]) (read big-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8]) (read big-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8]) (read big-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8, 6u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8, 6u8]) (read big-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8, 7u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8, 7u8]) (read big-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8, 7u8, 8u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8, 7u8, 8u8]) (read big-endian)
+================
+Input: [0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8] (LE: 72057594037927936) (BE: 1)
+Result: 1u64 (read big-endian)
+================
+Input: [255u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] (LE: 255) (BE: 18374686479671623680)
+Result: 18374686479671623680u64 (read big-endian)
+================
+Input: [1u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] (LE: 257) (BE: 72339069014638592)
+Result: 72339069014638592u64 (read big-endian)
+================
+Input: []
+Result: EOF (read little-endian)
+================
+Input: [1u8]
+Result: Incomplete ([1u8]) (read little-endian)
+================
+Input: [1u8, 2u8]
+Result: Incomplete ([1u8, 2u8]) (read little-endian)
+================
+Input: [1u8, 2u8, 3u8]
+Result: Incomplete ([1u8, 2u8, 3u8]) (read little-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8]) (read little-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8]) (read little-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8, 6u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8, 6u8]) (read little-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8, 7u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8, 7u8]) (read little-endian)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8, 7u8, 8u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8, 7u8, 8u8]) (read little-endian)
+================
+Input: [0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8] (LE: 72057594037927936) (BE: 1)
+Result: 72057594037927936u64 (read little-endian)
+================
+Input: [255u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] (LE: 255) (BE: 18374686479671623680)
+Result: 255u64 (read little-endian)
+================
+Input: [1u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] (LE: 257) (BE: 72339069014638592)
+Result: 257u64 (read little-endian)
+================
+Input: []
+Result: EOF (read native byte order)
+================
+Input: [1u8]
+Result: Incomplete ([1u8]) (read native byte order)
+================
+Input: [1u8, 2u8]
+Result: Incomplete ([1u8, 2u8]) (read native byte order)
+================
+Input: [1u8, 2u8, 3u8]
+Result: Incomplete ([1u8, 2u8, 3u8]) (read native byte order)
+================
+Input: [1u8, 2u8, 3u8, 4u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8]) (read native byte order)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8]) (read native byte order)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8, 6u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8, 6u8]) (read native byte order)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8, 7u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8, 7u8]) (read native byte order)
+================
+Input: [1u8, 2u8, 3u8, 4u8, 5u8, 7u8, 8u8]
+Result: Incomplete ([1u8, 2u8, 3u8, 4u8, 5u8, 7u8, 8u8]) (read native byte order)
+================
+Input: [0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8] (LE: 72057594037927936) (BE: 1)
+Result: 72057594037927936u64 (read native byte order)
+================
+Input: [255u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] (LE: 255) (BE: 18374686479671623680)
+Result: 255u64 (read native byte order)
+================
+Input: [1u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] (LE: 257) (BE: 72339069014638592)
+Result: 257u64 (read native byte order)
diff --git a/tests/hard_coded/read_binary_uint64.m b/tests/hard_coded/read_binary_uint64.m
new file mode 100644
index 0000000..e742472
--- /dev/null
+++ b/tests/hard_coded/read_binary_uint64.m
@@ -0,0 +1,149 @@
+%---------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et
+%---------------------------------------------------------------------------%
+%
+% Test reading of binary uint64s.
+%
+%---------------------------------------------------------------------------%
+
+:- module read_binary_uint64.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module list.
+:- import_module string.
+:- import_module uint64.
+
+%---------------------------------------------------------------------------%
+
+main(!IO) :-
+    list.foldl(run_test(big_endian), test_cases, !IO),
+    list.foldl(run_test(little_endian), test_cases, !IO),
+    list.foldl(run_test(native), test_cases, !IO).
+
+:- pred run_test(byte_order::in, test_case::in, io::di, io::uo) is det.
+
+run_test(ByteOrder, TestBytes, !IO) :-
+    io.remove_file(test_file, _, !IO),
+    io.write_string("================\n", !IO),
+    io.open_binary_output(test_file, OpenOutResult, !IO),
+    (
+        OpenOutResult = ok(OutFile),
+        io.write_string("Input: ", !IO),
+        io.write(TestBytes, !IO),
+        ( if
+            TestBytes = [Byte1, Byte2, Byte3, Byte4, Byte5, Byte6, Byte7, Byte8]
+        then
+            io.write_string(" (LE: ", !IO),
+            io.write_uint64(from_bytes_le(Byte1, Byte2, Byte3, Byte4, Byte5, Byte6, Byte7, Byte8), !IO),
+            io.write_string(") (BE: ", !IO),
+            io.write_uint64(from_bytes_be(Byte1, Byte2, Byte3, Byte4, Byte5, Byte6, Byte7, Byte8), !IO),
+            io.write_string(")\n", !IO)
+        else
+            io.nl(!IO)
+        ),
+        list.foldl(write_binary_uint8(OutFile), TestBytes, !IO),
+        io.close_binary_output(OutFile, !IO),
+        io.open_binary_input(test_file, OpenInResult, !IO),
+        (
+            OpenInResult = ok(InFile),
+            (
+                ByteOrder = big_endian,
+                read_binary_uint64_be(InFile, ReadResult, !IO)
+            ;
+                ByteOrder = little_endian,
+                read_binary_uint64_le(InFile, ReadResult, !IO)
+            ;
+                ByteOrder = native,
+                read_binary_uint64(InFile, ReadResult, !IO)
+            ),
+            io.close_binary_input(InFile, !IO),
+            (
+                ReadResult = ok(ResultUInt16),
+                io.write_string("Result: ", !IO),
+                io.write(ResultUInt16, !IO),
+                io.write_string(" (", !IO),
+                describe_byte_order(ByteOrder, !IO),
+                io.write_string(")\n", !IO)
+            ;
+                ReadResult = eof,
+                io.write_string("Result: EOF (", !IO),
+                describe_byte_order(ByteOrder, !IO),
+                io.write_string(")\n", !IO)
+            ;
+                ReadResult = incomplete(Bytes),
+                io.format("Result: Incomplete (%s) (", [s(string(Bytes))],
+                    !IO),
+                describe_byte_order(ByteOrder, !IO),
+                io.write_string(")\n", !IO)
+            ;
+                ReadResult = error(IO_Error),
+                io.format("Result: Error (%s)\n", [s(io.error_message(IO_Error))],
+                    !IO)
+            ),
+            io.remove_file(test_file, _, !IO)
+        ;
+            OpenInResult = error(IO_Error),
+            io.format("I/O ERROR: %s\n", [s(io.error_message(IO_Error))],
+                !IO)
+        )
+    ;
+        OpenOutResult = error(IO_Error),
+        io.format("I/O ERROR: %s\n", [s(io.error_message(IO_Error))], !IO)
+    ).
+
+:- pred describe_byte_order(byte_order::in, io::di, io::uo) is det.
+
+describe_byte_order(ByteOrder, !IO) :-
+    (
+        ByteOrder = big_endian,
+        io.write_string("read big-endian", !IO)
+    ;
+        ByteOrder = little_endian,
+        io.write_string("read little-endian", !IO)
+    ;
+        ByteOrder = native,
+        io.write_string("read native byte order", !IO)
+    ).
+
+:- func test_file = string.
+
+test_file = "read_binary_uint64.bin".
+
+%---------------------------------------------------------------------------%
+
+:- type byte_order
+    --->    big_endian
+    ;       little_endian
+    ;       native.
+
+:- type test_case == list(uint8).
+
+:- func test_cases = list(test_case).
+
+test_cases = [
+    [],
+    [1u8],
+    [1u8, 2u8],
+    [1u8, 2u8, 3u8],
+    [1u8, 2u8, 3u8, 4u8],
+    [1u8, 2u8, 3u8, 4u8, 5u8],
+    [1u8, 2u8, 3u8, 4u8, 5u8, 6u8],
+    [1u8, 2u8, 3u8, 4u8, 5u8, 7u8],
+    [1u8, 2u8, 3u8, 4u8, 5u8, 7u8, 8u8],
+    [0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8],
+    [0xffu8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8],
+    [1u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8]
+].
+
+%---------------------------------------------------------------------------%
+:- end_module read_binary_uint64.
+%---------------------------------------------------------------------------%


More information about the reviews mailing list