bsloane1650 commented on a change in pull request #225: Implement daf:lookAhead
URL: https://github.com/apache/incubator-daffodil/pull/225#discussion_r290328907
##########
File path:
daffodil-runtime1/src/main/scala/org/apache/daffodil/dpath/DAFFunctions.scala
##########
@@ -72,3 +78,63 @@ case object DAFError extends RecipeOp {
}
}
}
+
+case class DAFLookAhead(recipes: List[CompiledDPath])
+ extends FNTwoArgs(recipes) {
+
+ def computeValue(arg1: AnyRef, arg2: AnyRef, dstate: DState): AnyRef = {
+ val offset = arg1.asInstanceOf[JLong]
+ val lBitSize = arg2.asInstanceOf[JLong]
+
+ /*
+ * Since daf:lookAhead is defined to take unsigned arguements, the DPath
interperater
+ * will error out on the cast if a negative arguement is supplied, so we
do not need to SDE here.
+ */
+
+ Assert.invariant(offset >= 0)
+ Assert.invariant(lBitSize >= 0)
+
+ val totalLookahead = offset + lBitSize
+ if (totalLookahead > 512) {
+ dstate.SDE("Look-ahead distance of %s bits exceeds implementation
defined limit of 512 bits", totalLookahead)
+ }
+ //Safe since we guard on totalLookahead
+ val bitSize = lBitSize.toInt
+
+ if (!dstate.parseOrUnparseState.isDefined) {
+ Assert.invariant(dstate.isCompile)
+ /*
+ * This is an expected code path.
+ * Throwing an exception is how we indicated that this expression does
+ * cannot be reduced to a constant at compile time.
+ */
+ throw new IllegalStateException("No input stream at compile time")
+ }
+ if (dstate.parseOrUnparseState.get.isInstanceOf[PState]) {
+ val pstate = dstate.parseOrUnparseState.get.asInstanceOf[PState]
+ val dis = pstate.dataInputStream
+ if (!dis.isDefinedForLength(totalLookahead)) {
+ val maybeSFL =
+ if (dstate.runtimeData.isDefined)
One(dstate.runtimeData.get.schemaFileLocation)
+ else Nope
+ throw new ParseError(maybeSFL, dstate.contextLocation, Nope,
+ One("Insufficient bits available to satisfy daf:lookAhead(%s,%s)."),
+ offset, bitSize, totalLookahead)
+ }
+ val mark = dis.markPos
+ dis.skip(offset, pstate)
+ val ans: AnyRef = if (bitSize > 63) {
+ dis.getUnsignedBigInt(bitSize, pstate)
+ } else if (bitSize == 0) {
+ Long.box(0)
+ } else {
+ Long.box(dis.getUnsignedLong(bitSize, pstate).longValue)
+ }
Review comment:
The explicit boxing is due to limitations in Scala's type inference (well
justified in this case).
The core issue is that all DPath values are stored by Daffodil as AnyRef.
When we attempt to cast an unboxed long (or integer literal) to AnyRef, Scala
refused to do an automatic conversion.
I believe you are correct on the Long/JLong distinction, I am swiching these
over to JLong
For reference, the following two snippets are equivalent and both should
work:
new JLong(0)
0 : JLong
I tend to prefer the former. As long as we need to make the boxing explicit,
we might as well go all the way to explicitly boxing, instead of coercing the
type system to convince it to box for us.
I think the what we would normally do in this type of situation is let the
ans variable take on the JLong type and let Scala handle the boxing for us.
This doesn't work here since ans can also store a BigInt.
On a related note, it would be nice if we could formalize the fact that
DPath values are one of a limited number of types. Ideally make it an untagged
union type if Scala has a way of doing so. Passing around AnyRefs like this
seems to be asking for bugs.
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
With regards,
Apache Git Services