From 47b124ff46c720b3a7e669b69016665531073f0e Mon Sep 17 00:00:00 2001 From: Curtis Ruck Date: Fri, 16 Dec 2022 16:15:55 -0500 Subject: [PATCH] #442 - WIP attempt at processing InputStream --- .../java/org/jmisb/api/klv/BerDecoder.java | 52 +++++++++++++++++ .../java/org/jmisb/api/klv/KlvParser.java | 58 +++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/api/src/main/java/org/jmisb/api/klv/BerDecoder.java b/api/src/main/java/org/jmisb/api/klv/BerDecoder.java index a6922dd81..cd7adc604 100644 --- a/api/src/main/java/org/jmisb/api/klv/BerDecoder.java +++ b/api/src/main/java/org/jmisb/api/klv/BerDecoder.java @@ -1,7 +1,11 @@ package org.jmisb.api.klv; +import java.io.IOException; +import java.io.InputStream; + /** Decode data using Basic Encoding Rules (BER). */ public class BerDecoder { + private BerDecoder() {} /** @@ -77,4 +81,52 @@ public static BerField decode(byte[] data, int offset, boolean isOid) return new BerField(length, value); } + + public static BerField decode(InputStream is, boolean isOid) throws IOException { + if (!isOid) { + return decodeBer(is); + } + + return decodeBerOid(is); + } + + private static BerField decodeBer(InputStream is) throws IOException { + int length = is.read(); + + if ((length & 0x80) == 0) { + // BER Short Form. If the first bit of the BER is 0 then the BER is 1-byte. + return new BerField(1, length); + } + + // BER Long Form (variable length) + int berLength = length & 0x7f; + int fullBerSize = berLength + 1; + byte[] data = new byte[berLength]; + int read = is.read(data, 0, data.length); + if (read != data.length) { + throw new IllegalArgumentException("BER parsing ran out of bytes"); + } + int len = 0; + for (int i = 0; i < berLength; ++i) { + int b = 0x00FF & data[i]; + len = (len << 8) | b; + } + length = len; + + return new BerField(fullBerSize, length); + } + + private static BerField decodeBerOid(InputStream is) throws IOException { + int read; + int value = 0; + int length = 0; + do { + read = is.read(); + int highbits = (value << 7); + int lowbits = (read & 0x7F); + value = highbits + lowbits; + length++; + } while ((read & 0x80) == 0x80); + return new BerField(length, value); + } } diff --git a/api/src/main/java/org/jmisb/api/klv/KlvParser.java b/api/src/main/java/org/jmisb/api/klv/KlvParser.java index 619ac9803..053bd997e 100644 --- a/api/src/main/java/org/jmisb/api/klv/KlvParser.java +++ b/api/src/main/java/org/jmisb/api/klv/KlvParser.java @@ -1,8 +1,12 @@ package org.jmisb.api.klv; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.function.Consumer; import org.jmisb.api.common.KlvParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,6 +17,60 @@ public class KlvParser { private KlvParser() {} + public static void parseStream( + InputStream is, + Consumer handler, + Consumer exceptionHandler) + throws KlvParseException { + + // reusable key array to minimize garbage + byte[] key = new byte[UniversalLabel.LENGTH]; + + try { + while (true) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + // Read the UniversalLabel + int read = is.read(key, 0, key.length); + if (read < 0) { + break; + } + if (read != key.length) { + throw new KlvParseException( + "Read " + read + " bytes when expected " + key.length); + } + out.write(key); + + // Read the payload length + BerField length = BerDecoder.decode(is, false); + out.write(BerEncoder.encode(length.getValue())); + + // Read the payload + byte[] payload = new byte[length.getValue()]; + read = is.read(payload, 0, payload.length); + if (read == 0) { + break; + } + if (read != payload.length) { + throw new KlvParseException( + "Read " + read + " bytes when expected " + key.length); + } + out.write(payload); + + // hand off the IMisbMessage + byte[] buf = out.toByteArray(); + try { + IMisbMessage msg = MisbMessageFactory.getInstance().handleMessage(buf); + handler.accept(msg); + } catch (KlvParseException e) { + exceptionHandler.accept(e); + } + } + } catch (IOException e) { + throw new KlvParseException("IOException during stream parsing"); + } + } + /** * Parse a byte array containing one or more {@link IMisbMessage}s. *