Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**/.idea/
**/*.iml
**/target/
53 changes: 45 additions & 8 deletions src/main/java/com/igormaznitsa/jbbp/JBBPParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ public final class JBBPParser {
*/
private final JBBPBitOrder bitOrder;

/**
* The binary packed decimal representation to use.
*/
private final JBBPPackedDecimalType bcdType;

/**
* Empty structure array
*/
Expand All @@ -66,13 +71,15 @@ public final class JBBPParser {
* @param source the source script to parse binary blocks and streams, must
* not be null
* @param bitOrder the bit order for bit reading operations, must not be null
* @param bcdType the binary packed decimal representation to use, must not be null
* @param flags special flags for parsing process
* @see #FLAG_SKIP_REMAINING_FIELDS_IF_EOF
*/
private JBBPParser(final String source, final JBBPBitOrder bitOrder, final int flags) {
private JBBPParser(final String source, final JBBPBitOrder bitOrder, final JBBPPackedDecimalType bcdType, final int flags) {
JBBPUtils.assertNotNull(source, "Script is null");
JBBPUtils.assertNotNull(bitOrder, "Bit order is null");
this.bitOrder = bitOrder;
this.bcdType = bcdType;
this.flags = flags;
try {
this.compiledBlock = JBBPCompiler.compile(source);
Expand Down Expand Up @@ -290,6 +297,19 @@ private List<JBBPAbstractField> parseStruct(final JBBPBitInputStream inStream, f
}
}
break;
case JBBPCompiler.CODE_BCD: {
final int numberOfBytes = JBBPUtils.unpackInt(compiled, positionAtCompiledBlock);
if (resultNotIgnored) {
if (arrayLength < 0) {
final long value = inStream.readPackedDecimal(numberOfBytes, this.bcdType, byteOrder);
singleAtomicField = new JBBPFieldPackedDecimal(name, value);
}
else {
structureFields.add(new JBBPFieldArrayPackedDecimal(name, inStream.readPackedDecimalArray(wholeStreamArray ? -1 : arrayLength, numberOfBytes, this.bcdType, byteOrder)));
}
}
}
break;
case JBBPCompiler.CODE_SHORT: {
if (resultNotIgnored) {
if (arrayLength < 0) {
Expand Down Expand Up @@ -511,15 +531,30 @@ public JBBPFieldStruct parse(final byte[] array, final JBBPVarFieldProcessor var
* @see JBBPBitOrder#MSB0
*/
public static JBBPParser prepare(final String script, final JBBPBitOrder bitOrder) {
return new JBBPParser(script, bitOrder, 0);
return new JBBPParser(script, bitOrder, JBBPPackedDecimalType.UNSIGNED, 0);
}

/**
* Prepare a parser for a script and a packed decimal type.
*
* @param script a text script contains field order and types reference, it must not be null
* @param bcdType the binary packed decimal representation to use, must not be null
* @return the prepared parser for the script
* @see JBBPBitOrder#LSB0
* @see JBBPBitOrder#MSB0
*/
public static JBBPParser prepare(final String script, final JBBPPackedDecimalType bcdType) {
return new JBBPParser(script, JBBPBitOrder.LSB0, bcdType, 0);
}

/**
* Prepare a parser for a script with defined bit order and special flags.
* Prepare a parser for a script with defined bit order, packed decimal type, and
* special flags.
*
* @param script a text script contains field order and types reference, it
* must not be null
* @param bitOrder the bit order for reading operations, it must not be null
* @param bcdType the binary packed decimal representation to use, must not be null
* @param flags special flags for parsing
* @return the prepared parser for the script
* @see JBBPBitOrder#LSB0
Expand All @@ -528,13 +563,14 @@ public static JBBPParser prepare(final String script, final JBBPBitOrder bitOrde
*
* @since 1.1
*/
public static JBBPParser prepare(final String script, final JBBPBitOrder bitOrder, final int flags) {
return new JBBPParser(script, bitOrder, flags);
public static JBBPParser prepare(final String script, final JBBPBitOrder bitOrder, final JBBPPackedDecimalType bcdType, final int flags) {
return new JBBPParser(script, bitOrder, bcdType, flags);
}


/**
* Prepare a parser for a script with default bit order (LSB0) use.
* Prepare a parser for a script with default bit order (LSB0) and packed decimal
* type (UNSIGNED) use.
*
* @param script a text script contains field order and types reference, it must not be null
* @return the prepared parser for the script
Expand All @@ -545,7 +581,8 @@ public static JBBPParser prepare(final String script) {
}

/**
* Prepare a parser for a script with default bit order (LSB0) use and special flags
* Prepare a parser for a script with default bit order (LSB0) and packed decimal
* type (UNSIGNED) use and special flags.
*
* @param script a text script contains field order and types reference, it
* must not be null
Expand All @@ -557,7 +594,7 @@ public static JBBPParser prepare(final String script) {
* @since 1.1
*/
public static JBBPParser prepare(final String script, final int flags) {
return JBBPParser.prepare(script, JBBPBitOrder.LSB0, flags);
return JBBPParser.prepare(script, JBBPBitOrder.LSB0, JBBPPackedDecimalType.UNSIGNED, flags);
}

/**
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/com/igormaznitsa/jbbp/compiler/JBBPCompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@ private StructStackItem(final int namedFieldCounter, final int startStructureOff
*/
public static final int CODE_RESET_COUNTER = 0x0E;

/**
* The Byte code of the binary coded decimal (BCD, aka packed decimal) command.
* @since 1.1.1
*/
public static final int CODE_BCD = 0x0F;

/**
* The Byte-Code Flag shows that the field is a named one.
*/
Expand Down Expand Up @@ -289,6 +295,26 @@ public static JBBPCompiledBlock compile(final String script) throws IOException
}
}
break;
case CODE_BCD: {
final String parsedNumBytes = token.getFieldTypeParameters().getExtraData();
extraFieldPresented = true;
if (parsedNumBytes == null) {
extraField = 1;
}
else {
try {
extraField = Integer.parseInt(parsedNumBytes);
assertNonNegativeValue(extraField, token);
}
catch (NumberFormatException ex) {
extraField = -1;
}
if (extraField < 1 || extraField > 10) {
throw new JBBPCompilationException("Wrong number of BCD bytes, must be 1..10 [" + token.getFieldTypeParameters().getExtraData() + ']', token);
}
}
}
break;
case CODE_VAR: {
hasVarFields = true;
final String parsedExtraField = token.getFieldTypeParameters().getExtraData();
Expand Down Expand Up @@ -512,6 +538,9 @@ else if ("int".equals(name)) {
else if ("long".equals(name)) {
result |= CODE_LONG;
}
else if ("bcd".equals(name)) {
result |= CODE_BCD;
}
else if ("reset$$".equals(name)) {
result |= CODE_RESET_COUNTER;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public final class JBBPTokenizer implements Iterable<JBBPToken>, Iterator<JBBPTo
disabledFieldNames.add("ushort");
disabledFieldNames.add("int");
disabledFieldNames.add("long");
disabledFieldNames.add("bcd");
disabledFieldNames.add("$");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.igormaznitsa.jbbp.exceptions;

/**
* Thrown when errors occur while using {@link com.igormaznitsa.jbbp.io.JBBPOut}
* to create binary block byte arrays.
*/
public class JBBPOutException extends JBBPException {

public JBBPOutException(String message) {
super(message);
}

public JBBPOutException(String message, Throwable cause) {
super(message, cause);
}
}
117 changes: 112 additions & 5 deletions src/main/java/com/igormaznitsa/jbbp/io/JBBPBitInputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

import com.igormaznitsa.jbbp.utils.JBBPSystemProperty;
import com.igormaznitsa.jbbp.utils.JBBPUtils;
import com.igormaznitsa.jbbp.utils.PackedDecimalUtils;

import java.io.*;

/**
Expand Down Expand Up @@ -66,26 +68,57 @@ public class JBBPBitInputStream extends FilterInputStream implements JBBPCountab
private final boolean msb0;

/**
* A Constructor, the LSB0 bit order will be used by default.
* Binary Coded Decimal representation type to use.
*/
private final JBBPPackedDecimalType bcdType;

private PackedDecimalUtils pdUtils = new PackedDecimalUtils();

/**
* A Constructor, with LSB0 bit order and UNSIGNED binary coded decimal to be used
* by default.
*
* @param in an input stream to be filtered.
*/
public JBBPBitInputStream(final InputStream in) {
this(in, JBBPBitOrder.LSB0);
this(in, JBBPBitOrder.LSB0, JBBPPackedDecimalType.UNSIGNED);
}

/**
* A Constructor.
* A Constructor, with UNSIGNED binary coded decimal to be used by default.
*
* @param in an input stream to be filtered.
* @param order a bit order mode for the filter.
* @see JBBPBitOrder#LSB0
* @see JBBPBitOrder#MSB0
* @see JBBPBitOrder
*/
public JBBPBitInputStream(final InputStream in, final JBBPBitOrder order) {
this(in, order, JBBPPackedDecimalType.UNSIGNED);
}

/**
* A Constructor, with LSB0 bit order to be used by default.
*
* @param in an input stream to be filtered.
* @param bcdType the binary coded decimal representation
* @see JBBPPackedDecimalType
*/
public JBBPBitInputStream(final InputStream in, final JBBPPackedDecimalType bcdType) {
this(in, JBBPBitOrder.LSB0, bcdType);
}

/**
* A Constructor.
* @param in an input stream to be filtered.
* @param order a bit order mode for the filter.
* @param bcdType the binary coded decimal representation
* @see JBBPBitOrder
* @see JBBPPackedDecimalType
*/
public JBBPBitInputStream(final InputStream in, final JBBPBitOrder order, final JBBPPackedDecimalType bcdType) {
super(in);
this.bitsInBuffer = 0;
this.msb0 = order == JBBPBitOrder.MSB0;
this.bcdType = bcdType;
}

/**
Expand Down Expand Up @@ -341,6 +374,49 @@ public long[] readLongArray(final int items, final JBBPByteOrder byteOrder) thro
}
}

/**
* Read number of long items from the input stream.
*
* @param items number of items to be read from the input stream, if less than
* zero then all stream till the end will be read
* @param byteOrder the order of bytes to be used to decode values
* @return read items as a long array
* @throws IOException it will be thrown for any transport problem during the
* operation
* @see JBBPByteOrder#BIG_ENDIAN
* @see JBBPByteOrder#LITTLE_ENDIAN
*/
public long[] readPackedDecimalArray(final int items, final int bytes, final JBBPPackedDecimalType bcdType, final JBBPByteOrder byteOrder) throws IOException {
int pos = 0;
if (items < 0) {
long[] buffer = new long[INITIAL_ARRAY_BUFFER_SIZE];
// till end
while (hasAvailableData()) {
final long next = readPackedDecimal(bytes, bcdType, byteOrder);
if (buffer.length == pos) {
final long[] newbuffer = new long[buffer.length << 1];
System.arraycopy(buffer, 0, newbuffer, 0, buffer.length);
buffer = newbuffer;
}
buffer[pos++] = next;
}
if (buffer.length == pos) {
return buffer;
}
final long[] result = new long[pos];
System.arraycopy(buffer, 0, result, 0, pos);
return result;
}
else {
// number
final long[] buffer = new long[items];
for (int i = 0; i < items; i++) {
buffer[i] = readPackedDecimal(bytes, bcdType, byteOrder);
}
return buffer;
}
}

/**
* Read a unsigned short value from the stream.
*
Expand Down Expand Up @@ -406,6 +482,27 @@ public long readLong(final JBBPByteOrder byteOrder) throws IOException {
}
}

/**
* Read a packed decimal (BCD) value from the stream. Note that most (or all) packed
* decimal implementations are big endian, so little endian byte order is ignored, and
* big endian is always used.
*
* @param length length of packed decimal field in bytes
* @param bcdType packed decimal representation (signed or unsigned)
* @param byteOrder the order of bytes to be used to decode the read value
* @return the decoded value
* @throws IOException it will be thrown for any transport problem during the
* operation
* @throws EOFException if the end of the stream has been reached
*/
public long readPackedDecimal(final int length, final JBBPPackedDecimalType bcdType, final JBBPByteOrder byteOrder) throws IOException {
if (byteOrder.equals(JBBPByteOrder.LITTLE_ENDIAN)) {
System.out.println("***WARNING: Packed Decimal does not support little endian...using big endian instead");
}
byte[] theBytes = readByteArray(length);
return pdUtils.readValueFromPackedDecimal(theBytes, bcdType);
}

/**
* Get the current fully read byte counter.
*
Expand Down Expand Up @@ -444,6 +541,16 @@ public JBBPBitOrder getBitOrder() {
return this.msb0 ? JBBPBitOrder.MSB0 : JBBPBitOrder.LSB0;
}

/**
* Get the binary coded decimal represenation type for read operations.
*
* @return the BCD type
* @see JBBPPackedDecimalType
*/
public JBBPPackedDecimalType getBcdType() {
return bcdType;
}

/**
* Read number of bits from the input stream. It reads bits from input stream
* since 0 bit and make reversion to return bits in the right order when 0 bit
Expand Down
Loading