From 6edf6f939632986f0e05c033b0621734615f4389 Mon Sep 17 00:00:00 2001 From: Kevin Herron Date: Sun, 30 Apr 2017 06:47:38 -0700 Subject: [PATCH 1/2] Reduce byte[] allocations in SerialInputStream and SerialOutputStream * use a shared, re-usable buffer for single byte reads and writes * implement read(byte[]) directly instead of delegating to read(byte[], int, int), which makes an unnecessary copy. * implement write(byte[]) directly instead of delegating to write(byte[], int, int), which makes an unnecessary copy. * avoid a corner case unnecessary copy in write(byte[], int, int) --- .../com/fazecast/jSerialComm/SerialPort.java | 63 ++++++++++++++----- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/fazecast/jSerialComm/SerialPort.java b/src/main/java/com/fazecast/jSerialComm/SerialPort.java index e8d3801..c80dcc8 100644 --- a/src/main/java/com/fazecast/jSerialComm/SerialPort.java +++ b/src/main/java/com/fazecast/jSerialComm/SerialPort.java @@ -27,12 +27,12 @@ package com.fazecast.jSerialComm; import java.io.BufferedReader; import java.io.DataOutputStream; -import java.io.InputStreamReader; import java.io.File; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.OutputStream; import java.util.Date; @@ -1022,6 +1022,11 @@ public final class SerialPort // InputStream interface class private final class SerialPortInputStream extends InputStream { + // a shared, re-usable, single-byte scratch buffer to avoid allocating + // a new byte[] when reading a single byte. not thread safe, but neither + // SerialPort nor its Input/Output streams are to begin with. + private final byte[] singleByteBuffer = new byte[1]; + public SerialPortInputStream() {} @Override @@ -1036,23 +1041,30 @@ public final class SerialPort @Override public final int read() throws IOException { - byte[] buffer = new byte[1]; int bytesRead; while (isOpened) { - bytesRead = readBytes(portHandle, buffer, 1l); + bytesRead = readBytes(portHandle, singleByteBuffer, 1L); + if (bytesRead > 0) - return ((int)buffer[0] & 0x000000FF); - try { Thread.sleep(1); } catch (Exception e) {} + return ((int) singleByteBuffer[0] & 0xFF); + + sleep1(); } + throw new IOException("This port appears to have been shutdown or disconnected."); } @Override public final int read(byte[] b) throws IOException { - return read(b, 0, b.length); + if (!isOpened) + throw new IOException("This port appears to have been shutdown or disconnected."); + if (b.length == 0) + return 0; + + return readBytes(portHandle, b, b.length); } @Override @@ -1085,6 +1097,11 @@ public final class SerialPort // OutputStream interface class private final class SerialPortOutputStream extends OutputStream { + // a shared, re-usable, single-byte scratch buffer to avoid allocating + // a new byte[] when writing a single byte. not thread safe, but neither + // SerialPort nor its Input/Output streams are to begin with. + private final byte[] singleByteBuffer = new byte[1]; + public SerialPortOutputStream() {} @Override @@ -1093,28 +1110,40 @@ public final class SerialPort if (!isOpened) throw new IOException("This port appears to have been shutdown or disconnected."); - byte[] buffer = new byte[1]; - buffer[0] = (byte)(b & 0x000000FF); - if (writeBytes(portHandle, buffer, 1l) < 0) + singleByteBuffer[0] = (byte) (b & 0xFF); + + if (writeBytes(portHandle, singleByteBuffer, 1L) < 0) throw new IOException("This port appears to have been shutdown or disconnected."); } @Override public final void write(byte[] b) throws IOException { - write(b, 0, b.length); + if (!isOpened) + throw new IOException("This port appears to have been shutdown or disconnected."); + + if (writeBytes(portHandle, b, b.length) < 0) + throw new IOException("This port appears to have been shutdown or disconnected."); } @Override public final void write(byte[] b, int off, int len) throws IOException { - if (!isOpened) - throw new IOException("This port appears to have been shutdown or disconnected."); - - byte[] buffer = new byte[len]; - System.arraycopy(b, off, buffer, 0, len); - if (writeBytes(portHandle, buffer, len) < 0) - throw new IOException("This port appears to have been shutdown or disconnected."); + if (off == 0 && len == b.length) + { + write(b); + } + else + { + byte[] buffer = new byte[len]; + System.arraycopy(b, off, buffer, 0, len); + write(buffer); + } } } + + private static void sleep1() { + try { Thread.sleep(1); } catch (Exception ignored) {} + } + } From df4a8b7cb85d0576065be04f2cd61f4a47c0b7a3 Mon Sep 17 00:00:00 2001 From: Kevin Herron Date: Sun, 30 Apr 2017 06:48:46 -0700 Subject: [PATCH 2/2] Block/loop until all bytes are written SerialOutputStream Ensures that the write methods of SerialOutputStream fulfill their contract by acknowledging the possibility that not all bytes could be written in the first call to native writeBytes() and looping until all bytes have been written. --- .../com/fazecast/jSerialComm/SerialPort.java | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/fazecast/jSerialComm/SerialPort.java b/src/main/java/com/fazecast/jSerialComm/SerialPort.java index c80dcc8..2ec0155 100644 --- a/src/main/java/com/fazecast/jSerialComm/SerialPort.java +++ b/src/main/java/com/fazecast/jSerialComm/SerialPort.java @@ -1107,23 +1107,50 @@ public final class SerialPort @Override public final void write(int b) throws IOException { - if (!isOpened) - throw new IOException("This port appears to have been shutdown or disconnected."); + int bytesWritten = 0; - singleByteBuffer[0] = (byte) (b & 0xFF); + do { + if (!isOpened) + throw new IOException("This port appears to have been shutdown or disconnected."); - if (writeBytes(portHandle, singleByteBuffer, 1L) < 0) - throw new IOException("This port appears to have been shutdown or disconnected."); + singleByteBuffer[0] = (byte) (b & 0xFF); + + bytesWritten += writeBytes(portHandle, singleByteBuffer, 1L); + + if (bytesWritten < 0) + throw new IOException("This port appears to have been shutdown or disconnected."); + + if (bytesWritten == 0) sleep1(); + } while (bytesWritten < 1); } @Override public final void write(byte[] b) throws IOException { - if (!isOpened) - throw new IOException("This port appears to have been shutdown or disconnected."); + int bytesWritten = 0; + byte[] buffer = b; - if (writeBytes(portHandle, b, b.length) < 0) - throw new IOException("This port appears to have been shutdown or disconnected."); + do { + if (!isOpened) + throw new IOException("This port appears to have been shutdown or disconnected."); + + int written = writeBytes(portHandle, buffer, 1L); + + if (written < 0) + throw new IOException("This port appears to have been shutdown or disconnected."); + + bytesWritten += written; + + if (written == 0) + { + sleep1(); + } + else + { + buffer = new byte[b.length - bytesWritten]; + System.arraycopy(b, bytesWritten, buffer, 0, buffer.length); + } + } while (bytesWritten < b.length); } @Override