This is an automated email from the ASF dual-hosted git repository.

slawrence pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-daffodil.git


The following commit(s) were added to refs/heads/master by this push:
     new 57a6895  Fix issue where suspensions with mixed byteOrder caused 
incorrect bitOrder to be used
57a6895 is described below

commit 57a68953463f3ebbea779d6664ae56ebdf2912c4
Author: Steve Lawrence <[email protected]>
AuthorDate: Mon Mar 4 13:41:22 2019 -0500

    Fix issue where suspensions with mixed byteOrder caused incorrect bitOrder 
to be used
    
    DAFFODIL-2069
---
 .../daffodil/io/DataOutputStreamImplMixin.scala    | 56 ++++++++++++++--------
 .../TestOutputValueCalc.scala                      |  2 +-
 2 files changed, 36 insertions(+), 22 deletions(-)

diff --git 
a/daffodil-io/src/main/scala/org/apache/daffodil/io/DataOutputStreamImplMixin.scala
 
b/daffodil-io/src/main/scala/org/apache/daffodil/io/DataOutputStreamImplMixin.scala
index c6ade4e..11dddd8 100644
--- 
a/daffodil-io/src/main/scala/org/apache/daffodil/io/DataOutputStreamImplMixin.scala
+++ 
b/daffodil-io/src/main/scala/org/apache/daffodil/io/DataOutputStreamImplMixin.scala
@@ -757,32 +757,46 @@ trait DataOutputStreamImplMixin extends 
DataStreamCommonState
    * Assumed to be called from inner loops, so should be fast as possible.
    */
   protected final def putLongUnchecked(signedLong: Long, bitLengthFrom1To64: 
Int, finfo: FormatInfo): Boolean = {
+    // The order we check bitOrder vs byteOrder here is actually very
+    // important, primarily due to how suspensions end up resolved and data
+    // output streams (DOS) collapsed. To explain why:
     //
-    // based on bit and byte order, dispatch to specific code
+    // When we create new a buffered DOS, no byteOrder is really associated
+    // with the DOS, because a DOS is just a series of bytes. To figure out
+    // what bytes to write to the DOS, we use the FormatInfo associated with an
+    // element, call the appropriate function below to convert that element to
+    // bytes, and then write those bytes to the DOS. This works as expected
+    // most of the time.
     //
+    // However, a buffered DOS maintains a fragment byte, and at some point we
+    // collapse that fragment byte into a direct DOS using this function. But
+    // we just said that there not really a byteOrder associated with a DOS,
+    // only with elements. So if we do not know the byteOrder of the fragment
+    // byte of a DOS, how do we know which function below to call when
+    // collapsing a fragment byte?
+    //
+    // Well, we do keep track of the bitOrder, since we need to know if we
+    // change bitOrder not on a byte boundary, and we need to know the bitOrder
+    // when collapsing a fragment byte. And fortunately, byteOrder doesn't
+    // matter because a fragment byte is always less than 8 bits--byteOrder
+    // only makes a difference for lengths greater than 8 bits. So, as long as
+    // we call *some* function below that uses the right bitOrder, then it
+    // doesn't matter what the byteOrder is. So we need to make sure we check
+    // bitOrder and byteOrder below in the correct order so that that if
+    // bitOrer is MSBF we call one of the MBSF functions, and vice versa for
+    // LSBF. Note that for this reason, even if bitOrder is LSBF and byteOrder
+    // is BE, we can still call putLong_LE_LSBFirst function. That's because
+    // the only time that combination can legally occurs is in this edge case
+    // of collapsing a fragment byte, and then byte order doesn't matter.
     val res =
-      if (finfo.byteOrder eq ByteOrder.BigEndian) {
-        //
-        // You would think this invariant would hold
-        //
-        // Assert.invariant(bitOrder eq BitOrder.MostSignificantBitFirst)
-        //
-        // However, when collapsing DOS forward into each other as a part of
-        // evaluating suspensions, we encounter situations where the bitOrder
-        // in the DOS has LSBF, and that was just never modified though the
-        // byte order was modified. We could hammer the bitOrder to MSBF
-        // any time the byte order is set, but that would mask errors where the
-        // user really had the properties conflicting.
-        //
-        putLong_BE_MSBFirst(signedLong, bitLengthFrom1To64)
-      } else {
-        Assert.invariant(finfo.byteOrder eq ByteOrder.LittleEndian)
-        if (finfo.bitOrder eq BitOrder.MostSignificantBitFirst) {
-          putLong_LE_MSBFirst(signedLong, bitLengthFrom1To64)
+      if (finfo.bitOrder eq BitOrder.MostSignificantBitFirst) {
+        if (finfo.byteOrder eq ByteOrder.BigEndian) {
+          putLong_BE_MSBFirst(signedLong, bitLengthFrom1To64)
         } else {
-          Assert.invariant(finfo.bitOrder eq BitOrder.LeastSignificantBitFirst)
-          putLong_LE_LSBFirst(signedLong, bitLengthFrom1To64)
+          putLong_LE_MSBFirst(signedLong, bitLengthFrom1To64)
         }
+      } else {
+        putLong_LE_LSBFirst(signedLong, bitLengthFrom1To64)
       }
 
     if (res) {
diff --git 
a/daffodil-test/src/test/scala/org/apache/daffodil/section17/calc_value_properties/TestOutputValueCalc.scala
 
b/daffodil-test/src/test/scala/org/apache/daffodil/section17/calc_value_properties/TestOutputValueCalc.scala
index 3671890..00e6607 100644
--- 
a/daffodil-test/src/test/scala/org/apache/daffodil/section17/calc_value_properties/TestOutputValueCalc.scala
+++ 
b/daffodil-test/src/test/scala/org/apache/daffodil/section17/calc_value_properties/TestOutputValueCalc.scala
@@ -77,7 +77,7 @@ class TestOutputValueCalc {
   @Test def test_errorThreeArg() { runner.runOneTest("errorThreeArg") }
 
   // DAFFODIL-2069
-  // @Test def test_ovcHexBinaryLSBF1() { 
runner3.runOneTest("rHexBinaryLSBF1") }
+  @Test def test_ovcHexBinaryLSBF1() { runner3.runOneTest("rHexBinaryLSBF1") }
   @Test def test_ovcHexBinaryLSBF2() { runner3.runOneTest("rHexBinaryLSBF2") }
   @Test def test_ovcStringLSBF1() { runner3.runOneTest("rStringLSBF1") }
 }

Reply via email to