package com.peterhi.net3;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.peterhi.runtime.MemoryStream;
public final class Local {
public static interface Listener {
void received(SocketAddress address, byte[] data);
}
class Remote {
private final SocketAddress address;
class Window {
class Frame {
private final byte[] data;
private int sendCount;
public Frame(byte[] data) {
if (data != null && data.length == 0) {
throw new IllegalArgumentException();
}
this.data = data;
}
public byte[] getData() {
return data;
}
public int getSendCount() {
return sendCount;
}
public int incrementSendCount() {
sendCount ++;
return sendCount;
}
}
private final int id;
private final Frame[] frames;
public Window(int id, int size) {
if (id <= 0) {
throw new IllegalArgumentException("Window ID is zero or negative.");
}
if (id > 0x0f) {
throw new IllegalArgumentException("Window ID is too large (larger than 15).");
}
if (size <= 0) {
throw new IllegalArgumentException("Size is negative.");
}
if (size > 65535) {
throw new IllegalArgumentException("Size is too large (larger than 65535).");
}
this.id = id;
this.frames = new Frame[size];
}
public int size() {
return frames.length;
}
public Frame getFrame(int index) {
return frames[index];
}
public void setFrame(int index, Frame value) {
frames[index] = value;
}
public int indexOf(Frame frame) {
if (frame == null) {
return -1;
}
for (int i = 0; i < frames.length; i++) {
if (frame.equals(frames[i])) {
return i;
}
}
return -1;
}
public boolean send(Frame frame) throws IOException {
if (frame == null) {
return false;
}
if (frame.getData() == null) {
return false;
}
if (frame.getData().length == 0) {
throw new IllegalArgumentException("Data length is zero.");
}
int header = 0;
header |= (2 & 0x03) << 6;
header |= (id & 0x0f) << 2;
int sizeType = getSizeType();
header |= (sizeType & 0x03) << 0;
int index = indexOf(frame);
int length = frames.length;
MemoryStream ms = new MemoryStream();
ms.write(header);
if (sizeType == 1) {
ms.write(((index & 0x0f) << 4) | ((length & 0x0f) << 0));
} else if (sizeType == 2) {
ms.write(index);
ms.write(length);
} else {
ms.writeShort(index);
ms.writeShort(length);
}
ms.write(frame.getData());
byte[] data = ms.toByteArray();
DatagramPacket packet = new DatagramPacket(data, data.length);
getSocket().send(packet);
frame.incrementSendCount();
return true;
}
public int getSizeType() {
if (frames.length < 16) {
return 1;
} else if (frames.length < 256) {
return 2;
} else {
return 3;
}
}
}
/*class Frame {
private byte[] data;
private boolean acked;
private int sendCount;
public void send() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int header = 0;
header |= (2 & 0x03) << 6;
header |= (windowId & 0x0f) << 2;
}
}
private Frame[] frames;
private int windowId;
public int indexOf(Frame frame) {
if (frame == null) {
throw new NullPointerException();
}
for (int i = 0; i < frames.length; i++) {
if (frame.equals(frames[i])) {
return i;
}
}
return -1;
}
public int getSizeType() {
if (f)
}*/
protected Remote(SocketAddress address) {
if (address == null) {
throw new NullPointerException();
}
this.address = address;
}
public SocketAddress getAddress() {
return address;
}
}
private final DatagramSocket socket;
private final List<Listener> listeners = new ArrayList<Listener>();
public Local() throws SocketException {
socket = new DatagramSocket();
}
public Local(int port) throws SocketException {
socket = new DatagramSocket(port);
}
public Local(InetAddress address, int port) throws SocketException {
socket = new DatagramSocket(port, address);
}
public Local(SocketAddress address) throws SocketException {
socket = new DatagramSocket(address);
}
public DatagramSocket getSocket() {
return socket;
}
public int send(SocketAddress address, byte[] buffer, int offset, int length) throws IOException {
if (address == null) {
throw new NullPointerException();
}
validateBuffer(buffer, offset, length);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(0);
baos.write(buffer, offset, length);
DatagramPacket packet = new DatagramPacket(baos.toByteArray(), baos.size(), address);
socket.send(packet);
return length;
}
public DatagramPacket receive() throws IOException {
byte[] buffer = new byte[512];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
try {
socket.receive(packet);
} catch (SocketTimeoutException ex) {
return null;
}
return packet;
}
public void process(DatagramPacket packet) {
byte[] data = packet.getData();
int offset = packet.getOffset();
int type = (data[offset] >>> 6) & 0x03;
if (type == 0) {
byte[] bytes = Arrays.copyOfRange(data, 1, data.length);
fireReceived(packet.getSocketAddress(), bytes);
}
}
public void addListener(Listener listener) {
if (listener == null) {
throw new NullPointerException();
}
listeners.add(listener);
}
public void removeListener(Listener listener) {
if (listener == null) {
throw new NullPointerException();
}
listeners.remove(listener);
}
private void fireReceived(SocketAddress address, byte[] data) {
for (Listener listener : listeners) {
listener.received(address, data);
}
}
private void validateBuffer(byte[] buffer, int offset, int length) {
if (buffer == null) {
throw new NullPointerException();
}
if (buffer.length == 0) {
throw new IllegalArgumentException();
}
if (offset < 0) {
throw new IllegalArgumentException();
}
if (length <= 0) {
throw new IllegalArgumentException();
}
if (offset + length > buffer.length) {
throw new IllegalArgumentException();
}
}
}