Welcome to little lamb

Code » limb » commit c905c44

Add uint64_{,un}pack_trim functions..

author Olivier Brunel
2023-02-06 10:12:37 UTC
committer Olivier Brunel
2023-02-15 17:45:22 UTC
parent 67544a7d435f3ca0ce03d6c15cc92ea325dfa4fa

Add uint64_{,un}pack_trim functions..

..to (un)pack an u64 trimming "unneeded" bits, i.e. all most significant
bits set to zero need not be included.

In other words, it will only use as many bytes as necessary: The value
is stored in little-endian mode using only 7 bits per byte, with the last
(most significant) bit used as indicator whether or not an additional
byte is required.
Therefore the encoded number will take from only 1 up to 9 bytes.

Also define in int.h some u64_* macros for packing/unpacking in little,
big and trim mode, only with a slightly different interface (sce,dst
instead of char*,u64).

Bumping skalibs dependency to 2.12.0.0 to have new endianness primitives,
notably uint64_littlep()

doc/u64_pack.3.md +44 -0
doc/uint64_pack_trim.3.md +41 -0
include/limb/int.h +9 -0
include/limb/uint64.h +11 -0
meta/deps/skalibs/version +1 -1
meta/libs/limb +3 -0
src/uint64_pack_trim.c +28 -0
src/uint64_unpack_trim.c +23 -0

diff --git a/doc/u64_pack.3.md b/doc/u64_pack.3.md
new file mode 100644
index 0000000..1d0652f
--- /dev/null
+++ b/doc/u64_pack.3.md
@@ -0,0 +1,44 @@
+% limb manual
+% u64_pack(3)
+
+# NAME
+
+u64\_pack, u64\_unpack, u64\_pack\_big, u64\_unpack\_big, u64\_pack\_trim,
+u64\_unpack\_trim - pack\/unpack an integer (u64) into\/from a byte array
+
+# SYNOPSIS
+
+    #include <limb/int.h>
+
+```pre hl
+void u64_pack(u64 <em>val</em>, void *<em>dst</em>)
+void u64_unpack(const void *<em>sce</em>, u64 *<em>val</em>)
+void u64_pack_big(u64 <em>val</em>, void *<em>dst</em>)
+void u64_unpack_big(const void *<em>sce</em>, u64 *<em>val</em>)
+int u64_pack_trim(u64 <em>val</em>, void *<em>dst</em>)
+int u64_unpack_trim(const void *<em>sce</em>, u64 *<em>val</em>)
+```
+
+# DESCRIPTION
+
+All of these aim to either pack the given integer (u64) `val` into the specified
+byte array `dst`, or unpack from the specified byte array `sce` the integer
+value into the pointed `val`.
+
+The `u64_pack`() function packs `val` into 8 bytes encoded as little endian, the
+`u64_pack_big`() function does the same encoded as big endian, and the
+`u64_pack_trim`() function pack in a little endian mode but /trimming/ the
+"unneeded" bits, i.e. all most significant bits set to zero need not be
+included. It will therefore pack into from 1 to 9 bytes, depending on the value.
+See [uint64_pack_trim](3) for more.
+
+The `u64_unpack` family does the same, only unpacking from the byte array `sce`
+into `val`
+
+These are actually macros to the relevant functions, offering a slightly
+different interface.
+
+# RETURN VALUE
+
+Only `u64_pack_trim`() and `u64_unpack_trim`() have a return value, that of the
+number of bytes written to `dst` or read from `sce`, respectively.
diff --git a/doc/uint64_pack_trim.3.md b/doc/uint64_pack_trim.3.md
new file mode 100644
index 0000000..40e599d
--- /dev/null
+++ b/doc/uint64_pack_trim.3.md
@@ -0,0 +1,41 @@
+% limb manual
+% uint64_pack_trim(3)
+
+# NAME
+
+uint64\_pack\_trim, uint64\_unpack\_trim - pack\/unpack an integer (u64)
+into\/from a byte array
+
+# SYNOPSIS
+
+    #include <limb/uint64.h>
+
+```pre hl
+int uint64_pack_trim(char *<em>dst</em>, uint64_t <em>val</em>)
+int uint64_unpack_trim(const char *<em>sce</em>, uint64_t *<em>val</em>)
+```
+
+# DESCRIPTION
+
+The `uint64_pack_trim`() function will store the number `val` into `dst`
+/trimming/ the "unneeded" bits, i.e. all most significant bits set to zero need
+not be included.
+
+In other words, it will only use as many bytes as necessary: The value is
+stored in little-endian mode using only 7 bits per byte, with the last (most
+significant) bit used as indicator whether or not an additional byte is
+required.
+
+Therefore the encoded number will take from only 1 up to 9 bytes; As such the
+specified buffer `dst` should be able to hold at least 9 bytes.
+
+The `uint64_unpack_trim`() function will read at least 1 byte from `sce`, and up
+to 9, in order to decode the stored value and place it into `val`.
+
+# RETURN VALUE
+
+`uint64_pack_trim`() returns the number of bytes written to into `dst` to pack
+`val`
+
+`uint64_unpack_trim`() returns the number of bytes read from `sce` to unpack
+`val`
diff --git a/include/limb/int.h b/include/limb/int.h
index 013981a..df755ab 100644
--- a/include/limb/int.h
+++ b/include/limb/int.h
@@ -2,6 +2,8 @@
 #define LIMB_INT_H
 
 #include <stdint.h>
+#include <skalibs/uint64.h>
+#include "limb/uint64.h"
 
 typedef uint8_t  u8;
 typedef uint16_t u16;
@@ -10,4 +12,11 @@ typedef uint64_t u64;
 
 extern int msb64(u64 val);
 
+#define u64_pack(val,dst)           uint64_pack((char *) (dst), val)
+#define u64_unpack(sce,val)         uint64_unpack((const char *) (sce), val)
+#define u64_pack_big(val,dst)       uint64_pack_big((char *) (dst), val)
+#define u64_unpack_big(sce,val)     uint64_unpack_big((const char *) (sce), val)
+#define u64_pack_trim(val,dst)      uint64_pack_trim((char *) (dst), val)
+#define u64_unpack_trim(sce,val)    uint64_unpack_trim((const char *) (sce), val)
+
 #endif /* LIMB_INT_H */
diff --git a/include/limb/uint64.h b/include/limb/uint64.h
new file mode 100644
index 0000000..1643bd2
--- /dev/null
+++ b/include/limb/uint64.h
@@ -0,0 +1,11 @@
+#ifndef LIMB_UINT64_H
+#define LIMB_UINT64_H
+
+#include <stdint.h>
+
+extern int msb64(uint64_t val);
+
+extern int uint64_pack_trim(char *dst, uint64_t val);
+extern int uint64_unpack_trim(const char *sce, uint64_t *val);
+
+#endif /* LIMB_UINT64_H */
diff --git a/meta/deps/skalibs/version b/meta/deps/skalibs/version
index 0ab9020..ef9d112 100644
--- a/meta/deps/skalibs/version
+++ b/meta/deps/skalibs/version
@@ -1 +1 @@
-2.8.0.0
+2.12.0.0
diff --git a/meta/libs/limb b/meta/libs/limb
index 9f9e3b7..6e979ab 100644
--- a/meta/libs/limb
+++ b/meta/libs/limb
@@ -7,6 +7,9 @@ obj/openc_exclat.o
 obj/openc_createat.o
 # find msb
 obj/msb64.o
+# {,un}pack u64
+obj/uint64_pack_trim.o
+obj/uint64_unpack_trim.o
 # SHA3
 obj/sha3/byte_order.o
 obj/sha3/rhash_sha3_process_block.o
diff --git a/src/uint64_pack_trim.c b/src/uint64_pack_trim.c
new file mode 100644
index 0000000..7291506
--- /dev/null
+++ b/src/uint64_pack_trim.c
@@ -0,0 +1,28 @@
+#include <skalibs/uint64.h>
+#include "limb/uint64.h"
+
+int
+uint64_pack_trim(char *dst_, uint64_t val)
+{
+    uint8_t *dst = (uint8_t *) dst_;
+    int bits = msb64(val);
+    int b;
+
+    /* ensure little endianness */
+    uint64_littlep(&val);
+
+    for (b = 0; b < 8; ++b) {
+        int n = bits > 7 ? 7 : bits;
+        /* store the 7 low bits */
+        dst[b] = val & 127;
+        /* and shift them out */
+        val >>= 7;
+        bits -= n;
+        if (!bits)
+            break;
+        /* add our flag: one more byte */
+        dst[b] |= 128;
+    }
+    return b + 1;
+
+}
diff --git a/src/uint64_unpack_trim.c b/src/uint64_unpack_trim.c
new file mode 100644
index 0000000..05ccf93
--- /dev/null
+++ b/src/uint64_unpack_trim.c
@@ -0,0 +1,23 @@
+#include "limb/uint64.h"
+
+int
+uint64_unpack_trim(const char *sce_, uint64_t *val)
+{
+    const uint8_t *sce = (const uint8_t *) sce_;
+    int b;
+
+    *val = 0;
+    for (b = 0; b < 9; ++b) {
+        uint8_t byte = sce[b];
+        /* extract the last 7 bits from byte and add them into our val */
+        *val |= (byte & 127) << (7 * b);
+        /* is there another byte to read? */
+        if (!(byte & 128))
+            break;
+    }
+    /* if needed, add the last bit from the last byte */
+    if (b == 8 && sce[b] & 128)
+        *val |= 1ULL << 63;
+
+    return b + 1;
+}