103 lines
3.2 KiB
Java
103 lines
3.2 KiB
Java
package dorkbox.network.aeron.server;
|
|
|
|
import java.util.Collections;
|
|
|
|
import org.agrona.collections.IntArrayList;
|
|
import org.agrona.collections.IntHashSet;
|
|
|
|
import dorkbox.network.aeron.exceptions.EchoServerPortAllocationException;
|
|
|
|
/**
|
|
* <p>An allocator for port numbers.</p>
|
|
*
|
|
* <p>
|
|
* The allocator accepts a base number {@code p} and a maximum count {@code n | n > 0}, and will allocate
|
|
* up to {@code n} numbers, in a random order, in the range {@code [p, p + n - 1}.
|
|
* </p>
|
|
*/
|
|
public final
|
|
class PortAllocator {
|
|
private final int port_lo;
|
|
private final int port_hi;
|
|
|
|
private final IntHashSet ports_used;
|
|
private final IntArrayList ports_free;
|
|
|
|
/**
|
|
* Create a new port allocator.
|
|
*
|
|
* @param port_base The base port
|
|
* @param max_ports The maximum number of ports that will be allocated
|
|
*
|
|
* @return A new port allocator
|
|
*/
|
|
|
|
public static
|
|
PortAllocator create(final int port_base, final int max_ports) {
|
|
return new PortAllocator(port_base, max_ports);
|
|
}
|
|
|
|
private
|
|
PortAllocator(final int in_port_lo, final int in_max_ports) {
|
|
if (in_port_lo <= 0 || in_port_lo >= 65536) {
|
|
throw new IllegalArgumentException(String.format("Base port %d must be in the range [1, 65535]", Integer.valueOf(in_port_lo)));
|
|
}
|
|
|
|
this.port_lo = in_port_lo;
|
|
this.port_hi = in_port_lo + (in_max_ports - 1);
|
|
|
|
if (this.port_hi < 0 || this.port_hi >= 65536) {
|
|
throw new IllegalArgumentException(String.format("Uppermost port %d must be in the range [1, 65535]",
|
|
Integer.valueOf(this.port_hi)));
|
|
}
|
|
|
|
this.ports_used = new IntHashSet(in_max_ports);
|
|
this.ports_free = new IntArrayList();
|
|
|
|
for (int port = in_port_lo; port <= this.port_hi; ++port) {
|
|
this.ports_free.addInt(port);
|
|
}
|
|
Collections.shuffle(this.ports_free);
|
|
}
|
|
|
|
/**
|
|
* Free a given port. Has no effect if the given port is outside of the range
|
|
* considered by the allocator.
|
|
*
|
|
* @param port The port
|
|
*/
|
|
public
|
|
void free(final int port) {
|
|
if (port >= this.port_lo && port <= this.port_hi) {
|
|
this.ports_used.remove(port);
|
|
this.ports_free.addInt(port);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Allocate {@code count} ports.
|
|
*
|
|
* @param count The number of ports that will be allocated
|
|
*
|
|
* @return An array of allocated ports
|
|
*
|
|
* @throws EchoServerPortAllocationException If there are fewer than {@code count} ports available to allocate
|
|
*/
|
|
public
|
|
int[] allocate(final int count) throws EchoServerPortAllocationException {
|
|
if (this.ports_free.size() < count) {
|
|
throw new EchoServerPortAllocationException(String.format("Too few ports available to allocate %d ports",
|
|
Integer.valueOf(count)));
|
|
}
|
|
|
|
final int[] result = new int[count];
|
|
for (int index = 0; index < count; ++index) {
|
|
result[index] = this.ports_free.remove(0)
|
|
.intValue();
|
|
this.ports_used.add(result[index]);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|