Skip to content

Commit d0703e5

Browse files
committed
fixes to support expressions in extra numeric field part
1 parent a28bcfc commit d0703e5

File tree

8 files changed

+228
-82
lines changed

8 files changed

+228
-82
lines changed

changelog.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Change log
1010
- Fixed JBBPTextWriter, added support of logging for JBBPAbstractField objects
1111
- Added support of payload objects in JBBPAbstractField
1212
- Improved inside script compiler and interpreter to support future extensions.
13+
- Fixed expression evaluator to support single char field names in expressions.
14+
- Added support of expressions in extra field numeric data part (example bit:(field*2))
1315

1416
1.1.0
1517
- Added support to write mapped classes into JBBPOut

src/main/java/com/igormaznitsa/jbbp/JBBPParser.java

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,25 @@ private List<JBBPAbstractField> parseStruct(final JBBPBitInputStream inStream, f
133133
}
134134

135135
final int c = compiled[positionAtCompiledBlock.getAndIncrement()] & 0xFF;
136-
final boolean noExtraField = (c & JBBPCompiler.FLAG_WIDE) == 0;
137-
final int ec = noExtraField ? 0 : compiled[positionAtCompiledBlock.getAndIncrement()] & 0xFF;
138-
136+
final boolean wideCode = (c & JBBPCompiler.FLAG_WIDE) != 0;
137+
final int ec = wideCode ? compiled[positionAtCompiledBlock.getAndIncrement()] & 0xFF : 0;
138+
final boolean extraFieldNumAsExpr = (ec & JBBPCompiler.EXT_FLAG_EXTRA_AS_EXPRESSION)!=0;
139139
final int code = (ec<<8) | c;
140140

141141
final JBBPNamedFieldInfo name = (code & JBBPCompiler.FLAG_NAMED) == 0 ? null : compiledBlock.getNamedFields()[positionAtNamedFieldList.getAndIncrement()];
142142
final JBBPByteOrder byteOrder = (code & JBBPCompiler.FLAG_LITTLE_ENDIAN) == 0 ? JBBPByteOrder.BIG_ENDIAN : JBBPByteOrder.LITTLE_ENDIAN;
143143

144144
final boolean resultNotIgnored = !skipStructureFields;
145+
146+
final int extraFieldNumExprResult;
147+
if (extraFieldNumAsExpr) {
148+
final JBBPIntegerValueEvaluator evaluator = this.compiledBlock.getArraySizeEvaluators()[positionAtVarLengthProcessors.getAndIncrement()];
149+
extraFieldNumExprResult = resultNotIgnored ? evaluator.eval(inStream, positionAtCompiledBlock.get(), this.compiledBlock, namedNumericFieldMap) : 0;
150+
}
151+
else {
152+
extraFieldNumExprResult = 0;
153+
}
154+
145155
final boolean wholeStreamArray;
146156
final int arrayLength;
147157
final int packedArraySizeOffset;
@@ -186,14 +196,14 @@ private List<JBBPAbstractField> parseStruct(final JBBPBitInputStream inStream, f
186196
}
187197
break;
188198
case JBBPCompiler.CODE_ALIGN: {
189-
final int alignValue = JBBPUtils.unpackInt(compiled, positionAtCompiledBlock);
199+
final int alignValue = extraFieldNumAsExpr ? extraFieldNumExprResult : JBBPUtils.unpackInt(compiled, positionAtCompiledBlock);
190200
if (resultNotIgnored) {
191201
inStream.align(alignValue);
192202
}
193203
}
194204
break;
195205
case JBBPCompiler.CODE_SKIP: {
196-
final int skipByteNumber = JBBPUtils.unpackInt(compiled, positionAtCompiledBlock);
206+
final int skipByteNumber = extraFieldNumAsExpr ? extraFieldNumExprResult : JBBPUtils.unpackInt(compiled, positionAtCompiledBlock);
197207
if (resultNotIgnored && skipByteNumber > 0) {
198208
final long skippedBytes = inStream.skip(skipByteNumber);
199209
if (skippedBytes != skipByteNumber) {
@@ -203,7 +213,7 @@ private List<JBBPAbstractField> parseStruct(final JBBPBitInputStream inStream, f
203213
}
204214
break;
205215
case JBBPCompiler.CODE_BIT: {
206-
final int numberOfBits = JBBPUtils.unpackInt(compiled, positionAtCompiledBlock);
216+
final int numberOfBits = extraFieldNumAsExpr ? extraFieldNumExprResult : JBBPUtils.unpackInt(compiled, positionAtCompiledBlock);
207217
if (resultNotIgnored) {
208218
final JBBPBitNumber bitNumber = JBBPBitNumber.decode(numberOfBits);
209219
if (arrayLength < 0) {
@@ -220,7 +230,7 @@ private List<JBBPAbstractField> parseStruct(final JBBPBitInputStream inStream, f
220230
}
221231
break;
222232
case JBBPCompiler.CODE_VAR: {
223-
final int extraField = JBBPUtils.unpackInt(compiled, positionAtCompiledBlock);
233+
final int extraField = extraFieldNumAsExpr ? extraFieldNumExprResult : JBBPUtils.unpackInt(compiled, positionAtCompiledBlock);
224234
if (resultNotIgnored) {
225235
if (arrayLength < 0) {
226236
singleAtomicField = varFieldProcessor.readVarField(inStream, name, extraField, byteOrder, namedNumericFieldMap);
@@ -244,8 +254,8 @@ private List<JBBPAbstractField> parseStruct(final JBBPBitInputStream inStream, f
244254
}
245255
break;
246256
case JBBPCompiler.CODE_CUSTOMTYPE: {
257+
final int extraData = extraFieldNumAsExpr ? extraFieldNumExprResult : JBBPUtils.unpackInt(compiled, positionAtCompiledBlock);
247258
if (resultNotIgnored){
248-
final int extraData = JBBPUtils.unpackInt(compiled, positionAtCompiledBlock);
249259
final JBBPFieldTypeParameterContainer fieldTypeInfo = this.compiledBlock.getCustomTypeFields()[JBBPUtils.unpackInt(compiled, positionAtCompiledBlock)];
250260
final JBBPAbstractField field = this.customFieldTypeProcessor.readCustomFieldType(inStream, this.bitOrder, this.flags, fieldTypeInfo, name, extraData, wholeStreamArray, arrayLength);
251261
JBBPUtils.assertNotNull(field, "Must not return null as read result");
@@ -363,7 +373,7 @@ private List<JBBPAbstractField> parseStruct(final JBBPBitInputStream inStream, f
363373
final int structStart = JBBPUtils.unpackInt(compiled, positionAtCompiledBlock);
364374

365375
if (inStream.hasAvailableData()) {
366-
positionAtCompiledBlock.set(structStart + (noExtraField ? 1 : 2));
376+
positionAtCompiledBlock.set(structStart + (wideCode ? 2 : 1));
367377
}
368378
}
369379

@@ -390,7 +400,7 @@ private List<JBBPAbstractField> parseStruct(final JBBPBitInputStream inStream, f
390400
// not the last
391401
positionAtNamedFieldList.set(nameFieldCurrent);
392402
positionAtVarLengthProcessors.set(varLenProcCurrent);
393-
positionAtCompiledBlock.set(structBodyStart + packedArraySizeOffset + (noExtraField ? 1 : 2));
403+
positionAtCompiledBlock.set(structBodyStart + packedArraySizeOffset + (wideCode ? 2 : 1));
394404
}
395405
}
396406
}

src/main/java/com/igormaznitsa/jbbp/compiler/JBBPCompiler.java

Lines changed: 108 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -151,30 +151,38 @@ private StructStackItem(final int namedFieldCounter, final int startStructureOff
151151
* The Byte-Code Flag shows that the field is a named one.
152152
*/
153153
public static final int FLAG_NAMED = 0x10;
154-
154+
155155
/**
156156
* The Byte-Code Flag shows that the field is an array but it must be omitted
157157
* for unlimited field arrays.
158158
*/
159159
public static final int FLAG_ARRAY = 0x20;
160-
160+
161161
/**
162162
* The Byte-Code Flag shows that a multi-byte field must be decoded as
163163
* Little-endian one.
164164
*/
165165
public static final int FLAG_LITTLE_ENDIAN = 0x40;
166166

167167
/**
168-
* The Flag shows that the byte code is wide and contains extra byte in the next position of compiled block.
168+
* The Flag shows that the byte code is wide and contains extra byte in the
169+
* next position of compiled block.
169170
*/
170171
public static final int FLAG_WIDE = 0x80;
171172

172173
/**
173-
* The flag (placed only in the second byte of wide codes) shows that the field is an array which calculated size or unlimited and must be read till the end of a
174-
* stream.
174+
* The flag (placed only in the second byte of wide codes) shows that the
175+
* field is an array which calculated size or unlimited and must be read till
176+
* the end of a stream.
175177
*/
176178
public static final int EXT_FLAG_EXPRESSION_OR_WHOLESTREAM = 0x01;
177179

180+
/**
181+
* The flag shows that the extra numeric value for field should be recognized
182+
* not as number but as expression.
183+
*/
184+
public static final int EXT_FLAG_EXTRA_AS_EXPRESSION = 0x02;
185+
178186
public static JBBPCompiledBlock compile(final String script) throws IOException {
179187
return compile(script, null);
180188
}
@@ -218,25 +226,27 @@ public static JBBPCompiledBlock compile(final String script, final JBBPCustomFie
218226
final int startFieldOffset = offset;
219227

220228
final int extracode = code >>> 8;
221-
229+
222230
out.write(code);
223231
offset++;
224232

225-
if ((code & FLAG_WIDE)!=0){
233+
if ((code & FLAG_WIDE) != 0) {
226234
out.write(extracode);
227235
offset++;
228236
}
229-
237+
230238
StructStackItem currentClosedStructure = null;
231-
boolean extraFieldPresented = false;
232-
int extraField = -1;
239+
boolean writeExtraFieldNumberInCompiled = false;
240+
int extraFieldNumberAsInt = -1;
233241
int customTypeFieldIndex = -1;
234242

235243
// check that the field is not in the current structure which is a whole stream one
236244
if ((code & 0xF) != CODE_STRUCT_END && fieldUnrestrictedArrayOffset >= 0 && (structureStack.isEmpty() || structureStack.get(structureStack.size() - 1).startStructureOffset != fieldUnrestrictedArrayOffset)) {
237245
throw new JBBPCompilationException("Attempt to read after a 'till-the-end' field", token);
238246
}
239247

248+
final boolean extraFieldNumericDataAsExpression = ((code >>> 8) & EXT_FLAG_EXTRA_AS_EXPRESSION) != 0;
249+
240250
switch (code & 0xF) {
241251
case CODE_BOOL:
242252
case CODE_BYTE:
@@ -247,20 +257,25 @@ public static JBBPCompiledBlock compile(final String script, final JBBPCustomFie
247257
case CODE_CUSTOMTYPE:
248258
case CODE_LONG: {
249259
if ((code & 0x0F) == CODE_CUSTOMTYPE) {
250-
final String extraDataAsStr = token.getFieldTypeParameters().getExtraData();
251-
if (extraDataAsStr == null) {
252-
extraField = 0;
260+
if (extraFieldNumericDataAsExpression) {
261+
varLengthEvaluators.add(JBBPEvaluatorFactory.getInstance().make(token.getFieldTypeParameters().getExtraDataExpression(), namedFields, out.toByteArray()));
253262
}
254263
else {
255-
try {
256-
extraField = Integer.parseInt(extraDataAsStr);
264+
final String extraDataAsStr = token.getFieldTypeParameters().getExtraData();
265+
if (extraDataAsStr == null) {
266+
extraFieldNumberAsInt = 0;
257267
}
258-
catch (NumberFormatException ex) {
259-
throw new JBBPCompilationException("Can't parse extra data, must be numeric", token);
268+
else {
269+
try {
270+
extraFieldNumberAsInt = Integer.parseInt(extraDataAsStr);
271+
}
272+
catch (NumberFormatException ex) {
273+
throw new JBBPCompilationException("Can't parse extra data, must be numeric", token);
274+
}
260275
}
276+
writeExtraFieldNumberInCompiled = true;
261277
}
262-
extraFieldPresented = true;
263-
if (customTypeFieldProcessor.isAllowed(token.getFieldTypeParameters(), token.getFieldName(), extraField, token.isArray())) {
278+
if (customTypeFieldProcessor.isAllowed(token.getFieldTypeParameters(), token.getFieldName(), extraFieldNumberAsInt, token.isArray())) {
264279
customTypeFieldIndex = customTypeFields.size();
265280
customTypeFields.add(token.getFieldTypeParameters());
266281
}
@@ -277,18 +292,23 @@ public static JBBPCompiledBlock compile(final String script, final JBBPCustomFie
277292
if (token.getFieldName() != null) {
278293
throw new JBBPCompilationException("'skip' must not be named", token);
279294
}
280-
final String parsedSkipByteNumber = token.getFieldTypeParameters().getExtraData();
281-
extraFieldPresented = true;
282-
if (parsedSkipByteNumber == null) {
283-
extraField = 1;
295+
if (extraFieldNumericDataAsExpression) {
296+
varLengthEvaluators.add(JBBPEvaluatorFactory.getInstance().make(token.getFieldTypeParameters().getExtraDataExpression(), namedFields, out.toByteArray()));
284297
}
285298
else {
286-
try {
287-
extraField = Integer.parseInt(parsedSkipByteNumber);
288-
assertNonNegativeValue(extraField, token);
299+
final String extraNumberAsStr = token.getFieldTypeParameters().getExtraData();
300+
writeExtraFieldNumberInCompiled = true;
301+
if (extraNumberAsStr == null) {
302+
extraFieldNumberAsInt = 1;
289303
}
290-
catch (NumberFormatException ex) {
291-
extraField = -1;
304+
else {
305+
try {
306+
extraFieldNumberAsInt = Integer.parseInt(extraNumberAsStr);
307+
assertNonNegativeValue(extraFieldNumberAsInt, token);
308+
}
309+
catch (NumberFormatException ex) {
310+
extraFieldNumberAsInt = -1;
311+
}
292312
}
293313
}
294314
}
@@ -301,58 +321,79 @@ public static JBBPCompiledBlock compile(final String script, final JBBPCustomFie
301321
throw new JBBPCompilationException("'align' must not be named", token);
302322
}
303323

304-
final String parsedAlignBytesNumber = token.getFieldTypeParameters().getExtraData();
305-
extraFieldPresented = true;
306-
if (parsedAlignBytesNumber == null) {
307-
extraField = 1;
324+
if (extraFieldNumericDataAsExpression) {
325+
varLengthEvaluators.add(JBBPEvaluatorFactory.getInstance().make(token.getFieldTypeParameters().getExtraDataExpression(), namedFields, out.toByteArray()));
308326
}
309327
else {
310-
try {
311-
extraField = Integer.parseInt(parsedAlignBytesNumber);
312-
assertNonNegativeValue(extraField, token);
328+
final String extraNumberAsStr = token.getFieldTypeParameters().getExtraData();
329+
writeExtraFieldNumberInCompiled = true;
330+
if (extraNumberAsStr == null) {
331+
extraFieldNumberAsInt = 1;
313332
}
314-
catch (NumberFormatException ex) {
315-
extraField = -1;
316-
}
317-
if (extraField <= 0) {
318-
throw new JBBPCompilationException("'align' size must be greater than zero [" + token.getFieldTypeParameters().getExtraData() + ']', token);
333+
else {
334+
try {
335+
extraFieldNumberAsInt = Integer.parseInt(extraNumberAsStr);
336+
assertNonNegativeValue(extraFieldNumberAsInt, token);
337+
}
338+
catch (NumberFormatException ex) {
339+
extraFieldNumberAsInt = -1;
340+
}
341+
if (extraFieldNumberAsInt <= 0) {
342+
throw new JBBPCompilationException("'align' size must be greater than zero [" + token.getFieldTypeParameters().getExtraData() + ']', token);
343+
}
319344
}
320345
}
321346
}
322347
break;
323348
case CODE_BIT: {
324-
final String parsedBitNumber = token.getFieldTypeParameters().getExtraData();
325-
extraFieldPresented = true;
326-
if (parsedBitNumber == null) {
327-
extraField = 1;
349+
if (extraFieldNumericDataAsExpression) {
350+
varLengthEvaluators.add(JBBPEvaluatorFactory.getInstance().make(token.getFieldTypeParameters().getExtraDataExpression(), namedFields, out.toByteArray()));
328351
}
329352
else {
330-
try {
331-
extraField = Integer.parseInt(parsedBitNumber);
332-
assertNonNegativeValue(extraField, token);
353+
final String extraFieldNumAsStr = token.getFieldTypeParameters().getExtraData();
354+
writeExtraFieldNumberInCompiled = true;
355+
if (extraFieldNumAsStr == null) {
356+
extraFieldNumberAsInt = 1;
333357
}
334-
catch (NumberFormatException ex) {
335-
extraField = -1;
336-
}
337-
if (extraField < 1 || extraField > 8) {
338-
throw new JBBPCompilationException("Bit-width must be 1..8 [" + token.getFieldTypeParameters().getExtraData() + ']', token);
358+
else {
359+
try {
360+
extraFieldNumberAsInt = Integer.parseInt(extraFieldNumAsStr);
361+
assertNonNegativeValue(extraFieldNumberAsInt, token);
362+
}
363+
catch (NumberFormatException ex) {
364+
extraFieldNumberAsInt = -1;
365+
}
366+
if (extraFieldNumberAsInt < 1 || extraFieldNumberAsInt > 8) {
367+
throw new JBBPCompilationException("Bit-width must be 1..8 [" + token.getFieldTypeParameters().getExtraData() + ']', token);
368+
}
339369
}
340370
}
341371
}
342372
break;
343373
case CODE_VAR: {
344374
hasVarFields = true;
345-
final String parsedExtraField = token.getFieldTypeParameters().getExtraData();
346-
extraFieldPresented = true;
347-
if (parsedExtraField == null) {
348-
extraField = 0;
375+
if (extraFieldNumericDataAsExpression) {
376+
varLengthEvaluators.add(JBBPEvaluatorFactory.getInstance().make(token.getFieldTypeParameters().getExtraDataExpression(), namedFields, out.toByteArray()));
349377
}
350378
else {
351-
try {
352-
extraField = Integer.parseInt(parsedExtraField);
379+
final String extraFieldNumStr = token.getFieldTypeParameters().getExtraData();
380+
writeExtraFieldNumberInCompiled = true;
381+
if (extraFieldNumStr == null) {
382+
extraFieldNumberAsInt = 0;
353383
}
354-
catch (NumberFormatException ex) {
355-
throw new JBBPCompilationException("Can't parse the extra value of a VAR field, must be integer [" + token.getFieldTypeParameters().getExtraData() + ']', token);
384+
else {
385+
if ((code & EXT_FLAG_EXTRA_AS_EXPRESSION) != 0) {
386+
varLengthEvaluators.add(JBBPEvaluatorFactory.getInstance().make(token.getFieldTypeParameters().getExtraData(), namedFields, out.toByteArray()));
387+
writeExtraFieldNumberInCompiled = false;
388+
}
389+
else {
390+
try {
391+
extraFieldNumberAsInt = Integer.parseInt(extraFieldNumStr);
392+
}
393+
catch (NumberFormatException ex) {
394+
throw new JBBPCompilationException("Can't parse the extra value of a VAR field, must be integer [" + token.getFieldTypeParameters().getExtraData() + ']', token);
395+
}
396+
}
356397
}
357398
}
358399
}
@@ -410,8 +451,8 @@ public static JBBPCompiledBlock compile(final String script, final JBBPCustomFie
410451
}
411452
}
412453

413-
if (extraFieldPresented) {
414-
offset += writePackedInt(out, extraField);
454+
if (writeExtraFieldNumberInCompiled) {
455+
offset += writePackedInt(out, extraFieldNumberAsInt);
415456
}
416457

417458
if (customTypeFieldIndex >= 0) {
@@ -533,7 +574,11 @@ private static int prepareCodeForToken(final JBBPToken token, final JBBPCustomFi
533574
final JBBPFieldTypeParameterContainer descriptor = token.getFieldTypeParameters();
534575

535576
result = descriptor.getByteOrder() == JBBPByteOrder.LITTLE_ENDIAN ? FLAG_LITTLE_ENDIAN : 0;
536-
result |= token.getArraySizeAsString() == null ? 0 : (token.isVarArrayLength() ? FLAG_ARRAY | FLAG_WIDE | (EXT_FLAG_EXPRESSION_OR_WHOLESTREAM<<8) : FLAG_ARRAY);
577+
578+
final boolean hasExpressionAsExtraNumber = descriptor.hasExpressionAsExtraData();
579+
580+
result |= token.getArraySizeAsString() == null ? 0 : (token.isVarArrayLength() ? FLAG_ARRAY | FLAG_WIDE | (EXT_FLAG_EXPRESSION_OR_WHOLESTREAM << 8) : FLAG_ARRAY);
581+
result |= hasExpressionAsExtraNumber ? FLAG_WIDE | (EXT_FLAG_EXTRA_AS_EXPRESSION << 8) : 0;
537582
result |= token.getFieldName() == null ? 0 : FLAG_NAMED;
538583

539584
final String name = descriptor.getTypeName().toLowerCase(Locale.ENGLISH);

0 commit comments

Comments
 (0)