1
0
mirror of https://github.com/arduino/Arduino.git synced 2025-02-19 13:54:23 +01:00

Merge 782a35bf9e4d64b612a21443c7671c53087dfafc into 3278173ef810935e07808deed58783c1bc7ca4cf

This commit is contained in:
Cristian Maglie 2024-11-28 04:19:09 +01:00 committed by GitHub
commit 1853e14040
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 43 additions and 55 deletions

View File

@ -29,6 +29,7 @@
package processing.app; package processing.app;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import org.junit.Test; import org.junit.Test;
@ -47,6 +48,16 @@ public class SerialTest {
String output = ""; String output = "";
} }
@Test
public void testSerialUTF8DecoderWithInvalidChars() throws Exception {
NullSerial s = new NullSerial();
byte[] testdata = new byte[] { '>', (byte) 0xC3, (byte) 0x28, '<' };
byte[] expected = new byte[] { '>', (byte) 0xEF, (byte) 0xBF, (byte) 0xBD, (byte) 0x28, '<' };
s.processSerialEvent(testdata);
byte[] res = s.output.getBytes("UTF-8");
assertArrayEquals(expected, res);
}
@Test @Test
public void testSerialUTF8Decoder() throws Exception { public void testSerialUTF8Decoder() throws Exception {
NullSerial s = new NullSerial(); NullSerial s = new NullSerial();

View File

@ -22,23 +22,22 @@
package processing.app; package processing.app;
import jssc.SerialPort; import static processing.app.I18n.format;
import jssc.SerialPortEvent; import static processing.app.I18n.tr;
import jssc.SerialPortEventListener;
import jssc.SerialPortException;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.io.InputStreamReader;
import java.nio.CharBuffer; import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import static processing.app.I18n.format; import jssc.SerialPort;
import static processing.app.I18n.tr; import jssc.SerialPortEvent;
import jssc.SerialPortEventListener;
import jssc.SerialPortException;
public class Serial implements SerialPortEventListener { public class Serial implements SerialPortEventListener {
@ -53,11 +52,9 @@ public class Serial implements SerialPortEventListener {
private SerialPort port; private SerialPort port;
private CharsetDecoder bytesToStrings; private PipedOutputStream decoderInRaw;
private static final int IN_BUFFER_CAPACITY = 128; private InputStreamReader decoderOutputUTF8;
private static final int OUT_BUFFER_CAPACITY = 128; private final int DECODER_BUFF_SIZE = 16384;
private ByteBuffer inFromSerial = ByteBuffer.allocate(IN_BUFFER_CAPACITY);
private CharBuffer outToMessage = CharBuffer.allocate(OUT_BUFFER_CAPACITY);
public Serial() throws SerialException { public Serial() throws SerialException {
this(PreferencesData.get("serial.port"), this(PreferencesData.get("serial.port"),
@ -189,42 +186,18 @@ public class Serial implements SerialPortEventListener {
public void processSerialEvent(byte[] buf) { public void processSerialEvent(byte[] buf) {
int next = 0; int next = 0;
// This uses a CharsetDecoder to convert from bytes to UTF-8 in int max = buf.length;
// a streaming fashion (i.e. where characters might be split char chars[] = new char[DECODER_BUFF_SIZE];
// over multiple reads). This needs the data to be in a try {
// ByteBuffer (inFromSerial, which we also use to store leftover while (next < max) {
// incomplete characters for the nexst run) and produces a int w = Integer.min(max - next, chars.length);
// CharBuffer (outToMessage), which we then convert to char[] to decoderInRaw.write(buf, next, w);
// pass onwards. next += w;
// Note that these buffers switch from input to output mode int n = decoderOutputUTF8.read(chars);
// using flip/compact/clear message(chars, n);
while (next < buf.length || inFromSerial.position() > 0) { }
do { } catch (IOException e) {
// This might be 0 when all data was already read from buf e.printStackTrace();
// (but then there will be data in inFromSerial left to
// decode).
int copyNow = Math.min(buf.length - next, inFromSerial.remaining());
inFromSerial.put(buf, next, copyNow);
next += copyNow;
inFromSerial.flip();
bytesToStrings.decode(inFromSerial, outToMessage, false);
inFromSerial.compact();
// When there are multi-byte characters, outToMessage might
// still have room, so add more bytes if we have any.
} while (next < buf.length && outToMessage.hasRemaining());
// If no output was produced, the input only contained
// incomplete characters, so we're done processing
if (outToMessage.position() == 0)
break;
outToMessage.flip();
char[] chars = new char[outToMessage.remaining()];
outToMessage.get(chars);
message(chars, chars.length);
outToMessage.clear();
} }
} }
@ -295,10 +268,14 @@ public class Serial implements SerialPortEventListener {
* before they are handed as Strings to {@Link #message(char[], int)}. * before they are handed as Strings to {@Link #message(char[], int)}.
*/ */
public synchronized void resetDecoding(Charset charset) { public synchronized void resetDecoding(Charset charset) {
bytesToStrings = charset.newDecoder() try {
.onMalformedInput(CodingErrorAction.REPLACE) decoderInRaw = new PipedOutputStream();
.onUnmappableCharacter(CodingErrorAction.REPLACE) // add 16 extra bytes to make room for incomplete UTF-8 chars
.replaceWith("\u2e2e"); decoderOutputUTF8 = new InputStreamReader(new PipedInputStream(decoderInRaw, DECODER_BUFF_SIZE + 16), charset);
} catch (IOException e) {
// Should never happen...
e.printStackTrace();
}
} }
static public List<String> list() { static public List<String> list() {