aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRenato Botelho <garga@FreeBSD.org>2026-06-03 12:42:56 +0000
committerRenato Botelho <garga@FreeBSD.org>2026-06-03 12:46:59 +0000
commit5d154bfea96e26773ce19e8aa7103c338d128424 (patch)
tree38b149b64f46f85c5c7be77207ed8219d3812528
parentfc5acc9b0c2a155ce650f06402bd435319538662 (diff)
net-mgmt/iprange: Backport fix for 32-bit platforms
I've added it as a local patch instead of using PATCHFILES because upstream patch touches CMakeLists.txt, which is not present on release tarball for some unknown reason. Obtained from: upstream 268d7d8794f3f8a6c2d6f08dc4351e767990e683 Sponsored by: Rubicon Communications, LLC ("Netgate")
-rw-r--r--net-mgmt/iprange/Makefile7
-rw-r--r--net-mgmt/iprange/files/patch-268d7d8794f3f8a6c2d6f08dc4351e767990e6831083
2 files changed, 1087 insertions, 3 deletions
diff --git a/net-mgmt/iprange/Makefile b/net-mgmt/iprange/Makefile
index c25b60bdf246..bd912c6e6489 100644
--- a/net-mgmt/iprange/Makefile
+++ b/net-mgmt/iprange/Makefile
@@ -1,5 +1,6 @@
PORTNAME= iprange
PORTVERSION= 2.0.0
+PORTREVISION= 1
CATEGORIES= net-mgmt
MASTER_SITES= https://github.com/firehol/iprange/releases/download/v${PORTVERSION}/
@@ -10,13 +11,13 @@ WWW= https://github.com/firehol/iprange
LICENSE= GPLv2+
LICENSE_FILE= ${WRKSRC}/COPYING
-USES= tar:xz
-
-IGNORE_i386= __uint128_t is not available on i386 and is required for mandatory IPv6 support
+USES= autoreconf tar:xz
GNU_CONFIGURE= yes
GNU_CONFIGURE_MANPREFIX= ${PREFIX}/share
+PATCH_STRIP= -p1
+
PLIST_FILES= bin/iprange \
share/man/man1/iprange.1.gz
diff --git a/net-mgmt/iprange/files/patch-268d7d8794f3f8a6c2d6f08dc4351e767990e683 b/net-mgmt/iprange/files/patch-268d7d8794f3f8a6c2d6f08dc4351e767990e683
new file mode 100644
index 000000000000..1006dfafebf6
--- /dev/null
+++ b/net-mgmt/iprange/files/patch-268d7d8794f3f8a6c2d6f08dc4351e767990e683
@@ -0,0 +1,1083 @@
+From 268d7d8794f3f8a6c2d6f08dc4351e767990e683 Mon Sep 17 00:00:00 2001
+From: "R. Christian McDonald" <rcm@rcm.sh>
+Date: Tue, 2 Jun 2026 11:57:11 -0400
+Subject: [PATCH] ipv6: support 32-bit platforms without __uint128_t
+
+IPv6 support (2.0.0) relied on the __uint128_t builtin, which i386 and
+armv7 lack, so the build hard-errored there.
+
+Add src/uint128.h: on 64-bit it typedefs __uint128_t and inlines the bare
+operators (unchanged codegen); on 32-bit it falls back to a portable
+{hi, lo} struct with explicit arithmetic.
+---
+ Makefile.am | 1 +
+ configure.ac | 5 +-
+ src/iprange6.h | 53 +++++-----
+ src/iprange6_main.c | 24 ++---
+ src/ipset6.c | 4 +-
+ src/ipset6.h | 20 ++--
+ src/ipset6_binary.c | 36 +++----
+ src/ipset6_common.c | 8 +-
+ src/ipset6_diff.c | 24 ++---
+ src/ipset6_exclude.c | 16 +--
+ src/ipset6_load.c | 12 +--
+ src/ipset6_optimize.c | 16 +--
+ src/ipset6_print.c | 22 ++--
+ src/uint128.h | 230 ++++++++++++++++++++++++++++++++++++++++++
+ 15 files changed, 356 insertions(+), 121 deletions(-)
+ create mode 100644 src/uint128.h
+
+diff --git a/Makefile.am b/Makefile.am
+index 64353db..fa9043f 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -34,6 +34,7 @@ iprange_SOURCES = \
+ src/iprange.c \
+ src/iprange.h \
+ src/iprange6.h \
++ src/uint128.h \
+ src/iprange6_main.c \
+ src/ipset.c \
+ src/ipset.h \
+diff --git a/configure.ac b/configure.ac
+index 0015482..992b67b 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -73,8 +73,9 @@ CC="${PTHREAD_CC}"
+ AC_TYPE_UINT32_T
+ AC_C_INLINE
+
+-AC_CHECK_TYPE([__uint128_t], [],
+- [AC_MSG_ERROR([Your compiler does not support __uint128_t, required for IPv6 support.])],
++AC_CHECK_TYPE([__uint128_t],
++ [AC_MSG_NOTICE([Native __uint128_t detected — using hardware 128-bit integers for IPv6])],
++ [AC_MSG_NOTICE([__uint128_t not available — using portable 128-bit arithmetic for IPv6])],
+ [/* no includes needed */])
+
+ test "${with_compare_with_common}" = "yes" && AC_DEFINE([COMPARE_WITH_COMMON], [1], [compare settings])
+diff --git a/src/iprange6.h b/src/iprange6.h
+index 2bf0871..448f28c 100644
+--- a/src/iprange6.h
++++ b/src/iprange6.h
+@@ -2,10 +2,11 @@
+ #define IPRANGE_IPRANGE6_H
+
+ #include "iprange.h"
++#include "uint128.h"
+ #include <string.h>
+
+ /* IPv6 address type: 128-bit unsigned integer in host byte order */
+-typedef __uint128_t ipv6_addr_t;
++typedef uint128_t ipv6_addr_t;
+
+ /* IPv6 network address type: one field for the net address, one for broadcast */
+ typedef struct network_addr6 {
+@@ -14,11 +15,11 @@ typedef struct network_addr6 {
+ } network_addr6_t;
+
+ /* Maximum IPv6 address */
+-#define IPV6_ADDR_MAX ((ipv6_addr_t)((__uint128_t)(-1)))
++#define IPV6_ADDR_MAX U128_MAX
+
+ /* IPv4-mapped IPv6 prefix: ::ffff:0:0/96 */
+-#define IPV6_MAPPED_PREFIX ((ipv6_addr_t)0xFFFF00000000ULL)
+-#define IPV6_MAPPED_MASK ((ipv6_addr_t)0xFFFFFFFFULL)
++#define IPV6_MAPPED_PREFIX (u128_from_u64(0xFFFF00000000ULL))
++#define IPV6_MAPPED_MASK (u128_from_u64(0xFFFFFFFFULL))
+
+ #define IP6STR_MAX_LEN 46
+
+@@ -28,18 +29,18 @@ typedef struct network_addr6 {
+ /*----------------------------------------------------------------------*/
+
+ static inline ipv6_addr_t in6_addr_to_ipv6(const struct in6_addr *in6) {
+- ipv6_addr_t result = 0;
++ ipv6_addr_t result = U128_ZERO;
+ int i;
+ for(i = 0; i < 16; i++)
+- result = (result << 8) | in6->s6_addr[i];
++ result = u128_or(u128_shl(result, 8), u128_from_u64(in6->s6_addr[i]));
+ return result;
+ }
+
+ static inline void ipv6_to_in6_addr(ipv6_addr_t addr, struct in6_addr *in6) {
+ int i;
+ for(i = 15; i >= 0; i--) {
+- in6->s6_addr[i] = (uint8_t)(addr & 0xFF);
+- addr >>= 8;
++ in6->s6_addr[i] = (uint8_t)u128_lo64(addr);
++ addr = u128_shr(addr, 8);
+ }
+ }
+
+@@ -48,24 +49,24 @@ static inline void ipv6_to_in6_addr(ipv6_addr_t addr, struct in6_addr *in6) {
+ /*----------------------------------------------*/
+ static inline ipv6_addr_t netmask6(int prefix) {
+ if(prefix == 0)
+- return (ipv6_addr_t)0;
++ return U128_ZERO;
+ if(prefix >= 128)
+ return IPV6_ADDR_MAX;
+- return IPV6_ADDR_MAX << (128 - prefix);
++ return u128_shl(U128_MAX, 128 - prefix);
+ }
+
+ /*----------------------------------------------------*/
+ /* Compute broadcast address given address and prefix */
+ /*----------------------------------------------------*/
+ static inline ipv6_addr_t broadcast6(ipv6_addr_t addr, int prefix) {
+- return addr | ~netmask6(prefix);
++ return u128_or(addr, u128_not(netmask6(prefix)));
+ }
+
+ /*--------------------------------------------------*/
+ /* Compute network address given address and prefix */
+ /*--------------------------------------------------*/
+ static inline ipv6_addr_t network6(ipv6_addr_t addr, int prefix) {
+- return addr & netmask6(prefix);
++ return u128_and(addr, netmask6(prefix));
+ }
+
+ /*------------------------------------------------------------------*/
+@@ -73,9 +74,9 @@ static inline ipv6_addr_t network6(ipv6_addr_t addr, int prefix) {
+ /*------------------------------------------------------------------*/
+ static inline ipv6_addr_t set_bit6(ipv6_addr_t addr, int bitno, int val) {
+ if(val)
+- return addr | ((__uint128_t)1 << (128 - bitno));
++ return u128_or(addr, u128_shl(U128_ONE, 128 - bitno));
+ else
+- return addr & ~((__uint128_t)1 << (128 - bitno));
++ return u128_and(addr, u128_not(u128_shl(U128_ONE, 128 - bitno)));
+ }
+
+ /*-----------------------------------------------------------*/
+@@ -121,8 +122,8 @@ static inline network_addr6_t str2netaddr6(char *ipstr, int *err) {
+ || parsed_prefix < 0 || parsed_prefix > 128)) {
+ if(err) (*err)++;
+ fprintf(stderr, "%s: Invalid IPv6 prefix /%s\n", PROG, prefixstr);
+- netaddr.addr = 0;
+- netaddr.broadcast = 0;
++ netaddr.addr = U128_ZERO;
++ netaddr.broadcast = U128_ZERO;
+ return netaddr;
+ }
+ prefix = (int)parsed_prefix;
+@@ -131,8 +132,8 @@ static inline network_addr6_t str2netaddr6(char *ipstr, int *err) {
+ if(!str_to_ipv6(ipstr, &addr)) {
+ if(err) (*err)++;
+ fprintf(stderr, "%s: Invalid IPv6 address %s\n", PROG, ipstr);
+- netaddr.addr = 0;
+- netaddr.broadcast = 0;
++ netaddr.addr = U128_ZERO;
++ netaddr.broadcast = U128_ZERO;
+ return netaddr;
+ }
+
+@@ -149,21 +150,21 @@ static inline network_addr6_t str2netaddr6(char *ipstr, int *err) {
+ /* Check if an IPv6 address is IPv4-mapped (::ffff:x.x.x.x) */
+ /*-----------------------------------------------------------*/
+ static inline int is_ipv4_mapped(ipv6_addr_t addr) {
+- return (addr >> 32) == 0xFFFF;
++ return u128_eq(u128_shr(addr, 32), u128_from_u64(0xFFFF));
+ }
+
+ /*-----------------------------------------------------------*/
+ /* Convert IPv4 to IPv4-mapped IPv6 */
+ /*-----------------------------------------------------------*/
+ static inline ipv6_addr_t ipv4_to_mapped6(in_addr_t ipv4) {
+- return IPV6_MAPPED_PREFIX | (ipv6_addr_t)ipv4;
++ return u128_or(IPV6_MAPPED_PREFIX, u128_from_u64(ipv4));
+ }
+
+ /*-----------------------------------------------------------*/
+ /* Extract IPv4 from IPv4-mapped IPv6 */
+ /*-----------------------------------------------------------*/
+ static inline in_addr_t mapped6_to_ipv4(ipv6_addr_t addr) {
+- return (in_addr_t)(addr & IPV6_MAPPED_MASK);
++ return (in_addr_t)u128_lo64(u128_and(addr, IPV6_MAPPED_MASK));
+ }
+
+ /*-----------------------------------------------------------*/
+@@ -171,18 +172,18 @@ static inline in_addr_t mapped6_to_ipv4(ipv6_addr_t addr) {
+ /* Returns pointer to start of number within buf */
+ /* buf must be at least 40 bytes */
+ /*-----------------------------------------------------------*/
+-static inline char *u128_to_dec(char *buf, size_t buflen, __uint128_t val) {
++static inline char *u128_to_dec(char *buf, size_t buflen, uint128_t val) {
+ char *p = buf + buflen - 1;
+ *p = '\0';
+
+- if(val == 0) {
++ if(u128_is_zero(val)) {
+ *(--p) = '0';
+ return p;
+ }
+
+- while(val > 0) {
+- *(--p) = '0' + (char)(val % 10);
+- val /= 10;
++ while(!u128_is_zero(val)) {
++ *(--p) = '0' + (char)u128_mod10(val);
++ val = u128_div10(val);
+ }
+ return p;
+ }
+diff --git a/src/iprange6_main.c b/src/iprange6_main.c
+index 0484406..07c9e8d 100644
+--- a/src/iprange6_main.c
++++ b/src/iprange6_main.c
+@@ -42,9 +42,9 @@ static void free_pathnames6(char **files, size_t entries)
+ free(files);
+ }
+
+-static __uint128_t ipset6_report_unique_ips(ipset6 *ips, size_t *entries)
++static uint128_t ipset6_report_unique_ips(ipset6 *ips, size_t *entries)
+ {
+- __uint128_t unique_ips = ipset6_unique_ips(ips);
++ uint128_t unique_ips = ipset6_unique_ips(ips);
+ if(entries) *entries = ips->entries;
+ return unique_ips;
+ }
+@@ -337,7 +337,7 @@ int iprange6_run(int argc, char **argv, int mode, IPSET_PRINT_CMD print,
+ if(mode == MODE_COMBINE)
+ ipset6_print(root, print);
+ else if(mode == MODE_COUNT_UNIQUE_MERGED) {
+- __uint128_t unique_ips = ipset6_report_unique_ips(root, NULL);
++ uint128_t unique_ips = ipset6_report_unique_ips(root, NULL);
+ if(unlikely(header)) printf("entries,unique_ips\n");
+ printf("%zu,%s\n", root->entries, u128_to_dec(u128buf, sizeof(u128buf), unique_ips));
+ }
+@@ -381,7 +381,7 @@ int iprange6_run(int argc, char **argv, int mode, IPSET_PRINT_CMD print,
+ ips6 = ipset6_diff(root, second);
+ if(!quiet) ipset6_print(ips6, print);
+
+- if(ips6->unique_ips) ret = 1;
++ if(!u128_is_zero(ips6->unique_ips)) ret = 1;
+ else ret = 0;
+ }
+ else if(mode == MODE_COMPARE) {
+@@ -400,8 +400,8 @@ int iprange6_run(int argc, char **argv, int mode, IPSET_PRINT_CMD print,
+ for(ips2 = ips6; ips2; ips2 = ips2->next) {
+ ipset6 *comips;
+ size_t entries1, entries2;
+- __uint128_t unique1 = ipset6_report_unique_ips(ips6, &entries1);
+- __uint128_t unique2 = ipset6_report_unique_ips(ips2, &entries2);
++ uint128_t unique1 = ipset6_report_unique_ips(ips6, &entries1);
++ uint128_t unique2 = ipset6_report_unique_ips(ips2, &entries2);
+
+ if(ips6 == ips2) continue;
+
+@@ -416,7 +416,7 @@ int iprange6_run(int argc, char **argv, int mode, IPSET_PRINT_CMD print,
+ u128_to_dec(u128buf, sizeof(u128buf), unique1));
+ printf("%s,", u128_to_dec(u128buf, sizeof(u128buf), unique2));
+ printf("%s,", u128_to_dec(u128buf, sizeof(u128buf), comips->unique_ips));
+- printf("%s\n", u128_to_dec(u128buf, sizeof(u128buf), unique1 + unique2 - comips->unique_ips));
++ printf("%s\n", u128_to_dec(u128buf, sizeof(u128buf), u128_sub(u128_add(unique1, unique2), comips->unique_ips)));
+ ipset6_free(comips);
+ }
+ }
+@@ -437,8 +437,8 @@ int iprange6_run(int argc, char **argv, int mode, IPSET_PRINT_CMD print,
+ for(ips6 = root; ips6; ips6 = ips6->next) {
+ for(ips2 = second; ips2; ips2 = ips2->next) {
+ size_t entries1, entries2;
+- __uint128_t unique1 = ipset6_report_unique_ips(ips6, &entries1);
+- __uint128_t unique2 = ipset6_report_unique_ips(ips2, &entries2);
++ uint128_t unique1 = ipset6_report_unique_ips(ips6, &entries1);
++ uint128_t unique2 = ipset6_report_unique_ips(ips2, &entries2);
+
+ ipset6 *combined = ipset6_combine(ips6, ips2);
+ if(!combined) {
+@@ -451,7 +451,7 @@ int iprange6_run(int argc, char **argv, int mode, IPSET_PRINT_CMD print,
+ u128_to_dec(u128buf, sizeof(u128buf), unique1));
+ printf("%s,", u128_to_dec(u128buf, sizeof(u128buf), unique2));
+ printf("%s,", u128_to_dec(u128buf, sizeof(u128buf), combined->unique_ips));
+- printf("%s\n", u128_to_dec(u128buf, sizeof(u128buf), unique1 + unique2 - combined->unique_ips));
++ printf("%s\n", u128_to_dec(u128buf, sizeof(u128buf), u128_sub(u128_add(unique1, unique2), combined->unique_ips)));
+ ipset6_free(combined);
+ }
+ }
+@@ -468,7 +468,7 @@ int iprange6_run(int argc, char **argv, int mode, IPSET_PRINT_CMD print,
+
+ for(ips6 = root; ips6; ips6 = ips6->next) {
+ size_t entries;
+- __uint128_t unique_ips = ipset6_report_unique_ips(ips6, &entries);
++ uint128_t unique_ips = ipset6_report_unique_ips(ips6, &entries);
+
+ if(ips6 == first) continue;
+
+@@ -481,7 +481,7 @@ int iprange6_run(int argc, char **argv, int mode, IPSET_PRINT_CMD print,
+ ipset6_optimize(comips);
+ printf("%s,%zu,%s,", ips6->filename, entries,
+ u128_to_dec(u128buf, sizeof(u128buf), unique_ips));
+- printf("%s\n", u128_to_dec(u128buf, sizeof(u128buf), unique_ips + first->unique_ips - comips->unique_ips));
++ printf("%s\n", u128_to_dec(u128buf, sizeof(u128buf), u128_sub(u128_add(unique_ips, first->unique_ips), comips->unique_ips)));
+ ipset6_free(comips);
+ }
+ }
+diff --git a/src/ipset6.c b/src/ipset6.c
+index a34833d..79aa360 100644
+--- a/src/ipset6.c
++++ b/src/ipset6.c
+@@ -24,7 +24,7 @@ ipset6 *ipset6_create(const char *filename, size_t entries) {
+ ips->lines = 0;
+ ips->entries = 0;
+ ips->entries_max = entries;
+- ips->unique_ips = 0;
++ ips->unique_ips = U128_ZERO;
+ ips->next = NULL;
+ ips->prev = NULL;
+ ips->flags = 0;
+@@ -87,7 +87,7 @@ void ipset6_grow_internal(ipset6 *ips, size_t free_entries_needed) {
+ }
+ }
+
+-inline __uint128_t ipset6_unique_ips(ipset6 *ips) {
++inline uint128_t ipset6_unique_ips(ipset6 *ips) {
+ if(unlikely(!(ips->flags & IPSET_FLAG_OPTIMIZED)))
+ ipset6_optimize(ips);
+
+diff --git a/src/ipset6.h b/src/ipset6.h
+index aa9c9c5..c4cd11e 100644
+--- a/src/ipset6.h
++++ b/src/ipset6.h
+@@ -9,7 +9,7 @@ typedef struct ipset6 {
+ size_t lines;
+ size_t entries;
+ size_t entries_max;
+- __uint128_t unique_ips;
++ uint128_t unique_ips;
+
+ uint32_t flags;
+
+@@ -25,7 +25,7 @@ extern void ipset6_free_all(ipset6 *ips);
+
+ extern size_t prefix6_counters[129];
+
+-extern __uint128_t ipset6_unique_ips(ipset6 *ips);
++extern uint128_t ipset6_unique_ips(ipset6 *ips);
+
+ static inline int ipset6_entries_allocation_overflows(size_t entries) {
+ return (entries > (SIZE_MAX / sizeof(network_addr6_t)));
+@@ -56,26 +56,26 @@ static inline void ipset6_added_entry(ipset6 *ips) {
+
+ ips->lines++;
+
+- /* overflow-safe unique_ips: 2^128 doesn't fit in __uint128_t, saturate at max */
+- if(lo == 0 && hi == IPV6_ADDR_MAX)
++ /* overflow-safe unique_ips: 2^128 doesn't fit in uint128_t, saturate at max */
++ if(u128_is_zero(lo) && u128_eq(hi, IPV6_ADDR_MAX))
+ ips->unique_ips = IPV6_ADDR_MAX;
+ else {
+- __uint128_t size = hi - lo + 1;
+- if(ips->unique_ips > IPV6_ADDR_MAX - size)
++ uint128_t size = u128_add(u128_sub(hi, lo), U128_ONE);
++ if(u128_gt(ips->unique_ips, u128_sub(IPV6_ADDR_MAX, size)))
+ ips->unique_ips = IPV6_ADDR_MAX;
+ else
+- ips->unique_ips += size;
++ ips->unique_ips = u128_add(ips->unique_ips, size);
+ }
+
+ if(likely(ips->flags & IPSET_FLAG_OPTIMIZED && entries > 0)) {
+ /* overflow-safe adjacency: broadcast + 1 wraps at IPV6_ADDR_MAX */
+- if(unlikely(ips->netaddrs[entries - 1].broadcast != IPV6_ADDR_MAX &&
+- lo == (ips->netaddrs[entries - 1].broadcast + 1))) {
++ if(unlikely(!u128_eq(ips->netaddrs[entries - 1].broadcast, IPV6_ADDR_MAX) &&
++ u128_eq(lo, u128_inc(ips->netaddrs[entries - 1].broadcast)))) {
+ ips->netaddrs[entries - 1].broadcast = hi;
+ return;
+ }
+
+- if(likely(lo > ips->netaddrs[entries - 1].broadcast)) {
++ if(likely(u128_gt(lo, ips->netaddrs[entries - 1].broadcast))) {
+ ips->entries++;
+ return;
+ }
+diff --git a/src/ipset6_binary.c b/src/ipset6_binary.c
+index bc6caa9..c23a3e2 100644
+--- a/src/ipset6_binary.c
++++ b/src/ipset6_binary.c
+@@ -10,15 +10,15 @@ static void binary6_write_failed(void) {
+ exit(1);
+ }
+
+-static int binary6_validate_payload(ipset6 *ips, int header_optimized, size_t entries, __uint128_t expected_unique_ips, int *payload_is_optimized)
++static int binary6_validate_payload(ipset6 *ips, int header_optimized, size_t entries, uint128_t expected_unique_ips, int *payload_is_optimized)
+ {
+ size_t i;
+- __uint128_t actual_unique_ips = 0;
++ uint128_t actual_unique_ips = U128_ZERO;
+
+ *payload_is_optimized = 1;
+
+ if(!entries) {
+- if(unlikely(expected_unique_ips != 0)) {
++ if(unlikely(!u128_is_zero(expected_unique_ips))) {
+ fprintf(stderr, "%s: %s: unique IPs do not match the binary payload\n", PROG, ips->filename);
+ return 1;
+ }
+@@ -26,7 +26,7 @@ static int binary6_validate_payload(ipset6 *ips, int header_optimized, size_t en
+ }
+
+ for(i = 0; i < entries; i++) {
+- if(unlikely(ips->netaddrs[ips->entries + i].addr > ips->netaddrs[ips->entries + i].broadcast)) {
++ if(unlikely(u128_gt(ips->netaddrs[ips->entries + i].addr, ips->netaddrs[ips->entries + i].broadcast))) {
+ fprintf(stderr, "%s: %s: invalid binary record %zu has addr > broadcast\n", PROG, ips->filename, i + 1);
+ return 1;
+ }
+@@ -36,9 +36,9 @@ static int binary6_validate_payload(ipset6 *ips, int header_optimized, size_t en
+ network_addr6_t *prev = &ips->netaddrs[ips->entries + i - 1];
+ network_addr6_t *curr = &ips->netaddrs[ips->entries + i];
+
+- if(curr->addr < prev->addr
+- || curr->addr <= prev->broadcast
+- || (prev->broadcast != IPV6_ADDR_MAX && curr->addr == (prev->broadcast + 1))) {
++ if(u128_lt(curr->addr, prev->addr)
++ || u128_le(curr->addr, prev->broadcast)
++ || (!u128_eq(prev->broadcast, IPV6_ADDR_MAX) && u128_eq(curr->addr, u128_inc(prev->broadcast)))) {
+ *payload_is_optimized = 0;
+ break;
+ }
+@@ -46,8 +46,8 @@ static int binary6_validate_payload(ipset6 *ips, int header_optimized, size_t en
+
+ if(*payload_is_optimized) {
+ for(i = 0; i < entries; i++) {
+- __uint128_t size = ips->netaddrs[ips->entries + i].broadcast - ips->netaddrs[ips->entries + i].addr + 1;
+- actual_unique_ips += size;
++ uint128_t size = u128_add(u128_sub(ips->netaddrs[ips->entries + i].broadcast, ips->netaddrs[ips->entries + i].addr), U128_ONE);
++ actual_unique_ips = u128_add(actual_unique_ips, size);
+ }
+ }
+ else {
+@@ -57,7 +57,7 @@ static int binary6_validate_payload(ipset6 *ips, int header_optimized, size_t en
+ actual_unique_ips = expected_unique_ips;
+ }
+
+- if(unlikely(expected_unique_ips != actual_unique_ips)) {
++ if(unlikely(!u128_eq(expected_unique_ips, actual_unique_ips))) {
+ fprintf(stderr, "%s: %s: unique IPs do not match the binary payload\n", PROG, ips->filename);
+ return 1;
+ }
+@@ -91,9 +91,9 @@ static int parse_binary6_size_field(ipset6 *ips, const char *field, const char *
+ return 0;
+ }
+
+-static int parse_binary6_u128_field(ipset6 *ips, const char *field, const char *value, __uint128_t *parsed_value)
++static int parse_binary6_u128_field(ipset6 *ips, const char *field, const char *value, uint128_t *parsed_value)
+ {
+- __uint128_t result = 0;
++ uint128_t result = U128_ZERO;
+ const char *s = value;
+
+ if(!s || *s < '0' || *s > '9') {
+@@ -102,9 +102,9 @@ static int parse_binary6_u128_field(ipset6 *ips, const char *field, const char *
+ }
+
+ while(*s >= '0' && *s <= '9') {
+- __uint128_t prev = result;
+- result = result * 10 + (*s - '0');
+- if(unlikely(result < prev)) {
++ uint128_t prev = result;
++ result = u128_add(u128_mul_u64(result, 10), u128_from_u64(*s - '0'));
++ if(unlikely(u128_lt(result, prev))) {
+ fprintf(stderr, "%s: %s: %s value overflow\n", PROG, ips->filename, field);
+ return 1;
+ }
+@@ -123,7 +123,7 @@ static int parse_binary6_u128_field(ipset6 *ips, const char *field, const char *
+ int ipset6_load_binary_v20(FILE *fp, ipset6 *ips, int first_line_missing) {
+ char buffer[MAX_LINE + 1], *s;
+ size_t entries, bytes, lines, expected_bytes, record_size;
+- __uint128_t unique_ips;
++ uint128_t unique_ips;
+ uint32_t endian;
+ size_t loaded;
+ int header_optimized = 0;
+@@ -230,7 +230,7 @@ int ipset6_load_binary_v20(FILE *fp, ipset6 *ips, int first_line_missing) {
+ return 1;
+ }
+
+- if(unique_ips < entries && unique_ips != 0) {
++ if(u128_lt(unique_ips, u128_from_u64(entries)) && !u128_is_zero(unique_ips)) {
+ fprintf(stderr, "%s: %s: unique IPs cannot be less than entries (%zu)\n", PROG, ips->filename, entries);
+ return 1;
+ }
+@@ -263,7 +263,7 @@ int ipset6_load_binary_v20(FILE *fp, ipset6 *ips, int first_line_missing) {
+
+ ips->entries += loaded;
+ ips->lines += lines;
+- ips->unique_ips += unique_ips;
++ ips->unique_ips = u128_add(ips->unique_ips, unique_ips);
+ ips->flags &= ~IPSET_FLAG_OPTIMIZED;
+ if(header_optimized && payload_is_optimized) ips->flags |= IPSET_FLAG_OPTIMIZED;
+
+diff --git a/src/ipset6_common.c b/src/ipset6_common.c
+index f284144..d84cf1a 100644
+--- a/src/ipset6_common.c
++++ b/src/ipset6_common.c
+@@ -33,7 +33,7 @@ inline ipset6 *ipset6_common(ipset6 *ips1, ipset6 *ips2) {
+ hi2 = ips2->netaddrs[0].broadcast;
+
+ while(i1 < n1 && i2 < n2) {
+- if(lo1 > hi2) {
++ if(u128_gt(lo1, hi2)) {
+ i2++;
+ if(i2 < n2) {
+ lo2 = ips2->netaddrs[i2].addr;
+@@ -42,7 +42,7 @@ inline ipset6 *ipset6_common(ipset6 *ips1, ipset6 *ips2) {
+ continue;
+ }
+
+- if(lo2 > hi1) {
++ if(u128_gt(lo2, hi1)) {
+ i1++;
+ if(i1 < n1) {
+ lo1 = ips1->netaddrs[i1].addr;
+@@ -51,9 +51,9 @@ inline ipset6 *ipset6_common(ipset6 *ips1, ipset6 *ips2) {
+ continue;
+ }
+
+- lo = (lo1 > lo2) ? lo1 : lo2;
++ lo = u128_gt(lo1, lo2) ? lo1 : lo2;
+
+- if(hi1 < hi2) {
++ if(u128_lt(hi1, hi2)) {
+ hi = hi1;
+ i1++;
+ if(i1 < n1) {
+diff --git a/src/ipset6_diff.c b/src/ipset6_diff.c
+index f629ae3..7f07d9c 100644
+--- a/src/ipset6_diff.c
++++ b/src/ipset6_diff.c
+@@ -53,7 +53,7 @@ inline ipset6 *ipset6_diff(ipset6 *ips1, ipset6 *ips2) {
+ hi2 = ips2->netaddrs[0].broadcast;
+
+ while(i1 < n1 && i2 < n2) {
+- if(lo1 > hi2) {
++ if(u128_gt(lo1, hi2)) {
+ ipset6_add_ip_range(ips, lo2, hi2);
+ i2++;
+ if(i2 < n2) {
+@@ -62,7 +62,7 @@ inline ipset6 *ipset6_diff(ipset6 *ips1, ipset6 *ips2) {
+ }
+ continue;
+ }
+- if(lo2 > hi1) {
++ if(u128_gt(lo2, hi1)) {
+ ipset6_add_ip_range(ips, lo1, hi1);
+ i1++;
+ if(i1 < n1) {
+@@ -72,14 +72,14 @@ inline ipset6 *ipset6_diff(ipset6 *ips1, ipset6 *ips2) {
+ continue;
+ }
+
+- if(lo1 > lo2)
+- ipset6_add_ip_range(ips, lo2, lo1 - 1);
+- else if(lo2 > lo1)
+- ipset6_add_ip_range(ips, lo1, lo2 - 1);
++ if(u128_gt(lo1, lo2))
++ ipset6_add_ip_range(ips, lo2, u128_dec(lo1));
++ else if(u128_gt(lo2, lo1))
++ ipset6_add_ip_range(ips, lo1, u128_dec(lo2));
+
+- if(hi1 > hi2) {
+- if(hi2 == IPV6_ADDR_MAX) { i1++; i2++; }
+- else { lo1 = hi2 + 1; i2++; }
++ if(u128_gt(hi1, hi2)) {
++ if(u128_eq(hi2, IPV6_ADDR_MAX)) { i1++; i2++; }
++ else { lo1 = u128_inc(hi2); i2++; }
+ if(i2 < n2) {
+ lo2 = ips2->netaddrs[i2].addr;
+ hi2 = ips2->netaddrs[i2].broadcast;
+@@ -90,9 +90,9 @@ inline ipset6 *ipset6_diff(ipset6 *ips1, ipset6 *ips2) {
+ }
+ continue;
+ }
+- else if(hi2 > hi1) {
+- if(hi1 == IPV6_ADDR_MAX) { i1++; i2++; }
+- else { lo2 = hi1 + 1; i1++; }
++ else if(u128_gt(hi2, hi1)) {
++ if(u128_eq(hi1, IPV6_ADDR_MAX)) { i1++; i2++; }
++ else { lo2 = u128_inc(hi1); i1++; }
+ if(i1 < n1) {
+ lo1 = ips1->netaddrs[i1].addr;
+ hi1 = ips1->netaddrs[i1].broadcast;
+diff --git a/src/ipset6_exclude.c b/src/ipset6_exclude.c
+index c7bb969..75dbaaf 100644
+--- a/src/ipset6_exclude.c
++++ b/src/ipset6_exclude.c
+@@ -43,7 +43,7 @@ inline ipset6 *ipset6_exclude(ipset6 *ips1, ipset6 *ips2) {
+ hi2 = ips2->netaddrs[0].broadcast;
+
+ while(i1 < n1 && i2 < n2) {
+- if(lo1 > hi2) {
++ if(u128_gt(lo1, hi2)) {
+ i2++;
+ if(i2 < n2) {
+ lo2 = ips2->netaddrs[i2].addr;
+@@ -52,7 +52,7 @@ inline ipset6 *ipset6_exclude(ipset6 *ips1, ipset6 *ips2) {
+ continue;
+ }
+
+- if(lo2 > hi1) {
++ if(u128_gt(lo2, hi1)) {
+ ipset6_add_ip_range(ips, lo1, hi1);
+ i1++;
+ if(i1 < n1) {
+@@ -62,12 +62,12 @@ inline ipset6 *ipset6_exclude(ipset6 *ips1, ipset6 *ips2) {
+ continue;
+ }
+
+- if(lo1 < lo2) {
+- ipset6_add_ip_range(ips, lo1, lo2 - 1);
++ if(u128_lt(lo1, lo2)) {
++ ipset6_add_ip_range(ips, lo1, u128_dec(lo2));
+ lo1 = lo2;
+ }
+
+- if(hi1 == hi2) {
++ if(u128_eq(hi1, hi2)) {
+ i1++;
+ if(i1 < n1) {
+ lo1 = ips1->netaddrs[i1].addr;
+@@ -79,7 +79,7 @@ inline ipset6 *ipset6_exclude(ipset6 *ips1, ipset6 *ips2) {
+ hi2 = ips2->netaddrs[i2].broadcast;
+ }
+ }
+- else if(hi1 < hi2) {
++ else if(u128_lt(hi1, hi2)) {
+ i1++;
+ if(i1 < n1) {
+ lo1 = ips1->netaddrs[i1].addr;
+@@ -89,7 +89,7 @@ inline ipset6 *ipset6_exclude(ipset6 *ips1, ipset6 *ips2) {
+ else {
+ /* hi2 + 1 would overflow if hi2 == IPV6_ADDR_MAX, but that means
+ * ips2 covers everything from lo1..max, so nothing remains in ips1 */
+- if(hi2 == IPV6_ADDR_MAX) {
++ if(u128_eq(hi2, IPV6_ADDR_MAX)) {
+ i1++;
+ if(i1 < n1) {
+ lo1 = ips1->netaddrs[i1].addr;
+@@ -97,7 +97,7 @@ inline ipset6 *ipset6_exclude(ipset6 *ips1, ipset6 *ips2) {
+ }
+ }
+ else {
+- lo1 = hi2 + 1;
++ lo1 = u128_inc(hi2);
+ }
+ i2++;
+ if(i2 < n2) {
+diff --git a/src/ipset6_load.c b/src/ipset6_load.c
+index 6295ef2..b64ca9f 100644
+--- a/src/ipset6_load.c
++++ b/src/ipset6_load.c
+@@ -152,8 +152,8 @@ static network_addr6_t parse_address6(char *ipstr, int *err) {
+ else if(addr_class == 4) {
+ network_addr_t v4 = str2netaddr(ipstr, err);
+ if(*err) {
+- netaddr.addr = 0;
+- netaddr.broadcast = 0;
++ netaddr.addr = U128_ZERO;
++ netaddr.broadcast = U128_ZERO;
+ return netaddr;
+ }
+
+@@ -164,8 +164,8 @@ static network_addr6_t parse_address6(char *ipstr, int *err) {
+
+ if(err) (*err)++;
+ fprintf(stderr, "%s: Cannot parse address: %s\n", PROG, ipstr);
+- netaddr.addr = 0;
+- netaddr.broadcast = 0;
++ netaddr.addr = U128_ZERO;
++ netaddr.broadcast = U128_ZERO;
+ return netaddr;
+ }
+
+@@ -275,8 +275,8 @@ ipset6 *ipset6_load(const char *filename) {
+ continue;
+ }
+
+- ipv6_addr_t lo = (net1.addr < net2.addr) ? net1.addr : net2.addr;
+- ipv6_addr_t hi = (net1.broadcast > net2.broadcast) ? net1.broadcast : net2.broadcast;
++ ipv6_addr_t lo = u128_lt(net1.addr, net2.addr) ? net1.addr : net2.addr;
++ ipv6_addr_t hi = u128_gt(net1.broadcast, net2.broadcast) ? net1.broadcast : net2.broadcast;
+ ipset6_add_ip_range(ips, lo, hi);
+ }
+ break;
+diff --git a/src/ipset6_optimize.c b/src/ipset6_optimize.c
+index f11a7d4..d0c1734 100644
+--- a/src/ipset6_optimize.c
++++ b/src/ipset6_optimize.c
+@@ -6,10 +6,10 @@ static int compar_netaddr6(const void *p1, const void *p2) {
+ const network_addr6_t *na1 = (const network_addr6_t *)p1;
+ const network_addr6_t *na2 = (const network_addr6_t *)p2;
+
+- if(na1->addr < na2->addr) return -1;
+- if(na1->addr > na2->addr) return 1;
+- if(na1->broadcast > na2->broadcast) return -1;
+- if(na1->broadcast < na2->broadcast) return 1;
++ if(u128_lt(na1->addr, na2->addr)) return -1;
++ if(u128_gt(na1->addr, na2->addr)) return 1;
++ if(u128_gt(na1->broadcast, na2->broadcast)) return -1;
++ if(u128_lt(na1->broadcast, na2->broadcast)) return 1;
+ return 0;
+ }
+
+@@ -25,7 +25,7 @@ inline void ipset6_optimize(ipset6 *ips) {
+
+ if(unlikely(n == 0)) {
+ ips->flags |= IPSET_FLAG_OPTIMIZED;
+- ips->unique_ips = 0;
++ ips->unique_ips = U128_ZERO;
+ return;
+ }
+
+@@ -39,17 +39,17 @@ inline void ipset6_optimize(ipset6 *ips) {
+
+ ips->netaddrs = naddrs;
+ ips->entries = 0;
+- ips->unique_ips = 0;
++ ips->unique_ips = U128_ZERO;
+ ips->lines = 0;
+
+ lo = oaddrs[0].addr;
+ hi = oaddrs[0].broadcast;
+ for(i = 1; i < n; i++) {
+- if(oaddrs[i].broadcast <= hi)
++ if(u128_le(oaddrs[i].broadcast, hi))
+ continue;
+
+ /* overflow-safe adjacency check: hi + 1 would overflow if hi == max */
+- if(oaddrs[i].addr <= hi || (hi != IPV6_ADDR_MAX && oaddrs[i].addr == hi + 1)) {
++ if(u128_le(oaddrs[i].addr, hi) || (!u128_eq(hi, IPV6_ADDR_MAX) && u128_eq(oaddrs[i].addr, u128_inc(hi)))) {
+ hi = oaddrs[i].broadcast;
+ continue;
+ }
+diff --git a/src/ipset6_print.c b/src/ipset6_print.c
+index 32794b0..0dafe25 100644
+--- a/src/ipset6_print.c
++++ b/src/ipset6_print.c
+@@ -41,7 +41,7 @@ inline void print_addr6(ipv6_addr_t addr, int prefix) {
+ inline void print_addr6_range(ipv6_addr_t lo, ipv6_addr_t hi) {
+ char buf[IP6STR_MAX_LEN + 1];
+
+- if(unlikely(lo > hi)) {
++ if(unlikely(u128_gt(lo, hi))) {
+ ipv6_addr_t t = hi;
+ fprintf(stderr, "%s: WARNING: invalid range reversed start=%s", PROG, ip6str_r(buf, lo));
+ fprintf(stderr, " end=%s\n", ip6str_r(buf, hi));
+@@ -49,7 +49,7 @@ inline void print_addr6_range(ipv6_addr_t lo, ipv6_addr_t hi) {
+ lo = t;
+ }
+
+- if(lo == hi) {
++ if(u128_eq(lo, hi)) {
+ printf("%s%s-", print_prefix_ips, ip6str_r(buf, lo));
+ printf("%s%s\n", ip6str_r(buf, hi), print_suffix_ips);
+ }
+@@ -72,7 +72,7 @@ inline void print_addr6_single(ipv6_addr_t x) {
+ inline int split_range6(ipv6_addr_t addr, int prefix, ipv6_addr_t lo, ipv6_addr_t hi, void (*print)(ipv6_addr_t, int)) {
+ ipv6_addr_t bc, lower_half, upper_half;
+
+- if(unlikely(lo > hi)) {
++ if(unlikely(u128_gt(lo, hi))) {
+ ipv6_addr_t t = hi;
+ char buf[IP6STR_MAX_LEN + 1];
+ fprintf(stderr, "%s: WARNING: invalid range reversed start=%s", PROG, ip6str_r(buf, lo));
+@@ -88,13 +88,13 @@ inline int split_range6(ipv6_addr_t addr, int prefix, ipv6_addr_t lo, ipv6_addr_
+
+ bc = broadcast6(addr, prefix);
+
+- if(unlikely(lo < addr || hi > bc)) {
++ if(unlikely(u128_lt(lo, addr) || u128_gt(hi, bc))) {
+ char buf[IP6STR_MAX_LEN + 1];
+ fprintf(stderr, "%s: Out of range limits for IPv6 network %s/%d\n", PROG, ip6str_r(buf, addr), prefix);
+ return 0;
+ }
+
+- if(lo == addr && hi == bc && prefix6_enabled[prefix]) {
++ if(u128_eq(lo, addr) && u128_eq(hi, bc) && prefix6_enabled[prefix]) {
+ print(addr, prefix);
+ return 1;
+ }
+@@ -103,9 +103,9 @@ inline int split_range6(ipv6_addr_t addr, int prefix, ipv6_addr_t lo, ipv6_addr_
+ lower_half = addr;
+ upper_half = set_bit6(addr, prefix, 1);
+
+- if(hi < upper_half)
++ if(u128_lt(hi, upper_half))
+ return split_range6(lower_half, prefix, lo, hi, print);
+- else if(lo >= upper_half)
++ else if(u128_ge(lo, upper_half))
+ return split_range6(upper_half, prefix, lo, hi, print);
+ else
+ return (
+@@ -136,7 +136,7 @@ void ipset6_print(ipset6 *ips, IPSET_PRINT_CMD print) {
+
+ n = ips->entries;
+ for(i = 0; i < n; i++)
+- total += split_range6((__uint128_t)0, 0, ips->netaddrs[i].addr, ips->netaddrs[i].broadcast, print_addr6);
++ total += split_range6(U128_ZERO, 0, ips->netaddrs[i].addr, ips->netaddrs[i].broadcast, print_addr6);
+ break;
+
+ case PRINT_SINGLE_IPS:
+@@ -146,7 +146,7 @@ void ipset6_print(ipset6 *ips, IPSET_PRINT_CMD print) {
+ ipv6_addr_t end = ips->netaddrs[i].broadcast;
+ ipv6_addr_t x;
+
+- if(unlikely(start > end)) {
++ if(unlikely(u128_gt(start, end))) {
+ char buf[IP6STR_MAX_LEN + 1];
+ fprintf(stderr, "%s: WARNING: invalid range reversed start=%s", PROG, ip6str_r(buf, start));
+ fprintf(stderr, " end=%s\n", ip6str_r(buf, end));
+@@ -154,13 +154,13 @@ void ipset6_print(ipset6 *ips, IPSET_PRINT_CMD print) {
+ end = start;
+ start = x;
+ }
+- if(unlikely(end - start > IPV6_SINGLE_IP_CAP)) {
++ if(unlikely(u128_gt(u128_sub(end, start), u128_from_u64(IPV6_SINGLE_IP_CAP)))) {
+ char buf[IP6STR_MAX_LEN + 1];
+ fprintf(stderr, "%s: too big range eliminated start=%s", PROG, ip6str_r(buf, start));
+ fprintf(stderr, " end=%s\n", ip6str_r(buf, end));
+ continue;
+ }
+- for(x = start; x >= start && x <= end; x++) {
++ for(x = start; u128_ge(x, start) && u128_le(x, end); x = u128_inc(x)) {
+ print_addr6_single(x);
+ total++;
+ }
+diff --git a/src/uint128.h b/src/uint128.h
+new file mode 100644
+index 0000000..0da5873
+--- /dev/null
++++ b/src/uint128.h
+@@ -0,0 +1,230 @@
++#ifndef IPRANGE_UINT128_H
++#define IPRANGE_UINT128_H
++
++#include <stdint.h>
++
++/* Define IPRANGE_FORCE_PORTABLE_U128 to compile the portable (struct-based)
++ * 128-bit path even on platforms that have native __uint128_t. This lets the
++ * 32-bit code path be exercised by the test suite on a 64-bit host. */
++#if defined(__SIZEOF_INT128__) && !defined(IPRANGE_FORCE_PORTABLE_U128)
++
++/* ================================================================
++ * Native 128-bit path (64-bit platforms with compiler support).
++ * Every function compiles to the native operator — zero overhead.
++ * ================================================================ */
++
++typedef __uint128_t uint128_t;
++
++#define U128_ZERO ((uint128_t)0)
++#define U128_ONE ((uint128_t)1)
++#define U128_MAX ((uint128_t)((__uint128_t)(-1)))
++
++static inline uint128_t u128_from_u64(uint64_t v) { return (uint128_t)v; }
++static inline uint128_t u128_from_u32(uint32_t v) { return (uint128_t)v; }
++
++static inline uint64_t u128_hi64(uint128_t a) { return (uint64_t)(a >> 64); }
++static inline uint64_t u128_lo64(uint128_t a) { return (uint64_t)a; }
++
++static inline int u128_is_zero(uint128_t a) { return a == 0; }
++static inline int u128_eq(uint128_t a, uint128_t b) { return a == b; }
++static inline int u128_lt(uint128_t a, uint128_t b) { return a < b; }
++static inline int u128_gt(uint128_t a, uint128_t b) { return a > b; }
++static inline int u128_le(uint128_t a, uint128_t b) { return a <= b; }
++static inline int u128_ge(uint128_t a, uint128_t b) { return a >= b; }
++
++static inline uint128_t u128_add(uint128_t a, uint128_t b) { return a + b; }
++static inline uint128_t u128_sub(uint128_t a, uint128_t b) { return a - b; }
++static inline uint128_t u128_inc(uint128_t a) { return a + 1; }
++static inline uint128_t u128_dec(uint128_t a) { return a - 1; }
++
++static inline uint128_t u128_and(uint128_t a, uint128_t b) { return a & b; }
++static inline uint128_t u128_or(uint128_t a, uint128_t b) { return a | b; }
++static inline uint128_t u128_not(uint128_t a) { return ~a; }
++static inline uint128_t u128_shl(uint128_t a, int n) { return a << n; }
++static inline uint128_t u128_shr(uint128_t a, int n) { return a >> n; }
++
++static inline uint128_t u128_mul_u64(uint128_t a, uint64_t b) { return a * b; }
++static inline uint128_t u128_div10(uint128_t a) { return a / 10; }
++static inline int u128_mod10(uint128_t a) { return (int)(a % 10); }
++
++#else /* !__SIZEOF_INT128__ */
++
++/* ================================================================
++ * Portable 128-bit path (32-bit platforms without __uint128_t).
++ * Uses a struct of two uint64_t with explicit arithmetic.
++ * ================================================================ */
++
++/* Field order matches the byte layout of a native __uint128_t on the same
++ * endianness, so that an array of these structs is binary-compatible with one
++ * of native __uint128_t. This lets binary ipset files written by a 64-bit
++ * (native) build load correctly on a 32-bit (portable) build of the same
++ * endianness, and vice versa. All code refers to the fields by name, so the
++ * order is irrelevant to arithmetic; only the in-memory layout changes. */
++#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
++typedef struct { uint64_t hi; uint64_t lo; } uint128_t;
++#else
++typedef struct { uint64_t lo; uint64_t hi; } uint128_t;
++#endif
++
++#define U128_ZERO ((uint128_t){ .hi = 0, .lo = 0 })
++#define U128_ONE ((uint128_t){ .hi = 0, .lo = 1 })
++#define U128_MAX ((uint128_t){ .hi = UINT64_MAX, .lo = UINT64_MAX })
++
++static inline uint128_t u128_from_u64(uint64_t v) {
++ uint128_t r = { .hi = 0, .lo = v };
++ return r;
++}
++
++static inline uint128_t u128_from_u32(uint32_t v) {
++ uint128_t r = { .hi = 0, .lo = (uint64_t)v };
++ return r;
++}
++
++static inline uint64_t u128_hi64(uint128_t a) { return a.hi; }
++static inline uint64_t u128_lo64(uint128_t a) { return a.lo; }
++
++static inline int u128_is_zero(uint128_t a) {
++ return a.hi == 0 && a.lo == 0;
++}
++
++static inline int u128_eq(uint128_t a, uint128_t b) {
++ return a.hi == b.hi && a.lo == b.lo;
++}
++
++static inline int u128_lt(uint128_t a, uint128_t b) {
++ return a.hi < b.hi || (a.hi == b.hi && a.lo < b.lo);
++}
++
++static inline int u128_gt(uint128_t a, uint128_t b) {
++ return a.hi > b.hi || (a.hi == b.hi && a.lo > b.lo);
++}
++
++static inline int u128_le(uint128_t a, uint128_t b) { return !u128_gt(a, b); }
++static inline int u128_ge(uint128_t a, uint128_t b) { return !u128_lt(a, b); }
++
++static inline uint128_t u128_add(uint128_t a, uint128_t b) {
++ uint128_t r;
++ r.lo = a.lo + b.lo;
++ r.hi = a.hi + b.hi + (r.lo < a.lo);
++ return r;
++}
++
++static inline uint128_t u128_sub(uint128_t a, uint128_t b) {
++ uint128_t r;
++ r.lo = a.lo - b.lo;
++ r.hi = a.hi - b.hi - (a.lo < b.lo);
++ return r;
++}
++
++static inline uint128_t u128_inc(uint128_t a) {
++ uint128_t r;
++ r.lo = a.lo + 1;
++ r.hi = a.hi + (r.lo == 0);
++ return r;
++}
++
++static inline uint128_t u128_dec(uint128_t a) {
++ uint128_t r;
++ r.hi = a.hi - (a.lo == 0);
++ r.lo = a.lo - 1;
++ return r;
++}
++
++static inline uint128_t u128_and(uint128_t a, uint128_t b) {
++ uint128_t r = { .hi = a.hi & b.hi, .lo = a.lo & b.lo };
++ return r;
++}
++
++static inline uint128_t u128_or(uint128_t a, uint128_t b) {
++ uint128_t r = { .hi = a.hi | b.hi, .lo = a.lo | b.lo };
++ return r;
++}
++
++static inline uint128_t u128_not(uint128_t a) {
++ uint128_t r = { .hi = ~a.hi, .lo = ~a.lo };
++ return r;
++}
++
++static inline uint128_t u128_shl(uint128_t a, int n) {
++ uint128_t r;
++ if(n == 0) return a;
++ if(n >= 128) { r.hi = 0; r.lo = 0; return r; }
++ if(n >= 64) {
++ r.hi = a.lo << (n - 64);
++ r.lo = 0;
++ }
++ else {
++ r.hi = (a.hi << n) | (a.lo >> (64 - n));
++ r.lo = a.lo << n;
++ }
++ return r;
++}
++
++static inline uint128_t u128_shr(uint128_t a, int n) {
++ uint128_t r;
++ if(n == 0) return a;
++ if(n >= 128) { r.hi = 0; r.lo = 0; return r; }
++ if(n >= 64) {
++ r.lo = a.hi >> (n - 64);
++ r.hi = 0;
++ }
++ else {
++ r.lo = (a.lo >> n) | (a.hi << (64 - n));
++ r.hi = a.hi >> n;
++ }
++ return r;
++}
++
++/* multiply uint128 by uint64, keeping low 128 bits */
++static inline uint128_t u128_mul_u64(uint128_t a, uint64_t b) {
++ uint32_t al = (uint32_t)a.lo;
++ uint32_t ah = (uint32_t)(a.lo >> 32);
++ uint32_t bl = (uint32_t)b;
++ uint32_t bh = (uint32_t)(b >> 32);
++ uint64_t p0, p1, p2, p3, carry;
++ uint128_t r;
++
++ p0 = (uint64_t)al * bl;
++ p1 = (uint64_t)al * bh;
++ p2 = (uint64_t)ah * bl;
++ p3 = (uint64_t)ah * bh;
++
++ carry = (p0 >> 32) + (uint32_t)p1 + (uint32_t)p2;
++ r.lo = ((uint64_t)(uint32_t)p0) | (carry << 32);
++ r.hi = p3 + (p1 >> 32) + (p2 >> 32) + (carry >> 32) + a.hi * b;
++ return r;
++}
++
++/* long division by 10 using 32-bit digits */
++static inline uint128_t u128_div10(uint128_t a) {
++ uint128_t q;
++ uint32_t d3, d2, d1, d0, q3, q2, q1, q0;
++ uint64_t r, tmp;
++
++ d3 = (uint32_t)(a.hi >> 32);
++ d2 = (uint32_t)a.hi;
++ d1 = (uint32_t)(a.lo >> 32);
++ d0 = (uint32_t)a.lo;
++
++ tmp = (uint64_t)d3; q3 = (uint32_t)(tmp / 10); r = tmp % 10;
++ tmp = (r << 32) | d2; q2 = (uint32_t)(tmp / 10); r = tmp % 10;
++ tmp = (r << 32) | d1; q1 = (uint32_t)(tmp / 10); r = tmp % 10;
++ tmp = (r << 32) | d0; q0 = (uint32_t)(tmp / 10);
++
++ q.hi = ((uint64_t)q3 << 32) | q2;
++ q.lo = ((uint64_t)q1 << 32) | q0;
++ return q;
++}
++
++static inline int u128_mod10(uint128_t a) {
++ uint64_t r;
++ r = ((uint64_t)(uint32_t)(a.hi >> 32)) % 10;
++ r = ((r << 32) | (uint32_t)a.hi) % 10;
++ r = ((r << 32) | (uint32_t)(a.lo >> 32)) % 10;
++ r = ((r << 32) | (uint32_t)a.lo) % 10;
++ return (int)r;
++}
++
++#endif /* __SIZEOF_INT128__ */
++
++#endif /* IPRANGE_UINT128_H */