Welcome to little lamb

Code » limb » commit 30b1210

Add a few macros/functions to u16/u32/u64

author Olivier Brunel
2023-02-20 20:32:45 UTC
committer Olivier Brunel
2023-02-20 20:32:45 UTC
parent 43295558b288f95abc5ffdab686c30a711460e4f

Add a few macros/functions to u16/u32/u64

Namely add some _le/_be functions for endianness conversion and _fmt/_scan
functions for writing/reading integers.

Also add some _fmtg functions for thousand separated values, and our own
u64_fmt_generic() to do so. Also u640_fmt_generic() so we can specify
the fill character, should be want to lead with zeroes or e.g. spaces.

(We've also ensured our macros follow the destination first rule.)

doc/u16_fmt.3.md +84 -0
doc/u16_le.3.md +46 -0
doc/u16_scan.3.md +67 -0
doc/u32_fmt.3.md +84 -0
doc/u32_le.3.md +46 -0
doc/u32_scan.3.md +67 -0
doc/u64_fmt.3.md +84 -0
doc/u64_fmt_generic.3.md +38 -0
doc/u64_le.3.md +46 -0
doc/u64_scan.3.md +67 -0
include/limb/u16.h +37 -0
include/limb/u32.h +37 -0
include/limb/u64.h +40 -0
meta/libs/limb +3 -0
src/u640_fmt_generic.c +19 -0
src/u64_fmt_generic.c +29 -0

diff --git a/doc/u16_fmt.3.md b/doc/u16_fmt.3.md
new file mode 100644
index 0000000..8693bbc
--- /dev/null
+++ b/doc/u16_fmt.3.md
@@ -0,0 +1,84 @@
+% limb manual
+% u16_fmt(3)
+
+# NAME
+
+u16\_fmt, u16\_fmtg, u160\_fmt - print an u16 as decimal value into a byte array
+
+u16\_xfmt, u160\_xfmt - print an u16 as hexadecimal value into a byte array
+
+u16\_ofmt, u160\_ofmt - print an u16 as octal value into a byte array
+
+u16\_bfmt, u160\_bfmt - print an u16 as binary value into a byte array
+
+
+# SYNOPSIS
+
+    #include <limb/u16.h>
+
+```pre hl
+size_t u16_fmt(char *<em>s</em>, u16 <em>u</em>)
+size_t u160_fmt(char *<em>s</em>, u16 <em>u</em>, size_t <em>min</em>)
+
+size_t u16_fmtg(char *<em>s</em>, u16 <em>u</em>)
+size_t u160_fmtg(char *<em>s</em>, u16 <em>u</em>, size_t <em>min</em>)
+
+size_t u16_xfmt(char *<em>s</em>, u16 <em>u</em>)
+size_t u160_xfmt(char *<em>s</em>, u16 <em>u</em>, size_t <em>min</em>)
+
+size_t u16_ofmt(char *<em>s</em>, u16 <em>u</em>)
+size_t u160_ofmt(char *<em>s</em>, u16 <em>u</em>, size_t <em>min</em>)
+
+size_t u16_bfmt(char *<em>s</em>, u16 <em>u</em>)
+size_t u160_bfmt(char *<em>s</em>, u16 <em>u</em>, size_t <em>min</em>)
+```
+
+# DESCRIPTION
+
+The `u16_fmt`() function will put into `s` the value of `u` in decimal.
+The `u160_fmt`() function will put into `s` the value of `u` in decimal,
+prefixing it with leading zeroes if it requires less than `min` characters to do
+so.
+
+The `u16_fmtg`() and `u160_fmtg`() function are similar, only they will use a
+coma (`,`) as thousand separator as needed.
+
+The `u16_xfmt`() and `u160_xfmt`() functions are similar, only putting the value
+of `u` in hexadecimal.
+
+The `u16_ofmt`() and `u160_ofmt`() functions are similar, only putting the value
+of `u` in octal.
+
+The `u16_bfmt`() and `u160_bfmt`() functions are similar, only putting the value
+of `u` in binary.
+
+It is possible to use `NULL` as `s` to only have length computation performed.
+
+Note that those are macros to [u64_fmt_generic](3).
+
+## Constants
+
+Some constants are available if needed, e.g. to allocate a buffer large enough :
+
+: *U16_FMT*
+:: Maximum number of `char` needed to hold a decimal string
+
+: *U16_FMTG*
+:: Maximum number of `char` needed to hold a thousand-separated decimal string
+
+: *U16_XFMT*
+:: Maximum number of `char` needed to hold an hexadecimal string
+
+: *U16_OFMT*
+:: Maximum number of `char` needed to hold an octal string
+
+: *U16_BFMT*
+:: Maximum number of `char` needed to hold a binary string
+
+Note that all of those include an extra `char` for a proper NUL-terminated
+string.
+
+# RETURN VALUE
+
+All of these return the length of the string written into `s`, or required to
+do so when `s` is `NULL`
diff --git a/doc/u16_le.3.md b/doc/u16_le.3.md
new file mode 100644
index 0000000..b1539ff
--- /dev/null
+++ b/doc/u16_le.3.md
@@ -0,0 +1,46 @@
+% limb manual
+% u16_le(3)
+
+# NAME
+
+u16\_le, u16\_be - convert a value to little\/big endian
+
+u16p\_le, u16p\_be - convert an u16 into little\/big endian
+
+u16pa\_le, u16pa\_be - convert an array of u16 into little\/big endian
+
+# SYNOPSIS
+
+    #include <limb/u16.h>
+
+```pre hl
+u16 u16_le(u16 <em>u</em>)
+u16 u16_be(u16 <em>u</em>)
+
+void u16p_le(u16 *<em>val</em>)
+void u16p_be(u16 *<em>val</em>)
+
+void u16pa_le(u16 *<em>val</em>, size_t <em>length</em>)
+void u16pa_be(u16 *<em>val</em>, size_t <em>length</em>)
+```
+
+# DESCRIPTION
+
+All of these aim to ensure a value or variable is in expected endianness.
+
+The `u16_le`() and `u16_be`() functions will return the value after having read
+the given number `u` as a little endian and big endian value respectively.
+
+The `u16p_le`() and `u16p_be`() functions are similar, only they take a pointer
+to an u16 and will convert it into little endian and big endian, respectively.
+
+Finally the `u16pa_le`() and `u16pa_be`() are similar to the previous two, only
+they take an array of u16 of size `length` and convert every element.
+
+These are actually macros to the relevant `uint16_*` functions as needed, i.e.
+if the requested endianness is that of the host, no operation is performed.
+
+# RETURN VALUE
+
+Only `u16_le`() and `u16_be`() have a return value, that of the number `u` read
+as a little or big endian number, respectively.
diff --git a/doc/u16_scan.3.md b/doc/u16_scan.3.md
new file mode 100644
index 0000000..fb79fe7
--- /dev/null
+++ b/doc/u16_scan.3.md
@@ -0,0 +1,67 @@
+% limb manual
+% u16_scan(3)
+
+# NAME
+
+u16\_scan, u160\_scan - Read an u16 value from a decimal string
+
+u16\_xscan, u160\_xscan - Read an u16 value from an hexadecimal string
+
+u16\_oscan, u160\_oscan - Read an u16 value from an octal string
+
+u16\_bscan, u160\_bscan - Read an u16 value from a binary string
+
+
+# SYNOPSIS
+
+    #include <limb/u16.h>
+
+```pre hl
+size_t u16_scan(u16 *<em>dst</em>, const char *<em>s</em>)
+size_t u160_scan(u16 *<em>dst</em>, const char *<em>s</em>)
+
+size_t u16_xscan(u16 *<em>dst</em>, const char *<em>s</em>)
+size_t u160_xscan(u16 *<em>dst</em>, const char *<em>s</em>)
+
+size_t u16_oscan(u16 *<em>dst</em>, const char *<em>s</em>)
+size_t u160_oscan(u16 *<em>dst</em>, const char *<em>s</em>)
+
+size_t u16_bscan(u16 *<em>dst</em>, const char *<em>s</em>)
+size_t u160_bscan(u16 *<em>dst</em>, const char *<em>s</em>)
+```
+
+# DESCRIPTION
+
+The `u16_scan`() function will place into `dst` the number read as a decimal
+value from the string `s`, stopping as soon as a character isn't valid.
+
+The `u160_scan`() function is similar but expects the string to contain a valid
+value and nothing else, stopping with a NUL-terminating byte.
+
+The `u16_xscan`() and `u160_xscan`() functions are similar, but expecting an
+hexadecimal value in `s`.
+
+The `u16_oscan`() and `u160_oscan`() functions are similar, but expecting an
+octal value in `s`.
+
+The `u16_bscan`() and `u160_bscan`() functions are similar, but expecting a
+binary value in `s`.
+
+
+# RETURN VALUE
+
+The `u16_*` family of functions return the number of characters read from `s`
+(starting at 0 when the first character is invalid).
+
+The `u160_*` family of functions return the number of characters read from `s`
+or 0 on error.
+
+# ERRORS
+
+The `u160_*` family of functions may fail and set `errno` if :
+
+: *EINVAL*
+:: Invalid character in `s`
+
+: *ERANGE*
+:: The value in `s` is too large
diff --git a/doc/u32_fmt.3.md b/doc/u32_fmt.3.md
new file mode 100644
index 0000000..1cab022
--- /dev/null
+++ b/doc/u32_fmt.3.md
@@ -0,0 +1,84 @@
+% limb manual
+% u32_fmt(3)
+
+# NAME
+
+u32\_fmt, u32\_fmtg, u320\_fmt - print an u32 as decimal value into a byte array
+
+u32\_xfmt, u320\_xfmt - print an u32 as hexadecimal value into a byte array
+
+u32\_ofmt, u320\_ofmt - print an u32 as octal value into a byte array
+
+u32\_bfmt, u320\_bfmt - print an u32 as binary value into a byte array
+
+
+# SYNOPSIS
+
+    #include <limb/u32.h>
+
+```pre hl
+size_t u32_fmt(char *<em>s</em>, u32 <em>u</em>)
+size_t u320_fmt(char *<em>s</em>, u32 <em>u</em>, size_t <em>min</em>)
+
+size_t u32_fmtg(char *<em>s</em>, u32 <em>u</em>)
+size_t u320_fmtg(char *<em>s</em>, u32 <em>u</em>, size_t <em>min</em>)
+
+size_t u32_xfmt(char *<em>s</em>, u32 <em>u</em>)
+size_t u320_xfmt(char *<em>s</em>, u32 <em>u</em>, size_t <em>min</em>)
+
+size_t u32_ofmt(char *<em>s</em>, u32 <em>u</em>)
+size_t u320_ofmt(char *<em>s</em>, u32 <em>u</em>, size_t <em>min</em>)
+
+size_t u32_bfmt(char *<em>s</em>, u32 <em>u</em>)
+size_t u320_bfmt(char *<em>s</em>, u32 <em>u</em>, size_t <em>min</em>)
+```
+
+# DESCRIPTION
+
+The `u32_fmt`() function will put into `s` the value of `u` in decimal.
+The `u320_fmt`() function will put into `s` the value of `u` in decimal,
+prefixing it with leading zeroes if it requires less than `min` characters to do
+so.
+
+The `u32_fmtg`() and `u320_fmtg`() function are similar, only they will use a
+coma (`,`) as thousand separator as needed.
+
+The `u32_xfmt`() and `u320_xfmt`() functions are similar, only putting the value
+of `u` in hexadecimal.
+
+The `u32_ofmt`() and `u320_ofmt`() functions are similar, only putting the value
+of `u` in octal.
+
+The `u32_bfmt`() and `u320_bfmt`() functions are similar, only putting the value
+of `u` in binary.
+
+It is possible to use `NULL` as `s` to only have length computation performed.
+
+Note that those are macros to [u64_fmt_generic](3).
+
+## Constants
+
+Some constants are available if needed, e.g. to allocate a buffer large enough :
+
+: *U32_FMT*
+:: Maximum number of `char` needed to hold a decimal string
+
+: *U32_FMTG*
+:: Maximum number of `char` needed to hold a thousand-separated decimal string
+
+: *U32_XFMT*
+:: Maximum number of `char` needed to hold an hexadecimal string
+
+: *U32_OFMT*
+:: Maximum number of `char` needed to hold an octal string
+
+: *U32_BFMT*
+:: Maximum number of `char` needed to hold a binary string
+
+Note that all of those include an extra `char` for a proper NUL-terminated
+string.
+
+# RETURN VALUE
+
+All of these return the length of the string written into `s`, or required to
+do so when `s` is `NULL`
diff --git a/doc/u32_le.3.md b/doc/u32_le.3.md
new file mode 100644
index 0000000..d2e6aea
--- /dev/null
+++ b/doc/u32_le.3.md
@@ -0,0 +1,46 @@
+% limb manual
+% u32_le(3)
+
+# NAME
+
+u32\_le, u32\_be - convert a value to little\/big endian
+
+u32p\_le, u32p\_be - convert an u32 into little\/big endian
+
+u32pa\_le, u32pa\_be - convert an array of u32 into little\/big endian
+
+# SYNOPSIS
+
+    #include <limb/u32.h>
+
+```pre hl
+u32 u32_le(u32 <em>u</em>)
+u32 u32_be(u32 <em>u</em>)
+
+void u32p_le(u32 *<em>val</em>)
+void u32p_be(u32 *<em>val</em>)
+
+void u32pa_le(u32 *<em>val</em>, size_t <em>length</em>)
+void u32pa_be(u32 *<em>val</em>, size_t <em>length</em>)
+```
+
+# DESCRIPTION
+
+All of these aim to ensure a value or variable is in expected endianness.
+
+The `u32_le`() and `u32_be`() functions will return the value after having read
+the given number `u` as a little endian and big endian value respectively.
+
+The `u32p_le`() and `u32p_be`() functions are similar, only they take a pointer
+to an u32 and will convert it into little endian and big endian, respectively.
+
+Finally the `u32pa_le`() and `u32pa_be`() are similar to the previous two, only
+they take an array of u32 of size `length` and convert every element.
+
+These are actually macros to the relevant `uint32_*` functions as needed, i.e.
+if the requested endianness is that of the host, no operation is performed.
+
+# RETURN VALUE
+
+Only `u32_le`() and `u32_be`() have a return value, that of the number `u` read
+as a little or big endian number, respectively.
diff --git a/doc/u32_scan.3.md b/doc/u32_scan.3.md
new file mode 100644
index 0000000..4e07202
--- /dev/null
+++ b/doc/u32_scan.3.md
@@ -0,0 +1,67 @@
+% limb manual
+% u32_scan(3)
+
+# NAME
+
+u32\_scan, u320\_scan - Read an u32 value from a decimal string
+
+u32\_xscan, u320\_xscan - Read an u32 value from an hexadecimal string
+
+u32\_oscan, u320\_oscan - Read an u32 value from an octal string
+
+u32\_bscan, u320\_bscan - Read an u32 value from a binary string
+
+
+# SYNOPSIS
+
+    #include <limb/u32.h>
+
+```pre hl
+size_t u32_scan(u32 *<em>dst</em>, const char *<em>s</em>)
+size_t u320_scan(u32 *<em>dst</em>, const char *<em>s</em>)
+
+size_t u32_xscan(u32 *<em>dst</em>, const char *<em>s</em>)
+size_t u320_xscan(u32 *<em>dst</em>, const char *<em>s</em>)
+
+size_t u32_oscan(u32 *<em>dst</em>, const char *<em>s</em>)
+size_t u320_oscan(u32 *<em>dst</em>, const char *<em>s</em>)
+
+size_t u32_bscan(u32 *<em>dst</em>, const char *<em>s</em>)
+size_t u320_bscan(u32 *<em>dst</em>, const char *<em>s</em>)
+```
+
+# DESCRIPTION
+
+The `u32_scan`() function will place into `dst` the number read as a decimal
+value from the string `s`, stopping as soon as a character isn't valid.
+
+The `u320_scan`() function is similar but expects the string to contain a valid
+value and nothing else, stopping with a NUL-terminating byte.
+
+The `u32_xscan`() and `u320_xscan`() functions are similar, but expecting an
+hexadecimal value in `s`.
+
+The `u32_oscan`() and `u320_oscan`() functions are similar, but expecting an
+octal value in `s`.
+
+The `u32_bscan`() and `u320_bscan`() functions are similar, but expecting a
+binary value in `s`.
+
+
+# RETURN VALUE
+
+The `u32_*` family of functions return the number of characters read from `s`
+(starting at 0 when the first character is invalid).
+
+The `u320_*` family of functions return the number of characters read from `s`
+or 0 on error.
+
+# ERRORS
+
+The `u320_*` family of functions may fail and set `errno` if :
+
+: *EINVAL*
+:: Invalid character in `s`
+
+: *ERANGE*
+:: The value in `s` is too large
diff --git a/doc/u64_fmt.3.md b/doc/u64_fmt.3.md
new file mode 100644
index 0000000..d6954b6
--- /dev/null
+++ b/doc/u64_fmt.3.md
@@ -0,0 +1,84 @@
+% limb manual
+% u64_fmt(3)
+
+# NAME
+
+u64\_fmt, u64\_fmtg, u640\_fmt - print an u64 as decimal value into a byte array
+
+u64\_xfmt, u640\_xfmt - print an u64 as hexadecimal value into a byte array
+
+u64\_ofmt, u640\_ofmt - print an u64 as octal value into a byte array
+
+u64\_bfmt, u640\_bfmt - print an u64 as binary value into a byte array
+
+
+# SYNOPSIS
+
+    #include <limb/u64.h>
+
+```pre hl
+size_t u64_fmt(char *<em>s</em>, u64 <em>u</em>)
+size_t u640_fmt(char *<em>s</em>, u64 <em>u</em>, size_t <em>min</em>)
+
+size_t u64_fmtg(char *<em>s</em>, u64 <em>u</em>)
+size_t u640_fmtg(char *<em>s</em>, u64 <em>u</em>, size_t <em>min</em>)
+
+size_t u64_xfmt(char *<em>s</em>, u64 <em>u</em>)
+size_t u640_xfmt(char *<em>s</em>, u64 <em>u</em>, size_t <em>min</em>)
+
+size_t u64_ofmt(char *<em>s</em>, u64 <em>u</em>)
+size_t u640_ofmt(char *<em>s</em>, u64 <em>u</em>, size_t <em>min</em>)
+
+size_t u64_bfmt(char *<em>s</em>, u64 <em>u</em>)
+size_t u640_bfmt(char *<em>s</em>, u64 <em>u</em>, size_t <em>min</em>)
+```
+
+# DESCRIPTION
+
+The `u64_fmt`() function will put into `s` the value of `u` in decimal.
+The `u640_fmt`() function will put into `s` the value of `u` in decimal,
+prefixing it with leading zeroes if it requires less than `min` characters to do
+so.
+
+The `u64_fmtg`() and `u640_fmtg`() function are similar, only they will use a
+coma (`,`) as thousand separator as needed.
+
+The `u64_xfmt`() and `u640_xfmt`() functions are similar, only putting the value
+of `u` in hexadecimal.
+
+The `u64_ofmt`() and `u640_ofmt`() functions are similar, only putting the value
+of `u` in octal.
+
+The `u64_bfmt`() and `u640_bfmt`() functions are similar, only putting the value
+of `u` in binary.
+
+It is possible to use `NULL` as `s` to only have length computation performed.
+
+Note that those are macros to [u64_fmt_generic](3).
+
+## Constants
+
+Some constants are available if needed, e.g. to allocate a buffer large enough :
+
+: *U64_FMT*
+:: Maximum number of `char` needed to hold a decimal string
+
+: *U64_FMTG*
+:: Maximum number of `char` needed to hold a thousand-separated decimal string
+
+: *U64_XFMT*
+:: Maximum number of `char` needed to hold an hexadecimal string
+
+: *U64_OFMT*
+:: Maximum number of `char` needed to hold an octal string
+
+: *U64_BFMT*
+:: Maximum number of `char` needed to hold a binary string
+
+Note that all of those include an extra `char` for a proper NUL-terminated
+string.
+
+# RETURN VALUE
+
+All of these return the length of the string written into `s`, or required to
+do so when `s` is `NULL`
diff --git a/doc/u64_fmt_generic.3.md b/doc/u64_fmt_generic.3.md
new file mode 100644
index 0000000..85b85ca
--- /dev/null
+++ b/doc/u64_fmt_generic.3.md
@@ -0,0 +1,38 @@
+% limb manual
+% u64_fmt_generic(3)
+
+# NAME
+
+u64\_fmt\_generic, u640\_fmt\_generic - print an u64 value into a byte array
+
+# SYNOPSIS
+
+    #include <limb/u64.h>
+
+```pre hl
+size_t u64_fmt_generic(char *<em>s</em>, u64 <em>u</em>, u8 <em>base</em>, u8 <em>grp</em>, const char <em>sep</em>)
+size_t u640_fmt_generic(char *<em>s</em>, u64 <em>u</em>, u8 <em>base</em>, size_t <em>min</em>, const char <em>fill</em>,
+                        u8 <em>grp</em>, const char <em>sep</em>)
+```
+
+# DESCRIPTION
+
+The `u64_fmt_generic`() function will put into `s` the value of `u` encoded in
+base `base`. If `grp` is not zero, `sep` will be used as separator every `grp`
+characters, starting from the end.
+
+The `u640_fmt_generic`() function is similar, only if the size needed is less
+than `min` it will fill it up by prefixing it using as many instances of `fill`
+as needed.
+
+It is possible to use `NULL` as `s` to only have length computation performed.
+
+## Macros
+
+Note that many macros are available for ease of use, such as [u64_fmt](3),
+[u32_fmt](3) or [u16_fmt](3) and their related macros.
+
+# RETURN VALUE
+
+Both functions return the length of the string written into `s`, or required to
+do so when `s` is `NULL`
diff --git a/doc/u64_le.3.md b/doc/u64_le.3.md
new file mode 100644
index 0000000..05c41f8
--- /dev/null
+++ b/doc/u64_le.3.md
@@ -0,0 +1,46 @@
+% limb manual
+% u64_le(3)
+
+# NAME
+
+u64\_le, u64\_be - convert a value to little\/big endian
+
+u64p\_le, u64p\_be - convert an u64 into little\/big endian
+
+u64pa\_le, u64pa\_be - convert an array of u64 into little\/big endian
+
+# SYNOPSIS
+
+    #include <limb/u64.h>
+
+```pre hl
+u64 u64_le(u64 <em>u</em>)
+u64 u64_be(u64 <em>u</em>)
+
+void u64p_le(u64 *<em>val</em>)
+void u64p_be(u64 *<em>val</em>)
+
+void u64pa_le(u64 *<em>val</em>, size_t <em>length</em>)
+void u64pa_be(u64 *<em>val</em>, size_t <em>length</em>)
+```
+
+# DESCRIPTION
+
+All of these aim to ensure a value or variable is in expected endianness.
+
+The `u64_le`() and `u64_be`() functions will return the value after having read
+the given number `u` as a little endian and big endian value respectively.
+
+The `u64p_le`() and `u64p_be`() functions are similar, only they take a pointer
+to an u64 and will convert it into little endian and big endian, respectively.
+
+Finally the `u64pa_le`() and `u64pa_be`() are similar to the previous two, only
+they take an array of u64 of size `length` and convert every element.
+
+These are actually macros to the relevant `uint64_*` functions as needed, i.e.
+if the requested endianness is that of the host, no operation is performed.
+
+# RETURN VALUE
+
+Only `u64_le`() and `u64_be`() have a return value, that of the number `u` read
+as a little or big endian number, respectively.
diff --git a/doc/u64_scan.3.md b/doc/u64_scan.3.md
new file mode 100644
index 0000000..5d0b2d5
--- /dev/null
+++ b/doc/u64_scan.3.md
@@ -0,0 +1,67 @@
+% limb manual
+% u64_scan(3)
+
+# NAME
+
+u64\_scan, u640\_scan - Read an u64 value from a decimal string
+
+u64\_xscan, u640\_xscan - Read an u64 value from an hexadecimal string
+
+u64\_oscan, u640\_oscan - Read an u64 value from an octal string
+
+u64\_bscan, u640\_bscan - Read an u64 value from a binary string
+
+
+# SYNOPSIS
+
+    #include <limb/u64.h>
+
+```pre hl
+size_t u64_scan(u64 *<em>dst</em>, const char *<em>s</em>)
+size_t u640_scan(u64 *<em>dst</em>, const char *<em>s</em>)
+
+size_t u64_xscan(u64 *<em>dst</em>, const char *<em>s</em>)
+size_t u640_xscan(u64 *<em>dst</em>, const char *<em>s</em>)
+
+size_t u64_oscan(u64 *<em>dst</em>, const char *<em>s</em>)
+size_t u640_oscan(u64 *<em>dst</em>, const char *<em>s</em>)
+
+size_t u64_bscan(u64 *<em>dst</em>, const char *<em>s</em>)
+size_t u640_bscan(u64 *<em>dst</em>, const char *<em>s</em>)
+```
+
+# DESCRIPTION
+
+The `u64_scan`() function will place into `dst` the number read as a decimal
+value from the string `s`, stopping as soon as a character isn't valid.
+
+The `u640_scan`() function is similar but expects the string to contain a valid
+value and nothing else, stopping with a NUL-terminating byte.
+
+The `u64_xscan`() and `u640_xscan`() functions are similar, but expecting an
+hexadecimal value in `s`.
+
+The `u64_oscan`() and `u640_oscan`() functions are similar, but expecting an
+octal value in `s`.
+
+The `u64_bscan`() and `u640_bscan`() functions are similar, but expecting a
+binary value in `s`.
+
+
+# RETURN VALUE
+
+The `u64_*` family of functions return the number of characters read from `s`
+(starting at 0 when the first character is invalid).
+
+The `u640_*` family of functions return the number of characters read from `s`
+or 0 on error.
+
+# ERRORS
+
+The `u640_*` family of functions may fail and set `errno` if :
+
+: *EINVAL*
+:: Invalid character in `s`
+
+: *ERANGE*
+:: The value in `s` is too large
diff --git a/include/limb/u16.h b/include/limb/u16.h
index 9c824c6..8fada79 100644
--- a/include/limb/u16.h
+++ b/include/limb/u16.h
@@ -3,10 +3,47 @@
 
 #include <skalibs/uint16.h>
 #include "limb/int.h"
+#include "limb/u64.h"
+
+#define u16_le(u)                   uint16_little(u)
+#define u16_be(u)                   uint16_big(u)
+
+#define u16p_le(u)                  uint16_littlep(u)
+#define u16p_be(u)                  uint16_bigp(u)
+#define u16pa_le(a,l)               uint16_littlen(a,l)
+#define u16pa_be(a,l)               uint16_bign(a,l)
 
 #define u16_pack(dst,val)           uint16_pack((char *) (dst), val)
 #define u16_unpack(val,sce)         uint16_unpack((const char *) (sce), val)
 #define u16_pack_big(dst,val)       uint16_pack_big((char *) (dst), val)
 #define u16_unpack_big(val,sce)     uint16_unpack_big((const char *) (sce), val)
 
+#define U16_FMT      6
+#define U16_FMTG     7
+#define U16_XFMT     5
+#define U16_OFMT     7
+#define U16_BFMT    17
+
+#define u16_fmt(s,u)                u64_fmt_generic(s, (u), 10, 0, 0)
+#define u16_fmtg(s,u)               u64_fmt_generic(s, (u), 10, 3, ',')
+#define u16_xfmt(s,u)               u64_fmt_generic(s, (u), 16, 0, 0)
+#define u16_ofmt(s,u)               u64_fmt_generic(s, (u),  8, 0, 0)
+#define u16_bfmt(s,u)               u64_fmt_generic(s, (u),  2, 0, 0)
+
+#define u160_fmt(s,u,m)             u640_fmt_generic(s, (u), 10, (m), '0', 0, 0)
+#define u160_fmtg(s,u,m)            u640_fmt_generic(s, (u), 10, (m), '0', 3, ',')
+#define u160_xfmt(s,u,m)            u640_fmt_generic(s, (u), 16, (m), '0', 0, 0)
+#define u160_ofmt(s,u,m)            u640_fmt_generic(s, (u),  8, (m), '0', 0, 0)
+#define u160_bfmt(s,u,m)            u640_fmt_generic(s, (u),  2, (m), '0', 0, 0)
+
+#define u16_scan(dst,s)             uint16_scan(s, (dst))
+#define u16_xscan(dst,s)            uint16_xscan(s, (dst))
+#define u16_oscan(dst,s)            uint16_oscan(s, (dst))
+#define u16_bscan(dst,s)            uint16_bscan(s, (dst))
+
+#define u160_scan(dst,s)            uint160_scan(s, (dst))
+#define u160_xscan(dst,s)           uint160_xscan(s, (dst))
+#define u160_oscan(dst,s)           uint160_oscan(s, (dst))
+#define u160_bscan(dst,s)           uint160_bscan(s, (dst))
+
 #endif /* LIMB_U16_H */
diff --git a/include/limb/u32.h b/include/limb/u32.h
index d7c32de..ade1da1 100644
--- a/include/limb/u32.h
+++ b/include/limb/u32.h
@@ -3,10 +3,47 @@
 
 #include <skalibs/uint32.h>
 #include "limb/int.h"
+#include "limb/u64.h"
+
+#define u32_le(u)                   uint32_little(u)
+#define u32_be(u)                   uint32_big(u)
+
+#define u32p_le(u)                  uint32_littlep(u)
+#define u32p_be(u)                  uint32_bigp(u)
+#define u32pa_le(a,l)               uint32_littlen(a,l)
+#define u32pa_be(a,l)               uint32_bign(a,l)
 
 #define u32_pack(dst,val)           uint32_pack((char *) (dst), val)
 #define u32_unpack(val,sce)         uint32_unpack((const char *) (sce), val)
 #define u32_pack_big(dst,val)       uint32_pack_big((char *) (dst), val)
 #define u32_unpack_big(val,sce)     uint32_unpack_big((const char *) (sce), val)
 
+#define U32_FMT     11
+#define U32_FMTG    14
+#define U32_XFMT     9
+#define U32_OFMT    13
+#define U32_BFMT    33
+
+#define u32_fmt(s,u)                u64_fmt_generic(s, (u), 10, 0, 0)
+#define u32_fmtg(s,u)               u64_fmt_generic(s, (u), 10, 3, ',')
+#define u32_xfmt(s,u)               u64_fmt_generic(s, (u), 16, 0, 0)
+#define u32_ofmt(s,u)               u64_fmt_generic(s, (u),  8, 0, 0)
+#define u32_bfmt(s,u)               u64_fmt_generic(s, (u),  2, 0, 0)
+
+#define u320_fmt(s,u,m)             u640_fmt_generic(s, (u), 10, (m), '0', 0, 0)
+#define u320_fmtg(s,u,m)            u640_fmt_generic(s, (u), 10, (m), '0', 3, ',')
+#define u320_xfmt(s,u,m)            u640_fmt_generic(s, (u), 16, (m), '0', 0, 0)
+#define u320_ofmt(s,u,m)            u640_fmt_generic(s, (u),  8, (m), '0', 0, 0)
+#define u320_bfmt(s,u,m)            u640_fmt_generic(s, (u),  2, (m), '0', 0, 0)
+
+#define u32_scan(dst,s)             uint32_scan(s, (dst))
+#define u32_xscan(dst,s)            uint32_xscan(s, (dst))
+#define u32_oscan(dst,s)            uint32_oscan(s, (dst))
+#define u32_bscan(dst,s)            uint32_bscan(s, (dst))
+
+#define u320_scan(dst,s)            uint320_scan(s, (dst))
+#define u320_xscan(dst,s)           uint320_xscan(s, (dst))
+#define u320_oscan(dst,s)           uint320_oscan(s, (dst))
+#define u320_bscan(dst,s)           uint320_bscan(s, (dst))
+
 #endif /* LIMB_U32_H */
diff --git a/include/limb/u64.h b/include/limb/u64.h
index fe79dc4..f66fd20 100644
--- a/include/limb/u64.h
+++ b/include/limb/u64.h
@@ -5,6 +5,14 @@
 #include "limb/int.h"
 #include "limb/uint64.h"
 
+#define u64_le(u)                   uint64_little(u)
+#define u64_be(u)                   uint64_big(u)
+
+#define u64p_le(u)                  uint64_littlep(u)
+#define u64p_be(u)                  uint64_bigp(u)
+#define u64pa_le(a,l)               uint64_littlen(a,l)
+#define u64pa_be(a,l)               uint64_bign(a,l)
+
 #define u64_pack(dst,val)           uint64_pack((char *) (dst), val)
 #define u64_unpack(val,sce)         uint64_unpack((const char *) (sce), val)
 #define u64_pack_big(dst,val)       uint64_pack_big((char *) (dst), val)
@@ -12,4 +20,36 @@
 #define u64_pack_trim(dst,val)      uint64_pack_trim((char *) (dst), val)
 #define u64_unpack_trim(val,sce)    uint64_unpack_trim((const char *) (sce), val)
 
+extern size_t u64_fmt_generic(char *s, u64 u, u8 base, u8 grp, const char sep);
+extern size_t u640_fmt_generic(char *s, u64 u, u8 base, size_t min, const char fill,
+                               u8 grp, const char sep);
+
+#define U64_FMT     21
+#define U64_FMTG    27
+#define U64_XFMT    17
+#define U64_OFMT    25
+#define U64_BFMT    65
+
+#define u64_fmt(s,u)                u64_fmt_generic(s, (u), 10, 0, 0)
+#define u64_fmtg(s,u)               u64_fmt_generic(s, (u), 10, 3, ',')
+#define u64_xfmt(s,u)               u64_fmt_generic(s, (u), 16, 0, 0)
+#define u64_ofmt(s,u)               u64_fmt_generic(s, (u),  8, 0, 0)
+#define u64_bfmt(s,u)               u64_fmt_generic(s, (u),  2, 0, 0)
+
+#define u640_fmt(s,u,m)             u640_fmt_generic(s, (u), 10, (m), '0', 0, 0)
+#define u640_fmtg(s,u,m)            u640_fmt_generic(s, (u), 10, (m), '0', 3, ',')
+#define u640_xfmt(s,u,m)            u640_fmt_generic(s, (u), 16, (m), '0', 0, 0)
+#define u640_ofmt(s,u,m)            u640_fmt_generic(s, (u),  8, (m), '0', 0, 0)
+#define u640_bfmt(s,u,m)            u640_fmt_generic(s, (u),  2, (m), '0', 0, 0)
+
+#define u64_scan(dst,s)             uint64_scan(s, (dst))
+#define u64_xscan(dst,s)            uint64_xscan(s, (dst))
+#define u64_oscan(dst,s)            uint64_oscan(s, (dst))
+#define u64_bscan(dst,s)            uint64_bscan(s, (dst))
+
+#define u640_scan(dst,s)            uint640_scan(s, (dst))
+#define u640_xscan(dst,s)           uint640_xscan(s, (dst))
+#define u640_oscan(dst,s)           uint640_oscan(s, (dst))
+#define u640_bscan(dst,s)           uint640_bscan(s, (dst))
+
 #endif /* LIMB_U64_H */
diff --git a/meta/libs/limb b/meta/libs/limb
index c07cca7..e90f061 100644
--- a/meta/libs/limb
+++ b/meta/libs/limb
@@ -12,6 +12,9 @@ obj/msb64.o
 # {,un}pack u64
 obj/uint64_pack_trim.o
 obj/uint64_unpack_trim.o
+# u64
+obj/u64_fmt_generic.o
+obj/u640_fmt_generic.o
 # data-encoding (integers or blobs)
 obj/saencdata.o
 # content-based chunking
diff --git a/src/u640_fmt_generic.c b/src/u640_fmt_generic.c
new file mode 100644
index 0000000..fd1ff8f
--- /dev/null
+++ b/src/u640_fmt_generic.c
@@ -0,0 +1,19 @@
+#include <string.h>
+#include "limb/u64.h"
+
+size_t
+u640_fmt_generic(char *s, u64 u, u8 base, size_t min, const char fill,
+                 u8 grp, const char sep)
+{
+    size_t len = u64_fmt_generic(0, u, base, grp, sep);
+
+    if (s) {
+        if (len < min) {
+            memset(s, fill, min - len);
+            s += min - len;
+        }
+        u64_fmt_generic(s, u, base, grp, sep);
+    }
+
+    return (len > min) ? len : min;
+}
diff --git a/src/u64_fmt_generic.c b/src/u64_fmt_generic.c
new file mode 100644
index 0000000..fb3ff18
--- /dev/null
+++ b/src/u64_fmt_generic.c
@@ -0,0 +1,29 @@
+#include <skalibs/fmtscan.h>
+#include "limb/u64.h"
+
+size_t
+u64_fmt_generic(char *s, u64 u, u8 base, u8 grp, const char sep)
+{
+    size_t len = 1;
+    u64 n = u;
+
+    while (n >= base) {
+        ++len;
+        n /= base;
+    }
+    if (grp)
+        len += (len - 1) / grp;
+
+    if (s) {
+        s += len;
+        n = 0;
+        do {
+            *--s = fmtscan_asc(u % base);
+            if (grp && !(++n % grp))
+                *--s = sep;
+            u /= base;
+        } while (u);
+    }
+
+    return len;
+}