compareAddress(): -1 | 0 | 1
Compares two IP addresses of either version for sorting.
The order is version-first and total: every IPv4 address (number)
sorts before every IPv6 address (bigint), and within a version
addresses sort numerically ascending. Mixed-version arguments are not an
error — unlike the universal CIDR operations, this function never throws,
because sorting a mixed dual-stack list is the reason it exists. Go's
net/netip, Rust's std::net::IpAddr and PostgreSQL's inet all order
addresses the same way.
Note that "IPv4 sorts first" is a statement about order, not about
magnitude: the two address spaces are disjoint and nothing is converted
between them. An IPv4-mapped address held as a bigint is an IPv6 value
and sorts in the IPv6 half — see the example below. In practice
parseAddress already unwraps mapped addresses to their IPv4 number
form, so a mapped bigint only reaches this function via
parseAddressv6.
Sort a mixed dual-stack list, ascending or descending
Sort a mixed dual-stack list, ascending or descending
import { assertEquals } from "@std/assert"; import { compareAddress, parseAddress, stringifyAddress } from "@hertzg/ip/address"; const clients = ["2001:db8::1", "10.0.0.2", "::1", "10.0.0.1"].map((s) => parseAddress(s).address); assertEquals(clients.toSorted(compareAddress).map(stringifyAddress), [ "10.0.0.1", "10.0.0.2", "::1", "2001:db8::1", ]); // Descending: swap the arguments assertEquals(clients.toSorted((a, b) => compareAddress(b, a)).map(stringifyAddress), [ "2001:db8::1", "::1", "10.0.0.2", "10.0.0.1", ]);
Every IPv4 address sorts before every IPv6 address
Every IPv4 address sorts before every IPv6 address
import { assertEquals } from "@std/assert"; import { compareAddress, parseAddress } from "@hertzg/ip/address"; assertEquals(compareAddress(parseAddress("255.255.255.255").address, parseAddress("::").address), -1); assertEquals(compareAddress(parseAddress("::").address, parseAddress("0.0.0.0").address), 1);
An IPv4-mapped bigint is an IPv6 value, not its IPv4 twin
An IPv4-mapped bigint is an IPv6 value, not its IPv4 twin
import { assertEquals } from "@std/assert"; import { compareAddress } from "@hertzg/ip/address"; import { parseAddressv4 } from "@hertzg/ip/addressv4"; import { parseAddressv6 } from "@hertzg/ip/addressv6"; const mapped = parseAddressv6("::ffff:10.0.0.1").address; const plain = parseAddressv4("10.0.0.1").address; assertEquals(compareAddress(mapped, plain), 1);