Use whole buffers for reads and writes.

This commit is contained in:
akwizgran
2012-11-27 15:16:56 +00:00
parent 705b87b97c
commit 80543984be
12 changed files with 80 additions and 103 deletions

View File

@@ -7,20 +7,21 @@ class Ack extends Frame {
static final int LENGTH = 12; static final int LENGTH = 12;
Ack() { Ack() {
super(new byte[LENGTH], LENGTH); super(new byte[LENGTH]);
b[0] = (byte) Frame.ACK_FLAG; buf[0] = (byte) Frame.ACK_FLAG;
} }
Ack(byte[] b) { Ack(byte[] buf) {
super(b, LENGTH); super(buf);
b[0] = (byte) Frame.ACK_FLAG; if(buf.length != LENGTH) throw new IllegalArgumentException();
buf[0] = (byte) Frame.ACK_FLAG;
} }
int getWindowSize() { int getWindowSize() {
return ByteUtils.readUint24(b, 5); return ByteUtils.readUint24(buf, 5);
} }
void setWindowSize(int windowSize) { void setWindowSize(int windowSize) {
ByteUtils.writeUint24(windowSize, b, 5); ByteUtils.writeUint24(windowSize, buf, 5);
} }
} }

View File

@@ -7,21 +7,21 @@ class Data extends Frame {
static final int MAX_PAYLOAD_LENGTH = 1024; static final int MAX_PAYLOAD_LENGTH = 1024;
static final int MAX_LENGTH = MIN_LENGTH + MAX_PAYLOAD_LENGTH; static final int MAX_LENGTH = MIN_LENGTH + MAX_PAYLOAD_LENGTH;
Data(byte[] b, int length) { Data(byte[] buf) {
super(b, length); super(buf);
if(length < MIN_LENGTH || length > MAX_LENGTH) if(buf.length < MIN_LENGTH || buf.length > MAX_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
boolean isLastFrame() { boolean isLastFrame() {
return b[0] == Frame.FIN_FLAG; return buf[0] == Frame.FIN_FLAG;
} }
void setLastFrame(boolean lastFrame) { void setLastFrame(boolean lastFrame) {
if(lastFrame) b[0] = (byte) Frame.FIN_FLAG; if(lastFrame) buf[0] = (byte) Frame.FIN_FLAG;
} }
int getPayloadLength() { int getPayloadLength() {
return length - MIN_LENGTH; return buf.length - MIN_LENGTH;
} }
} }

View File

@@ -6,40 +6,38 @@ abstract class Frame {
static final byte ACK_FLAG = (byte) 128, FIN_FLAG = 64; static final byte ACK_FLAG = (byte) 128, FIN_FLAG = 64;
protected final byte[] b; protected final byte[] buf;
protected final int length;
Frame(byte[] b, int length) { Frame(byte[] buf) {
this.b = b; this.buf = buf;
this.length = length;
} }
byte[] getBuffer() { byte[] getBuffer() {
return b; return buf;
} }
int getLength() { int getLength() {
return length; return buf.length;
} }
long getChecksum() { long getChecksum() {
return ByteUtils.readUint32(b, length - 4); return ByteUtils.readUint32(buf, buf.length - 4);
} }
void setChecksum(long checksum) { void setChecksum(long checksum) {
ByteUtils.writeUint32(checksum, b, length - 4); ByteUtils.writeUint32(checksum, buf, buf.length - 4);
} }
long calculateChecksum() { long calculateChecksum() {
return Crc32.crc(b, 0, length - 4); return Crc32.crc(buf, 0, buf.length - 4);
} }
long getSequenceNumber() { long getSequenceNumber() {
return ByteUtils.readUint32(b, 1); return ByteUtils.readUint32(buf, 1);
} }
void setSequenceNumber(long sequenceNumber) { void setSequenceNumber(long sequenceNumber) {
ByteUtils.writeUint32(sequenceNumber, b, 1); ByteUtils.writeUint32(sequenceNumber, buf, 1);
} }
@Override @Override
@@ -51,7 +49,7 @@ abstract class Frame {
public boolean equals(Object o) { public boolean equals(Object o) {
if(o instanceof Frame) { if(o instanceof Frame) {
Frame f = (Frame) o; Frame f = (Frame) o;
if(b[0] != f.b[0]) return false; if(buf[0] != f.buf[0]) return false;
return getSequenceNumber() == f.getSequenceNumber(); return getSequenceNumber() == f.getSequenceNumber();
} }
return false; return false;

View File

@@ -135,12 +135,7 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
offHook.release(); offHook.release();
} }
public void handleWrite(byte[] b, int length) throws IOException { public void handleWrite(byte[] b) throws IOException {
if(length < b.length) {
byte[] copy = new byte[length];
System.arraycopy(b, 0, copy, 0, length);
b = copy;
}
try { try {
port.writeBytes(b); port.writeBytes(b);
} catch(SerialPortException e) { } catch(SerialPortException e) {
@@ -153,7 +148,7 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
try { try {
if(ev.isRXCHAR()) { if(ev.isRXCHAR()) {
byte[] b = port.readBytes(); byte[] b = port.readBytes();
if(connected.get()) reliabilityLayer.handleRead(b, b.length); if(connected.get()) reliabilityLayer.handleRead(b);
else handleText(b); else handleText(b);
} else if(ev.isDSR() && ev.getEventValue() == 0) { } else if(ev.isDSR() && ev.getEventValue() == 0) {
if(LOG.isLoggable(INFO)) LOG.info("Remote end hung up"); if(LOG.isLoggable(INFO)) LOG.info("Remote end hung up");
@@ -188,7 +183,7 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
if(off < b.length) { if(off < b.length) {
byte[] data = new byte[b.length - off]; byte[] data = new byte[b.length - off];
System.arraycopy(b, off, data, 0, data.length); System.arraycopy(b, off, data, 0, data.length);
reliabilityLayer.handleRead(data, data.length); reliabilityLayer.handleRead(data);
} }
return; return;
} else if(s.equals("OK")) { } else if(s.equals("OK")) {

View File

@@ -4,5 +4,5 @@ import java.io.IOException;
interface ReadHandler { interface ReadHandler {
void handleRead(byte[] b, int length) throws IOException; void handleRead(byte[] b) throws IOException;
} }

View File

@@ -65,20 +65,15 @@ class Receiver implements ReadHandler {
} }
} }
public void handleRead(byte[] b, int length) throws IOException { public void handleRead(byte[] b) throws IOException {
if(!valid) throw new IOException("Connection closed"); if(!valid) throw new IOException("Connection closed");
if(length < Data.MIN_LENGTH || length > Data.MAX_LENGTH) {
if(LOG.isLoggable(FINE))
LOG.fine("Ignoring frame with invalid length");
return;
}
switch(b[0]) { switch(b[0]) {
case 0: case 0:
case Frame.FIN_FLAG: case Frame.FIN_FLAG:
handleData(b, length); handleData(b);
break; break;
case Frame.ACK_FLAG: case Frame.ACK_FLAG:
sender.handleAck(b, length); sender.handleAck(b);
break; break;
default: default:
if(LOG.isLoggable(FINE)) LOG.fine("Ignoring unknown frame type"); if(LOG.isLoggable(FINE)) LOG.fine("Ignoring unknown frame type");
@@ -86,9 +81,13 @@ class Receiver implements ReadHandler {
} }
} }
private synchronized void handleData(byte[] b, int length) private synchronized void handleData(byte[] b) throws IOException {
throws IOException { if(b.length < Data.MIN_LENGTH || b.length > Data.MAX_LENGTH) {
Data d = new Data(b, length); if(LOG.isLoggable(FINE))
LOG.fine("Ignoring data frame with invalid length");
return;
}
Data d = new Data(b);
int payloadLength = d.getPayloadLength(); int payloadLength = d.getPayloadLength();
if(payloadLength > windowSize) { if(payloadLength > windowSize) {
if(LOG.isLoggable(FINE)) LOG.fine("No space in the window"); if(LOG.isLoggable(FINE)) LOG.fine("No space in the window");

View File

@@ -42,7 +42,7 @@ class ReliabilityLayer implements ReadHandler, WriteHandler {
while(valid) { while(valid) {
byte[] b = writes.take(); byte[] b = writes.take();
if(b.length == 0) return; // Poison pill if(b.length == 0) return; // Poison pill
writeHandler.handleWrite(b, b.length); writeHandler.handleWrite(b);
} }
} catch(InterruptedException e) { } catch(InterruptedException e) {
if(LOG.isLoggable(WARNING)) if(LOG.isLoggable(WARNING))
@@ -73,20 +73,14 @@ class ReliabilityLayer implements ReadHandler, WriteHandler {
} }
// The modem calls this method to pass data up to the SLIP decoder // The modem calls this method to pass data up to the SLIP decoder
public void handleRead(byte[] b, int length) throws IOException { public void handleRead(byte[] b) throws IOException {
if(!valid) throw new IOException("Connection closed"); if(!valid) throw new IOException("Connection closed");
decoder.handleRead(b, length); decoder.handleRead(b);
} }
// The SLIP encoder calls this method to pass data down to the modem // The SLIP encoder calls this method to pass data down to the modem
public void handleWrite(byte[] b, int length) throws IOException { public void handleWrite(byte[] b) throws IOException {
if(!valid) throw new IOException("Connection closed"); if(!valid) throw new IOException("Connection closed");
if(length == 0) return; if(b.length > 0) writes.add(b);
if(length < b.length) {
byte[] copy = new byte[length];
System.arraycopy(b, 0, copy, 0, length);
b = copy;
}
writes.add(b);
} }
} }

View File

@@ -47,13 +47,13 @@ class Sender {
if(LOG.isLoggable(FINE)) if(LOG.isLoggable(FINE))
LOG.fine("Acknowledging #" + sequenceNumber); LOG.fine("Acknowledging #" + sequenceNumber);
} }
writeHandler.handleWrite(a.getBuffer(), a.getLength()); writeHandler.handleWrite(a.getBuffer());
} }
void handleAck(byte[] b, int length) { void handleAck(byte[] b) {
if(length != Ack.LENGTH) { if(b.length != Ack.LENGTH) {
if(LOG.isLoggable(FINE)) if(LOG.isLoggable(FINE))
LOG.fine("Ignoring ack frame with wrong length"); LOG.fine("Ignoring ack frame with invalid length");
return; return;
} }
Ack a = new Ack(b); Ack a = new Ack(b);
@@ -115,7 +115,7 @@ class Sender {
if(fastRetransmit != null) { if(fastRetransmit != null) {
Data d = fastRetransmit.data; Data d = fastRetransmit.data;
try { try {
writeHandler.handleWrite(d.getBuffer(), d.getLength()); writeHandler.handleWrite(d.getBuffer());
} catch(IOException e) { } catch(IOException e) {
// FIXME: Do something more meaningful // FIXME: Do something more meaningful
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString()); if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
@@ -169,16 +169,14 @@ class Sender {
// Send a window probe if necessary // Send a window probe if necessary
if(sendProbe) { if(sendProbe) {
byte[] buf = new byte[Data.MIN_LENGTH]; byte[] buf = new byte[Data.MIN_LENGTH];
Data probe = new Data(buf, Data.MIN_LENGTH); Data probe = new Data(buf);
probe.setChecksum(probe.calculateChecksum()); probe.setChecksum(probe.calculateChecksum());
writeHandler.handleWrite(buf, Data.MIN_LENGTH); writeHandler.handleWrite(buf);
} }
// Retransmit any lost data frames // Retransmit any lost data frames
if(retransmit != null) { if(retransmit != null) {
for(Outstanding o : retransmit) { for(Outstanding o : retransmit)
Data d = o.data; writeHandler.handleWrite(o.data.getBuffer());
writeHandler.handleWrite(d.getBuffer(), d.getLength());
}
} }
} catch(IOException e) { } catch(IOException e) {
// FIXME: Do something more meaningful // FIXME: Do something more meaningful
@@ -202,7 +200,7 @@ class Sender {
} }
if(LOG.isLoggable(FINE)) if(LOG.isLoggable(FINE))
LOG.fine("Transmitting #" + d.getSequenceNumber()); LOG.fine("Transmitting #" + d.getSequenceNumber());
writeHandler.handleWrite(d.getBuffer(), d.getLength()); writeHandler.handleWrite(d.getBuffer());
} }
private static class Outstanding { private static class Outstanding {

View File

@@ -6,8 +6,8 @@ import java.io.OutputStream;
class SenderOutputStream extends OutputStream { class SenderOutputStream extends OutputStream {
private final Sender sender; private final Sender sender;
private final byte[] buf = new byte[Data.MAX_LENGTH];
private byte[] buf = null;
private int offset = 0; private int offset = 0;
private long sequenceNumber = 1L; private long sequenceNumber = 1L;
@@ -17,18 +17,16 @@ class SenderOutputStream extends OutputStream {
@Override @Override
public void close() throws IOException { public void close() throws IOException {
if(buf == null) assignBuffer();
send(true); send(true);
} }
@Override @Override
public void flush() throws IOException { public void flush() throws IOException {
if(buf != null) send(false); if(offset > Data.HEADER_LENGTH) send(false);
} }
@Override @Override
public void write(int b) throws IOException { public void write(int b) throws IOException {
if(buf == null) assignBuffer();
buf[offset] = (byte) b; buf[offset] = (byte) b;
offset++; offset++;
if(offset == Data.HEADER_LENGTH + Data.MAX_PAYLOAD_LENGTH) send(false); if(offset == Data.HEADER_LENGTH + Data.MAX_PAYLOAD_LENGTH) send(false);
@@ -41,13 +39,11 @@ class SenderOutputStream extends OutputStream {
@Override @Override
public void write(byte[] b, int off, int len) throws IOException { public void write(byte[] b, int off, int len) throws IOException {
if(buf == null) assignBuffer();
int available = Data.MAX_LENGTH - offset - Data.FOOTER_LENGTH; int available = Data.MAX_LENGTH - offset - Data.FOOTER_LENGTH;
while(available <= len) { while(available <= len) {
System.arraycopy(b, off, buf, offset, available); System.arraycopy(b, off, buf, offset, available);
offset += available; offset += available;
send(false); send(false);
assignBuffer();
off += available; off += available;
len -= available; len -= available;
available = Data.MAX_LENGTH - offset - Data.FOOTER_LENGTH; available = Data.MAX_LENGTH - offset - Data.FOOTER_LENGTH;
@@ -56,13 +52,10 @@ class SenderOutputStream extends OutputStream {
offset += len; offset += len;
} }
private void assignBuffer() {
buf = new byte[Data.MAX_LENGTH];
offset = Data.HEADER_LENGTH;
}
private void send(boolean lastFrame) throws IOException { private void send(boolean lastFrame) throws IOException {
Data d = new Data(buf, offset + Data.FOOTER_LENGTH); byte[] frame = new byte[offset + Data.FOOTER_LENGTH];
System.arraycopy(buf, 0, frame, 0, frame.length);
Data d = new Data(frame);
d.setLastFrame(lastFrame); d.setLastFrame(lastFrame);
d.setSequenceNumber(sequenceNumber++); d.setSequenceNumber(sequenceNumber++);
d.setChecksum(d.calculateChecksum()); d.setChecksum(d.calculateChecksum());
@@ -72,7 +65,6 @@ class SenderOutputStream extends OutputStream {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
throw new IOException("Interrupted while writing"); throw new IOException("Interrupted while writing");
} }
buf = null; offset = Data.HEADER_LENGTH;
offset = 0;
} }
} }

View File

@@ -24,16 +24,17 @@ class SlipDecoder implements ReadHandler {
this.readHandler = readHandler; this.readHandler = readHandler;
} }
public void handleRead(byte[] b, int length) throws IOException { public void handleRead(byte[] b) throws IOException {
for(int i = 0; i < length; i++) { for(int i = 0; i < b.length; i++) {
switch(b[i]) { switch(b[i]) {
case END: case END:
if(escape) { if(escape) {
reset(true); reset(true);
} else { } else {
if(decodedLength > 0) { if(decodedLength > 0) {
readHandler.handleRead(buf, decodedLength); byte[] decoded = new byte[decodedLength];
buf = new byte[Data.MAX_LENGTH]; System.arraycopy(buf, 0, decoded, 0, decodedLength);
readHandler.handleRead(decoded);
} }
reset(false); reset(false);
} }

View File

@@ -14,26 +14,25 @@ class SlipEncoder implements WriteHandler {
this.writeHandler = writeHandler; this.writeHandler = writeHandler;
} }
public void handleWrite(byte[] b, int length) throws IOException { public void handleWrite(byte[] b) throws IOException {
if(length > Data.MAX_LENGTH) throw new IllegalArgumentException(); if(b.length > Data.MAX_LENGTH) throw new IllegalArgumentException();
int encodedLength = length + 2; int encodedLength = b.length + 2;
for(int i = 0; i < length; i++) { for(int i = 0; i < b.length; i++)
if(b[i] == END || b[i] == ESC) encodedLength++; if(b[i] == END || b[i] == ESC) encodedLength++;
} byte[] encoded = new byte[encodedLength];
byte[] buf = new byte[encodedLength]; encoded[0] = END;
buf[0] = END; for(int i = 0, j = 1; i < b.length; i++) {
for(int i = 0, j = 1; i < length; i++) {
if(b[i] == END) { if(b[i] == END) {
buf[j++] = ESC; encoded[j++] = ESC;
buf[j++] = TEND; encoded[j++] = TEND;
} else if(b[i] == ESC) { } else if(b[i] == ESC) {
buf[j++] = ESC; encoded[j++] = ESC;
buf[j++] = TESC; encoded[j++] = TESC;
} else { } else {
buf[j++] = b[i]; encoded[j++] = b[i];
} }
} }
buf[encodedLength - 1] = END; encoded[encodedLength - 1] = END;
writeHandler.handleWrite(buf, encodedLength); writeHandler.handleWrite(encoded);
} }
} }

View File

@@ -4,5 +4,5 @@ import java.io.IOException;
interface WriteHandler { interface WriteHandler {
void handleWrite(byte[] b, int length) throws IOException; void handleWrite(byte[] b) throws IOException;
} }