Subnet / CIDR Calculator — Kotlin 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 Kotlin implementation — the same logic the interactive tool runs, in a shareable, citable form.
// subnet-cidr — Kotlin port: IPv4 subnet/CIDR math (dotted-quad ⇄ UInt, subnet details, CIDR splitting). Language: Kotlin (JVM 1.9+). Port of src/lib/subnet-cidr.ts — same logic as this dir's python.py / go.go; invalid input yields null rather than throwing.
data class SubnetInfo(
val network: String, val broadcast: String, val firstHost: String, val lastHost: String,
val hostCount: ULong, val prefix: Int, val ipClass: Char, val isPrivate: Boolean,
)
object SubnetCidr {
// The TS /^\d+$/ check — rejects signs, hex, whitespace, and the empty string.
private fun allDigits(s: String): Boolean = s.isNotEmpty() && s.all { it in '0'..'9' }
// "a.b.c.d" → UInt. null for a wrong octet count, junk octets, or an octet > 255.
fun ipToInt(ip: String): UInt? {
val parts = ip.split(".")
if (parts.size != 4) return null
var result = 0u
for (part in parts) {
if (!allDigits(part) || part.length > 3) return null
val octet = part.toInt()
if (octet > 255) return null
result = result * 256u + octet.toUInt()
}
return result
}
fun intToIp(v: UInt): String =
"${v shr 24}.${(v shr 16) and 255u}.${(v shr 8) and 255u}.${v and 255u}"
private fun maskFromPrefix(p: Int): UInt = if (p == 0) 0u else 0xFFFFFFFFu shl (32 - p)
// "ip/prefix" → (canonical ip, prefix). A second slash or a prefix > 32 is invalid.
private fun parseCidr(cidr: String): Pair<String, Int>? {
val slash = cidr.indexOf('/')
if (slash < 0) return null
val prefixPart = cidr.substring(slash + 1)
if ('/' in prefixPart || !allDigits(prefixPart) || prefixPart.length > 2) return null
val prefix = prefixPart.toInt()
val ip = ipToInt(cidr.substring(0, slash)) ?: return null
return intToIp(ip) to prefix
}
fun subnetDetails(cidr: String): SubnetInfo? {
val (ip, prefix) = parseCidr(cidr) ?: return null
if (prefix > 32) return null
val mask = maskFromPrefix(prefix)
val wildcard = mask.inv()
val network = ipToInt(ip)!! and mask
val broadcast = network or wildcard
// /31 (point-to-point) and /32 (single host) have no host/broadcast split.
val first = if (prefix >= 31) network else network + 1u
val last = if (prefix >= 31) broadcast else broadcast - 1u
val hosts = if (prefix >= 32) 1uL else if (prefix == 31) 2uL else (1uL shl (32 - prefix)) - 2uL
val o1 = network shr 24
val o2 = (network shr 16) and 255u
val klass = if (o1 < 128u) 'A' else if (o1 < 192u) 'B' else if (o1 < 224u) 'C' else '-' // D/E → '-'
val priv = o1 == 10u || (o1 == 172u && o2 in 16u..31u) || (o1 == 192u && o2 == 168u)
return SubnetInfo(intToIp(network), intToIp(broadcast), intToIp(first), intToIp(last),
hosts, prefix, klass, priv)
}
// Split into 2^(newPrefix-prefix) children; empty list for invalid input or range.
fun splitSubnet(cidr: String, newPrefix: Int): List<String> {
val parsed = parseCidr(cidr) ?: return emptyList()
val prefix = parsed.second
if (newPrefix < prefix || newPrefix > 32) return emptyList()
val network = ipToInt(parsed.first)!! and maskFromPrefix(prefix)
val block = 1u shl (32 - newPrefix)
return (0 until (1 shl (newPrefix - prefix)))
.map { intToIp(network + it.toUInt() * block) + "/$newPrefix" }
}
}
fun main() {
val d = SubnetCidr.subnetDetails("192.168.1.100/24") ?: return
println("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}")
SubnetCidr.splitSubnet("192.168.1.0/24", 26).forEach { println(" split /26 → $it") }
}
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 →