summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-05-26 14:58:07 -0400
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-05-26 17:10:36 -0400
commitcf4af09e9a20e9cdaec4b3896eb6d10c27f89eba (patch)
tree016f876531f7dfc822be17f686074f0c859fd508
parent42fb27570262b52e2ca889030c621b5f4af76fe1 (diff)
No more (static inline) function bodies in headers
-rw-r--r--lib9p/srv.c9
-rw-r--r--lib9p/srv_include/lib9p/srv.h9
-rw-r--r--libdhcp/CMakeLists.txt1
-rw-r--r--libdhcp/dhcp_common.c104
-rw-r--r--libdhcp/dhcp_common.h96
-rw-r--r--libhw_cr/CMakeLists.txt1
-rw-r--r--libhw_cr/host_util.c29
-rw-r--r--libhw_cr/host_util.h33
-rw-r--r--libhw_cr/rp2040_dma.c18
-rw-r--r--libhw_cr/rp2040_dma.h5
-rw-r--r--libhw_cr/w5500_ll.c105
-rw-r--r--libhw_cr/w5500_ll.h91
-rw-r--r--libhw_generic/alarmclock.c16
-rw-r--r--libhw_generic/include/libhw/generic/alarmclock.h19
-rw-r--r--libmisc/CMakeLists.txt4
-rw-r--r--libmisc/endian.c136
-rw-r--r--libmisc/fmt.c14
-rw-r--r--libmisc/hash.c24
-rw-r--r--libmisc/include/libmisc/endian.h219
-rw-r--r--libmisc/include/libmisc/fmt.h18
-rw-r--r--libmisc/include/libmisc/hash.h20
-rw-r--r--libmisc/include/libmisc/rand.h32
-rw-r--r--libmisc/include/libmisc/utf8.h33
-rw-r--r--libmisc/rand.c38
-rw-r--r--libmisc/utf8.c40
25 files changed, 596 insertions, 518 deletions
diff --git a/lib9p/srv.c b/lib9p/srv.c
index 56fc3ec..32e9a9a 100644
--- a/lib9p/srv.c
+++ b/lib9p/srv.c
@@ -61,6 +61,15 @@ void lib9p_srv_acknowledge_flush(struct lib9p_srv_ctx *ctx) {
/* structs ********************************************************************/
+void lib9p_srv_stat_assert(struct lib9p_srv_stat stat) {
+ assert( ((bool)(stat.mode & LIB9P_DM_DIR )) == ((bool)(stat.qid.type & LIB9P_QT_DIR )) );
+ assert( ((bool)(stat.mode & LIB9P_DM_APPEND)) == ((bool)(stat.qid.type & LIB9P_QT_APPEND)) );
+ assert( ((bool)(stat.mode & LIB9P_DM_EXCL )) == ((bool)(stat.qid.type & LIB9P_QT_EXCL )) );
+ assert( ((bool)(stat.mode & LIB9P_DM_AUTH )) == ((bool)(stat.qid.type & LIB9P_QT_AUTH )) );
+ assert( ((bool)(stat.mode & LIB9P_DM_TMP )) == ((bool)(stat.qid.type & LIB9P_QT_TMP )) );
+ assert( (stat.size == 0) || !(stat.mode & LIB9P_DM_DIR) );
+}
+
enum srv_filetype {
SRV_FILETYPE_FILE,
SRV_FILETYPE_DIR,
diff --git a/lib9p/srv_include/lib9p/srv.h b/lib9p/srv_include/lib9p/srv.h
index c40c85a..eb87d6f 100644
--- a/lib9p/srv_include/lib9p/srv.h
+++ b/lib9p/srv_include/lib9p/srv.h
@@ -96,14 +96,7 @@ struct lib9p_srv_stat {
#endif
};
-static inline void lib9p_srv_stat_assert(struct lib9p_srv_stat stat) {
- assert( ((bool)(stat.mode & LIB9P_DM_DIR )) == ((bool)(stat.qid.type & LIB9P_QT_DIR )) );
- assert( ((bool)(stat.mode & LIB9P_DM_APPEND)) == ((bool)(stat.qid.type & LIB9P_QT_APPEND)) );
- assert( ((bool)(stat.mode & LIB9P_DM_EXCL )) == ((bool)(stat.qid.type & LIB9P_QT_EXCL )) );
- assert( ((bool)(stat.mode & LIB9P_DM_AUTH )) == ((bool)(stat.qid.type & LIB9P_QT_AUTH )) );
- assert( ((bool)(stat.mode & LIB9P_DM_TMP )) == ((bool)(stat.qid.type & LIB9P_QT_TMP )) );
- assert( (stat.size == 0) || !(stat.mode & LIB9P_DM_DIR) );
-}
+void lib9p_srv_stat_assert(struct lib9p_srv_stat stat);
/* interface definitions ******************************************************/
diff --git a/libdhcp/CMakeLists.txt b/libdhcp/CMakeLists.txt
index dee7cb6..69ad470 100644
--- a/libdhcp/CMakeLists.txt
+++ b/libdhcp/CMakeLists.txt
@@ -7,6 +7,7 @@ add_library(libdhcp INTERFACE)
target_include_directories(libdhcp PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_sources(libdhcp INTERFACE
dhcp_client.c
+ dhcp_common.c
)
target_link_libraries(libdhcp INTERFACE
libmisc
diff --git a/libdhcp/dhcp_common.c b/libdhcp/dhcp_common.c
new file mode 100644
index 0000000..d691836
--- /dev/null
+++ b/libdhcp/dhcp_common.c
@@ -0,0 +1,104 @@
+/* libdhcp/dhcp_common.c - Base definitions for the DHCP protocol
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include "dhcp_common.h"
+
+/**
+ * DHCP Options
+ * https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#options
+ */
+bool dhcp_opt_length_is_valid(uint8_t opt, uint16_t len) {
+ switch (opt) {
+ /* RFC 2132 */
+ case DHCP_OPT_PAD: return len == 0;
+ case DHCP_OPT_SUBNET_MASK: return len == 4;
+ case DHCP_OPT_TIME_OFFSET: return len == 4;
+ case DHCP_OPT_ROUTER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_TIME_SERVER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_NAME_SERVER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_DOMAIN_SERVER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_LOG_SERVER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_QUOTES_SERVER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_LPR_SERVER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_IMPRESS_SERVER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_RLP_SERVER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_HOSTNAME: return len >= 1;
+ case DHCP_OPT_BOOT_FILE_SIZE: return len == 2;
+ case DHCP_OPT_MERIT_DUMP_FILE: return len >= 1;
+ case DHCP_OPT_DOMAIN_NAME: return len >= 1;
+ case DHCP_OPT_SWAP_SERVER: return len == 4; /* IANA says length is "N", but RFC 2132 says "length is 4"; likely releated to errata ID 487 */
+ case DHCP_OPT_ROOT_PATH: return len >= 1;
+ case DHCP_OPT_EXTENSION_FILE: return len >= 1;
+ case DHCP_OPT_FORWARD_ONOFF: return len == 1;
+ case DHCP_OPT_SRCRTE_ONOFF: return len == 1;
+ case DHCP_OPT_POLICY_FILTER: return len >= 8 && len % 8 == 0;
+ case DHCP_OPT_MAX_DG_ASSEMBLY: return len == 2;
+ case DHCP_OPT_DEFAULT_IP_TTL: return len == 1;
+ case DHCP_OPT_MTU_TIMEOUT: return len == 4;
+ case DHCP_OPT_MTU_PLATEAU: return len >= 2 && len % 2 == 0;
+ case DHCP_OPT_MTU_INTERFACE: return len == 2;
+ case DHCP_OPT_MTU_SUBNET: return len == 1;
+ case DHCP_OPT_BROADCAST_ADDRESS: return len == 4;
+ case DHCP_OPT_MASK_DISCOVERY: return len == 1;
+ case DHCP_OPT_MASK_SUPPLIER: return len == 1;
+ case DHCP_OPT_ROUTER_DISCOVERY: return len == 1;
+ case DHCP_OPT_ROUTER_REQUEST: return len == 4;
+ case DHCP_OPT_STATIC_ROUTE: return len >= 8 && len % 8 == 0;
+ case DHCP_OPT_TRAILERS: return len == 1;
+ case DHCP_OPT_ARP_TIMEOUT: return len == 4;
+ case DHCP_OPT_ETHERNET: return len == 1;
+ case DHCP_OPT_DEFAULT_TCP_TTL: return len == 1;
+ case DHCP_OPT_KEEPALIVE_TIME: return len == 4;
+ case DHCP_OPT_KEEPALIVE_DATA: return len == 1;
+ case DHCP_OPT_NIS_DOMAIN: return len >= 1;
+ case DHCP_OPT_NIS_SERVERS: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_NTP_SERVERS: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_VENDOR_SPECIFIC: return len >= 1;
+ case DHCP_OPT_NETBIOS_NAME_SRV: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_NETBIOS_DIST_SRV: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_NETBIOS_NODE_TYPE: return len == 1;
+ case DHCP_OPT_NETBIOS_SCOPE: return len >= 1;
+ case DHCP_OPT_X_WINDOW_FONT: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_X_WINDOW_MANAGER: return len >= 4 && len % 4 == 0;
+ case DHCP_OPT_ADDRESS_REQUEST: return len == 4;
+ case DHCP_OPT_ADDRESS_TIME: return len == 4;
+ case DHCP_OPT_OVERLOAD: return len == 1;
+ case DHCP_OPT_DHCP_MSG_TYPE: return len == 1;
+ case DHCP_OPT_DHCP_SERVER_ID: return len == 4;
+ case DHCP_OPT_PARAMETER_LIST: return len >= 1;
+ case DHCP_OPT_DHCP_MESSAGE: return len >= 1;
+ case DHCP_OPT_DHCP_MAX_MSG_SIZE: return len == 2;
+ case DHCP_OPT_RENEWAL_TIME: return len == 4;
+ case DHCP_OPT_REBINDING_TIME: return len == 4;
+ case DHCP_OPT_CLASS_ID: return len >= 1;
+ case DHCP_OPT_CLIENT_ID: return len >= 2;
+
+ /* RFC 2132 */
+ case DHCP_OPT_END: return len == 0;
+
+ /* Unrecognized */
+ default:
+ return true;
+ }
+}
+
+/**
+ * DHCP Message Type 53 Values
+ * https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#message-type-53
+ */
+const char *dhcp_msgtyp_str(uint8_t typ) {
+ switch (typ) {
+ case DHCP_MSGTYP_DISCOVER: return "DHCP_MSGTYP_DISCOVER";
+ case DHCP_MSGTYP_OFFER: return "DHCP_MSGTYP_OFFER";
+ case DHCP_MSGTYP_REQUEST: return "DHCP_MSGTYP_REQUEST";
+ case DHCP_MSGTYP_DECLINE: return "DHCP_MSGTYP_DECLINE";
+ case DHCP_MSGTYP_ACK: return "DHCP_MSGTYP_ACK";
+ case DHCP_MSGTYP_NAK: return "DHCP_MSGTYP_NAK";
+ case DHCP_MSGTYP_RELEASE: return "DHCP_MSGTYP_RELEASE";
+ case DHCP_MSGTYP_INFORM: return "DHCP_MSGTYP_INFORM";
+ default: return const_byte_str(typ);
+ }
+}
diff --git a/libdhcp/dhcp_common.h b/libdhcp/dhcp_common.h
index 5b51ce2..a0cdd3c 100644
--- a/libdhcp/dhcp_common.h
+++ b/libdhcp/dhcp_common.h
@@ -1,6 +1,6 @@
/* libdhcp/dhcp_common.h - Base definitions for the DHCP protocol
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* -----------------------------------------------------------------------------
@@ -67,8 +67,9 @@
#ifndef _LIBDHCP_DHCP_COMMON_H_
#define _LIBDHCP_DHCP_COMMON_H_
-#include <libmisc/endian.h>
-#include <libmisc/log.h> /* for const_byte_str() */
+#include <libhw/generic/net.h> /* for struct net_ip4_addr */
+#include <libmisc/endian.h> /* for uint{n}be_t */
+#include <libmisc/log.h> /* for const_byte_str() */
/* Config *********************************************************************/
@@ -215,80 +216,7 @@ static const uint8_t dhcp_magic_cookie[] = {99, 130, 83, 99};
/* ... */
#define DHCP_OPT_END ((uint8_t)255) /* RFC2132: length: 0; meaning: None */
-static inline bool dhcp_opt_length_is_valid(uint8_t opt, uint16_t len) {
- switch (opt) {
- /* RFC 2132 */
- case DHCP_OPT_PAD: return len == 0;
- case DHCP_OPT_SUBNET_MASK: return len == 4;
- case DHCP_OPT_TIME_OFFSET: return len == 4;
- case DHCP_OPT_ROUTER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_TIME_SERVER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_NAME_SERVER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_DOMAIN_SERVER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_LOG_SERVER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_QUOTES_SERVER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_LPR_SERVER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_IMPRESS_SERVER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_RLP_SERVER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_HOSTNAME: return len >= 1;
- case DHCP_OPT_BOOT_FILE_SIZE: return len == 2;
- case DHCP_OPT_MERIT_DUMP_FILE: return len >= 1;
- case DHCP_OPT_DOMAIN_NAME: return len >= 1;
- case DHCP_OPT_SWAP_SERVER: return len == 4; /* IANA says length is "N", but RFC 2132 says "length is 4"; likely releated to errata ID 487 */
- case DHCP_OPT_ROOT_PATH: return len >= 1;
- case DHCP_OPT_EXTENSION_FILE: return len >= 1;
- case DHCP_OPT_FORWARD_ONOFF: return len == 1;
- case DHCP_OPT_SRCRTE_ONOFF: return len == 1;
- case DHCP_OPT_POLICY_FILTER: return len >= 8 && len % 8 == 0;
- case DHCP_OPT_MAX_DG_ASSEMBLY: return len == 2;
- case DHCP_OPT_DEFAULT_IP_TTL: return len == 1;
- case DHCP_OPT_MTU_TIMEOUT: return len == 4;
- case DHCP_OPT_MTU_PLATEAU: return len >= 2 && len % 2 == 0;
- case DHCP_OPT_MTU_INTERFACE: return len == 2;
- case DHCP_OPT_MTU_SUBNET: return len == 1;
- case DHCP_OPT_BROADCAST_ADDRESS: return len == 4;
- case DHCP_OPT_MASK_DISCOVERY: return len == 1;
- case DHCP_OPT_MASK_SUPPLIER: return len == 1;
- case DHCP_OPT_ROUTER_DISCOVERY: return len == 1;
- case DHCP_OPT_ROUTER_REQUEST: return len == 4;
- case DHCP_OPT_STATIC_ROUTE: return len >= 8 && len % 8 == 0;
- case DHCP_OPT_TRAILERS: return len == 1;
- case DHCP_OPT_ARP_TIMEOUT: return len == 4;
- case DHCP_OPT_ETHERNET: return len == 1;
- case DHCP_OPT_DEFAULT_TCP_TTL: return len == 1;
- case DHCP_OPT_KEEPALIVE_TIME: return len == 4;
- case DHCP_OPT_KEEPALIVE_DATA: return len == 1;
- case DHCP_OPT_NIS_DOMAIN: return len >= 1;
- case DHCP_OPT_NIS_SERVERS: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_NTP_SERVERS: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_VENDOR_SPECIFIC: return len >= 1;
- case DHCP_OPT_NETBIOS_NAME_SRV: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_NETBIOS_DIST_SRV: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_NETBIOS_NODE_TYPE: return len == 1;
- case DHCP_OPT_NETBIOS_SCOPE: return len >= 1;
- case DHCP_OPT_X_WINDOW_FONT: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_X_WINDOW_MANAGER: return len >= 4 && len % 4 == 0;
- case DHCP_OPT_ADDRESS_REQUEST: return len == 4;
- case DHCP_OPT_ADDRESS_TIME: return len == 4;
- case DHCP_OPT_OVERLOAD: return len == 1;
- case DHCP_OPT_DHCP_MSG_TYPE: return len == 1;
- case DHCP_OPT_DHCP_SERVER_ID: return len == 4;
- case DHCP_OPT_PARAMETER_LIST: return len >= 1;
- case DHCP_OPT_DHCP_MESSAGE: return len >= 1;
- case DHCP_OPT_DHCP_MAX_MSG_SIZE: return len == 2;
- case DHCP_OPT_RENEWAL_TIME: return len == 4;
- case DHCP_OPT_REBINDING_TIME: return len == 4;
- case DHCP_OPT_CLASS_ID: return len >= 1;
- case DHCP_OPT_CLIENT_ID: return len >= 2;
-
- /* RFC 2132 */
- case DHCP_OPT_END: return len == 0;
-
- /* Unrecognized */
- default:
- return true;
- }
-};
+bool dhcp_opt_length_is_valid(uint8_t opt, uint16_t len);
/**
* DHCP Message Type 53 Values
@@ -303,18 +231,6 @@ static inline bool dhcp_opt_length_is_valid(uint8_t opt, uint16_t len) {
#define DHCP_MSGTYP_RELEASE ((uint8_t) 7) /* RFC2132, client->server */
#define DHCP_MSGTYP_INFORM ((uint8_t) 8) /* RFC2132, client->server */
-static const char *dhcp_msgtyp_str(uint8_t typ) {
- switch (typ) {
- case DHCP_MSGTYP_DISCOVER: return "DHCP_MSGTYP_DISCOVER";
- case DHCP_MSGTYP_OFFER: return "DHCP_MSGTYP_OFFER";
- case DHCP_MSGTYP_REQUEST: return "DHCP_MSGTYP_REQUEST";
- case DHCP_MSGTYP_DECLINE: return "DHCP_MSGTYP_DECLINE";
- case DHCP_MSGTYP_ACK: return "DHCP_MSGTYP_ACK";
- case DHCP_MSGTYP_NAK: return "DHCP_MSGTYP_NAK";
- case DHCP_MSGTYP_RELEASE: return "DHCP_MSGTYP_RELEASE";
- case DHCP_MSGTYP_INFORM: return "DHCP_MSGTYP_INFORM";
- default: return const_byte_str(typ);
- }
-}
+const char *dhcp_msgtyp_str(uint8_t typ);
#endif /* _LIBDHCP_DHCP_COMMON_H_ */
diff --git a/libhw_cr/CMakeLists.txt b/libhw_cr/CMakeLists.txt
index ba20b26..9dd6a27 100644
--- a/libhw_cr/CMakeLists.txt
+++ b/libhw_cr/CMakeLists.txt
@@ -24,6 +24,7 @@ if (PICO_PLATFORM STREQUAL "rp2040")
rp2040_hwspi.c
rp2040_hwtimer.c
w5500.c
+ w5500_ll.c
)
target_link_libraries(libhw_cr INTERFACE
hardware_gpio
diff --git a/libhw_cr/host_util.c b/libhw_cr/host_util.c
index 7b3200c..8cacd57 100644
--- a/libhw_cr/host_util.c
+++ b/libhw_cr/host_util.c
@@ -7,6 +7,8 @@
#include <error.h> /* for error(3gnu) */
#include <signal.h> /* for SIGRTMIN, SIGRTMAX */
+#include <libhw/generic/alarmclock.h> /* for {X}S_PER_S */
+
#include "host_util.h"
int host_sigrt_alloc(void) {
@@ -19,3 +21,30 @@ int host_sigrt_alloc(void) {
error(1, 0, "SIGRTMAX exceeded");
return ret;
}
+
+host_us_time_t ns_to_host_us_time(uint64_t time_ns) {
+ host_us_time_t ret;
+ ret.tv_sec = time_ns
+ /NS_PER_S;
+ ret.tv_usec = (time_ns - ((uint64_t)ret.tv_sec)*NS_PER_S)
+ /(NS_PER_S/US_PER_S);
+ return ret;
+}
+
+host_ns_time_t ns_to_host_ns_time(uint64_t time_ns) {
+ host_ns_time_t ret;
+ ret.tv_sec = time_ns
+ /NS_PER_S;
+ ret.tv_nsec = time_ns - ((uint64_t)ret.tv_sec)*NS_PER_S;
+ return ret;
+}
+
+uint64_t ns_from_host_us_time(host_us_time_t host_time) {
+ return (((uint64_t)host_time.tv_sec) * NS_PER_S) +
+ ((uint64_t)host_time.tv_usec * (NS_PER_S/US_PER_S));
+}
+
+uint64_t ns_from_host_ns_time(host_ns_time_t host_time) {
+ return (((uint64_t)host_time.tv_sec) * NS_PER_S) +
+ ((uint64_t)host_time.tv_nsec);
+}
diff --git a/libhw_cr/host_util.h b/libhw_cr/host_util.h
index 3f0a671..4adb94e 100644
--- a/libhw_cr/host_util.h
+++ b/libhw_cr/host_util.h
@@ -7,41 +7,18 @@
#ifndef _LIBHW_CR_HOST_UTIL_H_
#define _LIBHW_CR_HOST_UTIL_H_
+#include <stdint.h> /* for uint{n}_t */
#include <sys/time.h> /* for struct timeval */
#include <time.h> /* for struct timespec */
-#include <libhw/generic/alarmclock.h> /* for {X}S_PER_S */
-
int host_sigrt_alloc(void);
typedef struct timeval host_us_time_t;
typedef struct timespec host_ns_time_t;
-static inline host_us_time_t ns_to_host_us_time(uint64_t time_ns) {
- host_us_time_t ret;
- ret.tv_sec = time_ns
- /NS_PER_S;
- ret.tv_usec = (time_ns - ((uint64_t)ret.tv_sec)*NS_PER_S)
- /(NS_PER_S/US_PER_S);
- return ret;
-}
-
-static inline host_ns_time_t ns_to_host_ns_time(uint64_t time_ns) {
- host_ns_time_t ret;
- ret.tv_sec = time_ns
- /NS_PER_S;
- ret.tv_nsec = time_ns - ((uint64_t)ret.tv_sec)*NS_PER_S;
- return ret;
-}
-
-static inline uint64_t ns_from_host_us_time(host_us_time_t host_time) {
- return (((uint64_t)host_time.tv_sec) * NS_PER_S) +
- ((uint64_t)host_time.tv_usec * (NS_PER_S/US_PER_S));
-}
-
-static inline uint64_t ns_from_host_ns_time(host_ns_time_t host_time) {
- return (((uint64_t)host_time.tv_sec) * NS_PER_S) +
- ((uint64_t)host_time.tv_nsec);
-}
+host_us_time_t ns_to_host_us_time(uint64_t time_ns);
+host_ns_time_t ns_to_host_ns_time(uint64_t time_ns);
+uint64_t ns_from_host_us_time(host_us_time_t host_time);
+uint64_t ns_from_host_ns_time(host_ns_time_t host_time);
#endif /* _LIBHW_CR_HOST_UTIL_H_ */
diff --git a/libhw_cr/rp2040_dma.c b/libhw_cr/rp2040_dma.c
index e117c19..8901f06 100644
--- a/libhw_cr/rp2040_dma.c
+++ b/libhw_cr/rp2040_dma.c
@@ -1,5 +1,8 @@
/* libhw_cr/rp2040_dma.c - Utilities for sharing the DMA IRQs
*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
* Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
@@ -8,6 +11,21 @@
#include "rp2040_dma.h"
+/* Borrowed from <hardware/dma.h> *********************************************/
+
+dma_channel_hw_t *dma_channel_hw_addr(uint channel) {
+ assert(channel < NUM_DMA_CHANNELS);
+ return &dma_hw->ch[channel];
+}
+
+enum dma_channel_transfer_size {
+ DMA_SIZE_8 = 0, ///< Byte transfer (8 bits)
+ DMA_SIZE_16 = 1, ///< Half word transfer (16 bits)
+ DMA_SIZE_32 = 2 ///< Word transfer (32 bits)
+};
+
+/* Our own code ***************************************************************/
+
struct dmairq_handler_entry {
dmairq_handler_t fn;
void *arg;
diff --git a/libhw_cr/rp2040_dma.h b/libhw_cr/rp2040_dma.h
index e8bceec..5c1c7bb 100644
--- a/libhw_cr/rp2040_dma.h
+++ b/libhw_cr/rp2040_dma.h
@@ -21,10 +21,7 @@
/* Borrowed from <hardware/dma.h> *********************************************/
-static inline dma_channel_hw_t *dma_channel_hw_addr(uint channel) {
- assert(channel < NUM_DMA_CHANNELS);
- return &dma_hw->ch[channel];
-}
+dma_channel_hw_t *dma_channel_hw_addr(uint channel);
enum dma_channel_transfer_size {
DMA_SIZE_8 = 0, ///< Byte transfer (8 bits)
diff --git a/libhw_cr/w5500_ll.c b/libhw_cr/w5500_ll.c
new file mode 100644
index 0000000..d9f4fc5
--- /dev/null
+++ b/libhw_cr/w5500_ll.c
@@ -0,0 +1,105 @@
+/* libhw_cr/w5500_ll.c - Low-level library for the WIZnet W5500 chip
+ *
+ * Based entirely on the W5500 datasheet, v1.1.0.
+ * https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libmisc/alloc.h> /* for stack_alloc() */
+#include <libmisc/fmt.h>
+
+#include "w5500_ll.h"
+
+#if CONFIG_W5500_LL_DEBUG
+static void fmt_print_ctl_block(lo_interface fmt_dest, uint8_t b) {
+ static char *strs[] = {
+ "RES",
+ "REG",
+ "TX",
+ "RX",
+ };
+ fmt_print("CTL_BLOCK_SOCK(", (base10, (((b)>>5) & 0b111)), ", ", strs[((b)>>3)&0b11], ")");
+}
+#endif
+
+void
+#if CONFIG_W5500_LL_DEBUG
+_w5500ll_writev(const char *func,
+#else
+w5500ll_writev(
+#endif
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ const struct iovec *iov, int iovcnt,
+ size_t skip, size_t max)
+{
+ assert(!LO_IS_NULL(spidev));
+ assert((block & ~CTL_MASK_BLOCK) == 0);
+ assert(iov);
+ assert(iovcnt > 0);
+#if CONFIG_W5500_LL_DEBUG
+ log_n_debugln(W5500_LL,
+ func, "(): w5500ll_write(spidev",
+ ", addr=", (base16_u16_, addr),
+ ", block=", (ctl_block, block),
+ ", iov",
+ ", iovcnt=", iovcnt,
+ ")");
+#endif
+
+ uint8_t header[] = {
+ (uint8_t)((addr >> 8) & 0xFF),
+ (uint8_t)(addr & 0xFF),
+ (block & CTL_MASK_BLOCK) | CTL_W | CTL_OM_VDM,
+ };
+ int inner_cnt = 1+io_slice_cnt(iov, iovcnt, skip, max);
+ struct duplex_iovec *inner = stack_alloc(inner_cnt, struct duplex_iovec);
+ inner[0] = (struct duplex_iovec){
+ .iov_read_to = IOVEC_DISCARD,
+ .iov_write_from = header,
+ .iov_len = sizeof(header),
+ };
+ io_slice_wr_to_duplex(&inner[1], iov, iovcnt, skip, max);
+ LO_CALL(spidev, readwritev, inner, inner_cnt);
+}
+
+void
+#if CONFIG_W5500_LL_DEBUG
+_w5500ll_readv(const char *func,
+#else
+w5500ll_readv(
+#endif
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ const struct iovec *iov, int iovcnt,
+ size_t max)
+{
+ assert(!LO_IS_NULL(spidev));
+ assert((block & ~CTL_MASK_BLOCK) == 0);
+ assert(iov);
+ assert(iovcnt > 0);
+#if CONFIG_W5500_LL_DEBUG
+ log_n_debugln(W5500_LL,
+ func, "(): w5500ll_read(spidev",
+ ", addr=", (base16_u16_, addr),
+ ", block=", (ctl_block, block),
+ ", iov",
+ ", iovcnt=", iovcnt,
+ ")");
+#endif
+
+ uint8_t header[] = {
+ (uint8_t)((addr >> 8) & 0xFF),
+ (uint8_t)(addr & 0xFF),
+ (block & CTL_MASK_BLOCK) | CTL_R | CTL_OM_VDM,
+ };
+ int inner_cnt = 1+io_slice_cnt(iov, iovcnt, 0, max);
+ struct duplex_iovec *inner = stack_alloc(inner_cnt, struct duplex_iovec);
+ inner[0] = (struct duplex_iovec){
+ .iov_read_to = IOVEC_DISCARD,
+ .iov_write_from = header,
+ .iov_len = sizeof(header),
+ };
+ io_slice_rd_to_duplex(&inner[1], iov, iovcnt, 0, max);
+ LO_CALL(spidev, readwritev, inner, inner_cnt);
+}
diff --git a/libhw_cr/w5500_ll.h b/libhw_cr/w5500_ll.h
index aa41e7a..eeb2fb7 100644
--- a/libhw_cr/w5500_ll.h
+++ b/libhw_cr/w5500_ll.h
@@ -1,4 +1,4 @@
-/* libhw_cr/w5500_ll.h - Low-level header library for the WIZnet W5500 chip
+/* libhw_cr/w5500_ll.h - Low-level library for the WIZnet W5500 chip
*
* Based entirely on the W5500 datasheet, v1.1.0.
* https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf
@@ -13,8 +13,7 @@
#include <stdint.h> /* for uint{n}_t */
#include <string.h> /* for memcmp() */
-#include <libmisc/alloc.h> /* for stack_alloc() */
-#include <libmisc/assert.h> /* for assert(), static_assert() */
+#include <libmisc/assert.h> /* for static_assert() */
#include <libmisc/endian.h> /* for uint16be_t */
#include <libhw/generic/net.h> /* for struct net_eth_addr, struct net_ip4_addr */
@@ -53,102 +52,28 @@
#define CTL_OM_FDM2 0b10 /* fixed-length data mode: 2 byte data length */
#define CTL_OM_FDM4 0b11 /* fixed-length data mode: 4 byte data length */
-#if CONFIG_W5500_LL_DEBUG
-static void fmt_print_ctl_block(lo_interface fmt_dest, uint8_t b) {
- static char *strs[] = {
- "RES",
- "REG",
- "TX",
- "RX",
- };
- fmt_print("CTL_BLOCK_SOCK(", (base10, (((b)>>5) & 0b111)), ", ", strs[((b)>>3)&0b11], ")");
-}
-#endif
-
/* Even though SPI is a full-duplex protocol, the W5500's spiframe on top of it is only half-duplex.
* Lame. */
-static inline void
#if CONFIG_W5500_LL_DEBUG
#define w5500ll_writev(...) _w5500ll_writev(__func__, __VA_ARGS__)
-_w5500ll_writev(const char *func,
+void _w5500ll_writev(const char *func,
#else
-w5500ll_writev(
+void w5500ll_writev(
#endif
lo_interface spi spidev, uint16_t addr, uint8_t block,
const struct iovec *iov, int iovcnt,
- size_t skip, size_t max)
-{
- assert(!LO_IS_NULL(spidev));
- assert((block & ~CTL_MASK_BLOCK) == 0);
- assert(iov);
- assert(iovcnt > 0);
-#if CONFIG_W5500_LL_DEBUG
- log_n_debugln(W5500_LL,
- func, "(): w5500ll_write(spidev",
- ", addr=", (base16_u16_, addr),
- ", block=", (ctl_block, block),
- ", iov",
- ", iovcnt=", iovcnt,
- ")");
-#endif
+ size_t skip, size_t max);
- uint8_t header[] = {
- (uint8_t)((addr >> 8) & 0xFF),
- (uint8_t)(addr & 0xFF),
- (block & CTL_MASK_BLOCK) | CTL_W | CTL_OM_VDM,
- };
- int inner_cnt = 1+io_slice_cnt(iov, iovcnt, skip, max);
- struct duplex_iovec *inner = stack_alloc(inner_cnt, struct duplex_iovec);
- inner[0] = (struct duplex_iovec){
- .iov_read_to = IOVEC_DISCARD,
- .iov_write_from = header,
- .iov_len = sizeof(header),
- };
- io_slice_wr_to_duplex(&inner[1], iov, iovcnt, skip, max);
- LO_CALL(spidev, readwritev, inner, inner_cnt);
-}
-
-static inline void
#if CONFIG_W5500_LL_DEBUG
#define w5500ll_readv(...) _w5500ll_read(__func__, __VA_ARGS__)
-_w5500ll_readv(const char *func,
+void _w5500ll_readv(const char *func,
#else
-w5500ll_readv(
+void w5500ll_readv(
#endif
lo_interface spi spidev, uint16_t addr, uint8_t block,
const struct iovec *iov, int iovcnt,
- size_t max)
-{
- assert(!LO_IS_NULL(spidev));
- assert((block & ~CTL_MASK_BLOCK) == 0);
- assert(iov);
- assert(iovcnt > 0);
-#if CONFIG_W5500_LL_DEBUG
- log_n_debugln(W5500_LL,
- func, "(): w5500ll_read(spidev",
- ", addr=", (base16_u16_, addr),
- ", block=", (ctl_block, block),
- ", iov",
- ", iovcnt=", iovcnt,
- ")");
-#endif
-
- uint8_t header[] = {
- (uint8_t)((addr >> 8) & 0xFF),
- (uint8_t)(addr & 0xFF),
- (block & CTL_MASK_BLOCK) | CTL_R | CTL_OM_VDM,
- };
- int inner_cnt = 1+io_slice_cnt(iov, iovcnt, 0, max);
- struct duplex_iovec *inner = stack_alloc(inner_cnt, struct duplex_iovec);
- inner[0] = (struct duplex_iovec){
- .iov_read_to = IOVEC_DISCARD,
- .iov_write_from = header,
- .iov_len = sizeof(header),
- };
- io_slice_rd_to_duplex(&inner[1], iov, iovcnt, 0, max);
- LO_CALL(spidev, readwritev, inner, inner_cnt);
-}
+ size_t max);
/* Common chip-wide registers. ***********************************************/
diff --git a/libhw_generic/alarmclock.c b/libhw_generic/alarmclock.c
index e3aaea3..3579829 100644
--- a/libhw_generic/alarmclock.c
+++ b/libhw_generic/alarmclock.c
@@ -7,3 +7,19 @@
#include <libhw/generic/alarmclock.h>
lo_interface alarmclock bootclock = {};
+
+void alarmclock_sleep_for_ns(lo_interface alarmclock clock, uint64_t delta_ns) {
+ alarmclock_sleep_until_ns(clock, LO_CALL(clock, get_time_ns) + delta_ns);
+}
+
+void alarmclock_sleep_for_us(lo_interface alarmclock clock, uint64_t delta_us) {
+ alarmclock_sleep_for_ns(clock, delta_us * (NS_PER_S/US_PER_S));
+}
+
+void alarmclock_sleep_for_ms(lo_interface alarmclock clock, uint64_t delta_ms) {
+ alarmclock_sleep_for_ns(clock, delta_ms * (NS_PER_S/MS_PER_S));
+}
+
+void alarmclock_sleep_for_s(lo_interface alarmclock clock, uint64_t delta_s) {
+ alarmclock_sleep_for_ns(clock, delta_s * NS_PER_S);
+}
diff --git a/libhw_generic/include/libhw/generic/alarmclock.h b/libhw_generic/include/libhw/generic/alarmclock.h
index 7f603cb..697d4be 100644
--- a/libhw_generic/include/libhw/generic/alarmclock.h
+++ b/libhw_generic/include/libhw/generic/alarmclock.h
@@ -62,21 +62,10 @@ LO_INTERFACE(alarmclock);
void alarmclock_sleep_until_ns(lo_interface alarmclock clock, uint64_t abstime_ns);
-static inline void alarmclock_sleep_for_ns(lo_interface alarmclock clock, uint64_t delta_ns) {
- alarmclock_sleep_until_ns(clock, LO_CALL(clock, get_time_ns) + delta_ns);
-}
-
-static inline void alarmclock_sleep_for_us(lo_interface alarmclock clock, uint64_t delta_us) {
- alarmclock_sleep_for_ns(clock, delta_us * (NS_PER_S/US_PER_S));
-}
-
-static inline void alarmclock_sleep_for_ms(lo_interface alarmclock clock, uint64_t delta_ms) {
- alarmclock_sleep_for_ns(clock, delta_ms * (NS_PER_S/MS_PER_S));
-}
-
-static inline void alarmclock_sleep_for_s(lo_interface alarmclock clock, uint64_t delta_s) {
- alarmclock_sleep_for_ns(clock, delta_s * NS_PER_S);
-}
+void alarmclock_sleep_for_ns(lo_interface alarmclock clock, uint64_t delta_ns);
+void alarmclock_sleep_for_us(lo_interface alarmclock clock, uint64_t delta_us);
+void alarmclock_sleep_for_ms(lo_interface alarmclock clock, uint64_t delta_ms);
+void alarmclock_sleep_for_s(lo_interface alarmclock clock, uint64_t delta_s);
/* Globals ********************************************************************/
diff --git a/libmisc/CMakeLists.txt b/libmisc/CMakeLists.txt
index 48407bd..c6405ad 100644
--- a/libmisc/CMakeLists.txt
+++ b/libmisc/CMakeLists.txt
@@ -7,11 +7,15 @@ add_library(libmisc INTERFACE)
target_include_directories(libmisc PUBLIC INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_sources(libmisc INTERFACE
assert.c
+ endian.c
fmt.c
+ hash.c
intercept.c
linkedlist.c
log.c
map.c
+ rand.c
+ utf8.c
)
add_lib_test(libmisc test_assert)
diff --git a/libmisc/endian.c b/libmisc/endian.c
new file mode 100644
index 0000000..2528f48
--- /dev/null
+++ b/libmisc/endian.c
@@ -0,0 +1,136 @@
+/* libmisc/endian.c - Endian-conversion helpers
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libmisc/macro.h> /* for LM_FORCE_SEMICOLON */
+
+#include <libmisc/endian.h>
+
+#define endian_declare_wrappers(NBIT, ENDIAN) \
+ uint##NBIT##ENDIAN##_t uint##NBIT##ENDIAN##_marshal(uint##NBIT##_t in) { \
+ uint##NBIT##ENDIAN##_t out; \
+ uint##NBIT##ENDIAN##_encode(out.octets, in); \
+ return out; \
+ } \
+ uint##NBIT##_t uint##NBIT##ENDIAN##_unmarshal(uint##NBIT##ENDIAN##_t in) { \
+ return uint##NBIT##ENDIAN##_decode(in.octets); \
+ } \
+ LM_FORCE_SEMICOLON
+
+/* Big endian *****************************************************************/
+
+size_t uint16be_encode(uint8_t *out, uint16_t in) {
+ out[0] = (uint8_t)((in >> 8) & 0xFF);
+ out[1] = (uint8_t)((in >> 0) & 0xFF);
+ return 2;
+}
+
+uint16_t uint16be_decode(uint8_t *in) {
+ return (((uint16_t)(in[0])) << 8)
+ | (((uint16_t)(in[1])) << 0)
+ ;
+}
+
+size_t uint32be_encode(uint8_t *out, uint32_t in) {
+ out[0] = (uint8_t)((in >> 24) & 0xFF);
+ out[1] = (uint8_t)((in >> 16) & 0xFF);
+ out[2] = (uint8_t)((in >> 8) & 0xFF);
+ out[3] = (uint8_t)((in >> 0) & 0xFF);
+ return 4;
+}
+
+uint32_t uint32be_decode(uint8_t *in) {
+ return (((uint32_t)(in[0])) << 24)
+ | (((uint32_t)(in[1])) << 16)
+ | (((uint32_t)(in[2])) << 8)
+ | (((uint32_t)(in[3])) << 0)
+ ;
+}
+
+size_t uint64be_encode(uint8_t *out, uint64_t in) {
+ out[0] = (uint8_t)((in >> 56) & 0xFF);
+ out[1] = (uint8_t)((in >> 48) & 0xFF);
+ out[2] = (uint8_t)((in >> 40) & 0xFF);
+ out[3] = (uint8_t)((in >> 32) & 0xFF);
+ out[4] = (uint8_t)((in >> 24) & 0xFF);
+ out[5] = (uint8_t)((in >> 16) & 0xFF);
+ out[6] = (uint8_t)((in >> 8) & 0xFF);
+ out[7] = (uint8_t)((in >> 0) & 0xFF);
+ return 8;
+}
+
+uint64_t uint64be_decode(uint8_t *in) {
+ return (((uint64_t)(in[0])) << 56)
+ | (((uint64_t)(in[1])) << 48)
+ | (((uint64_t)(in[2])) << 40)
+ | (((uint64_t)(in[3])) << 32)
+ | (((uint64_t)(in[4])) << 24)
+ | (((uint64_t)(in[5])) << 16)
+ | (((uint64_t)(in[6])) << 8)
+ | (((uint64_t)(in[7])) << 0)
+ ;
+}
+
+endian_declare_wrappers(16, be);
+endian_declare_wrappers(32, be);
+endian_declare_wrappers(64, be);
+
+/* Little endian **************************************************************/
+
+size_t uint16le_encode(uint8_t *out, uint16_t in) {
+ out[0] = (uint8_t)((in >> 0) & 0xFF);
+ out[1] = (uint8_t)((in >> 8) & 0xFF);
+ return 2;
+}
+
+uint16_t uint16le_decode(uint8_t *in) {
+ return (((uint16_t)(in[0])) << 0)
+ | (((uint16_t)(in[1])) << 8)
+ ;
+}
+
+size_t uint32le_encode(uint8_t *out, uint32_t in) {
+ out[0] = (uint8_t)((in >> 0) & 0xFF);
+ out[1] = (uint8_t)((in >> 8) & 0xFF);
+ out[2] = (uint8_t)((in >> 16) & 0xFF);
+ out[3] = (uint8_t)((in >> 24) & 0xFF);
+ return 4;
+}
+
+uint32_t uint32le_decode(uint8_t *in) {
+ return (((uint32_t)(in[0])) << 0)
+ | (((uint32_t)(in[1])) << 8)
+ | (((uint32_t)(in[2])) << 16)
+ | (((uint32_t)(in[3])) << 24)
+ ;
+}
+
+size_t uint64le_encode(uint8_t *out, uint64_t in) {
+ out[0] = (uint8_t)((in >> 0) & 0xFF);
+ out[1] = (uint8_t)((in >> 8) & 0xFF);
+ out[2] = (uint8_t)((in >> 16) & 0xFF);
+ out[3] = (uint8_t)((in >> 24) & 0xFF);
+ out[4] = (uint8_t)((in >> 32) & 0xFF);
+ out[5] = (uint8_t)((in >> 40) & 0xFF);
+ out[6] = (uint8_t)((in >> 48) & 0xFF);
+ out[7] = (uint8_t)((in >> 56) & 0xFF);
+ return 8;
+}
+
+uint64_t uint64le_decode(uint8_t *in) {
+ return (((uint64_t)(in[0])) << 0)
+ | (((uint64_t)(in[1])) << 8)
+ | (((uint64_t)(in[2])) << 16)
+ | (((uint64_t)(in[3])) << 24)
+ | (((uint64_t)(in[4])) << 32)
+ | (((uint64_t)(in[5])) << 40)
+ | (((uint64_t)(in[6])) << 48)
+ | (((uint64_t)(in[7])) << 56)
+ ;
+}
+
+endian_declare_wrappers(16, le);
+endian_declare_wrappers(32, le);
+endian_declare_wrappers(64, le);
diff --git a/libmisc/fmt.c b/libmisc/fmt.c
index 6cf1d8d..d3ca14a 100644
--- a/libmisc/fmt.c
+++ b/libmisc/fmt.c
@@ -14,6 +14,20 @@ static const char *const hexdig = "0123456789ABCDEF";
/* small/trivial formatters ***************************************************/
+void fmt_print_mem(lo_interface fmt_dest w, const void *_str, size_t size) {
+ const uint8_t *str = _str;
+ while (size--)
+ fmt_print_byte(w, *(str++));
+}
+void fmt_print_str(lo_interface fmt_dest w, const char *str) {
+ while (*str)
+ fmt_print_byte(w, *(str++));
+}
+void fmt_print_strn(lo_interface fmt_dest w, const char *str, size_t size) {
+ while (size-- && *str)
+ fmt_print_byte(w, *(str++));
+}
+
void fmt_print_byte(lo_interface fmt_dest w, uint8_t b) {
LO_CALL(w, putb, b);
}
diff --git a/libmisc/hash.c b/libmisc/hash.c
new file mode 100644
index 0000000..3814cec
--- /dev/null
+++ b/libmisc/hash.c
@@ -0,0 +1,24 @@
+/* libmisc/hash.c - General-purpose hash utilities
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libmisc/hash.h>
+
+/* djb2 hash */
+void hash_init(hash_t *hash) {
+ *hash = 5381;
+}
+void hash_write(hash_t *hash, void *dat, size_t len) {
+ for (size_t i = 0; i < len; i++)
+ *hash = (*hash * 33) + (hash_t)(((unsigned char *)dat)[i]);
+}
+
+/* utilities */
+hash_t hash(void *dat, size_t len) {
+ hash_t h;
+ hash_init(&h);
+ hash_write(&h, dat, len);
+ return h;
+}
diff --git a/libmisc/include/libmisc/endian.h b/libmisc/include/libmisc/endian.h
index 75240fe..966c3bc 100644
--- a/libmisc/include/libmisc/endian.h
+++ b/libmisc/include/libmisc/endian.h
@@ -10,204 +10,25 @@
#include <stddef.h> /* for size_t */
#include <stdint.h> /* for uint{n}_t */
-#include <libmisc/assert.h>
-
-/* Big endian *****************************************************************/
-
-typedef struct {
- uint8_t octets[2];
-} uint16be_t;
-static_assert(sizeof(uint16be_t) == 2);
-
-static inline size_t uint16be_encode(uint8_t *out, uint16_t in) {
- out[0] = (uint8_t)((in >> 8) & 0xFF);
- out[1] = (uint8_t)((in >> 0) & 0xFF);
- return 2;
-}
-
-static inline uint16_t uint16be_decode(uint8_t *in) {
- return (((uint16_t)(in[0])) << 8)
- | (((uint16_t)(in[1])) << 0)
- ;
-}
-
-static inline uint16be_t uint16be_marshal(uint16_t in) {
- uint16be_t out;
- uint16be_encode(out.octets, in);
- return out;
-}
-
-static inline uint16_t uint16be_unmarshal(uint16be_t in) {
- return uint16be_decode(in.octets);
-}
-
-typedef struct {
- uint8_t octets[4];
-} uint32be_t;
-static_assert(sizeof(uint32be_t) == 4);
-
-static inline size_t uint32be_encode(uint8_t *out, uint32_t in) {
- out[0] = (uint8_t)((in >> 24) & 0xFF);
- out[1] = (uint8_t)((in >> 16) & 0xFF);
- out[2] = (uint8_t)((in >> 8) & 0xFF);
- out[3] = (uint8_t)((in >> 0) & 0xFF);
- return 4;
-}
-
-static inline uint32_t uint32be_decode(uint8_t *in) {
- return (((uint32_t)(in[0])) << 24)
- | (((uint32_t)(in[1])) << 16)
- | (((uint32_t)(in[2])) << 8)
- | (((uint32_t)(in[3])) << 0)
- ;
-}
-
-static inline uint32be_t uint32be_marshal(uint32_t in) {
- uint32be_t out;
- uint32be_encode(out.octets, in);
- return out;
-}
-
-static inline uint32_t uint32be_unmarshal(uint32be_t in) {
- return uint32be_decode(in.octets);
-}
-
-typedef struct {
- uint8_t octets[8];
-} uint64be_t;
-static_assert(sizeof(uint64be_t) == 8);
-
-static inline size_t uint64be_encode(uint8_t *out, uint64_t in) {
- out[0] = (uint8_t)((in >> 56) & 0xFF);
- out[1] = (uint8_t)((in >> 48) & 0xFF);
- out[2] = (uint8_t)((in >> 40) & 0xFF);
- out[3] = (uint8_t)((in >> 32) & 0xFF);
- out[4] = (uint8_t)((in >> 24) & 0xFF);
- out[5] = (uint8_t)((in >> 16) & 0xFF);
- out[6] = (uint8_t)((in >> 8) & 0xFF);
- out[7] = (uint8_t)((in >> 0) & 0xFF);
- return 8;
-}
-
-static inline uint64_t uint64be_decode(uint8_t *in) {
- return (((uint64_t)(in[0])) << 56)
- | (((uint64_t)(in[1])) << 48)
- | (((uint64_t)(in[2])) << 40)
- | (((uint64_t)(in[3])) << 32)
- | (((uint64_t)(in[4])) << 24)
- | (((uint64_t)(in[5])) << 16)
- | (((uint64_t)(in[6])) << 8)
- | (((uint64_t)(in[7])) << 0)
- ;
-}
-
-static inline uint64be_t uint64be_marshal(uint64_t in) {
- uint64be_t out;
- uint64be_encode(out.octets, in);
- return out;
-}
-
-static inline uint64_t uint64be_unmarshal(uint64be_t in) {
- return uint64be_decode(in.octets);
-}
-
-/* Little endian **************************************************************/
-
-typedef struct {
- uint8_t octets[2];
-} uint16le_t;
-static_assert(sizeof(uint16le_t) == 2);
-
-static inline size_t uint16le_encode(uint8_t *out, uint16_t in) {
- out[0] = (uint8_t)((in >> 0) & 0xFF);
- out[1] = (uint8_t)((in >> 8) & 0xFF);
- return 2;
-}
-
-static inline uint16_t uint16le_decode(uint8_t *in) {
- return (((uint16_t)(in[0])) << 0)
- | (((uint16_t)(in[1])) << 8)
- ;
-}
-
-static inline uint16le_t uint16le_marshal(uint16_t in) {
- uint16le_t out;
- uint16le_encode(out.octets, in);
- return out;
-}
-
-static inline uint16_t uint16le_unmarshal(uint16le_t in) {
- return uint16le_decode(in.octets);
-}
-
-typedef struct {
- uint8_t octets[4];
-} uint32le_t;
-static_assert(sizeof(uint32le_t) == 4);
-
-static inline size_t uint32le_encode(uint8_t *out, uint32_t in) {
- out[0] = (uint8_t)((in >> 0) & 0xFF);
- out[1] = (uint8_t)((in >> 8) & 0xFF);
- out[2] = (uint8_t)((in >> 16) & 0xFF);
- out[3] = (uint8_t)((in >> 24) & 0xFF);
- return 4;
-}
-
-static inline uint32_t uint32le_decode(uint8_t *in) {
- return (((uint32_t)(in[0])) << 0)
- | (((uint32_t)(in[1])) << 8)
- | (((uint32_t)(in[2])) << 16)
- | (((uint32_t)(in[3])) << 24)
- ;
-}
-
-static inline uint32le_t uint32le_marshal(uint32_t in) {
- uint32le_t out;
- uint32le_encode(out.octets, in);
- return out;
-}
-
-static inline uint32_t uint32le_unmarshal(uint32le_t in) {
- return uint32le_decode(in.octets);
-}
-
-typedef struct {
- uint8_t octets[8];
-} uint64le_t;
-static_assert(sizeof(uint64le_t) == 8);
-
-static inline size_t uint64le_encode(uint8_t *out, uint64_t in) {
- out[0] = (uint8_t)((in >> 0) & 0xFF);
- out[1] = (uint8_t)((in >> 8) & 0xFF);
- out[2] = (uint8_t)((in >> 16) & 0xFF);
- out[3] = (uint8_t)((in >> 24) & 0xFF);
- out[4] = (uint8_t)((in >> 32) & 0xFF);
- out[5] = (uint8_t)((in >> 40) & 0xFF);
- out[6] = (uint8_t)((in >> 48) & 0xFF);
- out[7] = (uint8_t)((in >> 56) & 0xFF);
- return 8;
-}
-
-static inline uint64_t uint64le_decode(uint8_t *in) {
- return (((uint64_t)(in[0])) << 0)
- | (((uint64_t)(in[1])) << 8)
- | (((uint64_t)(in[2])) << 16)
- | (((uint64_t)(in[3])) << 24)
- | (((uint64_t)(in[4])) << 32)
- | (((uint64_t)(in[5])) << 40)
- | (((uint64_t)(in[6])) << 48)
- | (((uint64_t)(in[7])) << 56)
- ;
-}
-
-static inline uint64le_t uint64le_marshal(uint64_t in) {
- uint64le_t out;
- uint64le_encode(out.octets, in);
- return out;
-}
-
-static inline uint64_t uint64le_unmarshal(uint64le_t in) {
- return uint64le_decode(in.octets);
-}
+#define _endian_declare_conv(NBIT, ENDIAN) \
+ /* byte array encode/decode */ \
+ size_t uint##NBIT##ENDIAN##_encode(uint8_t *out, uint##NBIT##_t in); \
+ uint##NBIT##_t uint##NBIT##ENDIAN##_decode(uint8_t *in); \
+ /* struct marshal/unmarshal */ \
+ typedef struct { \
+ uint8_t octets[NBIT/8]; \
+ } uint##NBIT##ENDIAN##_t; \
+ uint##NBIT##ENDIAN##_t uint##NBIT##ENDIAN##_marshal(uint##NBIT##_t in); \
+ uint##NBIT##_t uint##NBIT##ENDIAN##_unmarshal(uint##NBIT##ENDIAN##_t in)
+
+_endian_declare_conv(16, be);
+_endian_declare_conv(32, be);
+_endian_declare_conv(64, be);
+
+_endian_declare_conv(16, le);
+_endian_declare_conv(32, le);
+_endian_declare_conv(64, le);
+
+#undef _endian_declare_conv
#endif /* _LIBMISC_ENDIAN_H_ */
diff --git a/libmisc/include/libmisc/fmt.h b/libmisc/include/libmisc/fmt.h
index c29c085..4abe730 100644
--- a/libmisc/include/libmisc/fmt.h
+++ b/libmisc/include/libmisc/fmt.h
@@ -24,21 +24,9 @@ LO_INTERFACE(fmt_dest);
/* Simple bytes. */
void fmt_print_byte(lo_interface fmt_dest w, uint8_t b);
-
-/* These are `static inline` so that the compiler can unroll the loops. */
-static inline void fmt_print_mem(lo_interface fmt_dest w, const void *_str, size_t size) {
- const uint8_t *str = _str;
- while (size--)
- fmt_print_byte(w, *(str++));
-}
-static inline void fmt_print_str(lo_interface fmt_dest w, const char *str) {
- while (*str)
- fmt_print_byte(w, *(str++));
-}
-static inline void fmt_print_strn(lo_interface fmt_dest w, const char *str, size_t size) {
- while (size-- && *str)
- fmt_print_byte(w, *(str++));
-}
+void fmt_print_mem(lo_interface fmt_dest w, const void *str, size_t size);
+void fmt_print_str(lo_interface fmt_dest w, const char *str);
+void fmt_print_strn(lo_interface fmt_dest w, const char *str, size_t size);
/* Quoted bytes. */
void fmt_print_qbyte(lo_interface fmt_dest w, uint8_t b);
diff --git a/libmisc/include/libmisc/hash.h b/libmisc/include/libmisc/hash.h
index 58a895f..029bd3b 100644
--- a/libmisc/include/libmisc/hash.h
+++ b/libmisc/include/libmisc/hash.h
@@ -10,22 +10,12 @@
#include <stddef.h> /* for size_t */
#include <stdint.h> /* for uint{n}_t */
-/* djb2 hash */
-typedef uint32_t hash_t;
-static inline void hash_init(hash_t *hash) {
- *hash = 5381;
-}
-static inline void hash_write(hash_t *hash, void *dat, size_t len) {
- for (size_t i = 0; i < len; i++)
- *hash = (*hash * 33) + (hash_t)(((unsigned char *)dat)[i]);
-}
+/* base */
+typedef uint32_t hash_t; /* size subject to change */
+void hash_init(hash_t *hash);
+void hash_write(hash_t *hash, void *dat, size_t len);
/* utilities */
-static inline hash_t hash(void *dat, size_t len) {
- hash_t h;
- hash_init(&h);
- hash_write(&h, dat, len);
- return h;
-}
+hash_t hash(void *dat, size_t len);
#endif /* _LIBMISC_HASH_H_ */
diff --git a/libmisc/include/libmisc/rand.h b/libmisc/include/libmisc/rand.h
index 7ef238b..ca16f42 100644
--- a/libmisc/include/libmisc/rand.h
+++ b/libmisc/include/libmisc/rand.h
@@ -7,40 +7,12 @@
#ifndef _LIBMISC_RAND_H_
#define _LIBMISC_RAND_H_
-#include <stdint.h> /* for uint{n}_t, UINT{n}_C() */
-#include <stdlib.h> /* for random() */
-
-#include <libmisc/assert.h>
+#include <stdint.h> /* for uint{n}_t */
/**
* Return a psuedo-random number in the half-open interval [0,cnt).
* `cnt` must not be greater than 1<<63.
*/
-static inline uint64_t rand_uint63n(uint64_t cnt) {
- assert(cnt != 0 && ((cnt-1) & 0x8000000000000000) == 0);
- if (cnt <= UINT64_C(1)<<31) {
- uint32_t fair_cnt = ((UINT32_C(1)<<31) / cnt) * cnt;
- uint32_t rnd;
- do {
- rnd = random();
- } while (rnd >= fair_cnt);
- return rnd % cnt;
- } else if (cnt <= UINT64_C(1)<<62) {
- uint64_t fair_cnt = ((UINT64_C(1)<<62) / cnt) * cnt;
- uint64_t rnd;
- do {
- rnd = (((uint64_t)random()) << 31) | random();
- } while (rnd >= fair_cnt);
- return rnd % cnt;
- } else if (cnt <= UINT64_C(1)<<63) {
- uint64_t fair_cnt = ((UINT64_C(1)<<63) / cnt) * cnt;
- uint64_t rnd;
- do {
- rnd = (((uint64_t)random()) << 62) | (((uint64_t)random()) << 31) | random();
- } while (rnd >= fair_cnt);
- return rnd % cnt;
- }
- assert_notreached("cnt is out of bounds");
-}
+uint64_t rand_uint63n(uint64_t cnt);
#endif /* _LIBMISC_RAND_H_ */
diff --git a/libmisc/include/libmisc/utf8.h b/libmisc/include/libmisc/utf8.h
index b5e1b0b..54fcc92 100644
--- a/libmisc/include/libmisc/utf8.h
+++ b/libmisc/include/libmisc/utf8.h
@@ -15,38 +15,9 @@
* bytes. Invalid UTF-8 is indicated with chlen=0. For valid UTF-8,
* chlen is always in the range [1, 4].
*/
-static inline void utf8_decode_codepoint(const uint8_t *str, size_t len, uint32_t *ret_ch, uint8_t *ret_chlen) {
- uint32_t ch;
- uint8_t chlen;
- if ((str[0] & 0b10000000) == 0b00000000) { ch = str[0] & 0b01111111; chlen = 1; }
- else if ((str[0] & 0b11100000) == 0b11000000) { ch = str[0] & 0b00011111; chlen = 2; }
- else if ((str[0] & 0b11110000) == 0b11100000) { ch = str[0] & 0b00001111; chlen = 3; }
- else if ((str[0] & 0b11111000) == 0b11110000) { ch = str[0] & 0b00000111; chlen = 4; }
- else goto invalid;
- if ((ch == 0 && chlen != 1) || chlen > len) goto invalid;
- for (uint8_t i = 1; i < chlen; i++) {
- if ((str[i] & 0b11000000) != 0b10000000) goto invalid;
- ch = (ch << 6) | (str[i] & 0b00111111);
- }
- if (ch > 0x10FFFF) goto invalid;
- *ret_ch = ch;
- *ret_chlen = chlen;
- return;
- invalid:
- *ret_chlen = 0;
-}
+void utf8_decode_codepoint(const uint8_t *str, size_t len, uint32_t *ret_ch, uint8_t *ret_chlen);
-static inline bool _utf8_is_valid(const uint8_t *str, size_t len, bool forbid_nul) {
- for (size_t pos = 0; pos < len;) {
- uint32_t ch;
- uint8_t chlen;
- utf8_decode_codepoint(&str[pos], len-pos, &ch, &chlen);
- if (chlen == 0 || (forbid_nul && ch == 0))
- return false;
- pos += chlen;
- }
- return true;
-}
+bool _utf8_is_valid(const uint8_t *str, size_t len, bool forbid_nul);
#define utf8_is_valid(str, len) _utf8_is_valid(str, len, false)
#define utf8_is_valid_without_nul(str, len) _utf8_is_valid(str, len, true)
diff --git a/libmisc/rand.c b/libmisc/rand.c
new file mode 100644
index 0000000..d1643ee
--- /dev/null
+++ b/libmisc/rand.c
@@ -0,0 +1,38 @@
+/* libmisc/rand.c - Non-crytpographic random-number utilities
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <stdlib.h> /* for random() */
+
+#include <libmisc/assert.h>
+
+#include <libmisc/rand.h>
+
+uint64_t rand_uint63n(uint64_t cnt) {
+ assert(cnt != 0 && ((cnt-1) & 0x8000000000000000) == 0);
+ if (cnt <= UINT64_C(1)<<31) {
+ uint32_t fair_cnt = ((UINT32_C(1)<<31) / cnt) * cnt;
+ uint32_t rnd;
+ do {
+ rnd = random();
+ } while (rnd >= fair_cnt);
+ return rnd % cnt;
+ } else if (cnt <= UINT64_C(1)<<62) {
+ uint64_t fair_cnt = ((UINT64_C(1)<<62) / cnt) * cnt;
+ uint64_t rnd;
+ do {
+ rnd = (((uint64_t)random()) << 31) | random();
+ } while (rnd >= fair_cnt);
+ return rnd % cnt;
+ } else if (cnt <= UINT64_C(1)<<63) {
+ uint64_t fair_cnt = ((UINT64_C(1)<<63) / cnt) * cnt;
+ uint64_t rnd;
+ do {
+ rnd = (((uint64_t)random()) << 62) | (((uint64_t)random()) << 31) | random();
+ } while (rnd >= fair_cnt);
+ return rnd % cnt;
+ }
+ assert_notreached("cnt is out of bounds");
+}
diff --git a/libmisc/utf8.c b/libmisc/utf8.c
new file mode 100644
index 0000000..5f91021
--- /dev/null
+++ b/libmisc/utf8.c
@@ -0,0 +1,40 @@
+/* libmisc/utf8.c - UTF-8 routines
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libmisc/utf8.h>
+
+void utf8_decode_codepoint(const uint8_t *str, size_t len, uint32_t *ret_ch, uint8_t *ret_chlen) {
+ uint32_t ch;
+ uint8_t chlen;
+ if ((str[0] & 0b10000000) == 0b00000000) { ch = str[0] & 0b01111111; chlen = 1; }
+ else if ((str[0] & 0b11100000) == 0b11000000) { ch = str[0] & 0b00011111; chlen = 2; }
+ else if ((str[0] & 0b11110000) == 0b11100000) { ch = str[0] & 0b00001111; chlen = 3; }
+ else if ((str[0] & 0b11111000) == 0b11110000) { ch = str[0] & 0b00000111; chlen = 4; }
+ else goto invalid;
+ if ((ch == 0 && chlen != 1) || chlen > len) goto invalid;
+ for (uint8_t i = 1; i < chlen; i++) {
+ if ((str[i] & 0b11000000) != 0b10000000) goto invalid;
+ ch = (ch << 6) | (str[i] & 0b00111111);
+ }
+ if (ch > 0x10FFFF) goto invalid;
+ *ret_ch = ch;
+ *ret_chlen = chlen;
+ return;
+ invalid:
+ *ret_chlen = 0;
+}
+
+bool _utf8_is_valid(const uint8_t *str, size_t len, bool forbid_nul) {
+ for (size_t pos = 0; pos < len;) {
+ uint32_t ch;
+ uint8_t chlen;
+ utf8_decode_codepoint(&str[pos], len-pos, &ch, &chlen);
+ if (chlen == 0 || (forbid_nul && ch == 0))
+ return false;
+ pos += chlen;
+ }
+ return true;
+}