[m-rev.] for review: add casts to and from uint{16, 32, 64} and uint8

Julien Fischer jfischer at opturion.com
Sun Nov 7 18:31:10 AEDT 2021


For review by anyone.

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

Add casts to and from uint{16,32,64} and uint8.

While these casts can be implemented by casting via uint, the resulting code is
less readable than code that uses direct casts.

library/uint16.m:
library/uint32.m:
library/uint64.m:
     Add cast_from_uint8/1 and cast_to_uint8/1 to these modules.

NEWS:
     Announce the above additions.

tests/hard_coded/Mmakefile:
tests/hard_coded/uint{16,32,64}_uint_casts.{m,exp}:
     Add tests for the new casts.

Julien.

diff --git a/NEWS b/NEWS
index eab0985..db2ff09 100644
--- a/NEWS
+++ b/NEWS
@@ -409,6 +409,8 @@ Changes to the Mercury standard library
      - func `unchecked_bit_is_set/2`
      - func `bit_is_clear/2`
      - func `unchecked_bit_is_clear/2`
+    - func `cast_from_uint8/1`
+    - func `cast_to_uint8/1`

  ### Changes to the `uint32` module

@@ -430,6 +432,8 @@ Changes to the Mercury standard library
      - func `unchecked_bit_is_set/2`
      - func `bit_is_clear/2`
      - func `unchecked_bit_is_clear/2`
+    - func `cast_from_uint8/1`
+    - func `cast_to_uint8/1`

  ### Changes to the `uint64` module

@@ -450,6 +454,8 @@ Changes to the Mercury standard library
      - func `unchecked_bit_is_set/2`
      - func `bit_is_clear/2`
      - func `unchecked_bit_is_clear/2`
+    - func `cast_from_uint8/1`
+    - func `cast_to_uint8/1`

  ### Changes to the `uint8` module

diff --git a/library/uint16.m b/library/uint16.m
index 96b6a18..1493f19 100644
--- a/library/uint16.m
+++ b/library/uint16.m
@@ -108,6 +108,27 @@

  %---------------------------------------------------------------------------%
  %
+% Conversion to/from uint8
+%
+
+    % cast_to_uint8(U16) = U8:
+    %
+    % Convert a uint16 to a uint8.
+    % Always succeeds, but will yield a result that is mathematically equal
+    % to U16 only if U16 is in [0, 2^8 - 1].
+    %
+:- func cast_to_uint8(uint16) = uint8.
+
+    % cast_from_uint8(U8) = U16:
+    %
+    % Convert a uint8 to a uint16.
+    % Always succeeds, and yields a result that is mathemtically equal
+    % to U8.
+    %
+:- func cast_from_uint8(uint8) = uint16.
+
+%---------------------------------------------------------------------------%
+%
  % Conversion to/from uint64.
  %

@@ -624,6 +645,54 @@ det_from_uint(U) = U16 :-
  %---------------------------------------------------------------------------%

  :- pragma foreign_proc("C",
+    cast_to_uint8(U16::in) = (U8::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
+"
+    U8 = (uint8_t) U16;
+").
+
+:- pragma foreign_proc("C#",
+    cast_to_uint8(U16::in) = (U8::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U8 = (byte) U16;
+").
+
+:- pragma foreign_proc("Java",
+    cast_to_uint8(U16::in) = (U8::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U8 = (byte) U16;
+").
+
+%---------------------------------------------------------------------------%
+
+:- pragma foreign_proc("C",
+    cast_from_uint8(U8::in) = (U16::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
+"
+    U16 = (uint16_t) U8;
+").
+
+:- pragma foreign_proc("C#",
+    cast_from_uint8(U8::in) = (U16::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U16 = (ushort) U8;
+").
+
+:- pragma foreign_proc("Java",
+    cast_from_uint8(U8::in) = (U16::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U16 = (short) (U8 & 0xff);
+").
+
+%---------------------------------------------------------------------------%
+
+:- pragma foreign_proc("C",
      cast_to_uint64(U16::in) = (U64::out),
      [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
          does_not_affect_liveness],
diff --git a/library/uint32.m b/library/uint32.m
index 740e37b..168f74a 100644
--- a/library/uint32.m
+++ b/library/uint32.m
@@ -102,6 +102,27 @@

  %---------------------------------------------------------------------------%
  %
+% Conversion to/from uint8
+%
+
+    % cast_to_uint8(U32) = U8:
+    %
+    % Convert a uint32 to a uint8.
+    % Always succeeds, but will yield a result that is mathematically equal
+    % to U32 only if U32 is in [0, 2^8 - 1].
+    %
+:- func cast_to_uint8(uint32) = uint8.
+
+    % cast_from_uint8(U8) = U32:
+    %
+    % Convert a uint8 to a uint32.
+    % Always succeeds, and yields a result that is mathemtically equal
+    % to U8.
+    %
+:- func cast_from_uint8(uint8) = uint32.
+
+%---------------------------------------------------------------------------%
+%
  % Conversion to/from uint64.
  %

@@ -647,6 +668,54 @@ det_from_uint(U) = U32 :-
  %---------------------------------------------------------------------------%

  :- pragma foreign_proc("C",
+    cast_to_uint8(U32::in) = (U8::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
+"
+    U8 = (uint8_t) U32;
+").
+
+:- pragma foreign_proc("C#",
+    cast_to_uint8(U32::in) = (U8::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U8 = (byte) U32;
+").
+
+:- pragma foreign_proc("Java",
+    cast_to_uint8(U32::in) = (U8::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U8 = (byte) U32;
+").
+
+%---------------------------------------------------------------------------%
+
+:- pragma foreign_proc("C",
+    cast_from_uint8(U8::in) = (U32::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
+"
+    U32 = (uint32_t) U8;
+").
+
+:- pragma foreign_proc("C#",
+    cast_from_uint8(U8::in) = (U32::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U32 = (uint) U8;
+").
+
+:- pragma foreign_proc("Java",
+    cast_from_uint8(U8::in) = (U32::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U32 = U8 & 0xff;
+").
+
+%---------------------------------------------------------------------------%
+
+:- pragma foreign_proc("C",
      cast_to_uint64(U32::in) = (U64::out),
      [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
          does_not_affect_liveness],
diff --git a/library/uint64.m b/library/uint64.m
index 0f81082..838c790 100644
--- a/library/uint64.m
+++ b/library/uint64.m
@@ -88,6 +88,27 @@

  %---------------------------------------------------------------------------%
  %
+% Conversion to/from uint8
+%
+
+    % cast_to_uint8(U64) = U8:
+    %
+    % Convert a uint64 to a uint8.
+    % Always succeeds, but will yield a result that is mathematically equal
+    % to U64 only if U64 is in [0, 2^8 - 1].
+    %
+:- func cast_to_uint8(uint64) = uint8.
+
+    % cast_from_uint8(U8) = U64:
+    %
+    % Convert a uint8 to a uint64.
+    % Always succeeds, and yields a result that is mathemtically equal
+    % to U8.
+    %
+:- func cast_from_uint8(uint8) = uint64.
+
+%---------------------------------------------------------------------------%
+%
  % Change of signedness.
  %

@@ -574,6 +595,54 @@ det_from_int(I) = U64 :-
  %---------------------------------------------------------------------------%

  :- pragma foreign_proc("C",
+    cast_to_uint8(U64::in) = (U8::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
+"
+    U8 = (uint8_t) U64;
+").
+
+:- pragma foreign_proc("C#",
+    cast_to_uint8(U64::in) = (U8::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U8 = (byte) U64;
+").
+
+:- pragma foreign_proc("Java",
+    cast_to_uint8(U64::in) = (U8::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U8 = (byte) U64;
+").
+
+%---------------------------------------------------------------------------%
+
+:- pragma foreign_proc("C",
+    cast_from_uint8(U8::in) = (U64::out),
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
+"
+    U64 = (uint64_t) U8;
+").
+
+:- pragma foreign_proc("C#",
+    cast_from_uint8(U8::in) = (U64::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U64 = (ulong) U8;
+").
+
+:- pragma foreign_proc("Java",
+    cast_from_uint8(U8::in) = (U64::out),
+    [will_not_call_mercury, promise_pure, thread_safe],
+"
+    U64 = (long) (U8 & 0xff);
+").
+
+%---------------------------------------------------------------------------%
+
+:- 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],
diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile
index 72779592..3d27e62 100644
--- a/tests/hard_coded/Mmakefile
+++ b/tests/hard_coded/Mmakefile
@@ -462,15 +462,18 @@ ORDINARY_PROGS = \
  	uint16_from_bytes \
  	uint16_switch_test \
  	uint16_to_string \
+	uint16_uint8_casts \
  	uint32_from_bytes \
  	uint32_switch_test \
  	uint32_to_string \
  	uint32_to_uint64 \
+	uint32_uint8_casts \
  	uint64_from_bytes \
  	uint64_ground_term \
  	uint64_string_conv \
  	uint64_switch_test \
  	uint64_to_string \
+	uint64_uint8_casts \
  	uint8_switch_test \
  	uint8_to_string \
  	uint_string_conv \
diff --git a/tests/hard_coded/uint16_uint8_casts.exp b/tests/hard_coded/uint16_uint8_casts.exp
index e69de29..02c0b25 100644
--- a/tests/hard_coded/uint16_uint8_casts.exp
+++ b/tests/hard_coded/uint16_uint8_casts.exp
@@ -0,0 +1,27 @@
+cast_from_uint8(0u8) = 0u16
+cast_from_uint8(7u8) = 7u16
+cast_from_uint8(8u8) = 8u16
+cast_from_uint8(15u8) = 15u16
+cast_from_uint8(16u8) = 16u16
+cast_from_uint8(31u8) = 31u16
+cast_from_uint8(32u8) = 32u16
+cast_from_uint8(63u8) = 63u16
+cast_from_uint8(64u8) = 64u16
+cast_from_uint8(127u8) = 127u16
+cast_from_uint8(128u8) = 128u16
+cast_from_uint8(254u8) = 254u16
+cast_from_uint8(255u8) = 255u16
+
+cast_to_uint8(0u16) = 0u8
+cast_to_uint8(7u16) = 7u8
+cast_to_uint8(8u16) = 8u8
+cast_to_uint8(15u16) = 15u8
+cast_to_uint8(16u16) = 16u8
+cast_to_uint8(31u16) = 31u8
+cast_to_uint8(32u16) = 32u8
+cast_to_uint8(63u16) = 63u8
+cast_to_uint8(64u16) = 64u8
+cast_to_uint8(127u16) = 127u8
+cast_to_uint8(128u16) = 128u8
+cast_to_uint8(254u16) = 254u8
+cast_to_uint8(255u16) = 255u8
diff --git a/tests/hard_coded/uint16_uint8_casts.m b/tests/hard_coded/uint16_uint8_casts.m
index e69de29..32592d3 100644
--- a/tests/hard_coded/uint16_uint8_casts.m
+++ b/tests/hard_coded/uint16_uint8_casts.m
@@ -0,0 +1,83 @@
+%---------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et
+%---------------------------------------------------------------------------%
+%
+% Testing casting uint16s to/from uint8s.
+%
+%---------------------------------------------------------------------------%
+
+:- module uint16_uint8_casts.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module list.
+:- import_module string.
+:- import_module uint16.
+:- import_module uint8.
+
+%---------------------------------------------------------------------------%
+
+main(!IO) :-
+    list.foldl(do_cast_from_uint8_test, uint8s, !IO),
+    io.nl(!IO),
+    list.foldl(do_cast_to_uint8_test, uint16s, !IO).
+
+:- pred do_cast_from_uint8_test(uint8::in, io::di, io::uo) is det.
+
+do_cast_from_uint8_test(U8, !IO) :-
+    io.format("cast_from_uint8(%uu8) = %uu16\n",
+        [u8(U8), u16(cast_from_uint8(U8))], !IO).
+
+:- pred do_cast_to_uint8_test(uint16::in, io::di, io::uo) is det.
+
+do_cast_to_uint8_test(U16, !IO) :-
+    io.format("cast_to_uint8(%uu16) = %uu8\n",
+        [u16(U16), u8(cast_to_uint8(U16))], !IO).
+
+:- func uint8s = list(uint8).
+
+uint8s = [
+    0u8,
+    7u8,
+    8u8,
+    15u8,
+    16u8,
+    31u8,
+    32u8,
+    63u8,
+    64u8,
+    127u8,
+    128u8,
+    254u8,
+    255u8
+].
+
+:- func uint16s = list(uint16).
+
+uint16s = [
+    0u16,
+    7u16,
+    8u16,
+    15u16,
+    16u16,
+    31u16,
+    32u16,
+    63u16,
+    64u16,
+    127u16,
+    128u16,
+    254u16,
+    255u16
+].
+
+%---------------------------------------------------------------------------%
+:- end_module uint16_uint8_casts.
+%---------------------------------------------------------------------------%
diff --git a/tests/hard_coded/uint32_uint8_casts.exp b/tests/hard_coded/uint32_uint8_casts.exp
index e69de29..30f21d2 100644
--- a/tests/hard_coded/uint32_uint8_casts.exp
+++ b/tests/hard_coded/uint32_uint8_casts.exp
@@ -0,0 +1,27 @@
+cast_from_uint8(0u8) = 0u32
+cast_from_uint8(7u8) = 7u32
+cast_from_uint8(8u8) = 8u32
+cast_from_uint8(15u8) = 15u32
+cast_from_uint8(16u8) = 16u32
+cast_from_uint8(31u8) = 31u32
+cast_from_uint8(32u8) = 32u32
+cast_from_uint8(63u8) = 63u32
+cast_from_uint8(64u8) = 64u32
+cast_from_uint8(127u8) = 127u32
+cast_from_uint8(128u8) = 128u32
+cast_from_uint8(254u8) = 254u32
+cast_from_uint8(255u8) = 255u32
+
+cast_to_uint8(0u32) = 0u8
+cast_to_uint8(7u32) = 7u8
+cast_to_uint8(8u32) = 8u8
+cast_to_uint8(15u32) = 15u8
+cast_to_uint8(16u32) = 16u8
+cast_to_uint8(31u32) = 31u8
+cast_to_uint8(32u32) = 32u8
+cast_to_uint8(63u32) = 63u8
+cast_to_uint8(64u32) = 64u8
+cast_to_uint8(127u32) = 127u8
+cast_to_uint8(128u32) = 128u8
+cast_to_uint8(254u32) = 254u8
+cast_to_uint8(255u32) = 255u8
diff --git a/tests/hard_coded/uint32_uint8_casts.m b/tests/hard_coded/uint32_uint8_casts.m
index e69de29..0516844 100644
--- a/tests/hard_coded/uint32_uint8_casts.m
+++ b/tests/hard_coded/uint32_uint8_casts.m
@@ -0,0 +1,83 @@
+%---------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et
+%---------------------------------------------------------------------------%
+%
+% Test casting uint32s to/from uint8s.
+%
+%---------------------------------------------------------------------------%
+
+:- module uint32_uint8_casts.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module list.
+:- import_module string.
+:- import_module uint32.
+:- import_module uint8.
+
+%---------------------------------------------------------------------------%
+
+main(!IO) :-
+    list.foldl(do_cast_from_uint8_test, uint8s, !IO),
+    io.nl(!IO),
+    list.foldl(do_cast_to_uint8_test, uint32s, !IO).
+
+:- pred do_cast_from_uint8_test(uint8::in, io::di, io::uo) is det.
+
+do_cast_from_uint8_test(U8, !IO) :-
+    io.format("cast_from_uint8(%uu8) = %uu32\n",
+        [u8(U8), u32(cast_from_uint8(U8))], !IO).
+
+:- pred do_cast_to_uint8_test(uint32::in, io::di, io::uo) is det.
+
+do_cast_to_uint8_test(U32, !IO) :-
+    io.format("cast_to_uint8(%uu32) = %uu8\n",
+        [u32(U32), u8(cast_to_uint8(U32))], !IO).
+
+:- func uint8s = list(uint8).
+
+uint8s = [
+    0u8,
+    7u8,
+    8u8,
+    15u8,
+    16u8,
+    31u8,
+    32u8,
+    63u8,
+    64u8,
+    127u8,
+    128u8,
+    254u8,
+    255u8
+].
+
+:- func uint32s = list(uint32).
+
+uint32s = [
+    0u32,
+    7u32,
+    8u32,
+    15u32,
+    16u32,
+    31u32,
+    32u32,
+    63u32,
+    64u32,
+    127u32,
+    128u32,
+    254u32,
+    255u32
+].
+
+%---------------------------------------------------------------------------%
+:- end_module uint32_uint8_casts.
+%---------------------------------------------------------------------------%
diff --git a/tests/hard_coded/uint64_uint8_casts.exp b/tests/hard_coded/uint64_uint8_casts.exp
index e69de29..35103b9 100644
--- a/tests/hard_coded/uint64_uint8_casts.exp
+++ b/tests/hard_coded/uint64_uint8_casts.exp
@@ -0,0 +1,27 @@
+cast_from_uint8(0u8) = 0u64
+cast_from_uint8(7u8) = 7u64
+cast_from_uint8(8u8) = 8u64
+cast_from_uint8(15u8) = 15u64
+cast_from_uint8(16u8) = 16u64
+cast_from_uint8(31u8) = 31u64
+cast_from_uint8(32u8) = 32u64
+cast_from_uint8(63u8) = 63u64
+cast_from_uint8(64u8) = 64u64
+cast_from_uint8(127u8) = 127u64
+cast_from_uint8(128u8) = 128u64
+cast_from_uint8(254u8) = 254u64
+cast_from_uint8(255u8) = 255u64
+
+cast_to_uint8(0u64) = 0u8
+cast_to_uint8(7u64) = 7u8
+cast_to_uint8(8u64) = 8u8
+cast_to_uint8(15u64) = 15u8
+cast_to_uint8(16u64) = 16u8
+cast_to_uint8(31u64) = 31u8
+cast_to_uint8(32u64) = 32u8
+cast_to_uint8(63u64) = 63u8
+cast_to_uint8(64u64) = 64u8
+cast_to_uint8(127u64) = 127u8
+cast_to_uint8(128u64) = 128u8
+cast_to_uint8(254u64) = 254u8
+cast_to_uint8(255u64) = 255u8
diff --git a/tests/hard_coded/uint64_uint8_casts.m b/tests/hard_coded/uint64_uint8_casts.m
index e69de29..e1dc9a3 100644
--- a/tests/hard_coded/uint64_uint8_casts.m
+++ b/tests/hard_coded/uint64_uint8_casts.m
@@ -0,0 +1,83 @@
+%---------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et
+%---------------------------------------------------------------------------%
+%
+% Test casting uint64s to/from uint8s.
+%
+%---------------------------------------------------------------------------%
+
+:- module uint64_uint8_casts.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module list.
+:- import_module string.
+:- import_module uint64.
+:- import_module uint8.
+
+%---------------------------------------------------------------------------%
+
+main(!IO) :-
+    list.foldl(do_cast_from_uint8_test, uint8s, !IO),
+    io.nl(!IO),
+    list.foldl(do_cast_to_uint8_test, uint64s, !IO).
+
+:- pred do_cast_from_uint8_test(uint8::in, io::di, io::uo) is det.
+
+do_cast_from_uint8_test(U8, !IO) :-
+    io.format("cast_from_uint8(%uu8) = %uu64\n",
+        [u8(U8), u64(cast_from_uint8(U8))], !IO).
+
+:- pred do_cast_to_uint8_test(uint64::in, io::di, io::uo) is det.
+
+do_cast_to_uint8_test(U32, !IO) :-
+    io.format("cast_to_uint8(%uu64) = %uu8\n",
+        [u64(U32), u8(cast_to_uint8(U32))], !IO).
+
+:- func uint8s = list(uint8).
+
+uint8s = [
+    0u8,
+    7u8,
+    8u8,
+    15u8,
+    16u8,
+    31u8,
+    32u8,
+    63u8,
+    64u8,
+    127u8,
+    128u8,
+    254u8,
+    255u8
+].
+
+:- func uint64s = list(uint64).
+
+uint64s = [
+    0u64,
+    7u64,
+    8u64,
+    15u64,
+    16u64,
+    31u64,
+    32u64,
+    63u64,
+    64u64,
+    127u64,
+    128u64,
+    254u64,
+    255u64
+].
+
+%---------------------------------------------------------------------------%
+:- end_module uint64_uint8_casts.
+%---------------------------------------------------------------------------%



More information about the reviews mailing list