Subnet / CIDR Calculator — Swift source
Compute IPv4 subnet details from a CIDR block - network, broadcast, host range, netmask, wildcard, host count, IP class, and private-range detection. Split a block into smaller subnets, all in your browser.
This is the Swift implementation — the same logic the interactive tool runs, in a shareable, citable form.
// subnet-cidr — Swift port: IPv4 subnet/CIDR math (dotted-quad ⇄ UInt32, subnet details, CIDR splitting). Language: Swift (5.9+). Port of src/lib/subnet-cidr.ts — same logic as this dir's python.py / go.go; invalid input yields nil rather than throwing.
struct SubnetInfo {
let network: String, broadcast: String, firstHost: String, lastHost: String
let hostCount: UInt64, prefix: Int, ipClass: Character, isPrivate: Bool
}
enum SubnetCidr {
// The TS /^\d+$/ check — rejects signs, hex, whitespace, and the empty string.
private static func allDigits(_ s: Substring) -> Bool {
!s.isEmpty && s.allSatisfy { $0.isASCII && $0.isNumber }
}
// "a.b.c.d" → UInt32. nil for a wrong octet count, junk octets, or an octet > 255.
static func ipToInt(_ ip: String) -> UInt32? {
let parts = ip.split(separator: ".", omittingEmptySubsequences: false)
guard parts.count == 4 else { return nil }
var result: UInt32 = 0
for part in parts {
guard allDigits(part), part.count <= 3 else { return nil }
var octet: UInt32 = 0
for c in part { octet = octet * 10 + UInt32(c.wholeNumberValue!) }
guard octet <= 255 else { return nil }
result = result * 256 + octet
}
return result
}
static func intToIp(_ v: UInt32) -> String {
"\(v >> 24).\((v >> 16) & 255).\((v >> 8) & 255).\(v & 255)"
}
private static func mask(fromPrefix p: Int) -> UInt32 {
p == 0 ? 0 : UInt32.max << (32 - p)
}
// "ip/prefix" → full block details; nil for an invalid CIDR.
static func subnetDetails(_ cidr: String) -> SubnetInfo? {
guard let slash = cidr.firstIndex(of: "/") else { return nil }
let prefixPart = cidr[cidr.index(after: slash)...]
guard !prefixPart.contains("/"), allDigits(prefixPart), prefixPart.count <= 2 else { return nil }
let prefix = prefixPart.reduce(0) { $0 * 10 + $1.wholeNumberValue! }
guard prefix <= 32, let ipInt = ipToInt(String(cidr[..<slash])) else { return nil }
let maskValue = mask(fromPrefix: prefix)
let wildcard = ~maskValue
let network = ipInt & maskValue
let broadcast = network | wildcard
// /31 (point-to-point) and /32 (single host) have no host/broadcast split.
let first = prefix >= 31 ? network : network + 1
let last = prefix >= 31 ? broadcast : broadcast - 1
let hostCount: UInt64 = prefix >= 32 ? 1 : prefix == 31 ? 2 : (1 << (32 - prefix)) - 2
let o1 = network >> 24, o2 = (network >> 16) & 255
let ipClass: Character = o1 < 128 ? "A" : o1 < 192 ? "B" : o1 < 224 ? "C" : "-" // D/E → '-'
let isPrivate = o1 == 10 || (o1 == 172 && (16...31).contains(o2)) || (o1 == 192 && o2 == 168)
return SubnetInfo(network: intToIp(network), broadcast: intToIp(broadcast),
firstHost: intToIp(first), lastHost: intToIp(last),
hostCount: hostCount, prefix: prefix, ipClass: ipClass, isPrivate: isPrivate)
}
// Split into 2^(newPrefix-prefix) children; [] for invalid input or range.
static func splitSubnet(_ cidr: String, newPrefix: Int) -> [String] {
guard let base = subnetDetails(cidr) else { return [] }
guard newPrefix >= base.prefix, newPrefix <= 32 else { return [] }
let network = ipToInt(base.network)! & mask(fromPrefix: base.prefix)
let block = UInt32(1) << (32 - newPrefix)
return (0..<(1 << (newPrefix - base.prefix))).map {
intToIp(network + UInt32($0) * block) + "/\(newPrefix)"
}
}
}
let d = SubnetCidr.subnetDetails("192.168.1.100/24")!
print("192.168.1.100/24 net=\(d.network) bcast=\(d.broadcast) first=\(d.firstHost) " +
"last=\(d.lastHost) hosts=\(d.hostCount) class=\(d.ipClass) private=\(d.isPrivate)")
for kid in SubnetCidr.splitSubnet("192.168.1.0/24", newPrefix: 26) { print(" split /26 → \(kid)") }
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →