Random Port Generator — Kotlin source
Generate one or many random TCP/UDP port numbers across registered, ephemeral, or the full range - optionally unique. Runs entirely in your browser with crypto-grade randomness.
This is the Kotlin implementation — the same logic the interactive tool runs, in a shareable, citable form.
// random-port-generator — Kotlin port: random/dynamic port picker.
//
// Display port of the CosmoDev Random Port Generator tool — same contract as
// cli/random-port-generator/random-port-generator.go (the live Go twin) and
// src/lib/random-port.ts (the canonical TypeScript implementation). An
// injectable RNG makes generation deterministic (the TS test surface);
// invalid custom ranges throw IllegalArgumentException, mirroring the TS
// throw. The default RNG is the JDK SecureRandom — a CSPRNG standing in for
// the TS crypto.getRandomValues default.
import java.security.SecureRandom
import kotlin.math.floor
import kotlin.math.max
import kotlin.math.min
/** Named ranges — `Range` in the Go twin; REGISTERED is the zero-value default. */
enum class PortRange(val lo: Int, val hi: Int) {
ANY(1, 65535),
REGISTERED(1024, 49151), // the TS default (registered ports)
EPHEMERAL(49152, 65535), // dynamic/client ports
CUSTOM(-1, -1), // bounds come from min/max
}
/** PortOptions in the TS lib (count/unique cover randomPorts). */
data class PortOptions(
val range: PortRange = PortRange.REGISTERED,
val min: Int? = null, // custom-range lower bound
val max: Int? = null, // custom-range upper bound
val count: Int = 1,
val unique: Boolean = false,
val rng: (() -> Double)? = null, // injectable random in [0, 1)
)
private val SECURE = SecureRandom()
/** defaultRng() in the TS lib: a CSPRNG draw in [0, 1). */
private fun defaultRng(): Double = SECURE.nextDouble()
/** resolveRange() in the TS lib. */
fun resolveRange(opts: PortOptions): Pair<Int, Int> = when (opts.range) {
PortRange.CUSTOM -> (opts.min ?: 1) to (opts.max ?: 65535)
else -> opts.range.lo to opts.range.hi
}
/** assertRange() in the TS lib — the TS Error becomes an exception. */
private fun assertRange(lo: Int, hi: Int) {
if (lo < 0 || lo > 65535 || hi > 65535 || lo > hi) {
throw IllegalArgumentException("Invalid port range $lo-$hi")
}
}
/** randomPort() in the TS lib — Port() in the Go twin. */
fun randomPort(opts: PortOptions = PortOptions()): Int {
val rng = opts.rng ?: ::defaultRng
val (lo, hi) = resolveRange(opts)
assertRange(lo, hi)
return lo + floor(rng() * (hi - lo + 1)).toInt()
}
/** randomPorts() in the TS lib — Ports() in the Go twin. */
fun randomPorts(opts: PortOptions = PortOptions()): List<Int> {
val count = max(1, opts.count)
val (lo, hi) = resolveRange(opts)
assertRange(lo, hi)
if (!opts.unique) return List(count) { randomPort(opts) }
// Fisher-Yates partial shuffle over the range to pick n unique ports.
val capacity = hi - lo + 1
val n = min(count, capacity)
val rng = opts.rng ?: ::defaultRng
val pool = IntArray(capacity) { lo + it }
for (i in 0 until n) {
val j = i + floor(rng() * (capacity - i)).toInt()
val t = pool[i]
pool[i] = pool[j]
pool[j] = t
}
return pool.slice(0 until n)
}
fun main() {
// Vectors from src/lib/random-port.test.ts — a fixed rng pins every draw.
val zero = { 0.0 }
println("registered+0 = ${randomPort(PortOptions(rng = zero))}") // 1024
println("any+0 = ${randomPort(PortOptions(PortRange.ANY, rng = zero))}") // 1
println("ephemeral+0 = ${randomPort(PortOptions(PortRange.EPHEMERAL, rng = zero))}") // 49152
println(
"custom 8000-8000 @0.9 = " +
randomPort(PortOptions(PortRange.CUSTOM, min = 8000, max = 8000, rng = { 0.9 })),
) // 8000
println("resolveRange(custom defaults) = ${resolveRange(PortOptions(PortRange.CUSTOM))}") // (1, 65535)
println("count=5 @0 = ${randomPorts(PortOptions(count = 5, rng = zero))}") // [1024 x5]
val unique = PortOptions(
count = 3, unique = true, range = PortRange.CUSTOM, min = 1, max = 10, rng = { 0.5 },
)
println("unique 3 in 1..10 @0.5 = ${randomPorts(unique)}") // [6, 1, 7]
println("unique 5 in 1..3 @0.5 = ${randomPorts(unique.copy(count = 5, min = 1, max = 3))}") // capped at capacity 3
val p = randomPort() // default CSPRNG, registered range
println("default rng registered = $p (in 1024..49151)")
listOf(
PortOptions(PortRange.CUSTOM, min = 70000) to "min above 65535",
PortOptions(PortRange.CUSTOM, min = 100, max = 50) to "min above max",
).forEach { (bad, why) ->
runCatching { randomPort(bad) }.onFailure { println("invalid: ${it.message} ($why)") }
}
}
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 →