diff --git a/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/AlignedMixin.scala b/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/AlignedMixin.scala index a6d52b8a5c..256afd445b 100644 --- a/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/AlignedMixin.scala +++ b/daffodil-core/src/main/scala/org/apache/daffodil/core/grammar/AlignedMixin.scala @@ -134,11 +134,14 @@ trait AlignedMixin extends GrammarMixin { self: Term => LengthExact(trailingSkipInBits) } - // FIXME: DAFFODIL-2295 - // Does not take into account that in a sequence, what may be prior may be a separator. - // The separator is text in some encoding, might not be the same as this term's encoding, and - // the alignment will be left where that text leaves it. - // + /** + * The priorAlignmentApprox doesn't need to take into account the separator + * alignment/length as that comes after the priorAlignmentApprox, but before + * the leadingSkip. + * + * TODO: DAFFODIL-3056 We need to consider the initiator MTA/length, + * as well as the prefix length + */ private lazy val priorAlignmentApprox: AlignmentMultipleOf = LV(Symbol("priorAlignmentApprox")) { if (this.isInstanceOf[Root] || this.isInstanceOf[QuasiElementDeclBase]) { @@ -232,6 +235,31 @@ trait AlignedMixin extends GrammarMixin { self: Term => priorAlignmentApprox + leadingSkipApprox } + private lazy val terminatorMTAApprox = + if (this.hasTerminator) { + AlignmentMultipleOf(this.knownEncodingAlignmentInBits) + } else { + AlignmentMultipleOf(0) + } + + private lazy val terminatorLengthApprox = if (this.hasTerminator) { + getEncodingLengthApprox(this) + } else { + LengthMultipleOf(0) + } + + private def getEncodingLengthApprox(t: Term) = { + if (t.isKnownEncoding) { + val dfdlCharset = t.charsetEv.optConstant.get + val fixedWidth = + if (dfdlCharset.maybeFixedWidth.isDefined) dfdlCharset.maybeFixedWidth.get else 8 + LengthMultipleOf(fixedWidth) + } else { + // runtime encoding, which must be a byte-length encoding + LengthMultipleOf(8) + } + } + protected lazy val contentStartAlignment: AlignmentMultipleOf = { if (priorAlignmentWithLeadingSkipApprox % alignmentApprox == 0) { // alignment won't be needed, continue using prior alignment as start alignment @@ -242,15 +270,22 @@ trait AlignedMixin extends GrammarMixin { self: Term => } } + /** + * Accounts for the terminator MTA/Length and trailing skip + */ protected lazy val endingAlignmentApprox: AlignmentMultipleOf = { this match { case eb: ElementBase => { - if (eb.isComplexType && eb.lengthKind == LengthKind.Implicit) { - eb.complexType.group.endingAlignmentApprox + trailingSkipApprox + val res = if (eb.isComplexType && eb.lengthKind == LengthKind.Implicit) { + eb.complexType.group.endingAlignmentApprox } else { // simple type or complex type with specified length - contentStartAlignment + (elementSpecifiedLengthApprox + trailingSkipApprox) + contentStartAlignment + elementSpecifiedLengthApprox } + val cea = res * terminatorMTAApprox + + terminatorLengthApprox + + trailingSkipApprox + cea } case mg: ModelGroup => { // @@ -261,31 +296,36 @@ trait AlignedMixin extends GrammarMixin { self: Term => val (lastChildren, couldBeLast) = mg.potentialLastChildren val lastApproxesConsideringChildren: Seq[AlignmentMultipleOf] = lastChildren.map { lc => // - // for each possible last child, add its ending alignment - // to our trailing skip to get where it would leave off were - // it the actual last child. - // + // for each possible last child, gather its endingAlignmentApprox val lceaa = lc.endingAlignmentApprox - val res = lceaa + trailingSkipApprox - res + lceaa } val optApproxIfNoChildren = // // gather possibilities for this item itself // - if (couldBeLast) + if (couldBeLast) { // // if this model group could be last, then consider // if none of its content was present. - // We'd just have the contentStart, nothing, and the trailing Skip. + // We'd just have the contentStart // - Some(contentStartAlignment + trailingSkipApprox) - else + val res = Some(contentStartAlignment) + res + } else // can't be last, no possibilities to gather. None val lastApproxes = lastApproxesConsideringChildren ++ optApproxIfNoChildren - Assert.invariant(lastApproxes.nonEmpty) - val res = lastApproxes.reduce { _ * _ } + // take all the gathered possibilities and add the ending alignment + // to terminator MTA/length and our trailing skip to get where it would leave off were + // each the actual last child. + val lastApproxesFinal = lastApproxes.map { + _ * terminatorMTAApprox + + terminatorLengthApprox + + trailingSkipApprox + } + Assert.invariant(lastApproxesFinal.nonEmpty) + val res = lastApproxesFinal.reduce { _ * _ } res } } @@ -329,15 +369,7 @@ trait AlignedMixin extends GrammarMixin { self: Term => } private lazy val encodingLengthApprox: LengthApprox = { - if (isKnownEncoding) { - val dfdlCharset = charsetEv.optConstant.get - val fixedWidth = - if (dfdlCharset.maybeFixedWidth.isDefined) dfdlCharset.maybeFixedWidth.get else 8 - LengthMultipleOf(fixedWidth) - } else { - // runtime encoding, which must be a byte-length encoding - LengthMultipleOf(8) - } + getEncodingLengthApprox(this) } protected lazy val isKnownToBeByteAlignedAndByteLength: Boolean = { diff --git a/daffodil-test/src/test/resources/org/apache/daffodil/section12/aligned_data/Aligned_Data.tdml b/daffodil-test/src/test/resources/org/apache/daffodil/section12/aligned_data/Aligned_Data.tdml index 119c799854..c25818a934 100644 --- a/daffodil-test/src/test/resources/org/apache/daffodil/section12/aligned_data/Aligned_Data.tdml +++ b/daffodil-test/src/test/resources/org/apache/daffodil/section12/aligned_data/Aligned_Data.tdml @@ -602,6 +602,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + aaTBbb + + + + + + aa + bb + + + + + + + + aabbT2ee + + + + + + aa + bb + + ee + + + + + diff --git a/daffodil-test/src/test/scala/org/apache/daffodil/section12/aligned_data/TestAlignedData.scala b/daffodil-test/src/test/scala/org/apache/daffodil/section12/aligned_data/TestAlignedData.scala index 23967c96a8..860415d7b1 100644 --- a/daffodil-test/src/test/scala/org/apache/daffodil/section12/aligned_data/TestAlignedData.scala +++ b/daffodil-test/src/test/scala/org/apache/daffodil/section12/aligned_data/TestAlignedData.scala @@ -186,6 +186,10 @@ class TestAlignedData extends TdmlTests { @Test def alignmentFillByteDefined = test @Test def separatorMTA_01 = test + + // DAFFODIL-3057 + @Test def test_term_alignment_1 = test + @Test def test_term_alignment_2 = test } class TestBinaryInput extends TdmlTests {