This is an automated email from the ASF dual-hosted git repository.
mbeckerle pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/daffodil.git
The following commit(s) were added to refs/heads/main by this push:
new 31b90579b Add requireLengthInWholeWords feature to byteSwap layer
31b90579b is described below
commit 31b90579b21b05e95c43896798a1801884542aec
Author: Michael Beckerle <[email protected]>
AuthorDate: Wed May 29 19:26:29 2024 -0400
Add requireLengthInWholeWords feature to byteSwap layer
Eliminates the need for a separate byteSwap layer just to get an error if
the
data is not a mulitple of the word size.
DAFFODIL-2905
---
.../daffodil/layers/xsd/byteSwapLayer.dfdl.xsd | 8 +-
.../daffodil/layers/runtime1/ByteSwapLayer.scala | 81 +++++++--
.../layers/runtime1/TestByteSwapStream.scala | 4 +-
.../apache/daffodil/layers/TestTwoByteSwap.tdml | 192 +++++++++++++++++++++
.../daffodil/layers/xsd/testTwoByteSwap.dfdl.xsd | 90 ++++++++++
.../layers/xsd/testTwoByteSwapByte.dfdl.xsd | 86 +++++++++
.../layers/xsd/testTwoByteSwapShort.dfdl.xsd | 79 +++++++++
.../daffodil/runtime1/layers/TestByteSwap.scala | 57 ++++++
8 files changed, 578 insertions(+), 19 deletions(-)
diff --git
a/daffodil-runtime1-layers/src/main/resources/org/apache/daffodil/layers/xsd/byteSwapLayer.dfdl.xsd
b/daffodil-runtime1-layers/src/main/resources/org/apache/daffodil/layers/xsd/byteSwapLayer.dfdl.xsd
index 958b0a39c..a5ec47098 100644
---
a/daffodil-runtime1-layers/src/main/resources/org/apache/daffodil/layers/xsd/byteSwapLayer.dfdl.xsd
+++
b/daffodil-runtime1-layers/src/main/resources/org/apache/daffodil/layers/xsd/byteSwapLayer.dfdl.xsd
@@ -26,12 +26,10 @@
<annotation>
<appinfo source="http://www.ogf.org/dfdl/">
- <!--
- The twobyteswap and fourbyteswap layers have no parameters nor
- return variables
- -->
+ <!-- set/bind to yes if you want a parse error when the length is not a
multiple of the word size -->
+ <dfdl:defineVariable name="requireLengthInWholeWords" type="xs:string"
defaultValue="no"/>
</appinfo>
</annotation>
-</schema>
\ No newline at end of file
+</schema>
diff --git
a/daffodil-runtime1-layers/src/main/scala/org/apache/daffodil/layers/runtime1/ByteSwapLayer.scala
b/daffodil-runtime1-layers/src/main/scala/org/apache/daffodil/layers/runtime1/ByteSwapLayer.scala
index 387022288..fde77efb3 100644
---
a/daffodil-runtime1-layers/src/main/scala/org/apache/daffodil/layers/runtime1/ByteSwapLayer.scala
+++
b/daffodil-runtime1-layers/src/main/scala/org/apache/daffodil/layers/runtime1/ByteSwapLayer.scala
@@ -29,18 +29,51 @@ final class TwoByteSwapLayer extends
ByteSwap("twobyteswap", 2)
final class FourByteSwapLayer extends ByteSwap("fourbyteswap", 4)
-abstract class ByteSwap(name: String, count: Int)
+/**
+ * This class represents a byte swap layer that can be used to swap the byte
order of data.
+ *
+ * DFDL Variable `requireLengthInWholeWords` can be used to request that the
layer enforce
+ * the length being a multiple of the word size.
+ *
+ * @constructor Creates a new ByteSwap instance with the specified name and
word size.
+ * @param name The name of the byte swap layer.
+ * @param wordsize The word size in bytes.
+ */
+abstract class ByteSwap(name: String, wordsize: Int)
extends Layer(name, "urn:org.apache.daffodil.layers.byteSwap") {
+ private var wholeWords: Boolean = false
+
+ lazy val notWordSize = new IllegalStateException(
+ "Data length is not a multiple of " + wordsize
+ )
+
+ /**
+ * Initialize from DFDL variables that are parameters.
+ * @param requireLengthInWholeWords a string that is the value of the DFDL
variable of the same name in this layer's
+ * namespace. Must be "yes" or "no" or it
is a SDE.
+ */
+ def setLayerVariableParameters(requireLengthInWholeWords: String): Unit = {
+ requireLengthInWholeWords match {
+ case "yes" => this.wholeWords = true
+ case "no" => this.wholeWords = false // this is the default
+ case _ =>
+ runtimeSchemaDefinitionError(
+ "requireLengthInWholeWords variable must be either 'yes' or 'no',
but was: " + requireLengthInWholeWords
+ )
+ }
+ }
+
override def wrapLayerOutput(jos: OutputStream): OutputStream =
- new ByteSwapOutputStream(count, jos)
+ new ByteSwapOutputStream(this, wordsize, jos, wholeWords)
override def wrapLayerInput(jis: InputStream): InputStream =
- new ByteSwapInputStream(count, jis)
+ new ByteSwapInputStream(this, wordsize, jis, wholeWords)
}
/**
- * An input stream wrapper that re-orders bytes according to wordsize.
+ * This class represents an input stream that performs byte swapping on
+ * the data read from another input stream.
*
* This is streaming - does not require buffering up the data. So can be used
on
* very large data objects.
@@ -50,11 +83,17 @@ abstract class ByteSwap(name: String, count: Int)
* 4, then the bytes from the wrapped input stream are returned in the
* order 4 3 2 1 8 7 6 5 10 9. If wordsize were 2 then the bytes from the
* wrapped input stream are returned in the order 2 1 4 3 6 5 8 7 10 9.
+ *
+ * @param layer The layer object used for error reporting.
+ * @param wordsize The number of bytes to swap at a time.
+ * @param jis The underlying input stream.
+ * @param wholeWords It is a parse error if this is true and the length is not
a multiple of the wordsize.
*/
-class ByteSwapInputStream(wordsize: Int, jis: InputStream) extends InputStream
{
+class ByteSwapInputStream(layer: ByteSwap, wordsize: Int, jis: InputStream,
wholeWords: Boolean)
+ extends InputStream {
object State extends org.apache.daffodil.lib.util.Enum {
- abstract sealed trait Type extends EnumValueType
+ sealed trait Type extends EnumValueType
/**
* Buffering bytes in a word.
@@ -94,6 +133,10 @@ class ByteSwapInputStream(wordsize: Int, jis: InputStream)
extends InputStream {
c = jis.read()
if (c == -1) {
state = Draining
+ if (wholeWords && (stack.size() % wordsize != 0)) {
+ // end of data but we have only a partial word on stack.
+ layer.processingError(layer.notWordSize)
+ }
} else {
stack.push(c)
if (stack.size() == wordsize) {
@@ -102,7 +145,7 @@ class ByteSwapInputStream(wordsize: Int, jis: InputStream)
extends InputStream {
}
}
case Emptying => {
- if (stack.isEmpty()) {
+ if (stack.isEmpty) {
state = Filling
} else {
c = stack.pop()
@@ -110,7 +153,7 @@ class ByteSwapInputStream(wordsize: Int, jis: InputStream)
extends InputStream {
}
}
case Draining => {
- if (stack.isEmpty()) {
+ if (stack.isEmpty) {
state = Done
return -1
} else {
@@ -136,19 +179,33 @@ class ByteSwapInputStream(wordsize: Int, jis:
InputStream) extends InputStream {
* bytes are written to the wrapped output stream in the
* order 4 3 2 1 8 7 6 5 10 9. If wordsize were 2 then the bytes are written
* to the wrapped output stream in the order 2 1 4 3 6 5 8 7 10 9.
+ *
+ * @param layer The layer object used for error reporting.
+ * @param wordsize The number of bytes to swap at a time.
+ * @param jos OutputStream where the data is written after byte swapping
+ * @param wholeWords It is a unparse error if this is true and the length at
close time is not a multiple of the wordsize.
*/
-class ByteSwapOutputStream(wordsize: Int, jos: OutputStream) extends
OutputStream {
+class ByteSwapOutputStream(
+ layer: ByteSwap,
+ wordsize: Int,
+ jos: OutputStream,
+ wholeWords: Boolean
+) extends OutputStream {
private val stack: Deque[Byte] = new ArrayDeque[Byte](wordsize)
private var closed = false
override def close(): Unit = {
if (!closed) {
- while (!stack.isEmpty()) {
+ closed = true
+ if (wholeWords && (stack.size() % wordsize != 0)) {
+ // end of data but we have only a partial word on stack.
+ layer.processingError(layer.notWordSize)
+ }
+ while (!stack.isEmpty) {
jos.write(stack.pop())
}
jos.close()
- closed = true
}
}
@@ -156,7 +213,7 @@ class ByteSwapOutputStream(wordsize: Int, jos:
OutputStream) extends OutputStrea
Assert.usage(!closed)
stack.push(bInt.toByte)
if (stack.size() == wordsize) {
- while (!stack.isEmpty()) {
+ while (!stack.isEmpty) {
jos.write(stack.pop())
}
}
diff --git
a/daffodil-runtime1-layers/src/test/scala/org/apache/daffodil/layers/runtime1/TestByteSwapStream.scala
b/daffodil-runtime1-layers/src/test/scala/org/apache/daffodil/layers/runtime1/TestByteSwapStream.scala
index 142e41b73..6ff7abc66 100644
---
a/daffodil-runtime1-layers/src/test/scala/org/apache/daffodil/layers/runtime1/TestByteSwapStream.scala
+++
b/daffodil-runtime1-layers/src/test/scala/org/apache/daffodil/layers/runtime1/TestByteSwapStream.scala
@@ -34,7 +34,7 @@ class TestByteSwapStreams {
@Test def testFourByteSwapInputStream() = {
val data = unswapped32BitData
val bba = new ByteArrayInputStream(data)
- val bss = new ByteSwapInputStream(4, bba)
+ val bss = new ByteSwapInputStream(layer = null, 4, bba, wholeWords = false)
val baos = new ByteArrayOutputStream()
var c: Int = -1
@@ -55,7 +55,7 @@ class TestByteSwapStreams {
val bba = new ByteArrayInputStream(data)
val baos = new ByteArrayOutputStream()
- val bsos = new ByteSwapOutputStream(4, baos)
+ val bsos = new ByteSwapOutputStream(layer = null, 4, baos, wholeWords =
false)
var c: Int = -1
while ({
c = bba.read()
diff --git
a/daffodil-test/src/test/resources/org/apache/daffodil/layers/TestTwoByteSwap.tdml
b/daffodil-test/src/test/resources/org/apache/daffodil/layers/TestTwoByteSwap.tdml
new file mode 100644
index 000000000..ed228249b
--- /dev/null
+++
b/daffodil-test/src/test/resources/org/apache/daffodil/layers/TestTwoByteSwap.tdml
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<testSuite
+ suiteName="TwoByteSwap"
+ description="TwoByteSwap tests"
+ xmlns="http://www.ibm.com/xmlns/dfdl/testData"
+ xmlns:tdml="http://www.ibm.com/xmlns/dfdl/testData"
+ xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/"
+ xmlns:fn="http://www.w3.org/2005/xpath-functions"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:ex="http://example.com"
+ defaultRoundTrip="onePass"
+ defaultValidation="on">
+
+ <!-- 0 and 1 byteswapped results in 00 00 and 01 00 or 0 and 256 -->
+ <parserTestCase name="test_twobyteswap_01" root="TwoByteSwapTest"
model="org/apache/daffodil/layers/xsd/testTwoByteSwapShort.dfdl.xsd">
+ <document>
+ <documentPart type="byte">00 00 00 01</documentPart>
+ </document>
+ <infoset>
+ <dfdlInfoset>
+ <ex:TwoByteSwapTest xmlns="">
+ <Block>
+ <Data>0</Data>
+ <Data>256</Data>
+ </Block>
+ </ex:TwoByteSwapTest>
+ </dfdlInfoset>
+ </infoset>
+ </parserTestCase>
+
+ <unparserTestCase name="test_twobyteswap_unparse_odd" root="TwoByteSwapTest"
+
model="org/apache/daffodil/layers/xsd/testTwoByteSwapByte.dfdl.xsd"
+ roundTrip="none">
+ <infoset>
+ <dfdlInfoset>
+ <ex:TwoByteSwapTest xmlns="">
+ <Block>
+ <Data>1</Data>
+ <Data>2</Data>
+ <Data>3</Data>
+ </Block>
+ </ex:TwoByteSwapTest>
+ </dfdlInfoset>
+ </infoset>
+ <errors>
+ <error>not a multiple of 2</error>
+ </errors>
+ </unparserTestCase>
+
+ <!-- 0 and 1 byteswapped results in 00 00 and 01 00 or 0 and 256-->
+ <parserTestCase name="test_twobyteswap_02" root="TwoByteSwapTest"
model="org/apache/daffodil/layers/xsd/testTwoByteSwapShort.dfdl.xsd">
+ <document>
+ <documentPart type="byte">00 00 00 01</documentPart>
+ </document>
+ <infoset>
+ <dfdlInfoset>
+ <ex:TwoByteSwapTest xmlns="">
+ <Block>
+ <Data>0</Data>
+ <Data>256</Data>
+ </Block>
+ </ex:TwoByteSwapTest>
+ </dfdlInfoset>
+ </infoset>
+ </parserTestCase>
+
+ <!-- 1-9 byte swaped -->
+ <parserTestCase name="test_twobyteswap_03" root="TwoByteSwapTest"
model="org/apache/daffodil/layers/xsd/testTwoByteSwapShort.dfdl.xsd">
+ <document>
+ <documentPart type="byte">00 01 00 02 00 03 00 04 00 05 00 06 00 07 00
08 00 09</documentPart>
+ </document>
+ <infoset>
+ <dfdlInfoset>
+ <ex:TwoByteSwapTest xmlns="">
+ <Block>
+ <Data>256</Data>
+ <Data>512</Data>
+ <Data>768</Data>
+ <Data>1024</Data>
+ <Data>1280</Data>
+ <Data>1536</Data>
+ <Data>1792</Data>
+ <Data>2048</Data>
+ <Data>2304</Data>
+ </Block>
+ </ex:TwoByteSwapTest>
+ </dfdlInfoset>
+ </infoset>
+ </parserTestCase>
+
+ <!--The same as test 3 but reversed.-->
+ <parserTestCase name="test_twobyteswap_04" root="TwoByteSwapTest"
model="org/apache/daffodil/layers/xsd/testTwoByteSwapShort.dfdl.xsd">
+ <document>
+ <documentPart type="byte">01 00 02 00 03 00 04 00 05 00 06 00 07 00 08
00 09 00</documentPart>
+ </document>
+ <infoset>
+ <dfdlInfoset>
+ <ex:TwoByteSwapTest xmlns="">
+ <Block>
+ <Data>1</Data>
+ <Data>2</Data>
+ <Data>3</Data>
+ <Data>4</Data>
+ <Data>5</Data>
+ <Data>6</Data>
+ <Data>7</Data>
+ <Data>8</Data>
+ <Data>9</Data>
+ </Block>
+ </ex:TwoByteSwapTest>
+ </dfdlInfoset>
+ </infoset>
+ </parserTestCase>
+
+ <parserTestCase name="test_twobyteswap_05" root="TwoByteSwapTest"
model="org/apache/daffodil/layers/xsd/testTwoByteSwapShort.dfdl.xsd">
+ <document>
+ <documentPart type="byte">01 00 02 00 03 00 04 00 05 00 06 00 07 00 08
00 09 00</documentPart>
+ </document>
+ <infoset>
+ <dfdlInfoset>
+ <ex:TwoByteSwapTest xmlns="">
+ <Block>
+ <Data>1</Data>
+ <Data>2</Data>
+ <Data>3</Data>
+ <Data>4</Data>
+ <Data>5</Data>
+ <Data>6</Data>
+ <Data>7</Data>
+ <Data>8</Data>
+ <Data>9</Data>
+ </Block>
+ </ex:TwoByteSwapTest>
+ </dfdlInfoset>
+ </infoset>
+ </parserTestCase>
+
+ <!--Odd bytes.-->
+ <parserTestCase name="test_twobyteswap_bad_01" root="TwoByteSwapTest"
model="org/apache/daffodil/layers/xsd/testTwoByteSwapShort.dfdl.xsd">
+ <document>
+ <documentPart type="byte">
+ 01 00 02 00
+ 03 00 04 00
+ 05 00 06 00
+ 07 00 08 00
+ 09 00 00
+ </documentPart>
+ </document>
+ <errors>
+ <error>Data length is not a multiple of 2</error>
+ </errors>
+ </parserTestCase>
+
+ <!--Odd bytes.-->
+ <parserTestCase name="test_twobyteswap_bad_02" root="TwoByteSwapTest"
model="org/apache/daffodil/layers/xsd/testTwoByteSwapByte.dfdl.xsd">
+ <document>
+ <documentPart type="byte">01 00 02 00 03 00 04 00 05 00 06 00 07 00 08
00 09 00 10</documentPart>
+ </document>
+ <errors>
+ <error>Data length is not a multiple of 2</error>
+ </errors>
+ </parserTestCase>
+
+ <!--Odd bytes.-->
+ <parserTestCase name="test_twobyteswap_bad_03" root="TwoByteSwapTest"
model="org/apache/daffodil/layers/xsd/testTwoByteSwap.dfdl.xsd">
+ <document>
+ <documentPart type="byte">02 01 04 03 05 06</documentPart>
+ </document>
+ <errors>
+ <error>Failed to populate Data</error>
+ <error>Data length is not a multiple of 2</error>
+ </errors>
+ </parserTestCase>
+
+</testSuite>
diff --git
a/daffodil-test/src/test/resources/org/apache/daffodil/layers/xsd/testTwoByteSwap.dfdl.xsd
b/daffodil-test/src/test/resources/org/apache/daffodil/layers/xsd/testTwoByteSwap.dfdl.xsd
new file mode 100644
index 000000000..544b878d9
--- /dev/null
+++
b/daffodil-test/src/test/resources/org/apache/daffodil/layers/xsd/testTwoByteSwap.dfdl.xsd
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<schema xmlns="http://www.w3.org/2001/XMLSchema"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/"
+ xmlns:dfdlx="http://www.ogf.org/dfdl/dfdl-1.0/extensions"
+ xmlns:fn="http://www.w3.org/2005/xpath-functions"
+ xmlns:daf="urn:ogf:dfdl:2013:imp:daffodil.apache.org:2018:ext"
+ xmlns:twobyteSwap="urn:org.apache.daffodil.layers.byteSwap"
+ xmlns:ex="http://example.com"
+ targetNamespace="http://example.com">
+
+ <include
schemaLocation="/org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+
+ <xs:import namespace="urn:org.apache.daffodil.layers.byteSwap"
+
schemaLocation="/org/apache/daffodil/layers/xsd/byteSwapLayer.dfdl.xsd"/>
+
+ <annotation>
+ <appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:format ref="ex:GeneralFormat"
+ representation="binary"
+ lengthUnits="bits"
+ />
+ </appinfo>
+ </annotation>
+
+ <group name="setVars">
+ <sequence>
+ <annotation><appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:setVariable ref="twobyteSwap:requireLengthInWholeWords"
value="yes"/>
+ </appinfo></annotation>
+ </sequence>
+ </group>
+
+ <element name="TwoByteSwapTest">
+ <complexType>
+ <sequence>
+ <group ref="ex:setVars"/>
+ <element name="len5" dfdl:lengthKind="explicit" dfdl:length="5"
+ dfdl:lengthUnits="bytes"><!-- note odd fixed length -->
+ <complexType>
+ <sequence>
+ <sequence dfdlx:layer="twobyteSwap:twobyteswap">
+ <element name="Block">
+ <complexType>
+ <sequence>
+ <!-- this will not pull data from the underlying layer 2
bytes at a time.
+ Rather, the I/O layer tries to fill this and hits the
end of data and detects odd length so
+ causes a PE which ends the array with zero elements in
it.
+ That will not cause the layer to fail, just to
backtrack to the start of the layer data. -->
+ <element name="Data" type="unsignedShort"
+ minOccurs="2"
+ maxOccurs="30"
+ dfdl:lengthKind="explicit"
+ dfdl:length="16"
+ dfdl:occursCountKind="implicit"/>
+ <!-- Then this optional element is pulled. It will also
fail due to the odd length of the layer. -->
+ <element name="postData" type="unsignedByte"
minOccurs="0"/>
+ </sequence>
+ </complexType>
+ </element><!-- end block -->
+ </sequence><!-- end layer -->
+ <!-- this postLayer is after the empty layer
+ The layer -->
+ <element name="postLayer" type="unsignedByte" minOccurs="0"/>
+ </sequence>
+ </complexType>
+ </element><!-- end len5 -->
+ <element name="last" type="xs:unsignedByte" minOccurs="0"/>
+ </sequence>
+ </complexType>
+ </element>
+
+
+</schema>
diff --git
a/daffodil-test/src/test/resources/org/apache/daffodil/layers/xsd/testTwoByteSwapByte.dfdl.xsd
b/daffodil-test/src/test/resources/org/apache/daffodil/layers/xsd/testTwoByteSwapByte.dfdl.xsd
new file mode 100644
index 000000000..3a17fb0d4
--- /dev/null
+++
b/daffodil-test/src/test/resources/org/apache/daffodil/layers/xsd/testTwoByteSwapByte.dfdl.xsd
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<schema xmlns="http://www.w3.org/2001/XMLSchema"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/"
+ xmlns:dfdlx="http://www.ogf.org/dfdl/dfdl-1.0/extensions"
+ xmlns:fn="http://www.w3.org/2005/xpath-functions"
+ xmlns:daf="urn:ogf:dfdl:2013:imp:daffodil.apache.org:2018:ext"
+ xmlns:twobyteSwap="urn:org.apache.daffodil.layers.byteSwap"
+ xmlns:ex="http://example.com"
+ targetNamespace="http://example.com">
+
+ <include
schemaLocation="/org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+
+ <xs:import namespace="urn:org.apache.daffodil.layers.byteSwap"
+
schemaLocation="/org/apache/daffodil/layers/xsd/byteSwapLayer.dfdl.xsd"/>
+
+ <annotation>
+ <appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:format ref="ex:GeneralFormat"
+ representation="binary"
+ lengthUnits="bits"
+ />
+ </appinfo>
+ </annotation>
+
+ <group name="setVars">
+ <sequence>
+ <annotation><appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:setVariable ref="twobyteSwap:requireLengthInWholeWords"
value="yes"/>
+ </appinfo></annotation>
+ </sequence>
+ </group>
+
+ <element name="TwoByteSwapTest">
+ <complexType>
+ <sequence>
+ <group ref="ex:setVars"/>
+ <sequence dfdlx:layer="twobyteSwap:twobyteswap">
+ <element name="Block">
+ <complexType>
+ <sequence>
+ <element name="Data"
+ minOccurs="1"
+ maxOccurs="30"
+ dfdl:lengthKind="explicit"
+ dfdl:length="8"
+ dfdl:occursCountKind="implicit">
+ <simpleType>
+ <restriction base="unsignedByte">
+ <minInclusive value="0"/>
+ <maxInclusive value="255"/>
+ </restriction>
+ </simpleType>
+ </element>
+ <element name="Extra"
+ type="unsignedByte"
+ minOccurs="0"
+ maxOccurs="1"
+ dfdl:lengthKind="explicit"
+ dfdl:length="8"
+ dfdl:occursCountKind="implicit"/>
+ </sequence>
+ </complexType>
+ </element>
+ </sequence>
+ </sequence>
+ </complexType>
+ </element>
+
+</schema>
diff --git
a/daffodil-test/src/test/resources/org/apache/daffodil/layers/xsd/testTwoByteSwapShort.dfdl.xsd
b/daffodil-test/src/test/resources/org/apache/daffodil/layers/xsd/testTwoByteSwapShort.dfdl.xsd
new file mode 100644
index 000000000..3794d7e6b
--- /dev/null
+++
b/daffodil-test/src/test/resources/org/apache/daffodil/layers/xsd/testTwoByteSwapShort.dfdl.xsd
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<schema xmlns="http://www.w3.org/2001/XMLSchema"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/"
+ xmlns:dfdlx="http://www.ogf.org/dfdl/dfdl-1.0/extensions"
+ xmlns:fn="http://www.w3.org/2005/xpath-functions"
+ xmlns:daf="urn:ogf:dfdl:2013:imp:daffodil.apache.org:2018:ext"
+ xmlns:twobyteSwap="urn:org.apache.daffodil.layers.byteSwap"
+ xmlns:ex="http://example.com"
+ targetNamespace="http://example.com">
+
+ <include
schemaLocation="/org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+
+ <xs:import namespace="urn:org.apache.daffodil.layers.byteSwap"
+
schemaLocation="/org/apache/daffodil/layers/xsd/byteSwapLayer.dfdl.xsd"/>
+
+ <annotation>
+ <appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:format ref="ex:GeneralFormat"
+ representation="binary"
+ lengthUnits="bits"
+ />
+ </appinfo>
+ </annotation>
+
+ <group name="setVars">
+ <sequence>
+ <annotation><appinfo source="http://www.ogf.org/dfdl/">
+ <dfdl:setVariable ref="twobyteSwap:requireLengthInWholeWords"
value="yes"/>
+ </appinfo></annotation>
+ </sequence>
+ </group>
+
+ <element name="TwoByteSwapTest">
+ <complexType>
+ <sequence>
+ <group ref="ex:setVars"/>
+ <sequence dfdlx:layer="twobyteSwap:twobyteswap">
+ <element name="Block">
+ <complexType>
+ <sequence>
+ <element name="Data"
+ minOccurs="1"
+ maxOccurs="30"
+ dfdl:lengthKind="explicit"
+ dfdl:length="16"
+ dfdl:occursCountKind="implicit">
+ <simpleType>
+ <restriction base="unsignedShort">
+ <minInclusive value="0"/>
+ <maxInclusive value="65535"/>
+ </restriction>
+ </simpleType>
+ </element>
+ </sequence>
+ </complexType>
+ </element>
+ </sequence>
+ </sequence>
+ </complexType>
+ </element>
+
+</schema>
diff --git
a/daffodil-test/src/test/scala/org/apache/daffodil/runtime1/layers/TestByteSwap.scala
b/daffodil-test/src/test/scala/org/apache/daffodil/runtime1/layers/TestByteSwap.scala
new file mode 100644
index 000000000..3c233455c
--- /dev/null
+++
b/daffodil-test/src/test/scala/org/apache/daffodil/runtime1/layers/TestByteSwap.scala
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.daffodil.runtime1.layers
+
+import org.apache.daffodil.tdml.Runner
+
+import org.junit.AfterClass
+import org.junit.Test
+
+object TestByteSwap {
+ private val testDir = "/org/apache/daffodil/layers/"
+
+ private lazy val runner = Runner(testDir, "TestTwoByteSwap.tdml")
+
+ @AfterClass def shutDown(): Unit = {
+ runner.reset()
+ }
+}
+
+class TestByteSwap {
+ import TestByteSwap._
+
+ @Test def test_twobyteswap_01(): Unit = {
runner.runOneTest("test_twobyteswap_01") }
+
+ @Test def test_twobyteswap_02(): Unit = {
runner.runOneTest("test_twobyteswap_02") }
+
+ @Test def test_twobyteswap_03(): Unit = {
runner.runOneTest("test_twobyteswap_03") }
+
+ @Test def test_twobyteswap_04(): Unit = {
runner.runOneTest("test_twobyteswap_04") }
+
+ @Test def test_twobyteswap_05(): Unit = {
runner.runOneTest("test_twobyteswap_05") }
+
+ @Test def test_twobyteswap_bad_01(): Unit = {
runner.runOneTest("test_twobyteswap_bad_01") }
+
+ @Test def test_twobyteswap_bad_02(): Unit = {
runner.runOneTest("test_twobyteswap_bad_02") }
+
+ @Test def test_twobyteswap_bad_03(): Unit = {
runner.runOneTest("test_twobyteswap_bad_03") }
+
+ @Test def test_twobyteswap_unparse_odd(): Unit = {
+ runner.runOneTest("test_twobyteswap_unparse_odd")
+ }
+
+}