diff --git a/library/int64.m b/library/int64.m index 124aaa248..fd0f46ccd 100644 --- a/library/int64.m +++ b/library/int64.m @@ -221,6 +221,12 @@ % :- func \ (int64::in) = (int64::uo) is det. + % reverse_bytes(A) = B: + % B is the value that results from reversing the bytes in the + % representation of A. + % +:- func reverse_bytes(int64) = int64. + :- func min_int64 = int64. :- func max_int64 = int64. @@ -486,6 +492,42 @@ odd(X) :- %---------------------------------------------------------------------------% +:- pragma foreign_proc("C", + reverse_bytes(A::in) = (B::out), + [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail], +" + B = (int64_t) MR_uint64_reverse_bytes((uint64_t)A); +"). + +:- pragma foreign_proc("C#", + reverse_bytes(A::in) = (B::out), + [will_not_call_mercury, promise_pure, thread_safe], +" + ulong u_A = (ulong) A; + + B = (long) ((u_A & 0x00000000000000ffUL) << 56 | + (u_A & 0x000000000000ff00UL) << 40 | + (u_A & 0x0000000000ff0000UL) << 24 | + (u_A & 0x00000000ff000000UL) << 8 | + (u_A & 0x000000ff00000000UL) >> 8 | + (u_A & 0x0000ff0000000000UL) >> 24 | + (u_A & 0x00ff000000000000UL) >> 40 | + (u_A & 0xff00000000000000UL) >> 56); +"). + +:- pragma foreign_proc("Java", + reverse_bytes(A::in) = (B::out), + [will_not_call_mercury, promise_pure, thread_safe], +" + B = java.lang.Long.reverseBytes(A); +"). + +:- pragma no_determinism_warning(reverse_bytes/1). +reverse_bytes(_) = _ :- + sorry($module, "int64.reverse_bytes/1 NYI for Erlang"). + +%---------------------------------------------------------------------------% + min_int64 = -9_223_372_036_854_775_808_i64. max_int64 = 9_223_372_036_854_775_807_i64. diff --git a/library/io.m b/library/io.m index 271e327cf..f93dde41c 100644 --- a/library/io.m +++ b/library/io.m @@ -1120,6 +1120,32 @@ :- pred write_binary_uint32_be(io.binary_output_stream::in, uint32::in, io::di, io::uo) is det. +%---------------------% + +:- pred write_binary_int64(int64::in, io::di, io::uo) is det. +:- pred write_binary_int64(io.binary_output_stream::in, int64::in, + io::di, io::uo) is det. + +:- pred write_binary_int64_le(int64::in, io::di, io::uo) is det. +:- pred write_binary_int64_le(io.binary_output_stream::in, int64::in, + io::di, io::uo) is det. + +:- pred write_binary_int64_be(int64::in, io::di, io::uo) is det. +:- pred write_binary_int64_be(io.binary_output_stream::in, int64::in, + io::di, io::uo) is det. + +:- pred write_binary_uint64(uint64::in, io::di, io::uo) is det. +:- pred write_binary_uint64(io.binary_output_stream::in, uint64::in, + io::di, io::uo) is det. + +:- pred write_binary_uint64_le(uint64::in, io::di, io::uo) is det. +:- pred write_binary_uint64_le(io.binary_output_stream::in, uint64::in, + io::di, io::uo) is det. + +:- pred write_binary_uint64_be(uint64::in, io::di, io::uo) is det. +:- pred write_binary_uint64_be(io.binary_output_stream::in, uint64::in, + io::di, io::uo) is det. + %---------------------% % Write a bitmap to the current binary output stream @@ -1934,6 +1960,7 @@ :- import_module uint8. :- import_module uint16. :- import_module uint32. +:- import_module uint64. :- use_module rtti_implementation. :- use_module table_builtin. @@ -8099,6 +8126,34 @@ write_binary_uint32_be(UInt32, !IO) :- %---------------------% +write_binary_int64(Int64, !IO) :- + UInt64 = uint64.cast_from_int64(Int64), + write_binary_uint64(UInt64, !IO). + +write_binary_int64_le(Int64, !IO) :- + UInt64 = uint64.cast_from_int64(Int64), + write_binary_uint64_le(UInt64, !IO). + +write_binary_int64_be(Int64, !IO) :- + UInt64 = uint64.cast_from_int64(Int64), + write_binary_uint64_be(UInt64, !IO). + +%---------------------% + +write_binary_uint64(UInt64, !IO) :- + binary_output_stream(Stream, !IO), + write_binary_uint64(Stream, UInt64, !IO). + +write_binary_uint64_le(UInt64, !IO) :- + binary_output_stream(Stream, !IO), + write_binary_uint64_le(Stream, UInt64, !IO). + +write_binary_uint64_be(UInt64, !IO) :- + binary_output_stream(Stream, !IO), + write_binary_uint64_be(Stream, UInt64, !IO). + +%---------------------% + write_bitmap(Bitmap, !IO) :- binary_output_stream(Stream, !IO), write_bitmap(Stream, Bitmap, !IO). @@ -8840,6 +8895,184 @@ write_binary_uint32_be(binary_output_stream(Stream), UInt32, !IO) :- %---------------------% +write_binary_int64(Stream, Int64, !IO) :- + UInt64 = uint64.cast_from_int64(Int64), + write_binary_uint64(Stream, UInt64, !IO). + +write_binary_uint64(binary_output_stream(Stream), UInt64, !IO) :- + do_write_binary_uint64(Stream, UInt64, Error, !IO), + throw_on_output_error(Error, !IO). + +:- pred do_write_binary_uint64(stream::in, uint64::in, system_error::out, + io::di, io::uo) is det. + +:- pragma foreign_proc("C", + do_write_binary_uint64(Stream::in, U64::in, Error::out, + _IO0::di, _IO::uo), + [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io], +" + if (MR_WRITE(*Stream, (unsigned char *)(&U64), 8) != 8) { + Error = errno; + } else { + Error = 0; + } +"). + +:- pragma foreign_proc("Java", + do_write_binary_uint64(Stream::in, U64::in, Error::out, + _IO0::di, _IO::uo), + [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io], +" + try { + java.nio.ByteBuffer buffer = java.nio.ByteBuffer.allocate(8); + buffer.order(java.nio.ByteOrder.nativeOrder()); + buffer.putLong(U64); + ((io.MR_BinaryOutputFile) Stream).write(buffer.array(), 0, 8); + Error = null; + } catch (java.io.IOException e) { + Error = e; + } +"). + +:- pragma foreign_proc("C#", + do_write_binary_uint64(Stream::in, U64::in, Error::out, _IO0::di, _IO::uo), + [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io], +" + byte[] bytes = BitConverter.GetBytes(U64); + try { + Stream.stream.Write(bytes, 0, 8); + Error = null; + } catch (System.Exception e) { + Error = e; + } +"). + +%---------------------% + +write_binary_int64_le(Stream, Int64, !IO) :- + UInt64 = uint64.cast_from_int64(Int64), + write_binary_uint64_le(Stream, UInt64, !IO). + +write_binary_uint64_le(binary_output_stream(Stream), UInt64, !IO) :- + do_write_binary_uint64_le(Stream, UInt64, Error, !IO), + throw_on_output_error(Error, !IO). + +:- pred do_write_binary_uint64_le(stream::in, uint64::in, system_error::out, + io::di, io::uo) is det. + +:- pragma foreign_proc("C", + do_write_binary_uint64_le(Stream::in, U64::in, Error::out, + _IO0::di, _IO::uo), + [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io], +" + #if defined(MR_BIG_ENDIAN) + U64 = MR_uint64_reverse_bytes(U64); + #endif + + if (MR_WRITE(*Stream, (unsigned char *)(&U64), 8) != 8) { + Error = errno; + } else { + Error = 0; + } +"). + +:- pragma foreign_proc("Java", + do_write_binary_uint64_le(Stream::in, U64::in, Error::out, + _IO0::di, _IO::uo), + [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io], +" + try { + java.nio.ByteBuffer buffer = java.nio.ByteBuffer.allocate(8); + buffer.order(java.nio.ByteOrder.LITTLE_ENDIAN); + buffer.putLong(U64); + ((io.MR_BinaryOutputFile) Stream).write(buffer.array(), 0, 8); + Error = null; + } catch (java.io.IOException e) { + Error = e; + } +"). + +:- pragma foreign_proc("C#", + do_write_binary_uint64_le(Stream::in, U64::in, Error::out, + _IO0::di, _IO::uo), + [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io], +" + byte[] bytes = BitConverter.GetBytes(U64); + if (!BitConverter.IsLittleEndian) { + Array.Reverse(bytes); + } + try { + Stream.stream.Write(bytes, 0, 8); + Error = null; + } catch (System.Exception e) { + Error = e; + } +"). + +%---------------------% + +write_binary_int64_be(Stream, Int64, !IO) :- + UInt64 = uint64.cast_from_int64(Int64), + write_binary_uint64_be(Stream, UInt64, !IO). + +write_binary_uint64_be(binary_output_stream(Stream), UInt64, !IO) :- + do_write_binary_uint64_be(Stream, UInt64, Error, !IO), + throw_on_output_error(Error, !IO). + +:- pred do_write_binary_uint64_be(stream::in, uint64::in, system_error::out, + io::di, io::uo) is det. + +:- pragma foreign_proc("C", + do_write_binary_uint64_be(Stream::in, U64::in, Error::out, + _IO0::di, _IO::uo), + [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io], +" + #if defined(MR_LITTLE_ENDIAN) + U64 = MR_uint64_reverse_bytes(U64); + #endif + + if (MR_WRITE(*Stream, (unsigned char *)(&U64), 8) != 8) { + Error = errno; + } else { + Error = 0; + } +"). + +:- pragma foreign_proc("Java", + do_write_binary_uint64_be(Stream::in, U64::in, Error::out, + _IO0::di, _IO::uo), + [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io], +" + try { + java.nio.ByteBuffer buffer = java.nio.ByteBuffer.allocate(8); + // Order in a byte buffer is big endian by default. + buffer.putLong(U64); + ((io.MR_BinaryOutputFile) Stream).write(buffer.array(), 0, 8); + Error = null; + } catch (java.io.IOException e) { + Error = e; + } +"). + +:- pragma foreign_proc("C#", + do_write_binary_uint64_be(Stream::in, U64::in, Error::out, + _IO0::di, _IO::uo), + [will_not_call_mercury, promise_pure, thread_safe, tabled_for_io], +" + byte[] bytes = BitConverter.GetBytes(U64); + if (BitConverter.IsLittleEndian) { + Array.Reverse(bytes); + } + try { + Stream.stream.Write(bytes, 0, 8); + Error = null; + } catch (System.Exception e) { + Error = e; + } +"). + +%---------------------% + write_bitmap(binary_output_stream(Stream), Bitmap, !IO) :- ( if NumBytes = Bitmap ^ num_bytes then do_write_bitmap(Stream, Bitmap, 0, NumBytes, Error, !IO), diff --git a/library/uint64.m b/library/uint64.m index 6e1269cf7..134da8049 100644 --- a/library/uint64.m +++ b/library/uint64.m @@ -33,6 +33,8 @@ :- func cast_to_int(uint64) = int. +:- func cast_from_int64(int64) = uint64. + % from_bytes_le(Byte0, Byte1, ..., Byte7) = U64: % U64 is the uint64 whose bytes are given in little-endian order by the % arguments from left-to-right (i.e. Byte0 is the least significant byte @@ -191,6 +193,12 @@ % :- func \ (uint64::in) = (uint64::uo) is det. + % reverse_bytes(A) = B: + % B is the value that results from reversing the bytes in the + % representation of A. + % +:- func reverse_bytes(uint64) = uint64. + :- func max_uint64 = uint64. % Convert a uint64 to a pretty_printer.doc for formatting. @@ -309,6 +317,34 @@ cast_to_int(_) = _ :- %---------------------------------------------------------------------------% +:- pragma foreign_proc("C", + cast_from_int64(I64::in) = (U64::out), + [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail, + does_not_affect_liveness], +" + U64 = (uint64_t) I64; +"). + +:- pragma foreign_proc("C#", + cast_from_int64(I64::in) = (U64::out), + [will_not_call_mercury, promise_pure, thread_safe], +" + U64 = (ulong) I64; +"). + +:- pragma foreign_proc("Java", + cast_from_int64(I64::in) = (U64::out), + [will_not_call_mercury, promise_pure, thread_safe], +" + U64 = I64; +"). + +:- pragma no_determinism_warning(cast_from_int64/1). +cast_from_int64(_) = _ :- + sorry($module, "uint64.cast_from_int64/1 NYI for Erlang"). + +%---------------------------------------------------------------------------% + :- pragma foreign_proc("C", from_bytes_le(Byte0::in, Byte1::in, Byte2::in, Byte3::in, Byte4::in, Byte5::in, Byte6::in, Byte7::in) = (U64::out), @@ -438,6 +474,32 @@ odd(X) :- %---------------------------------------------------------------------------% +:- pragma foreign_proc("C", + reverse_bytes(A::in) = (B::out), + [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail], +" + B = MR_uint64_reverse_bytes(A); +"). + +:- pragma foreign_proc("Java", + reverse_bytes(A::in) = (B::out), + [will_not_call_mercury, promise_pure, thread_safe], +" + B = java.lang.Long.reverseBytes(A); +"). + +reverse_bytes(A) = B :- + B = ((A /\ 0x_0000_0000_0000_00ff_u64) << 56) \/ + ((A /\ 0x_0000_0000_0000_ff00_u64) << 40) \/ + ((A /\ 0x_0000_0000_00ff_0000_u64) << 24) \/ + ((A /\ 0x_0000_0000_ff00_0000_u64) << 8) \/ + ((A /\ 0x_0000_00ff_0000_0000_u64) >> 8) \/ + ((A /\ 0x_0000_ff00_0000_0000_u64) >> 24) \/ + ((A /\ 0x_00ff_0000_0000_0000_u64) >> 40) \/ + ((A /\ 0x_ff00_0000_0000_0000_u64) >> 56). + +%---------------------------------------------------------------------------% + max_uint64 = 18_446_744_073_709_551_615_u64. %---------------------------------------------------------------------------% diff --git a/runtime/mercury_int.h b/runtime/mercury_int.h index 4351ef835..bb8fbdf6f 100644 --- a/runtime/mercury_int.h +++ b/runtime/mercury_int.h @@ -114,4 +114,20 @@ extern MR_Integer MR_hash_uint64(uint64_t); (U & UINT32_C(0xff000000)) >> 24 ) #endif +#if defined(MR_GNUC) || defined(MR_CLANG) + #define MR_uint64_reverse_bytes(U) __builtin_bswap64((U)) +#elif defined(MR_MSVC) + #define MR_uint64_reverse_bytes(U) _byteswap_uint64((UU)) +#else + #define MR_uint64_reverse_bytes(U) \ + ((U & UINT64_C(0x00000000000000ff)) << 56 | \ + (U & UINT64_C(0x000000000000ff00)) << 40 | \ + (U & UINT64_C(0x0000000000ff0000)) << 24 | \ + (U & UINT64_C(0x00000000ff000000)) << 8 | \ + (U & UINT64_C(0x000000ff00000000)) >> 8 | \ + (U & UINT64_C(0x0000ff0000000000)) >> 24 | \ + (U & UINT64_C(0x00ff000000000000)) >> 40 | \ + (U & UINT64_C(0xff00000000000000)) >> 56) +#endif + #endif // not MERCURY_INT_H diff --git a/tests/hard_coded/write_binary_multibyte_int.exp b/tests/hard_coded/write_binary_multibyte_int.exp index 29b9782b0..273603a55 100644 --- a/tests/hard_coded/write_binary_multibyte_int.exp +++ b/tests/hard_coded/write_binary_multibyte_int.exp @@ -1 +1,56 @@ -0x0a 0x0b 0x0b 0x0a 0xaa 0xbb 0xbb 0xaa 0x0a 0x0b 0x0c 0x0d 0x0d 0x0c 0x0b 0x0a 0xaa 0xbb 0xcc 0xdd 0xdd 0xcc 0xbb 0xaa +0x0a +0x0b +0x0b +0x0a +0xaa +0xbb +0xbb +0xaa +0x0a +0x0b +0x0c +0x0d +0x0d +0x0c +0x0b +0x0a +0xaa +0xbb +0xcc +0xdd +0xdd +0xcc +0xbb +0xaa +0x0a +0x0b +0x0c +0x0d +0x0e +0x0f +0x00 +0x00 +0x00 +0x00 +0x0f +0x0e +0x0d +0x0c +0x0b +0x0a +0xaa +0xbb +0xcc +0xdd +0xee +0xff +0x00 +0x00 +0x00 +0x00 +0xff +0xee +0xdd +0xcc +0xbb +0xaa diff --git a/tests/hard_coded/write_binary_multibyte_int.m b/tests/hard_coded/write_binary_multibyte_int.m index 5eb9770b8..f2c487c8c 100644 --- a/tests/hard_coded/write_binary_multibyte_int.m +++ b/tests/hard_coded/write_binary_multibyte_int.m @@ -2,7 +2,8 @@ % vim: ft=mercury ts=4 sw=4 et %---------------------------------------------------------------------------% % -% Test writing of int16, uint16, in32 and uint32 to binary file streams. +% Test writing of int16, uint16, int32, uint32, int64 and uint64 to binary file +% streams. :- module write_binary_multibyte_int. :- interface. @@ -44,6 +45,10 @@ main_2(FileName, !IO) :- io.write_binary_int32_be(OutputFile, 0x0d0c0b0a_i32, !IO), io.write_binary_uint32_le(OutputFile, 0xddccbbaa_u32, !IO), io.write_binary_uint32_be(OutputFile, 0xddccbbaa_u32, !IO), + io.write_binary_int64_le(OutputFile, 0x0f0e0d0c0b0a_i64, !IO), + io.write_binary_int64_be(OutputFile, 0x0f0e0d0c0b0a_i64, !IO), + io.write_binary_uint64_le(OutputFile, 0xffeeddccbbaa_u64, !IO), + io.write_binary_uint64_be(OutputFile, 0xffeeddccbbaa_u64, !IO), io.close_binary_output(OutputFile, !IO), io.open_binary_input(FileName, OpenInputResult, !IO), @@ -53,7 +58,7 @@ main_2(FileName, !IO) :- ( ReadResult = ok(Bytes), io.close_binary_input(InputFile, !IO), - io.write_list(Bytes, " ", print_byte, !IO), + io.write_list(Bytes, "\n", print_byte, !IO), io.nl(!IO) ; ReadResult = error(IO_Error),