Repository navigation
swift-cidr 0.9.0
Release date: 2026-11-07. These notes cover the changes since 0.8.1.
This minor version intentionally changes public APIs before 1.0. It routes
shared CIDR formatting through the address family, renames the structural
protocol to CIDRForm, makes storage width authoritative for IP prefix math,
and exposes concrete subnet and address iterators.
The Swift 6.2 minimum, macOS 15 / iOS 18 deployment floors, and four products
(CIDR, CIDRPOSIX, CIDRNIO, and CIDRArgumentParser) are unchanged.
Family formatting and parsing
IPAddressFamily now requires
formatCIDR(_:prefixLength:). The IPv4 and IPv6 families implement it with
their family formatters; the default implementation joins formatAddress(_:)
and the prefix length. Shared CIDRForm.description and
formatted(_:) implementations call that requirement. Generic and existential
formatting therefore reaches the family's implementation without depending on
a constrained extension overload at the original call site.
The shared address and network string initializers are inlinable. This exposes
their bodies to the caller's optimizer and permits specialization where a
concrete family is visible. It does not guarantee specialization across opaque
generic, existential, Codable, or ArgumentParser boundaries.
The per-family string-initializer and formatting overloads have been removed.
Ordinary calls such as IPv4Address(text), IPv6Network(text), and
value.formatted(.compressed) retain their syntax through the shared APIs.
The IPv6 style-formatting wrapper is now inlinable, and its out-of-line helper
entry points are usable from that inlinable code. PrefixLength raw-value
validation and the default host-prefix construction chain are also inlinable.
These visibility changes let
concrete callers retain the fast style formatter and fold host prefixes to
/32 or /128; they do not change formatting or validation rules. No additional
IPAddress constructor annotations are required.
Migration
Rename protocol references to CIDRForm
Update conformances, generic constraints, and extensions that refer to the
former CIDR protocol. The module is still named CIDR. There is deliberately
no typealias CIDR: that would hide the module again and prevent qualified
names from resolving alongside similarly named types in other modules.
For example, on Apple platforms using Network.framework:
import CIDR
import Network
func describe<T: CIDR.CIDRForm>(_ value: T) -> String {
value.description
}
let address: CIDR.IPv4Address? = .init("192.0.2.1/24")Use UInt16 for IANA family numbers
AddressFamily.ianaValue and the corresponding family-erased properties now
return UInt16. Update custom conformers and explicitly typed consumers:
static let ianaValue: UInt16 = 65_535Convert explicitly at an integration boundary that requires another integer
type, for example Int32(Family.ianaValue).
Treat IP storage width as authoritative
AF.V4.bitWidth and AF.V6.bitWidth now use the storage-derived computed
default instead of stored static let declarations. IP prefix validation,
default host lengths, masks, summarization, and traversal use
Family.Storage.bitWidth directly.
A custom IP family can still override bitWidth as metadata. Such an override
does not change the valid prefix range or the arithmetic width. A custom family
that previously advertised a narrower or wider width than its storage must
review this behavioral change. Non-IP families retain their declared widths;
AF.MAC48 still reports 48 while using UInt64 storage.
Use mutable concrete iterators
subnets(prefixLength:) now returns IPPrefixSubnets<Self> instead of
AnySequence<Self>. IPNetwork.makeIterator() returns Addresses.Iterator
instead of AnyIterator<Element>. The sequences remain lazy.
Direct calls to next() now require a var iterator binding, including code
that previously relied on inferred types:
func firstSubnet(of network: IPv4Network) -> IPv4Network? {
var iterator = network.subnets(prefixLength: 26).makeIterator()
return iterator.next()
}Copies of these concrete iterators have independent cursor state. Copies of
the former erased AnyIterator shared a cursor. Use an explicit
AnySequence(...) or AnyIterator(...) wrapper where an existing API boundary
still requires the erased type.
An invalid integer subnet length still yields an empty sequence, including
for a /0 parent. This preserves 0.8.1 behavior; it is not a newly fixed edge
case in this release.
Known downstream migration points
The following uses were verified in the local downstream checkouts on
2026-11-07. The version ranges describe their current manifests, not completed
upgrades.
| Consumer | Current swift-cidr requirement | Migration point |
|---|---|---|
| RouteObjects | 0.8.x |
Change the iterator binding from let to var in RouteObjects/Onboarding/IPv4SplitDemoProjection.swift. |
| Legacy standalone swift-cidr-nio | Local ../swift-cidr |
Rename extension CIDR in Sources/CIDRNIO/CIDRNIO.swift. This is separate from the CIDRNIO product shipped in swift-cidr. |
| cidrwalk | 0.5.x |
Rename the two Input: CIDR constraints in Sources/cidrwalk/cidrwalk.swift. |
| swift-rpsl | 0.4.x |
Change the custom test family's ianaValue: Int32 to UInt16 in Tests/RPSLTests/SemanticValueTests.swift. |
The minor-version bounds require an explicit dependency upgrade to adopt
0.9.0. A consumer using the local package path sees changes as its checkout
changes. These downstream repositories have not been edited as part of this
release.
Preserved semantics
IPv4/IPv6 parsing grammar and prefix-aware address identity are unchanged.
An address still preserves host bits and prefix context; a network still
stores an aligned prefix. Address iteration retains the parent's context and
includes both endpoints. CIDRBlock retains its existing allocation-shaped
block role and capabilities.
Performance and verification
All 287 tests passed in Debug and Release on both macOS ARM64 and native Intel
x86_64 Linux. Both hosts completed the full 166-case core, CPU, and NIO benchmark
suite against fresh 0.8.1 builds, together with supplemental dynamic-client
probes and repeated focused comparisons. Validation used an Apple M1 Max with
macOS 26.7.2 and Apple Swift 6.3.3, and an Intel Xeon E5520 with Ubuntu 24.04.5
and Swift 6.3.3. These results qualify those environments and workloads; they
are not a new-toolchain or all-platform guarantee.
Tests cover canonical JSON encoding, independent decoding fixtures, and
round-trips for IPv4/IPv6 addresses, networks, blocks, and multicast ranges;
invalid block and multicast input; custom-family formatter/parser dispatch;
copied, partially advanced subnet iterators; and storage-width boundaries.
Compared with 0.8.1, visible-generic parsing probes were approximately
2.1–9.4× faster across the two hosts. Repeated unspecialized-generic and
existential address/network formatting probes took about 47–69% less time,
concrete default-address construction took about 60–70% less time, and inbound
NIO socket conversion took about 11–16% less time. All eleven targeted IPv6
presentation-style cases were within the 5% comparison budget on both hosts.
These are selected workload results, not a package-wide speedup. Percentiles
refer to batch-mean operation times, and repeated percentage comparisons use
paired run ratios rather than ratios of separately summarized times.
In these measurements, “unspecialized generic” means a generic function across
an optimizer boundary, not Swift's some syntax. Existential formatting uses
any CustomStringConvertible. Inlinable parsing permits specialization where
the family is visible; it does not provide a general speedup to unspecialized
or runtime-selected parsing, Codable, or ArgumentParser calls.
Measured limitations remain:
- Unspecialized default construction remains about 29% slower on macOS and
13% slower on Intel. Some opaque address-parsing probes are 7–16% slower. - IPv4 address-only formatting is 16–18% slower in the selected macOS probes;
the Intel repetitions do not reproduce that result. - Coverage-containment probes are 7–13% slower with sparse samples, while
separate dynamic containment scans remain within 1% at p50. These are
different workloads and are not interchangeable measurements. - Intel's retained outbound IPv4 NIO benchmark remains about 5% slower at p50
in the focused follow-up. Dynamic-client comparisons do not consistently
reproduce that result. Some other outbound controls also vary by harness;
their causes remain unresolved.
Follow-up macOS investigations reproduced the IPv6 octet-projection difference
in a matched workload and removed the gap with two link orders using unchanged
compiled objects. A separate matched IPv6 formatting investigation found small
direct-description differences that also disappeared under both controlled
link orders. These results support executable-placement sensitivity in those
measured clients; they do not identify a particular CPU mechanism or guarantee
that every application is unaffected. The original generic/existential IPv6
formatting differences did not consistently reproduce, and their historical
cause remains unassigned.
No production layout tuning or profile-guided optimization was added. Sparse
CPU-suite samples, constant-input loops, and differing benchmark controls are
not treated as evidence of universal performance or allocation behavior.