You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@daffodil.apache.org by ji...@apache.org on 2023/04/07 23:14:04 UTC

[daffodil] branch main updated: Add left over data check to generated C parsers

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

jinterrante 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 7d52935d8 Add left over data check to generated C parsers
7d52935d8 is described below

commit 7d52935d8983d6038a8f071c44e547cf3e6b85ad
Author: John Interrante <in...@research.ge.com>
AuthorDate: Sat Apr 1 11:38:25 2023 -0700

    Add left over data check to generated C parsers
    
    Fix generated C parsers not checking for left over data after parse
    calls and not clearing infoset between parse calls.  Rename some
    infoset C functions more consistently (C snake style).
    
    Also enhance C code generator to support DFDL schemas containing
    simple type root elements, allowing generated C parsers to parse data
    and infosets containing only a single element.  Add examples and new
    tests for simple root elements as well.
    
    Also replace any non-alphabetical / non-numerical characters with
    underscores when converting XML identifiers to C identifiers, allowing
    XML element names like "simple-boolean" to become "simple_boolean" in
    generated C code.
    
    DAFFODIL-2807
    
    Main.scala: Avoid second exception if TDMLRunner throws a
    NullPointerException and Main tries to print its message (NPEs don't
    have a message).
    
    testNonCompatibleImplementation.tdml: Both ibm and daffodilC can use
    same schema with single element now.  Merge s1 into s2 and remove s2.
    
    TestCLItdml.scala: Remove unnecessary "-iii" options from CLI test.
    
    daffodil_main.c: Call `get_infoset(CLEAR_INFOSET)` instead of
    `rootElement()`, call `parse_data(infoset, &pstate)` instead of
    `root->erd->parseSelf(root, &pstate)`, call `walk_infoset` instead of
    `walkInfoset`, and call `unparse_infoset(infoset, &ustate)` instead of
    `root->erd->unparseSelf(root, &ustate)`, showing how you can clear the
    infoset between `parse_data` calls if you call the C parser in a loop.
    
    xml_reader.h: Remove unused `root` field from XMLReader struct.
    
    errors.c: Add error message for new error `ERR_LEFTOVER_DATA`.
    
    errors.h: Define new error `ERR_LEFTOVER_DATA` and remove `UNUSED`
    macro (define it in infoset.h instead).
    
    infoset.c: Rename some infoset C functions more consistently (C snake
    style).  Define new functions `parse_data` and `unparse_infoset` and
    make them call `no_leftover_data` and `flush_fragment_byte` so user
    will only need to call `get_infoset(CLEAR_INFOSET)` and `parse_data`
    to get correct C parser behavior.  Also rename ERD field `offsets` to
    `childrenOffsets`.  Remove `flushUstate` function (define it as
    `flush_fragment_byte` in unparsers.c instead).
    
    infoset.h: Rename ERD field `offsets` to `childrenOffsets` and PState,
    UState fields `unreadLen`, `unwritLen` to `numUnreadBits`,
    `numUnwritBits`.  Rename function `rootElement(void)` to
    `get_infoset(clear_infoset)`.  Declare new functions `parse_data` and
    `unparse_infoset`.  Rename `walkInfoset` to `walk_infoset` (snake
    style).  Define `UNUSED` macro here to let extras.c use it.
    
    parsers.[ch]: Say fragment byte, not fractional byte.  Define function
    `no_leftover_data` to check for leftover data (called by
    `parse_data`).  Use renamed `pstate->numUnreadBits`.
    
    unparsers.[ch]: Say fragment byte, not fractional byte.  Use renamed
    `ustate->numUnwritBits`.  Define function `flush_fragment_byte`, give
    it a fill_byte parameter, fill fragment byte from fill byte, write
    fragment byte, and increment ustate->bitPos0b correctly (was adding
    BYTE_WIDTH - ustate->numUnwritBits instead of ustate->numUnwritBits).
    
    bits.c: Use renamed `numUnreadBits` and `numUnwritBits`.  Call
    `flush_fragment_byte` instead of `flushUstate` and pass it a zero
    fill_byte.  Fix incorrect ustate.bitPos0b expected values after
    `flush_fragment_byte` calls.
    
    extras.c: Implement `get_infoset` instead of `rootElement`.
    
    DaffodilCExamplesGenerator.scala: Replace multiple schema, root name,
    and example dir variables with Array of tuples to be more terse.
    Generate simple root element C example too.
    
    BinaryBooleanCodeGenerator.scala: Convert XML names to C names.
    
    BinaryValueCodeGenerator.scala: Convert XML names to C names.
    
    CodeGeneratorState.scala: Detect the case when a root element is a
    simple type and translate it as a hybrid complex and simple type (that
    is, push state for a complex element on the stack and when popping
    that state, generate the hybrid offset computations and ERD
    declarations needed to parse the simple type root element
    successfully).  Generate C function `get_infoset(clear_infoset)`
    instead of `rootElement(void)` to clear infoset between parses with a
    comment to warn callers that get_infoset(CLEAR_INFOSET) won't free
    malloc'ed storage for hexBinary prefixed length elements at this time.
    Define new `cName` method and `makeLegalForC` helper method to convert
    XML names to C names.  Use renamed ERD field `childrenOffsets` and
    remove comments suffixing identifiers with redundant names.
    
    HexBinaryCodeGenerator.scala: Convert XML names to C names.
    
    examples/**/generated_code.c: Regenerate C examples to show effect of
    pull request changes and add a simple root element's C example.
    
    data/simple*.dat: Add new data files to test simple root elements.
    
    infosets/simple*.dat.xml: Add simple root element infosets.
    
    simple.dfdl.xsd: Add new DFDL schema with choice of simple root
    elements to test (one root element for each primitive type).
    
    simple.tdml: Add test cases for all simple root elements listed in
    simple.dfdl.xsd.
    
    simple-errors.tdml: Add error test cases for simple root elements
    listed in simple.dfdl.xsd, although don't need to test every integer
    type.
    
    TestSimple.scala: Add unit tests for all test cases listed in
    simple.tdml.
    
    TestSimpleErrors.scala: Add unit tests for all test cases listed in
    simple-errors.tdml.
---
 .../main/scala/org/apache/daffodil/cli/Main.scala  |   2 +-
 .../cli/testNonCompatibleImplementation.tdml       |  33 +--
 .../apache/daffodil/cli/cliTest/TestCLItdml.scala  |   2 +-
 .../codegen/c/files/libcli/daffodil_main.c         |  29 +-
 .../daffodil/codegen/c/files/libcli/xml_reader.h   |   5 +-
 .../daffodil/codegen/c/files/libcli/xml_writer.h   |   2 +-
 .../daffodil/codegen/c/files/libruntime/errors.c   |   1 +
 .../daffodil/codegen/c/files/libruntime/errors.h   |   5 +-
 .../daffodil/codegen/c/files/libruntime/infoset.c  | 148 ++++-----
 .../daffodil/codegen/c/files/libruntime/infoset.h  |  53 ++--
 .../daffodil/codegen/c/files/libruntime/parsers.c  |  75 +++--
 .../daffodil/codegen/c/files/libruntime/parsers.h  |   7 +-
 .../codegen/c/files/libruntime/unparsers.c         |  68 +++--
 .../codegen/c/files/libruntime/unparsers.h         |   7 +-
 .../apache/daffodil/codegen/c/files/tests/bits.c   |  90 +++---
 .../apache/daffodil/codegen/c/files/tests/extras.c |   8 +-
 .../codegen/c/DaffodilCExamplesGenerator.scala     |  35 +--
 .../c/generators/BinaryBooleanCodeGenerator.scala  |   2 +-
 .../c/generators/BinaryValueCodeGenerator.scala    |   4 +-
 .../codegen/c/generators/CodeGeneratorState.scala  | 161 +++++++---
 .../c/generators/HexBinaryCodeGenerator.scala      |   6 +-
 .../src/test/examples/NestedUnion/generated_code.c |  70 ++---
 .../src/test/examples/ex_nums/generated_code.c     | 142 ++++-----
 .../src/test/examples/padtest/generated_code.c     |  46 +--
 .../src/test/examples/simple/generated_code.c      |  79 +++++
 .../src/test/examples/simple/generated_code.h      |  19 ++
 .../src/test/examples/variablelen/generated_code.c |  58 ++--
 .../daffodil/codegen/c/data/simple-boolean.dat     | Bin 0 -> 4 bytes
 .../apache/daffodil/codegen/c/data/simple-byte.dat | Bin 0 -> 1 bytes
 .../daffodil/codegen/c/data/simple-double.dat      | Bin 0 -> 8 bytes
 .../daffodil/codegen/c/data/simple-float.dat       | Bin 0 -> 4 bytes
 .../daffodil/codegen/c/data/simple-hexBinary.dat   | Bin 0 -> 4 bytes
 .../codegen/c/data/simple-hexBinaryPrefixed.dat    | Bin 0 -> 6 bytes
 .../apache/daffodil/codegen/c/data/simple-int.dat  | Bin 0 -> 4 bytes
 .../daffodil/codegen/c/data/simple-integer.dat     | Bin 0 -> 4 bytes
 .../apache/daffodil/codegen/c/data/simple-long.dat | Bin 0 -> 8 bytes
 .../codegen/c/data/simple-nonNegativeInteger.dat   | Bin 0 -> 4 bytes
 .../daffodil/codegen/c/data/simple-short.dat       | Bin 0 -> 2 bytes
 .../codegen/c/data/simple-unsignedByte.dat         | Bin 0 -> 1 bytes
 .../daffodil/codegen/c/data/simple-unsignedInt.dat | Bin 0 -> 4 bytes
 .../codegen/c/data/simple-unsignedLong.dat         | Bin 0 -> 8 bytes
 .../codegen/c/data/simple-unsignedShort.dat        | Bin 0 -> 2 bytes
 .../codegen/c/infosets/simple-boolean.dat.xml      |  19 ++
 .../codegen/c/infosets/simple-byte.dat.xml         |  19 ++
 .../codegen/c/infosets/simple-double.dat.xml       |  19 ++
 .../codegen/c/infosets/simple-float.dat.xml        |  19 ++
 .../codegen/c/infosets/simple-hexBinary.dat.xml    |  19 ++
 .../c/infosets/simple-hexBinaryPrefixed.dat.xml    |  19 ++
 .../daffodil/codegen/c/infosets/simple-int.dat.xml |  19 ++
 .../codegen/c/infosets/simple-integer.dat.xml      |  19 ++
 .../codegen/c/infosets/simple-long.dat.xml         |  19 ++
 .../c/infosets/simple-nonNegativeInteger.dat.xml   |  19 ++
 .../codegen/c/infosets/simple-short.dat.xml        |  19 ++
 .../codegen/c/infosets/simple-unsignedByte.dat.xml |  19 ++
 .../codegen/c/infosets/simple-unsignedInt.dat.xml  |  19 ++
 .../codegen/c/infosets/simple-unsignedLong.dat.xml |  19 ++
 .../c/infosets/simple-unsignedShort.dat.xml        |  19 ++
 .../org/apache/daffodil/codegen/c/simple.dfdl.xsd  |  65 ++++
 .../org/apache/daffodil/codegen/c/simple.tdml      | 330 +++++++++++++++++++++
 .../apache/daffodil/codegen/c/simple_errors.tdml   | 158 ++++++++++
 .../org/apache/daffodil/codegen/c/TestSimple.scala |  53 ++++
 .../daffodil/codegen/c/TestSimpleErrors.scala      |  46 +++
 62 files changed, 1639 insertions(+), 455 deletions(-)

diff --git a/daffodil-cli/src/main/scala/org/apache/daffodil/cli/Main.scala b/daffodil-cli/src/main/scala/org/apache/daffodil/cli/Main.scala
index 26141b797..68dbf5542 100644
--- a/daffodil-cli/src/main/scala/org/apache/daffodil/cli/Main.scala
+++ b/daffodil-cli/src/main/scala/org/apache/daffodil/cli/Main.scala
@@ -1618,7 +1618,7 @@ class Main(
                         fail += 1
                         if (testOpts.info() > 0) {
                           STDOUT.println("  Failure Information:")
-                          STDOUT.println(indent(e.getMessage(), 4))
+                          STDOUT.println(indent(e.toString, 4))
                         }
                         if (testOpts.info() > 1) {
                           STDOUT.println("  Backtrace:")
diff --git a/daffodil-cli/src/test/resources/org/apache/daffodil/cli/testNonCompatibleImplementation.tdml b/daffodil-cli/src/test/resources/org/apache/daffodil/cli/testNonCompatibleImplementation.tdml
index adb663afd..26b3c1a08 100644
--- a/daffodil-cli/src/test/resources/org/apache/daffodil/cli/testNonCompatibleImplementation.tdml
+++ b/daffodil-cli/src/test/resources/org/apache/daffodil/cli/testNonCompatibleImplementation.tdml
@@ -26,27 +26,16 @@
 
   <tdml:defineSchema name="s1">
     <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
-    <dfdl:format ref="ex:GeneralFormat" />
-    <xs:element name="e1" dfdl:lengthKind="explicit"
-      dfdl:length="2" type="xs:int" />
+    <dfdl:format ref="ex:GeneralFormat" representation="binary"/>
+    <xs:element name="e1" type="xs:int"/>
   </tdml:defineSchema>
 
-  <tdml:defineSchema name="s2">
-    <xs:include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
-    <dfdl:format ref="ex:GeneralFormat" representation="binary" />
-    <xs:element name="e1">
-      <xs:complexType>
-        <xs:sequence>
-          <xs:element name="e2" type="xs:int"/>
-        </xs:sequence>
-      </xs:complexType>
-    </xs:element>
-  </tdml:defineSchema>
-
-  <tdml:parserTestCase name="testNotCompatibleImplementation1" root="e1"
-    model="s1" description="this test specifies ibm as implementation."
+  <tdml:parserTestCase name="testNotCompatibleImplementation1" model="s1"
+    root="e1" description="this test specifies ibm as implementation."
     implementations="ibm">
-    <tdml:document>42</tdml:document>
+    <tdml:document>
+      <tdml:documentPart type="byte">0000002a</tdml:documentPart>
+    </tdml:document>
     <tdml:infoset>
       <tdml:dfdlInfoset>
         <e1>42</e1>
@@ -54,17 +43,15 @@
     </tdml:infoset>
   </tdml:parserTestCase>
 
-  <tdml:parserTestCase name="testDaffodilCImplementation1" root="e1"
-    model="s2" description="this test specifies daffodilC as implementation."
+  <tdml:parserTestCase name="testDaffodilCImplementation1" model="s1"
+    root="e1" description="this test specifies daffodilC as implementation."
     implementations="daffodilC">
     <tdml:document>
       <tdml:documentPart type="byte">0000002a</tdml:documentPart>
     </tdml:document>
     <tdml:infoset>
       <tdml:dfdlInfoset>
-        <e1>
-          <e2>42</e2>
-        </e1>
+        <e1>42</e1>
       </tdml:dfdlInfoset>
     </tdml:infoset>
   </tdml:parserTestCase>
diff --git a/daffodil-cli/src/test/scala/org/apache/daffodil/cli/cliTest/TestCLItdml.scala b/daffodil-cli/src/test/scala/org/apache/daffodil/cli/cliTest/TestCLItdml.scala
index 667589799..cb087f669 100644
--- a/daffodil-cli/src/test/scala/org/apache/daffodil/cli/cliTest/TestCLItdml.scala
+++ b/daffodil-cli/src/test/scala/org/apache/daffodil/cli/cliTest/TestCLItdml.scala
@@ -115,7 +115,7 @@ class TestCLItdml {
       "daffodil-cli/src/test/resources/org/apache/daffodil/cli/testNonCompatibleImplementation.tdml",
     )
 
-    runCLI(args"test -iii $tdml testNotCompatibleImplementation1") { cli =>
+    runCLI(args"test $tdml testNotCompatibleImplementation1") { cli =>
       cli.expect(
         "[Skipped] testNotCompatibleImplementation1 (not compatible with implementation: daffodil)",
       )
diff --git a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libcli/daffodil_main.c b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libcli/daffodil_main.c
index bb0e8e010..bd9c66aa5 100644
--- a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libcli/daffodil_main.c
+++ b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libcli/daffodil_main.c
@@ -16,12 +16,13 @@
  */
 
 // clang-format off
+#include <stdbool.h>          // for bool, true
 #include <stdio.h>            // for NULL, FILE, perror, fclose, fopen, stdin, stdout
 #include <string.h>           // for strcmp
 #include "cli_errors.h"       // for CLI_DIAGNOSTICS, CLI_FILE_CLOSE, CLI_FILE_OPEN
-#include "daffodil_getopt.h"  // for daffodil_cli, parse_daffodil_cli, daffodil_parse, daffodil_parse_cli, daffodil_unparse, daffodil_unparse_cli, DAFFODIL_PARSE, DAFFODIL_UNPARSE
+#include "daffodil_getopt.h"  // for daffodil_cli, daffodil_parse, daffodil_parse_cli, parse_daffodil_cli, daffodil_unparse, daffodil_unparse_cli, DAFFODIL_PARSE, DAFFODIL_UNPARSE
 #include "errors.h"           // for continue_or_exit, print_diagnostics, Error, Diagnostics, Error::(anonymous)
-#include "infoset.h"          // for walkInfoset, InfosetBase, PState, UState, flushUState, rootElement, ERD, VisitEventHandler
+#include "infoset.h"          // for get_infoset, walk_infoset, PState, UState, parse_data, unparse_infoset, InfosetBase, VisitEventHandler
 #include "xml_reader.h"       // for xmlReaderMethods, XMLReader
 #include "xml_writer.h"       // for xmlWriterMethods, XMLWriter
 // clang-format on
@@ -66,10 +67,9 @@ main(int argc, char *argv[])
     const Error *error = parse_daffodil_cli(argc, argv);
     continue_or_exit(error);
 
-    // Get our infoset ready
-    FILE *       input = stdin;
-    FILE *       output = stdout;
-    InfosetBase *root = rootElement();
+    // We will read from stdin and write to stdout unless overridden
+    FILE *input = stdin;
+    FILE *output = stdout;
 
     // Perform our command
     if (daffodil_cli.subcommand == DAFFODIL_PARSE)
@@ -79,14 +79,16 @@ main(int argc, char *argv[])
         output = fopen_or_exit(output, daffodil_parse.outfile, "w");
 
         // Parse the input file into our infoset
-        PState pstate = {input, 0, NULL, NULL, 0, 0};
-        root->erd->parseSelf(root, &pstate);
+        const bool   CLEAR_INFOSET = true;
+        InfosetBase *infoset = get_infoset(CLEAR_INFOSET);
+        PState       pstate = {input, 0, NULL, NULL, 0, 0};
+        parse_data(infoset, &pstate);
         print_diagnostics(pstate.diagnostics);
         continue_or_exit(pstate.error);
 
         // Visit the infoset and print XML from it
         XMLWriter xmlWriter = {xmlWriterMethods, output, {NULL, NULL, 0}};
-        error = walkInfoset((VisitEventHandler *)&xmlWriter, root);
+        error = walk_infoset((VisitEventHandler *)&xmlWriter, infoset);
         continue_or_exit(error);
 
         // Any diagnostics will fail the parse if validate mode is on
@@ -105,14 +107,15 @@ main(int argc, char *argv[])
         output = fopen_or_exit(output, daffodil_unparse.outfile, "w");
 
         // Initialize our infoset's values from the XML data
-        XMLReader xmlReader = {xmlReaderMethods, input, root, NULL, NULL};
-        error = walkInfoset((VisitEventHandler *)&xmlReader, root);
+        const bool   CLEAR_INFOSET = true;
+        InfosetBase *infoset = get_infoset(CLEAR_INFOSET);
+        XMLReader    xmlReader = {xmlReaderMethods, input, NULL, NULL};
+        error = walk_infoset((VisitEventHandler *)&xmlReader, infoset);
         continue_or_exit(error);
 
         // Unparse our infoset to the output file
         UState ustate = {output, 0, NULL, NULL, 0, 0};
-        root->erd->unparseSelf(root, &ustate);
-        flushUState(&ustate);
+        unparse_infoset(infoset, &ustate);
         print_diagnostics(ustate.diagnostics);
         continue_or_exit(ustate.error);
     }
diff --git a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libcli/xml_reader.h b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libcli/xml_reader.h
index cc15e01e7..c90f93d93 100644
--- a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libcli/xml_reader.h
+++ b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libcli/xml_reader.h
@@ -21,7 +21,7 @@
 // clang-format off
 #include <mxml.h>     // for mxml_node_t
 #include <stdio.h>    // for FILE
-#include "infoset.h"  // for VisitEventHandler, InfosetBase
+#include "infoset.h"  // for VisitEventHandler
 // clang-format on
 
 // XMLReader - infoset visitor with methods to read XML
@@ -30,12 +30,11 @@ typedef struct XMLReader
 {
     const VisitEventHandler handler;
     FILE *                  stream;
-    InfosetBase *           root;
     mxml_node_t *           xml;
     mxml_node_t *           node;
 } XMLReader;
 
-// XMLReader methods to pass to walkInfoset method
+// XMLReader methods to pass to walk_infoset method
 
 extern const VisitEventHandler xmlReaderMethods;
 
diff --git a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libcli/xml_writer.h b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libcli/xml_writer.h
index ea9db3e7a..3fa5d5737 100644
--- a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libcli/xml_writer.h
+++ b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libcli/xml_writer.h
@@ -33,7 +33,7 @@ typedef struct XMLWriter
     c_stack_t               stack;
 } XMLWriter;
 
-// XMLWriter methods to pass to walkInfoset method
+// XMLWriter methods to pass to walk_infoset method
 
 extern const VisitEventHandler xmlWriterMethods;
 
diff --git a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/errors.c b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/errors.c
index 55d8325a1..608b1b577 100644
--- a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/errors.c
+++ b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/errors.c
@@ -82,6 +82,7 @@ error_lookup(uint8_t code)
         {ERR_CHOICE_KEY, "no match between choice dispatch key %" PRId64 " and any branch key\n", FIELD_D64},
         {ERR_FIXED_VALUE, "value of element '%s' does not match value of its 'fixed' attribute\n", FIELD_S},
         {ERR_HEXBINARY_ALLOC, "error allocating hexBinary memory -- %" PRId64 " bytes\n", FIELD_D64},
+        {ERR_LEFTOVER_DATA, "Left over data, at least %i bit(s) remaining after end of parse\n", FIELD_C},
         {ERR_PARSE_BOOL, "error parsing binary value %" PRId64 " as either true or false\n", FIELD_D64},
         {ERR_STREAM_EOF, "EOF in stream, stopping program\n", FIELD_ZZZ},
         {ERR_STREAM_ERROR, "error in stream, stopping program\n", FIELD_ZZZ},
diff --git a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/errors.h b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/errors.h
index 52315c888..6a22a2af9 100644
--- a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/errors.h
+++ b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/errors.h
@@ -33,6 +33,7 @@ enum ErrorCode
     ERR_CHOICE_KEY,
     ERR_FIXED_VALUE,
     ERR_HEXBINARY_ALLOC,
+    ERR_LEFTOVER_DATA,
     ERR_PARSE_BOOL,
     ERR_STREAM_EOF,
     ERR_STREAM_ERROR,
@@ -117,8 +118,4 @@ extern cli_error_lookup_t *cli_error_lookup;
 
 extern const char *daffodil_program_version;
 
-// UNUSED - suppress compiler warning about unused variable
-
-#define UNUSED(x) (void)(x)
-
 #endif // ERRORS_H
diff --git a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/infoset.c b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/infoset.c
index 88e3f28ac..0aa123592 100644
--- a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/infoset.c
+++ b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/infoset.c
@@ -17,18 +17,19 @@
 
 // clang-format off
 #include "infoset.h"
-#include <stdio.h>   // for fwrite
-#include <string.h>  // for memccpy
-#include "errors.h"  // for Error, eof_or_error, LIMIT_NAME_LENGTH
+#include <string.h>     // for memccpy
+#include "errors.h"     // for Error, LIMIT_NAME_LENGTH
+#include "parsers.h"    // for no_leftover_data
+#include "unparsers.h"  // for flush_fragment_byte
 // clang-format on
 
 // Declare prototypes for easier compilation
 
-static const Error *walkInfosetNode(const VisitEventHandler *handler, const InfosetBase *infoNode);
-static const Error *walkInfosetNodeChild(const VisitEventHandler *handler, const InfosetBase *infoNode,
-                                         const ERD *childERD, const void *child);
-static const Error *walkArray(const VisitEventHandler *handler, const InfosetBase *infoNode,
-                              const ERD *arrayERD, const void *child);
+static const Error *walk_infoset_node(const VisitEventHandler *handler, const InfosetBase *infoNode,
+                                      const ERD *childERD, const void *child);
+static const Error *walk_array(const VisitEventHandler *handler, const InfosetBase *infoNode,
+                               const ERD *arrayERD, const void *child);
+static const Error *walk_infoset_node_children(const VisitEventHandler *handler, const InfosetBase *infoNode);
 
 // get_erd_name, get_erd_xmlns, get_erd_ns - get name and xmlns
 // attribute/value from ERD to use on XML element
@@ -123,64 +124,56 @@ get_erd_ns(const ERD *erd)
     return erd->namedQName.ns;
 }
 
-// walkInfoset - walk each node of an infoset and call
-// VisitEventHandler methods
+// parse_data - parse an input stream into an infoset, check for
+// leftover data, and return any errors in pstate
 
-const Error *
-walkInfoset(const VisitEventHandler *handler, const InfosetBase *infoNode)
+void
+parse_data(InfosetBase *infoset, PState *pstate)
 {
-    const Error *error = handler->visitStartDocument(handler);
+    infoset->erd->parseSelf(infoset, pstate);
+    no_leftover_data(pstate);
+}
 
-    if (!error)
-    {
-        error = walkInfosetNode(handler, infoNode);
-    }
-    if (!error)
-    {
-        error = handler->visitEndDocument(handler);
-    }
+// unparse_infoset - unparse an infoset to an output stream, flush the
+// fragment byte if not done yet, and return any errors in ustate
 
-    return error;
+void
+unparse_infoset(InfosetBase *infoset, UState *ustate)
+{
+    infoset->erd->unparseSelf(infoset, ustate);
+    const uint8_t fill_byte = '\0';
+    flush_fragment_byte(fill_byte, ustate);
 }
 
-// walkInfosetNode - walk each child of an infoset node and call
-// VisitEventHandler methods
+// walk_infoset - walk an infoset and call VisitEventHandler methods
 
-static const Error *
-walkInfosetNode(const VisitEventHandler *handler, const InfosetBase *infoNode)
+const Error *
+walk_infoset(const VisitEventHandler *handler, const InfosetBase *infoNode)
 {
-    const size_t            numChildren = infoNode->erd->numChildren;
-    const ERD *const *const childrenERDs = infoNode->erd->childrenERDs;
-    const size_t *          offsets = infoNode->erd->offsets;
-
-    // Start visiting the node
-    const Error *error = handler->visitStartComplex(handler, infoNode);
+    const Error *error = handler->visitStartDocument(handler);
 
-    // Walk each child of the node
-    for (size_t i = 0; i < numChildren && !error; i++)
+    if (!error)
     {
-        const ERD *  childERD = childrenERDs[i];
-        const size_t offset = offsets[i];
-        const void * child = (const void *)((const char *)infoNode + offset);
+        const ERD *  childERD = infoNode->erd;
+        const size_t childOffset = childERD->numChildren ? 0 : childERD->childrenOffsets[0];
+        const void * child = (const void *)((const char *)infoNode + childOffset);
 
-        error = walkInfosetNodeChild(handler, infoNode, childERD, child);
+        error = walk_infoset_node(handler, infoNode, childERD, child);
     }
-
-    // End visiting the node
     if (!error)
     {
-        error = handler->visitEndComplex(handler, infoNode);
+        error = handler->visitEndDocument(handler);
     }
 
     return error;
 }
 
-// walkInfosetNodeChild - walk a child of an infoset node and call
-// VisitEventHandler methods
+// walk_infoset_node - walk an infoset node and call VisitEventHandler
+// methods
 
 static const Error *
-walkInfosetNodeChild(const VisitEventHandler *handler, const InfosetBase *infoNode, const ERD *childERD,
-                     const void *child)
+walk_infoset_node(const VisitEventHandler *handler, const InfosetBase *infoNode, const ERD *childERD,
+                  const void *child)
 {
     // Get the type of child to walk
     const Error *       error = NULL;
@@ -192,15 +185,15 @@ walkInfosetNodeChild(const VisitEventHandler *handler, const InfosetBase *infoNo
     {
     case ARRAY:
         // Walk an array recursively
-        error = walkArray(handler, infoNode, childERD, child);
+        error = walk_array(handler, infoNode, childERD, child);
         break;
     case CHOICE:
         // Point next ERD to choice of alternative elements' ERDs
         error = infoNode->erd->initChoice(infoNode);
         break;
     case COMPLEX:
-        // Walk a child node recursively
-        error = walkInfosetNode(handler, childNode);
+        // Walk a node's children recursively
+        error = walk_infoset_node_children(handler, childNode);
         break;
     case PRIMITIVE_BOOLEAN:
     case PRIMITIVE_DOUBLE:
@@ -222,53 +215,60 @@ walkInfosetNodeChild(const VisitEventHandler *handler, const InfosetBase *infoNo
     return error;
 }
 
-// walkArray - walk each element of an array and call
+// walk_array - walk each element of an array and call
 // VisitEventHandler methods
 
 static const Error *
-walkArray(const VisitEventHandler *handler, const InfosetBase *infoNode, const ERD *arrayERD,
-          const void *child)
+walk_array(const VisitEventHandler *handler, const InfosetBase *infoNode, const ERD *arrayERD,
+           const void *child)
 {
     // Get the array's size, type of its elements, and offset between its elements
     const Error *error = NULL;
     const size_t arraySize = arrayERD->getArraySize(infoNode);
     const ERD *  childERD = arrayERD->childrenERDs[0];
-    const size_t offset = arrayERD->offsets[0];
+    const size_t childOffset = arrayERD->childrenOffsets[0];
 
     // Walk each element of the array
     for (size_t i = 0; i < arraySize && !error; i++)
     {
         // Walk an element of the array recursively
-        error = walkInfosetNodeChild(handler, infoNode, childERD, child);
+        error = walk_infoset_node(handler, infoNode, childERD, child);
 
-        // Increment child by offset in order to walk the next element
-        child = (const char *)child + offset;
+        // Increment child by childOffset in order to walk the next element
+        child = (const char *)child + childOffset;
     }
 
     return error;
 }
 
-// flushUState - flush any buffered bits not written yet
-
-#define BYTE_WIDTH 8
+// walk_infoset_node_children - walk each child of an infoset node and
+// call VisitEventHandler methods
 
-void
-flushUState(UState *ustate)
+static const Error *
+walk_infoset_node_children(const VisitEventHandler *handler, const InfosetBase *infoNode)
 {
-    // Do we have any bits within the fractional byte?
-    if (ustate->unwritLen)
+    const size_t            numChildren = infoNode->erd->numChildren;
+    const ERD *const *const childrenERDs = infoNode->erd->childrenERDs;
+    const size_t *          childrenOffsets = infoNode->erd->childrenOffsets;
+
+    // Start visiting the node
+    const Error *error = handler->visitStartComplex(handler, infoNode);
+
+    // Walk each child of the node
+    for (size_t i = 0; i < numChildren && !error; i++)
     {
-        // Fill the fractional byte
-        size_t num_bits_fill = BYTE_WIDTH - ustate->unwritLen;
-        ustate->unwritBits <<= num_bits_fill;
-        ustate->bitPos0b += num_bits_fill;
-        ustate->unwritLen = 0; // Even though we don't flush until next line
-
-        // Flush the fractional byte
-        size_t count = fwrite(&ustate->unwritBits, 1, 1, ustate->stream);
-        if (count < 1)
-        {
-            ustate->error = eof_or_error(ustate->stream);
-        }
+        const ERD *  childERD = childrenERDs[i];
+        const size_t childOffset = childrenOffsets[i];
+        const void * child = (const void *)((const char *)infoNode + childOffset);
+
+        error = walk_infoset_node(handler, infoNode, childERD, child);
+    }
+
+    // End visiting the node
+    if (!error)
+    {
+        error = handler->visitEndComplex(handler, infoNode);
     }
+
+    return error;
 }
diff --git a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/infoset.h b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/infoset.h
index e8cb6d895..73374c339 100644
--- a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/infoset.h
+++ b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/infoset.h
@@ -85,7 +85,7 @@ typedef struct ERD
     const NamedQName         namedQName;
     const enum TypeCode      typeCode;
     const size_t             numChildren;
-    const size_t *           offsets;
+    const size_t *           childrenOffsets;
     const struct ERD *const *childrenERDs;
 
     const ERDParseSelf   parseSelf;
@@ -119,24 +119,24 @@ typedef struct InfosetBase
 
 typedef struct PState
 {
-    FILE *       stream;      // stream to read data from (input)
-    size_t       bitPos0b;    // 0-based position of last successful parse (1-bit granularity)
-    Diagnostics *diagnostics; // any validation diagnostics
-    const Error *error;       // any error which stops parse
-    uint8_t      unreadBits;  // any buffered bits not read yet
-    uint8_t      unreadLen;   // number of buffered bits not read yet
+    FILE *       stream;        // stream to read data from (input)
+    size_t       bitPos0b;      // 0-based position of last successful parse (1-bit granularity)
+    Diagnostics *diagnostics;   // any validation diagnostics
+    const Error *error;         // any error which stops parse
+    uint8_t      unreadBits;    // any buffered bits not read yet
+    uint8_t      numUnreadBits; // number of buffered bits not read yet
 } PState;
 
 // UState - mutable state while unparsing infoset
 
 typedef struct UState
 {
-    FILE *       stream;      // stream to write data to (output)
-    size_t       bitPos0b;    // 0-based position of last successful write (1-bit granularity)
-    Diagnostics *diagnostics; // any validation diagnostics
-    const Error *error;       // any error which stops unparse
-    uint8_t      unwritBits;  // any buffered bits not written yet
-    uint8_t      unwritLen;   // number of buffered bits not written yet
+    FILE *       stream;        // stream to write data to (output)
+    size_t       bitPos0b;      // 0-based position of last successful write (1-bit granularity)
+    Diagnostics *diagnostics;   // any validation diagnostics
+    const Error *error;         // any error which stops unparse
+    uint8_t      unwritBits;    // any buffered bits not written yet
+    uint8_t      numUnwritBits; // number of buffered bits not written yet
 } UState;
 
 // VisitEventHandler - methods to be called when walking an infoset
@@ -157,18 +157,29 @@ extern const char *get_erd_name(const ERD *erd);
 extern const char *get_erd_xmlns(const ERD *erd);
 extern const char *get_erd_ns(const ERD *erd);
 
-// rootElement - return an infoset's root element for parsing,
-// walking, or unparsing (implementation actually is generated in
-// generated_code.c, not defined in infoset.c)
+// get_infoset - get an infoset (optionally clearing it first) for
+// parsing/walking (note get_infoset actually is defined in
+// generated_code.c, not infoset.c)
 
-extern InfosetBase *rootElement(void);
+extern InfosetBase *get_infoset(bool clear_infoset);
 
-// walkInfoset - walk each node of an infoset and call VisitEventHandler methods
+// parse_data - parse an input stream into an infoset, check for
+// leftover data, and return any errors in pstate
 
-extern const Error *walkInfoset(const VisitEventHandler *handler, const InfosetBase *infoset);
+extern void parse_data(InfosetBase *infoset, PState *pstate);
 
-// flushUState - flush any buffered bits not written yet
+// unparse_infoset - unparse an infoset to an output stream, flush the
+// fragment byte if not done yet, and return any errors in ustate
 
-extern void flushUState(UState *ustate);
+extern void unparse_infoset(InfosetBase *infoset, UState *ustate);
+
+// walk_infoset - walk each node of an infoset and call
+// VisitEventHandler methods
+
+extern const Error *walk_infoset(const VisitEventHandler *handler, const InfosetBase *infoset);
+
+// UNUSED - suppress compiler warning about unused variable
+
+#define UNUSED(x) (void)(x)
 
 #endif // INFOSET_H
diff --git a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/parsers.c b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/parsers.c
index da231e4f1..f39739582 100644
--- a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/parsers.c
+++ b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/parsers.c
@@ -19,9 +19,9 @@
 #include "parsers.h"
 #include <assert.h>   // for assert
 #include <stdbool.h>  // for bool, false, true
-#include <stdio.h>    // for fread
+#include <stdio.h>    // for fread, fgetc, EOF
 #include <stdlib.h>   // for free, malloc
-#include "errors.h"   // for eof_or_error, Error, add_diagnostic, get_diagnostics, ERR_ARRAY_BOUNDS, ERR_FIXED_VALUE, ERR_HEXBINARY_ALLOC, ERR_PARSE_BOOL, Error::(anonymous), Diagnostics
+#include "errors.h"   // for Error, eof_or_error, Error::(anonymous), ERR_LEFTOVER_DATA, add_diagnostic, get_diagnostics, ERR_ARRAY_BOUNDS, ERR_FIXED_VALUE, ERR_HEXBINARY_ALLOC, ERR_PARSE_BOOL, Diagnostics
 #include "p_endian.h" // for be64toh, le64toh, be32toh, le32toh
 // clang-format on
 
@@ -35,7 +35,7 @@
 #define HIGH_BITS(byte, n) ((byte & HIGH_MASK(n)) >> (BYTE_WIDTH - n))
 
 // Helper method to read bits using whole bytes while storing
-// remaining bits not yet read within a fractional byte; returns last
+// remaining bits not yet read within a fragment byte; returns last
 // bits of last byte already shifted to left end
 
 // Note callers must check pstate->error after calling read_bits and
@@ -46,7 +46,7 @@ read_bits(uint8_t *bytes, size_t num_bits, PState *pstate)
 {
     // Copy as many bytes directly from stream as possible
     size_t ix_bytes = 0;
-    if (!pstate->unreadLen)
+    if (!pstate->numUnreadBits)
     {
         size_t num_bytes = num_bits / BYTE_WIDTH;
         if (num_bytes)
@@ -62,8 +62,8 @@ read_bits(uint8_t *bytes, size_t num_bits, PState *pstate)
         }
     }
 
-    // Copy and fill the fractional byte as many times as needed
-    while (num_bits > pstate->unreadLen)
+    // Copy and fill the fragment byte as many times as needed
+    while (num_bits > pstate->numUnreadBits)
     {
         // Copy one whole byte from stream to temporary storage
         size_t whole_byte = 0;
@@ -74,42 +74,42 @@ read_bits(uint8_t *bytes, size_t num_bits, PState *pstate)
             return;
         }
 
-        // Copy bits from whole byte to fill fractional byte
-        size_t num_bits_fill = BYTE_WIDTH - pstate->unreadLen;
+        // Copy bits from whole byte to fill fragment byte
+        size_t num_bits_fill = BYTE_WIDTH - pstate->numUnreadBits;
         pstate->unreadBits <<= num_bits_fill;
         pstate->unreadBits |= HIGH_BITS(whole_byte, num_bits_fill);
-        pstate->unreadLen += num_bits_fill;
+        pstate->numUnreadBits += num_bits_fill;
         whole_byte <<= num_bits_fill;
 
-        // Copy bits from fractional byte to `bytes`
+        // Copy bits from fragment byte to `bytes`
         size_t num_bits_read = BYTE_WIDTH;
         if (num_bits_read > num_bits) num_bits_read = num_bits;
         num_bits -= num_bits_read;
         bytes[ix_bytes++] = pstate->unreadBits & HIGH_MASK(num_bits_read);
-        pstate->unreadLen -= num_bits_read;
+        pstate->numUnreadBits -= num_bits_read;
 
-        // Copy rest of bits from whole byte to fractional byte
+        // Copy rest of bits from whole byte to fragment byte
         size_t num_bits_unread = BYTE_WIDTH - num_bits_fill;
-        assert(num_bits_unread + pstate->unreadLen < BYTE_WIDTH);
+        assert(num_bits_unread + pstate->numUnreadBits < BYTE_WIDTH);
         if (num_bits_unread)
         {
             pstate->unreadBits <<= num_bits_unread;
             pstate->unreadBits |= HIGH_BITS(whole_byte, num_bits_unread);
-            pstate->unreadLen += num_bits_unread;
+            pstate->numUnreadBits += num_bits_unread;
         }
     }
 
-    // Copy bits from fractional byte to `bytes` one last time,
-    // keeping unread bits in right end (NOT shifted left)
-    assert(num_bits <= pstate->unreadLen);
+    // Copy bits from fragment byte to `bytes` one last time, keeping
+    // unread bits in right end (NOT shifted left)
+    assert(num_bits <= pstate->numUnreadBits);
     if (num_bits)
     {
         // Shift the unread bits to the left end so we can copy some
         // of them starting from the first unread bit
-        size_t shift = BYTE_WIDTH - pstate->unreadLen;
+        size_t shift = BYTE_WIDTH - pstate->numUnreadBits;
         pstate->unreadBits <<= shift;
         bytes[ix_bytes] = pstate->unreadBits & HIGH_MASK(num_bits);
-        pstate->unreadLen -= num_bits;
+        pstate->numUnreadBits -= num_bits;
 
         // Shift any remaining unread bits back to the right end since
         // we append new unread bits from the right
@@ -195,7 +195,7 @@ parse_endian_float(bool big_endian_data, float *number, size_t num_bits, PState
     pstate->bitPos0b += num_bits;
 }
 
-// Helper method to read signed integers using fractional byte shifts
+// Helper method to read signed integers using fragment byte shifts
 // depending on data endianness (note not tested on big-endian
 // architecture; might work only on low-endian architecture)
 
@@ -256,8 +256,8 @@ parse_endian_int64(bool big_endian_data, int64_t *number, size_t num_bits, PStat
     pstate->bitPos0b += num_bits;
 }
 
-// Helper method to read unsigned integers using fractional byte
-// shifts depending on data endianness (note not tested on big-endian
+// Helper method to read unsigned integers using fragment byte shifts
+// depending on data endianness (note not tested on big-endian
 // architecture; might work only on low-endian architecture)
 
 static void
@@ -594,3 +594,34 @@ parse_check_bounds(const char *name, size_t count, size_t minOccurs, size_t maxO
         pstate->error = &error;
     }
 }
+
+// Check for any data left over after end of parse
+
+void
+no_leftover_data(PState *pstate)
+{
+    // Skip the check if we already have an error
+    if (!pstate->error)
+    {
+        // Check for any unread bits left in pstate's fragment byte
+        if (pstate->numUnreadBits)
+        {
+            // We have some unread bits remaining, so report leftover data
+            static Error error = {ERR_LEFTOVER_DATA, {0}};
+            error.arg.c = pstate->numUnreadBits;
+            pstate->error = &error;
+        }
+        else
+        {
+            // Check for any unread bytes left in input stream
+            int c = fgetc(pstate->stream);
+            if (c != EOF)
+            {
+                // We have some unread bytes remaining, so report leftover data
+                static Error error = {ERR_LEFTOVER_DATA, {0}};
+                error.arg.c = BYTE_WIDTH;
+                pstate->error = &error;
+            }
+        }
+    }
+}
diff --git a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/parsers.h b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/parsers.h
index 38812c366..4428c07c9 100644
--- a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/parsers.h
+++ b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/parsers.h
@@ -76,6 +76,11 @@ extern void parse_validate_fixed(bool same, const char *element, PState *pstate)
 
 // Check array count is within bounds
 
-extern void parse_check_bounds(const char *name, size_t count, size_t minOccurs, size_t maxOccurs, PState *pstate);
+extern void parse_check_bounds(const char *name, size_t count, size_t minOccurs, size_t maxOccurs,
+                               PState *pstate);
+
+// Check for any data left over after end of parse
+
+extern void no_leftover_data(PState *pstate);
 
 #endif // PARSERS_H
diff --git a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/unparsers.c b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/unparsers.c
index 37e5ba4fa..9614b39fc 100644
--- a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/unparsers.c
+++ b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/unparsers.c
@@ -20,7 +20,7 @@
 #include <assert.h>   // for assert
 #include <stdbool.h>  // for bool
 #include <stdio.h>    // for fwrite
-#include "errors.h"   // for eof_or_error, add_diagnostic, get_diagnostics, ERR_ARRAY_BOUNDS, ERR_FIXED_VALUE, Diagnostics, Error
+#include "errors.h"   // for eof_or_error, Error, add_diagnostic, get_diagnostics, ERR_ARRAY_BOUNDS, ERR_FIXED_VALUE, Diagnostics, Error::(anonymous)
 #include "p_endian.h" // for htobe64, htole64, htobe32, htole32
 // clang-format on
 
@@ -34,8 +34,8 @@
 #define HIGH_BITS(byte, n) ((byte & HIGH_MASK(n)) >> (BYTE_WIDTH - n))
 
 // Helper method to write bits using whole bytes while storing
-// remaining bits not yet written within a fractional byte; expects
-// last bits of last byte to be already shifted to left end
+// remaining bits not yet written within a fragment byte; expects last
+// bits of last byte to be already shifted to left end
 
 // Note callers must check ustate->error after calling write_bits and
 // update ustate->bitPos0b themselves after successful unparses
@@ -45,7 +45,7 @@ write_bits(const uint8_t *bytes, size_t num_bits, UState *ustate)
 {
     // Copy as many bytes directly to stream as possible
     size_t ix_bytes = 0;
-    if (!ustate->unwritLen)
+    if (!ustate->numUnwritBits)
     {
         size_t num_bytes = num_bits / BYTE_WIDTH;
         if (num_bytes)
@@ -61,19 +61,19 @@ write_bits(const uint8_t *bytes, size_t num_bits, UState *ustate)
         }
     }
 
-    // Fill and copy the fractional byte as many times as needed
-    while (num_bits + ustate->unwritLen >= BYTE_WIDTH)
+    // Fill and copy the fragment byte as many times as needed
+    while (num_bits + ustate->numUnwritBits >= BYTE_WIDTH)
     {
-        // Fill the fractional byte
+        // Fill the fragment byte
         uint8_t whole_byte = bytes[ix_bytes++];
-        size_t  num_bits_fill = BYTE_WIDTH - ustate->unwritLen;
+        size_t  num_bits_fill = BYTE_WIDTH - ustate->numUnwritBits;
         ustate->unwritBits <<= num_bits_fill;
         ustate->unwritBits |= HIGH_BITS(whole_byte, num_bits_fill);
-        ustate->unwritLen += num_bits_fill;
+        ustate->numUnwritBits += num_bits_fill;
         num_bits -= num_bits_fill;
         whole_byte <<= num_bits_fill;
 
-        // Copy the fractional byte to stream
+        // Copy the fragment byte to stream
         size_t num_bits_write = BYTE_WIDTH;
         size_t count = fwrite(&ustate->unwritBits, 1, 1, ustate->stream);
         if (count < 1)
@@ -81,29 +81,29 @@ write_bits(const uint8_t *bytes, size_t num_bits, UState *ustate)
             ustate->error = eof_or_error(ustate->stream);
             return;
         }
-        ustate->unwritLen -= num_bits_write;
+        ustate->numUnwritBits -= num_bits_write;
 
         // Copy any remaining unused bits from the whole byte to the
-        // fractional byte
+        // fragment byte
         size_t num_bits_unused = BYTE_WIDTH - num_bits_fill;
         if (num_bits_unused > num_bits) num_bits_unused = num_bits;
         if (num_bits_unused)
         {
             ustate->unwritBits <<= num_bits_unused;
             ustate->unwritBits |= HIGH_BITS(whole_byte, num_bits_unused);
-            ustate->unwritLen += num_bits_unused;
+            ustate->numUnwritBits += num_bits_unused;
             num_bits -= num_bits_unused;
         }
     }
 
-    // Fill the fractional byte one last time
+    // Fill the fragment byte one last time
     if (num_bits)
     {
-        assert(num_bits + ustate->unwritLen < BYTE_WIDTH);
+        assert(num_bits + ustate->numUnwritBits < BYTE_WIDTH);
 
         ustate->unwritBits <<= num_bits;
         ustate->unwritBits |= HIGH_BITS(bytes[ix_bytes], num_bits);
-        ustate->unwritLen += num_bits;
+        ustate->numUnwritBits += num_bits;
     }
 }
 
@@ -177,7 +177,7 @@ unparse_endian_float(bool big_endian_data, float number, size_t num_bits, UState
     ustate->bitPos0b += num_bits;
 }
 
-// Helper method to write signed integers using fractional byte shifts
+// Helper method to write signed integers using fragment byte shifts
 // depending on data endianness (note not tested on big-endian
 // architecture; might work only on low-endian architecture)
 
@@ -219,8 +219,8 @@ unparse_endian_int64(bool big_endian_data, int64_t number, size_t num_bits, USta
     ustate->bitPos0b += num_bits;
 }
 
-// Helper method to write unsigned integers using fractional byte
-// shifts depending on data endianness (note not tested on big-endian
+// Helper method to write unsigned integers using fragment byte shifts
+// depending on data endianness (note not tested on big-endian
 // architecture; might work only on low-endian architecture)
 
 // Also note that we probably could use this helper function to write
@@ -465,3 +465,33 @@ unparse_check_bounds(const char *name, size_t count, size_t minOccurs, size_t ma
         ustate->error = &error;
     }
 }
+
+// Flush the fragment byte if not done yet
+
+void
+flush_fragment_byte(const uint8_t fill_byte, UState *ustate)
+{
+    // Skip the flush if we already have an error
+    if (!ustate->error)
+    {
+        // Do we have any unwritten bits left in the fragment byte?
+        if (ustate->numUnwritBits)
+        {
+            // Fill the fragment byte
+            size_t num_bits_fill = BYTE_WIDTH - ustate->numUnwritBits;
+            ustate->unwritBits <<= num_bits_fill;
+            ustate->unwritBits |= HIGH_BITS(fill_byte, num_bits_fill);
+
+            // Flush the fragment byte
+            size_t num_bits_write = ustate->numUnwritBits;
+            size_t count = fwrite(&ustate->unwritBits, 1, 1, ustate->stream);
+            if (count < 1)
+            {
+                ustate->error = eof_or_error(ustate->stream);
+                num_bits_write = 0;
+            }
+            ustate->numUnwritBits -= num_bits_write;
+            ustate->bitPos0b += num_bits_write;
+        }
+    }
+}
diff --git a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/unparsers.h b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/unparsers.h
index 6eee51f23..af5b9c8ea 100644
--- a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/unparsers.h
+++ b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/libruntime/unparsers.h
@@ -72,6 +72,11 @@ extern void unparse_validate_fixed(bool same, const char *element, UState *ustat
 
 // Check array count is within bounds
 
-extern void unparse_check_bounds(const char *name, size_t count, size_t minOccurs, size_t maxOccurs, UState *ustate);
+extern void unparse_check_bounds(const char *name, size_t count, size_t minOccurs, size_t maxOccurs,
+                                 UState *ustate);
+
+// Flush the fragment byte if not done yet
+
+extern void flush_fragment_byte(const uint8_t fill_byte, UState *ustate);
 
 #endif // UNPARSERS_H
diff --git a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/tests/bits.c b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/tests/bits.c
index 6fb1bb47e..6bdde9928 100644
--- a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/tests/bits.c
+++ b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/tests/bits.c
@@ -23,9 +23,9 @@
 #include <stdint.h>                // for uint32_t, int16_t, int32_t, int64_t, int8_t, uint16_t, uint64_t, uint8_t
 #include <stdio.h>                 // for fclose, NULL, fflush, fmemopen, open_memstream, FILE, size_t
 #include <stdlib.h>                // for free
-#include "infoset.h"               // for UState, PState, flushUState
+#include "infoset.h"               // for UState, PState
 #include "parsers.h"               // for parse_be_bool, parse_le_bool, parse_be_int16, parse_be_int32, parse_be_int64, parse_be_int8, parse_be_uint16, parse_be_uint32, parse_be_uint64, parse_be_uint8, parse_le_int16, parse_le_int32, parse_le_int64, parse_le_int8, parse_le_uint16, parse_le_uint32, parse_le_uint64, parse_le_uint8
-#include "unparsers.h"             // for unparse_be_bool, unparse_le_bool, unparse_be_int16, unparse_be_int32, unparse_be_int64, unparse_be_int8, unparse_be_uint16, unparse_be_uint32, unparse_be_uint64, unparse_be_uint8, unparse_le_int16, unparse_le_int32, unparse_le_int64, unparse_le_int8, unparse_le_uint16, unparse_le_uint32, unparse_le_uint64, unparse_le_uint8
+#include "unparsers.h"             // for unparse_be_bool, unparse_le_bool, flush_fragment_byte, unparse_be_int16, unparse_be_int32, unparse_be_int64, unparse_be_int8, unparse_be_uint16, unparse_be_uint32, unparse_be_uint64, unparse_be_uint8, unparse_le_int16, unparse_le_int32, unparse_le_int64, unparse_le_int8, unparse_le_uint16, unparse_le_uint32, unparse_le_uint64, unparse_le_uint8
 // clang-format on
 
 Test(bits, be_bool_24)
@@ -44,7 +44,7 @@ Test(bits, be_bool_24)
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
     cr_expect(eq(sz, ustate.bitPos0b, 24), "ustate should advance 24 bits");
     cr_expect(eq(u8, ustate.unwritBits, 0), "ustate should hold nothing");
-    cr_expect(eq(u8, ustate.unwritLen, 0), "ustate should be empty");
+    cr_expect(eq(u8, ustate.numUnwritBits, 0), "ustate should be empty");
     cr_expect(eq(sz, size, 3), "stream should hold 3 bytes");
     cr_expect(eq(u8, buffer[0], 0372), "stream should hold 0b_11_111_010");
     cr_expect(eq(u8, buffer[1], 0306), "stream should hold 0b_11_000_110");
@@ -62,7 +62,7 @@ Test(bits, be_bool_24)
     cr_expect(eq(ptr, (void *)pstate.error, 0), "pstate should have no error");
     cr_expect(eq(sz, pstate.bitPos0b, 24), "pstate should advance 24 bits");
     cr_expect(eq(u8, pstate.unreadBits, 0), "pstate should hold nothing");
-    cr_expect(eq(u8, pstate.unreadLen, 0), "pstate should be empty");
+    cr_expect(eq(u8, pstate.numUnreadBits, 0), "pstate should be empty");
 
     // Close stream and free dynamic buffer
     fclose(stream);
@@ -85,14 +85,14 @@ Test(bits, be_bool_4)
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
     cr_expect(eq(sz, ustate.bitPos0b, 4), "ustate should advance 4 bits");
     cr_expect(eq(u8, ustate.unwritBits, 012), "ustate should hold 0b_1_010");
-    cr_expect(eq(u8, ustate.unwritLen, 4), "ustate should buffer 4 bits");
+    cr_expect(eq(u8, ustate.numUnwritBits, 4), "ustate should buffer 4 bits");
     cr_expect(eq(sz, size, 0), "stream should be empty");
     unparse_be_bool(false, 4, true4_rep, false_rep, &ustate);
     fflush(stream);
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
     cr_expect(eq(sz, ustate.bitPos0b, 8), "ustate should advance 4 bits");
     cr_expect(eq(u8, ustate.unwritBits, 0255), "ustate should hold 0b_10_101_101");
-    cr_expect(eq(u8, ustate.unwritLen, 0), "ustate should be empty");
+    cr_expect(eq(u8, ustate.numUnwritBits, 0), "ustate should be empty");
     cr_expect(eq(sz, size, 1), "stream should have 1 byte");
     cr_expect(eq(u8, buffer[0], 0255), "stream should hold 0b_10_101_101");
 
@@ -108,14 +108,14 @@ Test(bits, be_bool_4)
     cr_expect(eq(ptr, (void *)pstate.error, 0), "pstate should have no error");
     cr_expect(eq(sz, pstate.bitPos0b, 4), "pstate should advance 4 bits");
     cr_expect(eq(u8, pstate.unreadBits, 0255), "pstate should hold 0b_10_101_101");
-    cr_expect(eq(u8, pstate.unreadLen, 4), "pstate should buffer 4 bits");
+    cr_expect(eq(u8, pstate.numUnreadBits, 4), "pstate should buffer 4 bits");
     number = false;
     parse_be_bool(&number, 4, true4_rep, false_rep, &pstate);
     cr_expect(eq(int, number, false), "boolean number should be false");
     cr_expect(eq(ptr, (void *)pstate.error, 0), "pstate should have no error");
     cr_expect(eq(sz, pstate.bitPos0b, 8), "pstate should advance 4 bits");
     cr_expect(eq(u8, pstate.unreadBits, 015), "pstate should hold 0b_1_101");
-    cr_expect(eq(u8, pstate.unreadLen, 0), "pstate should be empty");
+    cr_expect(eq(u8, pstate.numUnreadBits, 0), "pstate should be empty");
 
     // Close stream and free dynamic buffer
     fclose(stream);
@@ -138,24 +138,25 @@ Test(bits, be_bool_7_7)
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
     cr_expect(eq(sz, ustate.bitPos0b, 7), "ustate should advance 7 bits");
     cr_expect(eq(u8, ustate.unwritBits, 0146), "ustate should hold 0b_01_100_110");
-    cr_expect(eq(u8, ustate.unwritLen, 7), "ustate should buffer 7 bits");
+    cr_expect(eq(u8, ustate.numUnwritBits, 7), "ustate should buffer 7 bits");
     cr_expect(eq(sz, size, 0), "stream should be empty");
     unparse_be_bool(false, 7, true7_rep, false_rep, &ustate);
     fflush(stream);
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
     cr_expect(eq(sz, ustate.bitPos0b, 14), "ustate should advance 7 bits");
     cr_expect(eq(u8, ustate.unwritBits, 0157), "ustate should hold 0b_01_101_111");
-    cr_expect(eq(u8, ustate.unwritLen, 6), "ustate should buffer 6 bits");
+    cr_expect(eq(u8, ustate.numUnwritBits, 6), "ustate should buffer 6 bits");
     cr_expect(eq(sz, size, 1), "stream should have 1 byte");
     cr_expect(eq(u8, buffer[0], 0315), "stream should hold 0b_11_001_101");
 
     // Verify that flushing ustate writes 10111100
-    flushUState(&ustate);
+    const uint8_t fill_byte = '\0';
+    flush_fragment_byte(fill_byte, &ustate);
     fflush(stream);
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
-    cr_expect(eq(sz, ustate.bitPos0b, 16), "ustate should advance 2 bits");
+    cr_expect(eq(sz, ustate.bitPos0b, 20), "ustate should advance 6 bits");
     cr_expect(eq(u8, ustate.unwritBits, 0274), "ustate should hold 0b_10_111_100");
-    cr_expect(eq(u8, ustate.unwritLen, 0), "ustate should be empty");
+    cr_expect(eq(u8, ustate.numUnwritBits, 0), "ustate should be empty");
     cr_expect(eq(sz, size, 2), "stream should have 2 bytes");
     cr_expect(eq(u8, buffer[1], 0274), "stream should hold 0b_10_111_100");
 
@@ -171,14 +172,14 @@ Test(bits, be_bool_7_7)
     cr_expect(eq(ptr, (void *)pstate.error, 0), "pstate should have no error");
     cr_expect(eq(sz, pstate.bitPos0b, 7), "pstate should advance 7 bits");
     cr_expect(eq(u8, pstate.unreadBits, 0315), "pstate should hold 0b_11_001_101");
-    cr_expect(eq(u8, pstate.unreadLen, 1), "pstate should buffer 1 bit");
+    cr_expect(eq(u8, pstate.numUnreadBits, 1), "pstate should buffer 1 bit");
     number = false;
     parse_be_bool(&number, 7, true7_rep, false_rep, &pstate);
     cr_expect(eq(int, number, false), "boolean number should be false");
     cr_expect(eq(ptr, (void *)pstate.error, 0), "pstate should have no error");
     cr_expect(eq(sz, pstate.bitPos0b, 14), "pstate should advance 7 bits");
     cr_expect(eq(u8, pstate.unreadBits, 0274), "pstate should hold 0b_10_111_100");
-    cr_expect(eq(u8, pstate.unreadLen, 2), "pstate should buffer 2 bits");
+    cr_expect(eq(u8, pstate.numUnreadBits, 2), "pstate should buffer 2 bits");
 
     // Close stream and free dynamic buffer
     fclose(stream);
@@ -202,7 +203,7 @@ Test(bits, be_bool_9_7)
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
     cr_expect(eq(sz, ustate.bitPos0b, 9), "ustate should advance 9 bits");
     cr_expect(eq(u8, ustate.unwritBits, 01), "ustate should hold 0b_00_000_001");
-    cr_expect(eq(u8, ustate.unwritLen, 1), "ustate should buffer 1 bit");
+    cr_expect(eq(u8, ustate.numUnwritBits, 1), "ustate should buffer 1 bit");
     cr_expect(eq(sz, size, 1), "stream should hold 1 byte");
     cr_expect(eq(u8, buffer[0], 0363), "stream should hold 0b_11_110_011");
     unparse_be_bool(true, 7, true7_rep, false_rep, &ustate);
@@ -210,7 +211,7 @@ Test(bits, be_bool_9_7)
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
     cr_expect(eq(sz, ustate.bitPos0b, 16), "ustate should advance 7 bits");
     cr_expect(eq(u8, ustate.unwritBits, 0367), "ustate should hold 0b_11_110_111");
-    cr_expect(eq(u8, ustate.unwritLen, 0), "ustate should be empty");
+    cr_expect(eq(u8, ustate.numUnwritBits, 0), "ustate should be empty");
     cr_expect(eq(sz, size, 2), "stream should hold 2 bytes");
     cr_expect(eq(u8, buffer[1], 0367), "stream should hold 0b_11_110_111");
 
@@ -226,14 +227,14 @@ Test(bits, be_bool_9_7)
     cr_expect(eq(ptr, (void *)pstate.error, 0), "pstate should have no error");
     cr_expect(eq(sz, pstate.bitPos0b, 9), "pstate should advance 9 bits");
     cr_expect(eq(u8, pstate.unreadBits, 0367), "pstate should hold 0b_11_110_111");
-    cr_expect(eq(u8, pstate.unreadLen, 7), "pstate should buffer 7 bits");
+    cr_expect(eq(u8, pstate.numUnreadBits, 7), "pstate should buffer 7 bits");
     number = false;
     parse_be_bool(&number, 7, true7_rep, false_rep, &pstate);
     cr_expect(eq(int, number, true), "boolean number should be true");
     cr_expect(eq(ptr, (void *)pstate.error, 0), "pstate should have no error");
     cr_expect(eq(sz, pstate.bitPos0b, 16), "pstate should advance 7 bits");
     cr_expect(eq(u8, pstate.unreadBits, 0167), "pstate should hold 0b_01_110_111");
-    cr_expect(eq(u8, pstate.unreadLen, 0), "pstate should be empty");
+    cr_expect(eq(u8, pstate.numUnreadBits, 0), "pstate should be empty");
 
     // Close stream and free dynamic buffer
     fclose(stream);
@@ -271,7 +272,7 @@ Test(bits, be_signed_integers)
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
     cr_expect(eq(sz, ustate.bitPos0b, 176), "ustate should advance 176 bits");
     cr_expect(eq(sz, size, 22), "stream should have 22 bytes");
-    cr_expect(eq(u8, ustate.unwritLen, 0), "ustate should be empty");
+    cr_expect(eq(u8, ustate.numUnwritBits, 0), "ustate should be empty");
 
     // Reopen stream for reading from same dynamic buffer
     fclose(stream);
@@ -297,7 +298,7 @@ Test(bits, be_signed_integers)
     parse_be_int64(&tin63, 63, &pstate);
     cr_expect(eq(ptr, (void *)pstate.error, 0), "pstate should have no error");
     cr_expect(eq(sz, pstate.bitPos0b, 176), "pstate should advance 176 bits");
-    cr_expect(eq(u8, pstate.unreadLen, 0), "pstate should be empty");
+    cr_expect(eq(u8, pstate.numUnreadBits, 0), "pstate should be empty");
 
     // Verify that these 8 integers are the same integers originally written
     cr_expect(eq(i8, int1, tin1), "numbers should be the same");
@@ -345,7 +346,7 @@ Test(bits, be_unsigned_integers)
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
     cr_expect(eq(sz, ustate.bitPos0b, 176), "ustate should advance 176 bits");
     cr_expect(eq(sz, size, 22), "stream should have 22 bytes");
-    cr_expect(eq(u8, ustate.unwritLen, 0), "ustate should be empty");
+    cr_expect(eq(u8, ustate.numUnwritBits, 0), "ustate should be empty");
 
     // Reopen stream for reading from same dynamic buffer
     fclose(stream);
@@ -371,7 +372,7 @@ Test(bits, be_unsigned_integers)
     parse_be_uint64(&tniu63, 63, &pstate);
     cr_expect(eq(ptr, (void *)pstate.error, 0), "pstate should have no error");
     cr_expect(eq(sz, pstate.bitPos0b, 176), "pstate should advance 176 bits");
-    cr_expect(eq(u8, pstate.unreadLen, 0), "pstate should be empty");
+    cr_expect(eq(u8, pstate.numUnreadBits, 0), "pstate should be empty");
 
     // Verify that these 8 integers are the same integers originally written
     cr_expect(eq(u8, uint1, tniu1), "numbers should be the same");
@@ -404,7 +405,7 @@ Test(bits, le_bool_24)
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
     cr_expect(eq(sz, ustate.bitPos0b, 24), "ustate should advance 24 bits");
     cr_expect(eq(u8, ustate.unwritBits, 0), "ustate should hold nothing");
-    cr_expect(eq(u8, ustate.unwritLen, 0), "ustate should be empty");
+    cr_expect(eq(u8, ustate.numUnwritBits, 0), "ustate should be empty");
     cr_expect(eq(sz, size, 3), "stream should hold 3 bytes");
     cr_expect(eq(u8, buffer[0], 0210), "stream should hold 0b_10_001_000");
     cr_expect(eq(u8, buffer[1], 0306), "stream should hold 0b_11_000_110");
@@ -422,7 +423,7 @@ Test(bits, le_bool_24)
     cr_expect(eq(ptr, (void *)pstate.error, 0), "pstate should have no error");
     cr_expect(eq(sz, pstate.bitPos0b, 24), "pstate should advance 24 bits");
     cr_expect(eq(u8, pstate.unreadBits, 0), "pstate should hold nothing");
-    cr_expect(eq(u8, pstate.unreadLen, 0), "pstate should be empty");
+    cr_expect(eq(u8, pstate.numUnreadBits, 0), "pstate should be empty");
 
     // Close stream and free dynamic buffer
     fclose(stream);
@@ -445,14 +446,14 @@ Test(bits, le_bool_4_4)
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
     cr_expect(eq(sz, ustate.bitPos0b, 4), "ustate should advance 4 bits");
     cr_expect(eq(u8, ustate.unwritBits, 012), "ustate should hold 0b_1_010");
-    cr_expect(eq(u8, ustate.unwritLen, 4), "ustate should buffer 4 bits");
+    cr_expect(eq(u8, ustate.numUnwritBits, 4), "ustate should buffer 4 bits");
     cr_expect(eq(sz, size, 0), "stream should be empty");
     unparse_le_bool(false, 4, true4_rep, false_rep, &ustate);
     fflush(stream);
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
     cr_expect(eq(sz, ustate.bitPos0b, 8), "ustate should advance 4 bits");
     cr_expect(eq(u8, ustate.unwritBits, 0255), "ustate should hold 0b_10_101_101");
-    cr_expect(eq(u8, ustate.unwritLen, 0), "ustate should be empty");
+    cr_expect(eq(u8, ustate.numUnwritBits, 0), "ustate should be empty");
     cr_expect(eq(sz, size, 1), "stream should have 1 byte");
     cr_expect(eq(u8, buffer[0], 0255), "stream should hold 0b_10_101_101");
 
@@ -468,14 +469,14 @@ Test(bits, le_bool_4_4)
     cr_expect(eq(ptr, (void *)pstate.error, 0), "pstate should have no error");
     cr_expect(eq(sz, pstate.bitPos0b, 4), "pstate should advance 4 bits");
     cr_expect(eq(u8, pstate.unreadBits, 0255), "pstate should hold 0b_10_101_101");
-    cr_expect(eq(u8, pstate.unreadLen, 4), "pstate should buffer 4 bits");
+    cr_expect(eq(u8, pstate.numUnreadBits, 4), "pstate should buffer 4 bits");
     number = false;
     parse_le_bool(&number, 4, true4_rep, false_rep, &pstate);
     cr_expect(eq(int, number, false), "boolean number should be false");
     cr_expect(eq(ptr, (void *)pstate.error, 0), "pstate should have no error");
     cr_expect(eq(sz, pstate.bitPos0b, 8), "pstate should advance 4 bits");
     cr_expect(eq(u8, pstate.unreadBits, 015), "pstate should hold 0b_1_101");
-    cr_expect(eq(u8, pstate.unreadLen, 0), "pstate should be empty");
+    cr_expect(eq(u8, pstate.numUnreadBits, 0), "pstate should be empty");
 
     // Close stream and free dynamic buffer
     fclose(stream);
@@ -498,24 +499,25 @@ Test(bits, le_bool_7_7)
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
     cr_expect(eq(sz, ustate.bitPos0b, 7), "ustate should advance 7 bits");
     cr_expect(eq(u8, ustate.unwritBits, 0146), "ustate should hold 0b_01_100_110");
-    cr_expect(eq(u8, ustate.unwritLen, 7), "ustate should buffer 7 bits");
+    cr_expect(eq(u8, ustate.numUnwritBits, 7), "ustate should buffer 7 bits");
     cr_expect(eq(sz, size, 0), "stream should be empty");
     unparse_le_bool(false, 7, true7_rep, false_rep, &ustate);
     fflush(stream);
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
     cr_expect(eq(sz, ustate.bitPos0b, 14), "ustate should advance 7 bits");
     cr_expect(eq(u8, ustate.unwritBits, 0157), "ustate should hold 0b_01_101_111");
-    cr_expect(eq(u8, ustate.unwritLen, 6), "ustate should buffer 6 bits");
+    cr_expect(eq(u8, ustate.numUnwritBits, 6), "ustate should buffer 6 bits");
     cr_expect(eq(sz, size, 1), "stream should have 1 byte");
     cr_expect(eq(u8, buffer[0], 0315), "stream should hold 0b_11_001_101");
 
     // Verify that flushing ustate writes 10111100
-    flushUState(&ustate);
+    const uint8_t fill_byte = '\0';
+    flush_fragment_byte(fill_byte, &ustate);
     fflush(stream);
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
-    cr_expect(eq(sz, ustate.bitPos0b, 16), "ustate should advance 2 bits");
+    cr_expect(eq(sz, ustate.bitPos0b, 20), "ustate should advance 6 bits");
     cr_expect(eq(u8, ustate.unwritBits, 0274), "ustate should hold 0b_10_111_100");
-    cr_expect(eq(u8, ustate.unwritLen, 0), "ustate should be empty");
+    cr_expect(eq(u8, ustate.numUnwritBits, 0), "ustate should be empty");
     cr_expect(eq(sz, size, 2), "stream should have 2 bytes");
     cr_expect(eq(u8, buffer[1], 0274), "stream should hold 0b_10_111_100");
 
@@ -531,14 +533,14 @@ Test(bits, le_bool_7_7)
     cr_expect(eq(ptr, (void *)pstate.error, 0), "pstate should have no error");
     cr_expect(eq(sz, pstate.bitPos0b, 7), "pstate should advance 7 bits");
     cr_expect(eq(u8, pstate.unreadBits, 0315), "pstate should hold 0b_11_001_101");
-    cr_expect(eq(u8, pstate.unreadLen, 1), "pstate should buffer 1 bit");
+    cr_expect(eq(u8, pstate.numUnreadBits, 1), "pstate should buffer 1 bit");
     number = false;
     parse_le_bool(&number, 7, true7_rep, false_rep, &pstate);
     cr_expect(eq(int, number, false), "boolean number should be false");
     cr_expect(eq(ptr, (void *)pstate.error, 0), "pstate should have no error");
     cr_expect(eq(sz, pstate.bitPos0b, 14), "pstate should advance 7 bits");
     cr_expect(eq(u8, pstate.unreadBits, 0274), "pstate should hold 0b_10_111_100");
-    cr_expect(eq(u8, pstate.unreadLen, 2), "pstate should buffer 2 bits");
+    cr_expect(eq(u8, pstate.numUnreadBits, 2), "pstate should buffer 2 bits");
 
     // Close stream and free dynamic buffer
     fclose(stream);
@@ -562,7 +564,7 @@ Test(bits, le_bool_9_7)
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
     cr_expect(eq(sz, ustate.bitPos0b, 9), "ustate should advance 9 bits");
     cr_expect(eq(u8, ustate.unwritBits, 01), "ustate should hold 0b_00_000_001");
-    cr_expect(eq(u8, ustate.unwritLen, 1), "ustate should buffer 1 bit");
+    cr_expect(eq(u8, ustate.numUnwritBits, 1), "ustate should buffer 1 bit");
     cr_expect(eq(sz, size, 1), "stream should hold 1 byte");
     cr_expect(eq(u8, buffer[0], 0347), "stream should hold 0b_11_100_111");
     unparse_le_bool(true, 7, true7_rep, false_rep, &ustate);
@@ -570,7 +572,7 @@ Test(bits, le_bool_9_7)
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
     cr_expect(eq(sz, ustate.bitPos0b, 16), "ustate should advance 7 bits");
     cr_expect(eq(u8, ustate.unwritBits, 0367), "ustate should hold 0b_11_110_111");
-    cr_expect(eq(u8, ustate.unwritLen, 0), "ustate should be empty");
+    cr_expect(eq(u8, ustate.numUnwritBits, 0), "ustate should be empty");
     cr_expect(eq(sz, size, 2), "stream should hold 2 bytes");
     cr_expect(eq(u8, buffer[1], 0367), "stream should hold 0b_11_110_111");
 
@@ -586,14 +588,14 @@ Test(bits, le_bool_9_7)
     cr_expect(eq(ptr, (void *)pstate.error, 0), "pstate should have no error");
     cr_expect(eq(sz, pstate.bitPos0b, 9), "pstate should advance 9 bits");
     cr_expect(eq(u8, pstate.unreadBits, 0367), "pstate should hold 0b_11_110_111");
-    cr_expect(eq(u8, pstate.unreadLen, 7), "pstate should buffer 7 bits");
+    cr_expect(eq(u8, pstate.numUnreadBits, 7), "pstate should buffer 7 bits");
     number = false;
     parse_le_bool(&number, 7, true7_rep, false_rep, &pstate);
     cr_expect(eq(int, number, true), "boolean number should be true");
     cr_expect(eq(ptr, (void *)pstate.error, 0), "pstate should have no error");
     cr_expect(eq(sz, pstate.bitPos0b, 16), "pstate should advance 7 bits");
     cr_expect(eq(u8, pstate.unreadBits, 0167), "pstate should hold 0b_01_110_111");
-    cr_expect(eq(u8, pstate.unreadLen, 0), "pstate should be empty");
+    cr_expect(eq(u8, pstate.numUnreadBits, 0), "pstate should be empty");
 
     // Close stream and free dynamic buffer
     fclose(stream);
@@ -631,7 +633,7 @@ Test(bits, le_signed_integers)
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
     cr_expect(eq(sz, ustate.bitPos0b, 176), "ustate should advance 176 bits");
     cr_expect(eq(sz, size, 22), "stream should have 22 bytes");
-    cr_expect(eq(u8, ustate.unwritLen, 0), "ustate should be empty");
+    cr_expect(eq(u8, ustate.numUnwritBits, 0), "ustate should be empty");
 
     // Reopen stream for reading from same dynamic buffer
     fclose(stream);
@@ -657,7 +659,7 @@ Test(bits, le_signed_integers)
     parse_le_int64(&tin63, 63, &pstate);
     cr_expect(eq(ptr, (void *)pstate.error, 0), "pstate should have no error");
     cr_expect(eq(sz, pstate.bitPos0b, 176), "pstate should advance 176 bits");
-    cr_expect(eq(u8, pstate.unreadLen, 0), "pstate should be empty");
+    cr_expect(eq(u8, pstate.numUnreadBits, 0), "pstate should be empty");
 
     // Verify that these 8 integers are the same integers originally written
     cr_expect(eq(i8, int1, tin1), "numbers should be the same");
@@ -705,7 +707,7 @@ Test(bits, le_unsigned_integers)
     cr_expect(eq(ptr, (void *)ustate.error, 0), "ustate should have no error");
     cr_expect(eq(sz, ustate.bitPos0b, 176), "ustate should advance 176 bits");
     cr_expect(eq(sz, size, 22), "stream should have 22 bytes");
-    cr_expect(eq(u8, ustate.unwritLen, 0), "ustate should be empty");
+    cr_expect(eq(u8, ustate.numUnwritBits, 0), "ustate should be empty");
 
     // Reopen stream for reading from same dynamic buffer
     fclose(stream);
@@ -731,7 +733,7 @@ Test(bits, le_unsigned_integers)
     parse_le_uint64(&tniu63, 63, &pstate);
     cr_expect(eq(ptr, (void *)pstate.error, 0), "pstate should have no error");
     cr_expect(eq(sz, pstate.bitPos0b, 176), "pstate should advance 176 bits");
-    cr_expect(eq(u8, pstate.unreadLen, 0), "pstate should be empty");
+    cr_expect(eq(u8, pstate.numUnreadBits, 0), "pstate should be empty");
 
     // Verify that these 8 integers are the same integers originally written
     cr_expect(eq(u8, uint1, tniu1), "numbers should be the same");
diff --git a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/tests/extras.c b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/tests/extras.c
index 1fe997607..d7b69e689 100644
--- a/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/tests/extras.c
+++ b/daffodil-codegen-c/src/main/resources/org/apache/daffodil/codegen/c/files/tests/extras.c
@@ -16,14 +16,16 @@
  */
 
 // clang-format off
+#include <stdbool.h>  // for bool
 #include <stddef.h>   // for NULL
-#include "infoset.h"  // for InfosetBase, rootElement
+#include "infoset.h"  // for InfosetBase, get_infoset
 // clang-format on
 
-// Define `rootElement' to avoid an undefined reference when linking.
+// Define `get_infoset' to avoid an undefined reference when linking.
 
 InfosetBase *
-rootElement(void)
+get_infoset(bool clear_infoset)
 {
+    UNUSED(clear_infoset);
     return NULL;
 }
diff --git a/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/DaffodilCExamplesGenerator.scala b/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/DaffodilCExamplesGenerator.scala
index 6b6d93550..9ba25ef72 100644
--- a/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/DaffodilCExamplesGenerator.scala
+++ b/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/DaffodilCExamplesGenerator.scala
@@ -29,11 +29,11 @@ object DaffodilCExamplesGenerator {
   // Update one example of generated C code from a sample schema
   private def updateCExample(
     schemaFile: os.Path,
-    rootName: Option[String],
+    optRootName: Option[String],
     exampleDir: os.Path,
   ): Unit = {
     // Generate example code from the sample schema
-    val pf = Compiler().compileFile(schemaFile.toIO, rootName)
+    val pf = Compiler().compileFile(schemaFile.toIO, optRootName)
     assert(!pf.isError, pf.getDiagnostics.map(_.getMessage()).mkString("\n"))
     val cg = pf.forLanguage("c")
     val tempDir = os.temp.dir(dir = null, prefix = TDMLImplementation.DaffodilC.toString)
@@ -75,25 +75,22 @@ object DaffodilCExamplesGenerator {
 
     val schemaDir =
       rootDir / "daffodil-codegen-c" / "src" / "test" / "resources" / "org" / "apache" / "daffodil" / "codegen" / "c"
-    val exNumsSchema = schemaDir / "ex_nums.dfdl.xsd"
-    val exNumsRootName = None
-    val nestedSchema = schemaDir / "nested.dfdl.xsd"
-    val nestedRootName = Some("NestedUnion")
-    val padTestSchema = schemaDir / "padtest.dfdl.xsd"
-    val padTestRootName = None
-    val variableLenSchema = schemaDir / "variablelen.dfdl.xsd"
-    val variableLenRootName = Some("expressionElement")
-
     val examplesDir = os.Path(args(0))
-    val exNumsExampleDir = examplesDir / "ex_nums"
-    val nestedExampleDir = examplesDir / "NestedUnion"
-    val padTestExampleDir = examplesDir / "padtest"
-    val variableLenExampleDir = examplesDir / "variablelen"
+    val examples = Array(
+      (schemaDir / "ex_nums.dfdl.xsd", None, examplesDir / "ex_nums"),
+      (schemaDir / "nested.dfdl.xsd", Some("NestedUnion"), examplesDir / "NestedUnion"),
+      (schemaDir / "padtest.dfdl.xsd", None, examplesDir / "padtest"),
+      (schemaDir / "simple.dfdl.xsd", Some("simple-byte"), examplesDir / "simple"),
+      (
+        schemaDir / "variablelen.dfdl.xsd",
+        Some("expressionElement"),
+        examplesDir / "variablelen",
+      ),
+    )
 
     // Update each example of generated C code
-    updateCExample(exNumsSchema, exNumsRootName, exNumsExampleDir)
-    updateCExample(nestedSchema, nestedRootName, nestedExampleDir)
-    updateCExample(padTestSchema, padTestRootName, padTestExampleDir)
-    updateCExample(variableLenSchema, variableLenRootName, variableLenExampleDir)
+    examples.foreach { case (schemaFile, optRootName, exampleDir) =>
+      updateCExample(schemaFile, optRootName, exampleDir)
+    }
   }
 }
diff --git a/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/BinaryBooleanCodeGenerator.scala b/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/BinaryBooleanCodeGenerator.scala
index 177dde884..81222215d 100644
--- a/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/BinaryBooleanCodeGenerator.scala
+++ b/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/BinaryBooleanCodeGenerator.scala
@@ -51,7 +51,7 @@ trait BinaryBooleanCodeGenerator extends BinaryValueCodeGenerator {
   ): Unit = {
     val indent1 = if (cgState.hasChoice) INDENT else NO_INDENT
     val indent2 = if (deref.nonEmpty) INDENT else NO_INDENT
-    val localName = e.namedQName.local
+    val localName = cgState.cName(e)
     val field = s"instance->$localName$deref"
     val conv = if (e.byteOrderEv.constValue eq ByteOrder.BigEndian) "be" else "le"
     val function = s"${conv}_$primType"
diff --git a/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/BinaryValueCodeGenerator.scala b/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/BinaryValueCodeGenerator.scala
index 7c23afad1..374e08e5d 100644
--- a/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/BinaryValueCodeGenerator.scala
+++ b/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/BinaryValueCodeGenerator.scala
@@ -68,7 +68,7 @@ trait BinaryValueCodeGenerator {
   ): Unit = {
     val indent1 = if (cgState.hasChoice) INDENT else NO_INDENT
     val indent2 = if (deref.nonEmpty) INDENT else NO_INDENT
-    val localName = e.namedQName.local
+    val localName = cgState.cName(e)
     val field = s"instance->$localName$deref"
     val conv = if (e.byteOrderEv.constValue eq ByteOrder.BigEndian) "be" else "le"
     val function = s"${conv}_$primType"
@@ -92,7 +92,7 @@ trait BinaryValueCodeGenerator {
   ): Unit = {
     val indent1 = if (cgState.hasChoice) INDENT else NO_INDENT
     val indent2 = if (deref.nonEmpty) INDENT else NO_INDENT
-    val localName = e.namedQName.local
+    val localName = cgState.cName(e)
     val field = s"instance->$localName$deref"
     val fixed = e.fixedValueAsString
 
diff --git a/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/CodeGeneratorState.scala b/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/CodeGeneratorState.scala
index abdedc09f..107305a58 100644
--- a/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/CodeGeneratorState.scala
+++ b/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/CodeGeneratorState.scala
@@ -17,6 +17,7 @@
 
 package org.apache.daffodil.codegen.c.generators
 
+import scala.collection.immutable
 import scala.collection.mutable
 
 import org.apache.daffodil.core.dsom.Choice
@@ -40,8 +41,8 @@ class CodeGeneratorState(private val root: ElementBase) {
   private val finalStructs = mutable.ArrayBuffer[String]()
   private val finalImplementation = mutable.ArrayBuffer[String]()
 
-  // Create the topmost "main_struct" struct immediately
-  structs.push(new ComplexCGState("main_struct"))
+  // Push a dummy topmost state to simplify code
+  structs.push(new ComplexCGState(cStructName(root), root))
 
   // Returns true if the generator is currently processing an array
   def hasArray: Boolean = structs.nonEmpty && structs.top.inArray
@@ -62,8 +63,8 @@ class CodeGeneratorState(private val root: ElementBase) {
       structs.top.unparserStatements += s"    case $position:"
     }
 
-    if (context.isComplexType) {
-      // Initialize complex element
+    if (context.isComplexType || context == root) {
+      // Initialize complex element or distinguished root element which we treat like a complex element
       val C = cStructName(context)
       structs.push(new ComplexCGState(C, context))
       val erd = erdName(context)
@@ -72,7 +73,9 @@ class CodeGeneratorState(private val root: ElementBase) {
            |    instance->_base.parent = parent;""".stripMargin
 
       // Calculate padding if complex element has an explicit length
-      if (context.maybeFixedLengthInBits.isDefined && context.maybeFixedLengthInBits.get > 0) {
+      if (
+        context.isComplexType && context.maybeFixedLengthInBits.isDefined && context.maybeFixedLengthInBits.get > 0
+      ) {
         val lengthInBits = context.maybeFixedLengthInBits.get
         structs.top.parserStatements += s"    const size_t end_bitPos0b = pstate->bitPos0b + $lengthInBits;"
         structs.top.unparserStatements += s"    const size_t end_bitPos0b = ustate->bitPos0b + $lengthInBits;"
@@ -107,7 +110,7 @@ class CodeGeneratorState(private val root: ElementBase) {
       val indent1 = if (hasChoice) INDENT else NO_INDENT
       val indent2 = if (hasArray) INDENT else NO_INDENT
       val C = cStructName(context)
-      val e = context.namedQName.local
+      val e = cName(context)
       val deref = if (hasArray) "[i]" else ""
       if (hasChoice)
         structs.top.initChoiceStatements += s"$indent2        ${C}_initERD(&instance->$e$deref, (InfosetBase *)instance);"
@@ -119,6 +122,14 @@ class CodeGeneratorState(private val root: ElementBase) {
       structs.top.unparserStatements +=
         s"""$indent1$indent2    ${C}_unparseSelf(&instance->$e$deref, ustate);
            |$indent1$indent2    if (ustate->error) return;""".stripMargin
+    } else if (context == root) {
+      // Treat a simple type root element as a hybrid of simple and complex types
+      addFieldDeclaration(context) // struct member for element
+      addComputations(context) // offset, ERD computations
+      addSimpleTypeERD(context) // ERD static initializer
+      addStruct(context) // struct definition
+      addImplementation(context)
+      structs.pop()
     } else {
       // Prevent redundant definitions on reused types
       if (elementNotSeenYet(context, cStructName(context))) {
@@ -338,7 +349,7 @@ class CodeGeneratorState(private val root: ElementBase) {
          |#include "generated_code.h"
          |#include <stdbool.h>    // for false, bool, true
          |#include <stddef.h>     // for NULL, size_t
-         |#include <string.h>     // for memcmp
+         |#include <string.h>     // for memcmp, memset
          |#include "errors.h"     // for Error, PState, UState, ERR_CHOICE_KEY, Error::(anonymous), UNUSED
          |#include "parsers.h"    // for alloc_hexBinary, parse_hexBinary, parse_be_float, parse_be_int16, parse_validate_fixed, parse_be_bool32, parse_be_bool16, parse_be_int32, parse_be_uint16, parse_be_uint32, parse_le_bool32, parse_le_int64, parse_le_uint16, parse_le_uint8, parse_be_bool8, parse_be_double, parse_be_int64, parse_be_int8, parse_be_uint64, parse_be_uint8, parse_le_bool16, parse_le_bool8, parse_le_double, parse_le_float, parse_le_int16, parse_le_int32, parse_le_int8, par [...]
          |#include "unparsers.h"  // for unparse_hexBinary, unparse_be_float, unparse_be_int16, unparse_validate_fixed, unparse_be_bool32, unparse_be_bool16, unparse_be_int32, unparse_be_uint16, unparse_be_uint32, unparse_le_bool32, unparse_le_int64, unparse_le_uint16, unparse_le_uint8, unparse_be_bool8, unparse_be_double, unparse_be_int64, unparse_be_int8, unparse_be_uint64, unparse_be_uint8, unparse_le_bool16, unparse_le_bool8, unparse_le_double, unparse_le_float, unparse_le_int16, unp [...]
@@ -354,19 +365,23 @@ class CodeGeneratorState(private val root: ElementBase) {
          |// Initialize, parse, and unparse nodes of the infoset
          |
          |$finalImplementation
-         |// Return a root element for parsing or unparsing the infoset
+         |// Get an infoset (optionally clearing it first) for parsing/walking
          |
          |InfosetBase *
-         |rootElement(void)
+         |get_infoset(bool clear_infoset)
          |{
-         |    static bool initialized;
-         |    static $rootName root;
-         |    if (!initialized)
+         |    static $rootName infoset;
+         |
+         |    if (clear_infoset)
          |    {
-         |        ${rootName}_initERD(&root, (InfosetBase *)&root);
-         |        initialized = true;
+         |        // If your infoset contains hexBinary prefixed length elements,
+         |        // you may want to walk infoset first to free their malloc'ed
+         |        // storage - we are not handling that case for now...
+         |        memset(&infoset, 0, sizeof(infoset));
+         |        ${rootName}_initERD(&infoset, (InfosetBase *)&infoset);
          |    }
-         |    return &root._base;
+         |
+         |    return &infoset._base;
          |}
          |""".stripMargin
     code.replace("\r\n", "\n").replace("\n", System.lineSeparator)
@@ -375,13 +390,23 @@ class CodeGeneratorState(private val root: ElementBase) {
   // Returns a name for the given element's C struct identifier
   private def cStructName(context: ElementBase): String = {
     val sb = buildName(context, new StringBuilder)
+    makeLegalForC(sb)
     val name = sb.toString
     name
   }
 
-  // Returns a name for the given element's element runtime data.
+  // Returns a name for the given element's element runtime data
   private def erdName(context: ElementBase): String = {
     val sb = buildName(context, new StringBuilder) ++= "ERD"
+    makeLegalForC(sb)
+    val name = sb.toString
+    name
+  }
+
+  // Returns a name for the given element's local name
+  def cName(context: ElementBase): String = {
+    val sb = new StringBuilder(context.namedQName.local)
+    makeLegalForC(sb)
     val name = sb.toString
     name
   }
@@ -400,7 +425,7 @@ class CodeGeneratorState(private val root: ElementBase) {
       if (numChildren > 0)
         s"""static const $C ${C}_compute_offsets;
          |
-         |static const size_t ${C}_offsets[$count] = {
+         |static const size_t ${C}_childrenOffsets[$count] = {
          |$offsetComputations
          |};
          |
@@ -412,11 +437,11 @@ class CodeGeneratorState(private val root: ElementBase) {
          |$qNameInit
          |    COMPLEX, // typeCode
          |    $numChildren, // numChildren
-         |    ${C}_offsets, // offsets
-         |    ${C}_childrenERDs, // childrenERDs
-         |    (ERDParseSelf)&${C}_parseSelf, // parseSelf
-         |    (ERDUnparseSelf)&${C}_unparseSelf, // unparseSelf
-         |    {$initChoice} // initChoice
+         |    ${C}_childrenOffsets,
+         |    ${C}_childrenERDs,
+         |    (ERDParseSelf)&${C}_parseSelf,
+         |    (ERDUnparseSelf)&${C}_unparseSelf,
+         |    {.initChoice = $initChoice}
          |};
          |""".stripMargin
       else
@@ -424,11 +449,11 @@ class CodeGeneratorState(private val root: ElementBase) {
          |$qNameInit
          |    COMPLEX, // typeCode
          |    $numChildren, // numChildren
-         |    NULL, // offsets
+         |    NULL, // childrenOffsets
          |    NULL, // childrenERDs
-         |    (ERDParseSelf)&${C}_parseSelf, // parseSelf
-         |    (ERDUnparseSelf)&${C}_unparseSelf, // unparseSelf
-         |    {$initChoice} // initChoice
+         |    (ERDParseSelf)&${C}_parseSelf,
+         |    (ERDUnparseSelf)&${C}_unparseSelf,
+         |    {.initChoice = $initChoice}
          |};
          |""".stripMargin
 
@@ -521,9 +546,12 @@ class CodeGeneratorState(private val root: ElementBase) {
     !alreadySeen
   }
 
-  // Adds an ERD definition for a simple element
+  // Adds an ERD definition for a simple element or simple root element
   private def addSimpleTypeERD(context: ElementBase): Unit = {
+    val C = cStructName(context)
     val erd = erdName(context)
+    val count = structs.top.offsetComputations.length
+    val offsetComputations = structs.top.offsetComputations.mkString(",\n")
     val qNameInit = defineQNameInit(context)
     val typeCode = getPrimType(context) match {
       case PrimType.Boolean => "PRIMITIVE_BOOLEAN"
@@ -540,13 +568,34 @@ class CodeGeneratorState(private val root: ElementBase) {
       case PrimType.UnsignedByte => "PRIMITIVE_UINT8"
       case p => context.SDE("PrimType %s is not supported.", p.toString)
     }
+    // Treat a simple type root element as a hybrid of simple and complex types
     val erdDef =
-      s"""static const ERD $erd = {
-         |$qNameInit
-         |    $typeCode, // typeCode
-         |    0, NULL, NULL, NULL, NULL, {NULL}
-         |};
-         |""".stripMargin
+      if (context == root)
+        s"""|static const $C ${C}_compute_offsets;
+          |
+          |static const size_t ${C}_childrenOffsets[$count] = {
+          |$offsetComputations
+          |};
+          |
+          |static const ERD $erd = {
+          |$qNameInit
+          |    $typeCode, // typeCode
+          |    0, // numChildren
+          |    ${C}_childrenOffsets,
+          |    NULL, // childrenERDs
+          |    (ERDParseSelf)&${C}_parseSelf,
+          |    (ERDUnparseSelf)&${C}_unparseSelf,
+          |    {.initChoice = NULL}
+          |};
+          |""".stripMargin
+      else
+        s"""|static const ERD $erd = {
+          |$qNameInit
+          |    $typeCode, // typeCode
+          |    0, NULL, NULL, NULL, NULL, {NULL}
+          |};
+          |""".stripMargin
+
     erds += erdDef
   }
 
@@ -571,7 +620,7 @@ class CodeGeneratorState(private val root: ElementBase) {
     } else {
       cStructName(child)
     }
-    val e = child.namedQName.local
+    val e = cName(child)
     val arraySize = arrayMaxOccurs(child)
     val arrayDef = if (arraySize > 0) s"[$arraySize]" else ""
     val indent = if (hasChoice) INDENT else NO_INDENT
@@ -593,7 +642,7 @@ class CodeGeneratorState(private val root: ElementBase) {
   // Adds an element's ERD & offset to its parent element's children ERD & offset computations.
   private def addComputations(child: ElementBase): Unit = {
     val C = structs.top.C
-    val e = child.namedQName.local
+    val e = cName(child)
     val hasArray = arrayMaxOccurs(child) > 0
     val arrayName = s"array_${cStructName(child)}$C"
     val erd = if (hasArray) s"${arrayName}ERD" else erdName(child)
@@ -605,21 +654,21 @@ class CodeGeneratorState(private val root: ElementBase) {
     structs.top.erdComputations += erdComputation
   }
 
-  // Generates an array's ERD, offsets, childrenERDs, initERD, parseSelf, unparseSelf, getArraySize
+  // Generates an array's ERD, childrenOffsets, childrenERDs, initERD, parseSelf, unparseSelf, getArraySize
   private def addArrayImplementation(elem: ElementBase): Unit = {
     val C = structs.top.C
-    val e = elem.namedQName.local
+    val e = cName(elem)
     val arrayName = s"array_${cStructName(elem)}$C"
     val erd = erdName(elem)
     val maxOccurs = elem.maxOccurs
     val minOccurs = elem.minOccurs
     val qNameInit = defineQNameInit(elem)
 
-    // Add the array's ERD, offsets, childrenERDs
+    // Add the array's ERD, childrenOffsets, childrenERDs
     val arrayERD =
       s"""static const $C ${arrayName}_compute_offsets;
          |
-         |static const size_t ${arrayName}_offsets[1] = {
+         |static const size_t ${arrayName}_childrenOffsets[1] = {
          |    (const char *)&${arrayName}_compute_offsets.$e[1] - (const char *)&${arrayName}_compute_offsets.$e[0]
          |};
          |
@@ -631,11 +680,11 @@ class CodeGeneratorState(private val root: ElementBase) {
          |$qNameInit
          |    ARRAY, // typeCode
          |    $maxOccurs, // maxOccurs
-         |    ${arrayName}_offsets, // offsets
-         |    ${arrayName}_childrenERDs, // childrenERDs
-         |    (ERDParseSelf)&${arrayName}_parseSelf, // parseSelf
-         |    (ERDUnparseSelf)&${arrayName}_unparseSelf, // unparseSelf
-         |    {.getArraySize = (GetArraySize)&${arrayName}_getArraySize} // getArraySize
+         |    ${arrayName}_childrenOffsets,
+         |    ${arrayName}_childrenERDs,
+         |    (ERDParseSelf)&${arrayName}_parseSelf,
+         |    (ERDUnparseSelf)&${arrayName}_unparseSelf,
+         |    {.getArraySize = (GetArraySize)&${arrayName}_getArraySize}
          |};
          |""".stripMargin
     erds += arrayERD
@@ -727,22 +776,36 @@ class CodeGeneratorState(private val root: ElementBase) {
 
   // Recursively builds a hopefully unique name using the given StringBuilder
   private def buildName(sc: SchemaComponent, sb: StringBuilder): StringBuilder = {
+    // Append schema component's name
     sc match {
       case eb: ElementBase => sb ++= eb.namedQName.local += '_'
       case gd: GlobalElementDecl => sb ++= gd.namedQName.local += '_'
       case ct: GlobalComplexTypeDef => sb ++= ct.namedQName.local += '_'
       case _ => // don't include other schema components in qualified name
     }
+    // Recursively append parent schema components' names
     sc.optLexicalParent.foreach {
       buildName(_, sb)
     }
     sb
   }
 
+  // Makes any XML identifier a legal C identifier
+  private def makeLegalForC(sb: StringBuilder): Unit = {
+    lazy val legalCharsForC: immutable.Set[Char] =
+      Set('_') ++ ('a' to 'z') ++ ('A' to 'Z') ++ ('0' to '9')
+    // Ensure sb contains only legal characters for C identifiers
+    for (i <- sb.indices) {
+      if (!legalCharsForC.contains(sb.charAt(i))) {
+        sb.setCharAt(i, '_')
+      }
+    }
+  }
+
   // Generates the name part of an ERD definition
   private def defineQNameInit(context: ElementBase): String = {
     val prefix = context.namedQName.prefix.map(p => s""""$p"""").getOrElse("NULL")
-    val local = context.namedQName.local
+    val local = context.namedQName.local // we want XML name not C name
     val nsUri = context.namedQName.namespace.toStringOrNullIfNoNS
     // Optimize away ns declaration if possible, although this approach may not be entirely correct
     val parentNsUri = context.enclosingElements.headOption
@@ -870,19 +933,19 @@ class CodeGeneratorState(private val root: ElementBase) {
   // assumptions to make generating the field access easier:
   // - the expression contains only a relative or absolute path, nothing else (e.g.,
   //   the expression doesn't call any functions or perform any computation)
-  // - we can convert an absolute path to a rootElement()-> indirection
+  // - we can convert an absolute path to a get_infoset()-> indirection
   // - we can convert a relative path beginning with up dirs to a parents-> indirection
   // - we can convert a relative path without any up dirs to an instance-> indirection
   // - we can convert slashes in the path to dots in a C struct field access notation
   private def cStructFieldAccess(expr: String): String = {
     // Strip any namespace prefixes from expr and the root element's local name since
     // C code uses only struct fields' names.
-    val rootName = root.namedQName.local
+    val rootName = cName(root)
     val exprPath = expr.replaceAll("/[^/:]+:", "/").stripPrefix(s"/$rootName")
     val fieldAccess = if (exprPath.startsWith("/")) {
-      // Convert exprPath to a rootElement()-> indirection
+      // Convert exprPath to a get_infoset()-> indirection
       val C = cStructName(root)
-      s"""(($C *)rootElement())->${exprPath.stripPrefix("/")}"""
+      s"""(($C *)get_infoset(false))->${exprPath.stripPrefix("/")}"""
     } else if (exprPath.startsWith("../")) {
       // Split exprPath into the up dirs and after the up dirs
       val afterUpDirs = exprPath.split("\\.\\./").mkString
diff --git a/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/HexBinaryCodeGenerator.scala b/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/HexBinaryCodeGenerator.scala
index b66624376..96d90897a 100644
--- a/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/HexBinaryCodeGenerator.scala
+++ b/daffodil-codegen-c/src/main/scala/org/apache/daffodil/codegen/c/generators/HexBinaryCodeGenerator.scala
@@ -49,7 +49,7 @@ trait HexBinaryCodeGenerator extends BinaryValueCodeGenerator {
   ): Unit = {
     val indent1 = if (cgState.hasChoice) INDENT else NO_INDENT
     val indent2 = if (deref.nonEmpty) INDENT else NO_INDENT
-    val localName = e.namedQName.local
+    val localName = cgState.cName(e)
     val field = s"instance->$localName$deref"
     val intType = e.prefixedLengthElementDecl.optPrimType.get match {
       case PrimType.Byte | PrimType.Short | PrimType.Int | PrimType.Long | PrimType.Integer =>
@@ -93,7 +93,7 @@ trait HexBinaryCodeGenerator extends BinaryValueCodeGenerator {
   ): Unit = {
     val indent1 = if (cgState.hasChoice) INDENT else NO_INDENT
     val indent2 = if (deref.nonEmpty) INDENT else NO_INDENT
-    val localName = e.namedQName.local
+    val localName = cgState.cName(e)
     val field = s"instance->$localName$deref"
     val fieldArray = s"instance->_a_$localName$deref"
     val specifiedLength = e.elementLengthInBitsEv.constValue.get
@@ -124,7 +124,7 @@ trait HexBinaryCodeGenerator extends BinaryValueCodeGenerator {
   ): Unit = {
     val indent1 = if (cgState.hasChoice) INDENT else NO_INDENT
     val indent2 = if (deref.nonEmpty) INDENT else NO_INDENT
-    val localName = e.namedQName.local
+    val localName = cgState.cName(e)
     val field = s"instance->$localName$deref"
     val fixed = s"${localName}_fixed"
     val array = e.fixedValueAsString.grouped(2).mkString("0x", ", 0x", "")
diff --git a/daffodil-codegen-c/src/test/examples/NestedUnion/generated_code.c b/daffodil-codegen-c/src/test/examples/NestedUnion/generated_code.c
index 094551efb..a8ecfe8d8 100644
--- a/daffodil-codegen-c/src/test/examples/NestedUnion/generated_code.c
+++ b/daffodil-codegen-c/src/test/examples/NestedUnion/generated_code.c
@@ -2,7 +2,7 @@
 #include "generated_code.h"
 #include <stdbool.h>    // for false, bool, true
 #include <stddef.h>     // for NULL, size_t
-#include <string.h>     // for memcmp
+#include <string.h>     // for memcmp, memset
 #include "errors.h"     // for Error, PState, UState, ERR_CHOICE_KEY, Error::(anonymous), UNUSED
 #include "parsers.h"    // for alloc_hexBinary, parse_hexBinary, parse_be_float, parse_be_int16, parse_validate_fixed, parse_be_bool32, parse_be_bool16, parse_be_int32, parse_be_uint16, parse_be_uint32, parse_le_bool32, parse_le_int64, parse_le_uint16, parse_le_uint8, parse_be_bool8, parse_be_double, parse_be_int64, parse_be_int8, parse_be_uint64, parse_be_uint8, parse_le_bool16, parse_le_bool8, parse_le_double, parse_le_float, parse_le_int16, parse_le_int32, parse_le_int8, parse_le_uint [...]
 #include "unparsers.h"  // for unparse_hexBinary, unparse_be_float, unparse_be_int16, unparse_validate_fixed, unparse_be_bool32, unparse_be_bool16, unparse_be_int32, unparse_be_uint16, unparse_be_uint32, unparse_le_bool32, unparse_le_int64, unparse_le_uint16, unparse_le_uint8, unparse_be_bool8, unparse_be_double, unparse_be_int64, unparse_be_int8, unparse_be_uint64, unparse_be_uint8, unparse_le_bool16, unparse_le_bool8, unparse_le_double, unparse_le_float, unparse_le_int16, unparse_le_in [...]
@@ -74,7 +74,7 @@ static const ERD c_FooType_ERD = {
 
 static const foo_data_NestedUnionType_ foo_data_NestedUnionType__compute_offsets;
 
-static const size_t foo_data_NestedUnionType__offsets[3] = {
+static const size_t foo_data_NestedUnionType__childrenOffsets[3] = {
     (const char *)&foo_data_NestedUnionType__compute_offsets.a - (const char *)&foo_data_NestedUnionType__compute_offsets,
     (const char *)&foo_data_NestedUnionType__compute_offsets.b - (const char *)&foo_data_NestedUnionType__compute_offsets,
     (const char *)&foo_data_NestedUnionType__compute_offsets.c - (const char *)&foo_data_NestedUnionType__compute_offsets
@@ -94,11 +94,11 @@ static const ERD foo_data_NestedUnionType_ERD = {
     },
     COMPLEX, // typeCode
     3, // numChildren
-    foo_data_NestedUnionType__offsets, // offsets
-    foo_data_NestedUnionType__childrenERDs, // childrenERDs
-    (ERDParseSelf)&foo_data_NestedUnionType__parseSelf, // parseSelf
-    (ERDUnparseSelf)&foo_data_NestedUnionType__unparseSelf, // unparseSelf
-    {NULL} // initChoice
+    foo_data_NestedUnionType__childrenOffsets,
+    foo_data_NestedUnionType__childrenERDs,
+    (ERDParseSelf)&foo_data_NestedUnionType__parseSelf,
+    (ERDUnparseSelf)&foo_data_NestedUnionType__unparseSelf,
+    {.initChoice = NULL}
 };
 
 static const ERD x_BarType_ERD = {
@@ -133,7 +133,7 @@ static const ERD z_BarType_ERD = {
 
 static const bar_data_NestedUnionType_ bar_data_NestedUnionType__compute_offsets;
 
-static const size_t bar_data_NestedUnionType__offsets[3] = {
+static const size_t bar_data_NestedUnionType__childrenOffsets[3] = {
     (const char *)&bar_data_NestedUnionType__compute_offsets.x - (const char *)&bar_data_NestedUnionType__compute_offsets,
     (const char *)&bar_data_NestedUnionType__compute_offsets.y - (const char *)&bar_data_NestedUnionType__compute_offsets,
     (const char *)&bar_data_NestedUnionType__compute_offsets.z - (const char *)&bar_data_NestedUnionType__compute_offsets
@@ -153,16 +153,16 @@ static const ERD bar_data_NestedUnionType_ERD = {
     },
     COMPLEX, // typeCode
     3, // numChildren
-    bar_data_NestedUnionType__offsets, // offsets
-    bar_data_NestedUnionType__childrenERDs, // childrenERDs
-    (ERDParseSelf)&bar_data_NestedUnionType__parseSelf, // parseSelf
-    (ERDUnparseSelf)&bar_data_NestedUnionType__unparseSelf, // unparseSelf
-    {NULL} // initChoice
+    bar_data_NestedUnionType__childrenOffsets,
+    bar_data_NestedUnionType__childrenERDs,
+    (ERDParseSelf)&bar_data_NestedUnionType__parseSelf,
+    (ERDUnparseSelf)&bar_data_NestedUnionType__unparseSelf,
+    {.initChoice = NULL}
 };
 
 static const data_NestedUnionType_ data_NestedUnionType__compute_offsets;
 
-static const size_t data_NestedUnionType__offsets[3] = {
+static const size_t data_NestedUnionType__childrenOffsets[3] = {
     (const char *)&data_NestedUnionType__compute_offsets._choice - (const char *)&data_NestedUnionType__compute_offsets,
     (const char *)&data_NestedUnionType__compute_offsets.foo - (const char *)&data_NestedUnionType__compute_offsets,
     (const char *)&data_NestedUnionType__compute_offsets.bar - (const char *)&data_NestedUnionType__compute_offsets
@@ -182,16 +182,16 @@ static const ERD data_NestedUnionType_ERD = {
     },
     COMPLEX, // typeCode
     2, // numChildren
-    data_NestedUnionType__offsets, // offsets
-    data_NestedUnionType__childrenERDs, // childrenERDs
-    (ERDParseSelf)&data_NestedUnionType__parseSelf, // parseSelf
-    (ERDUnparseSelf)&data_NestedUnionType__unparseSelf, // unparseSelf
-    {(InitChoiceRD)&data_NestedUnionType__initChoice} // initChoice
+    data_NestedUnionType__childrenOffsets,
+    data_NestedUnionType__childrenERDs,
+    (ERDParseSelf)&data_NestedUnionType__parseSelf,
+    (ERDUnparseSelf)&data_NestedUnionType__unparseSelf,
+    {.initChoice = (InitChoiceRD)&data_NestedUnionType__initChoice}
 };
 
 static const NestedUnion_ NestedUnion__compute_offsets;
 
-static const size_t NestedUnion__offsets[2] = {
+static const size_t NestedUnion__childrenOffsets[2] = {
     (const char *)&NestedUnion__compute_offsets.tag - (const char *)&NestedUnion__compute_offsets,
     (const char *)&NestedUnion__compute_offsets.data - (const char *)&NestedUnion__compute_offsets
 };
@@ -209,11 +209,11 @@ static const ERD NestedUnion_ERD = {
     },
     COMPLEX, // typeCode
     2, // numChildren
-    NestedUnion__offsets, // offsets
-    NestedUnion__childrenERDs, // childrenERDs
-    (ERDParseSelf)&NestedUnion__parseSelf, // parseSelf
-    (ERDUnparseSelf)&NestedUnion__unparseSelf, // unparseSelf
-    {NULL} // initChoice
+    NestedUnion__childrenOffsets,
+    NestedUnion__childrenERDs,
+    (ERDParseSelf)&NestedUnion__parseSelf,
+    (ERDUnparseSelf)&NestedUnion__unparseSelf,
+    {.initChoice = NULL}
 };
 
 // Initialize, parse, and unparse nodes of the infoset
@@ -387,17 +387,21 @@ NestedUnion__unparseSelf(const NestedUnion_ *instance, UState *ustate)
     if (ustate->error) return;
 }
 
-// Return a root element for parsing or unparsing the infoset
+// Get an infoset (optionally clearing it first) for parsing/walking
 
 InfosetBase *
-rootElement(void)
+get_infoset(bool clear_infoset)
 {
-    static bool initialized;
-    static NestedUnion_ root;
-    if (!initialized)
+    static NestedUnion_ infoset;
+
+    if (clear_infoset)
     {
-        NestedUnion__initERD(&root, (InfosetBase *)&root);
-        initialized = true;
+        // If your infoset contains hexBinary prefixed length elements,
+        // you may want to walk infoset first to free their malloc'ed
+        // storage - we are not handling that case for now...
+        memset(&infoset, 0, sizeof(infoset));
+        NestedUnion__initERD(&infoset, (InfosetBase *)&infoset);
     }
-    return &root._base;
+
+    return &infoset._base;
 }
diff --git a/daffodil-codegen-c/src/test/examples/ex_nums/generated_code.c b/daffodil-codegen-c/src/test/examples/ex_nums/generated_code.c
index 8e84102cd..29228f267 100644
--- a/daffodil-codegen-c/src/test/examples/ex_nums/generated_code.c
+++ b/daffodil-codegen-c/src/test/examples/ex_nums/generated_code.c
@@ -2,7 +2,7 @@
 #include "generated_code.h"
 #include <stdbool.h>    // for false, bool, true
 #include <stddef.h>     // for NULL, size_t
-#include <string.h>     // for memcmp
+#include <string.h>     // for memcmp, memset
 #include "errors.h"     // for Error, PState, UState, ERR_CHOICE_KEY, Error::(anonymous), UNUSED
 #include "parsers.h"    // for alloc_hexBinary, parse_hexBinary, parse_be_float, parse_be_int16, parse_validate_fixed, parse_be_bool32, parse_be_bool16, parse_be_int32, parse_be_uint16, parse_be_uint32, parse_le_bool32, parse_le_int64, parse_le_uint16, parse_le_uint8, parse_be_bool8, parse_be_double, parse_be_int64, parse_be_int8, parse_be_uint64, parse_be_uint8, parse_le_bool16, parse_le_bool8, parse_le_double, parse_le_float, parse_le_int16, parse_le_int32, parse_le_int8, parse_le_uint [...]
 #include "unparsers.h"  // for unparse_hexBinary, unparse_be_float, unparse_be_int16, unparse_validate_fixed, unparse_be_bool32, unparse_be_bool16, unparse_be_int32, unparse_be_uint16, unparse_be_uint32, unparse_le_bool32, unparse_le_int64, unparse_le_uint16, unparse_le_uint8, unparse_be_bool8, unparse_be_double, unparse_be_int64, unparse_be_int8, unparse_be_uint64, unparse_be_uint8, unparse_le_bool16, unparse_le_bool8, unparse_le_double, unparse_le_float, unparse_le_int16, unparse_le_in [...]
@@ -50,7 +50,7 @@ static const ERD be_boolean_array_ex_nums_ERD = {
 
 static const array_ex_nums_ array_be_boolean_array_ex_nums_array_ex_nums__compute_offsets;
 
-static const size_t array_be_boolean_array_ex_nums_array_ex_nums__offsets[1] = {
+static const size_t array_be_boolean_array_ex_nums_array_ex_nums__childrenOffsets[1] = {
     (const char *)&array_be_boolean_array_ex_nums_array_ex_nums__compute_offsets.be_boolean[1] - (const char *)&array_be_boolean_array_ex_nums_array_ex_nums__compute_offsets.be_boolean[0]
 };
 
@@ -66,11 +66,11 @@ static const ERD array_be_boolean_array_ex_nums_array_ex_nums_ERD = {
     },
     ARRAY, // typeCode
     2, // maxOccurs
-    array_be_boolean_array_ex_nums_array_ex_nums__offsets, // offsets
-    array_be_boolean_array_ex_nums_array_ex_nums__childrenERDs, // childrenERDs
-    (ERDParseSelf)&array_be_boolean_array_ex_nums_array_ex_nums__parseSelf, // parseSelf
-    (ERDUnparseSelf)&array_be_boolean_array_ex_nums_array_ex_nums__unparseSelf, // unparseSelf
-    {.getArraySize = (GetArraySize)&array_be_boolean_array_ex_nums_array_ex_nums__getArraySize} // getArraySize
+    array_be_boolean_array_ex_nums_array_ex_nums__childrenOffsets,
+    array_be_boolean_array_ex_nums_array_ex_nums__childrenERDs,
+    (ERDParseSelf)&array_be_boolean_array_ex_nums_array_ex_nums__parseSelf,
+    (ERDUnparseSelf)&array_be_boolean_array_ex_nums_array_ex_nums__unparseSelf,
+    {.getArraySize = (GetArraySize)&array_be_boolean_array_ex_nums_array_ex_nums__getArraySize}
 };
 
 static const ERD be_float_array_ex_nums_ERD = {
@@ -85,7 +85,7 @@ static const ERD be_float_array_ex_nums_ERD = {
 
 static const array_ex_nums_ array_be_float_array_ex_nums_array_ex_nums__compute_offsets;
 
-static const size_t array_be_float_array_ex_nums_array_ex_nums__offsets[1] = {
+static const size_t array_be_float_array_ex_nums_array_ex_nums__childrenOffsets[1] = {
     (const char *)&array_be_float_array_ex_nums_array_ex_nums__compute_offsets.be_float[1] - (const char *)&array_be_float_array_ex_nums_array_ex_nums__compute_offsets.be_float[0]
 };
 
@@ -101,11 +101,11 @@ static const ERD array_be_float_array_ex_nums_array_ex_nums_ERD = {
     },
     ARRAY, // typeCode
     3, // maxOccurs
-    array_be_float_array_ex_nums_array_ex_nums__offsets, // offsets
-    array_be_float_array_ex_nums_array_ex_nums__childrenERDs, // childrenERDs
-    (ERDParseSelf)&array_be_float_array_ex_nums_array_ex_nums__parseSelf, // parseSelf
-    (ERDUnparseSelf)&array_be_float_array_ex_nums_array_ex_nums__unparseSelf, // unparseSelf
-    {.getArraySize = (GetArraySize)&array_be_float_array_ex_nums_array_ex_nums__getArraySize} // getArraySize
+    array_be_float_array_ex_nums_array_ex_nums__childrenOffsets,
+    array_be_float_array_ex_nums_array_ex_nums__childrenERDs,
+    (ERDParseSelf)&array_be_float_array_ex_nums_array_ex_nums__parseSelf,
+    (ERDUnparseSelf)&array_be_float_array_ex_nums_array_ex_nums__unparseSelf,
+    {.getArraySize = (GetArraySize)&array_be_float_array_ex_nums_array_ex_nums__getArraySize}
 };
 
 static const ERD be_int16_array_ex_nums_ERD = {
@@ -120,7 +120,7 @@ static const ERD be_int16_array_ex_nums_ERD = {
 
 static const array_ex_nums_ array_be_int16_array_ex_nums_array_ex_nums__compute_offsets;
 
-static const size_t array_be_int16_array_ex_nums_array_ex_nums__offsets[1] = {
+static const size_t array_be_int16_array_ex_nums_array_ex_nums__childrenOffsets[1] = {
     (const char *)&array_be_int16_array_ex_nums_array_ex_nums__compute_offsets.be_int16[1] - (const char *)&array_be_int16_array_ex_nums_array_ex_nums__compute_offsets.be_int16[0]
 };
 
@@ -136,11 +136,11 @@ static const ERD array_be_int16_array_ex_nums_array_ex_nums_ERD = {
     },
     ARRAY, // typeCode
     3, // maxOccurs
-    array_be_int16_array_ex_nums_array_ex_nums__offsets, // offsets
-    array_be_int16_array_ex_nums_array_ex_nums__childrenERDs, // childrenERDs
-    (ERDParseSelf)&array_be_int16_array_ex_nums_array_ex_nums__parseSelf, // parseSelf
-    (ERDUnparseSelf)&array_be_int16_array_ex_nums_array_ex_nums__unparseSelf, // unparseSelf
-    {.getArraySize = (GetArraySize)&array_be_int16_array_ex_nums_array_ex_nums__getArraySize} // getArraySize
+    array_be_int16_array_ex_nums_array_ex_nums__childrenOffsets,
+    array_be_int16_array_ex_nums_array_ex_nums__childrenERDs,
+    (ERDParseSelf)&array_be_int16_array_ex_nums_array_ex_nums__parseSelf,
+    (ERDUnparseSelf)&array_be_int16_array_ex_nums_array_ex_nums__unparseSelf,
+    {.getArraySize = (GetArraySize)&array_be_int16_array_ex_nums_array_ex_nums__getArraySize}
 };
 
 static const ERD hexBinary2_array_ex_nums_ERD = {
@@ -155,7 +155,7 @@ static const ERD hexBinary2_array_ex_nums_ERD = {
 
 static const array_ex_nums_ array_hexBinary2_array_ex_nums_array_ex_nums__compute_offsets;
 
-static const size_t array_hexBinary2_array_ex_nums_array_ex_nums__offsets[1] = {
+static const size_t array_hexBinary2_array_ex_nums_array_ex_nums__childrenOffsets[1] = {
     (const char *)&array_hexBinary2_array_ex_nums_array_ex_nums__compute_offsets.hexBinary2[1] - (const char *)&array_hexBinary2_array_ex_nums_array_ex_nums__compute_offsets.hexBinary2[0]
 };
 
@@ -171,11 +171,11 @@ static const ERD array_hexBinary2_array_ex_nums_array_ex_nums_ERD = {
     },
     ARRAY, // typeCode
     3, // maxOccurs
-    array_hexBinary2_array_ex_nums_array_ex_nums__offsets, // offsets
-    array_hexBinary2_array_ex_nums_array_ex_nums__childrenERDs, // childrenERDs
-    (ERDParseSelf)&array_hexBinary2_array_ex_nums_array_ex_nums__parseSelf, // parseSelf
-    (ERDUnparseSelf)&array_hexBinary2_array_ex_nums_array_ex_nums__unparseSelf, // unparseSelf
-    {.getArraySize = (GetArraySize)&array_hexBinary2_array_ex_nums_array_ex_nums__getArraySize} // getArraySize
+    array_hexBinary2_array_ex_nums_array_ex_nums__childrenOffsets,
+    array_hexBinary2_array_ex_nums_array_ex_nums__childrenERDs,
+    (ERDParseSelf)&array_hexBinary2_array_ex_nums_array_ex_nums__parseSelf,
+    (ERDUnparseSelf)&array_hexBinary2_array_ex_nums_array_ex_nums__unparseSelf,
+    {.getArraySize = (GetArraySize)&array_hexBinary2_array_ex_nums_array_ex_nums__getArraySize}
 };
 
 static const ERD hexBinaryPrefixed_array_ex_nums_ERD = {
@@ -190,7 +190,7 @@ static const ERD hexBinaryPrefixed_array_ex_nums_ERD = {
 
 static const array_ex_nums_ array_hexBinaryPrefixed_array_ex_nums_array_ex_nums__compute_offsets;
 
-static const size_t array_hexBinaryPrefixed_array_ex_nums_array_ex_nums__offsets[1] = {
+static const size_t array_hexBinaryPrefixed_array_ex_nums_array_ex_nums__childrenOffsets[1] = {
     (const char *)&array_hexBinaryPrefixed_array_ex_nums_array_ex_nums__compute_offsets.hexBinaryPrefixed[1] - (const char *)&array_hexBinaryPrefixed_array_ex_nums_array_ex_nums__compute_offsets.hexBinaryPrefixed[0]
 };
 
@@ -206,16 +206,16 @@ static const ERD array_hexBinaryPrefixed_array_ex_nums_array_ex_nums_ERD = {
     },
     ARRAY, // typeCode
     3, // maxOccurs
-    array_hexBinaryPrefixed_array_ex_nums_array_ex_nums__offsets, // offsets
-    array_hexBinaryPrefixed_array_ex_nums_array_ex_nums__childrenERDs, // childrenERDs
-    (ERDParseSelf)&array_hexBinaryPrefixed_array_ex_nums_array_ex_nums__parseSelf, // parseSelf
-    (ERDUnparseSelf)&array_hexBinaryPrefixed_array_ex_nums_array_ex_nums__unparseSelf, // unparseSelf
-    {.getArraySize = (GetArraySize)&array_hexBinaryPrefixed_array_ex_nums_array_ex_nums__getArraySize} // getArraySize
+    array_hexBinaryPrefixed_array_ex_nums_array_ex_nums__childrenOffsets,
+    array_hexBinaryPrefixed_array_ex_nums_array_ex_nums__childrenERDs,
+    (ERDParseSelf)&array_hexBinaryPrefixed_array_ex_nums_array_ex_nums__parseSelf,
+    (ERDUnparseSelf)&array_hexBinaryPrefixed_array_ex_nums_array_ex_nums__unparseSelf,
+    {.getArraySize = (GetArraySize)&array_hexBinaryPrefixed_array_ex_nums_array_ex_nums__getArraySize}
 };
 
 static const array_ex_nums_ array_ex_nums__compute_offsets;
 
-static const size_t array_ex_nums__offsets[5] = {
+static const size_t array_ex_nums__childrenOffsets[5] = {
     (const char *)&array_ex_nums__compute_offsets.be_boolean[0] - (const char *)&array_ex_nums__compute_offsets,
     (const char *)&array_ex_nums__compute_offsets.be_float[0] - (const char *)&array_ex_nums__compute_offsets,
     (const char *)&array_ex_nums__compute_offsets.be_int16[0] - (const char *)&array_ex_nums__compute_offsets,
@@ -239,11 +239,11 @@ static const ERD array_ex_nums_ERD = {
     },
     COMPLEX, // typeCode
     5, // numChildren
-    array_ex_nums__offsets, // offsets
-    array_ex_nums__childrenERDs, // childrenERDs
-    (ERDParseSelf)&array_ex_nums__parseSelf, // parseSelf
-    (ERDUnparseSelf)&array_ex_nums__unparseSelf, // unparseSelf
-    {NULL} // initChoice
+    array_ex_nums__childrenOffsets,
+    array_ex_nums__childrenERDs,
+    (ERDParseSelf)&array_ex_nums__parseSelf,
+    (ERDUnparseSelf)&array_ex_nums__unparseSelf,
+    {.initChoice = NULL}
 };
 
 static const ERD be_bool16_bigEndian_ex_nums_ERD = {
@@ -408,7 +408,7 @@ static const ERD hexBinaryPrefixed_bigEndian_ex_nums_ERD = {
 
 static const bigEndian_ex_nums_ bigEndian_ex_nums__compute_offsets;
 
-static const size_t bigEndian_ex_nums__offsets[16] = {
+static const size_t bigEndian_ex_nums__childrenOffsets[16] = {
     (const char *)&bigEndian_ex_nums__compute_offsets.be_bool16 - (const char *)&bigEndian_ex_nums__compute_offsets,
     (const char *)&bigEndian_ex_nums__compute_offsets.be_boolean - (const char *)&bigEndian_ex_nums__compute_offsets,
     (const char *)&bigEndian_ex_nums__compute_offsets.be_double - (const char *)&bigEndian_ex_nums__compute_offsets,
@@ -454,11 +454,11 @@ static const ERD bigEndian_ex_nums_ERD = {
     },
     COMPLEX, // typeCode
     16, // numChildren
-    bigEndian_ex_nums__offsets, // offsets
-    bigEndian_ex_nums__childrenERDs, // childrenERDs
-    (ERDParseSelf)&bigEndian_ex_nums__parseSelf, // parseSelf
-    (ERDUnparseSelf)&bigEndian_ex_nums__unparseSelf, // unparseSelf
-    {NULL} // initChoice
+    bigEndian_ex_nums__childrenOffsets,
+    bigEndian_ex_nums__childrenERDs,
+    (ERDParseSelf)&bigEndian_ex_nums__parseSelf,
+    (ERDUnparseSelf)&bigEndian_ex_nums__unparseSelf,
+    {.initChoice = NULL}
 };
 
 static const ERD le_bool16_littleEndian_ex_nums_ERD = {
@@ -623,7 +623,7 @@ static const ERD hexBinaryPrefixed_littleEndian_ex_nums_ERD = {
 
 static const littleEndian_ex_nums_ littleEndian_ex_nums__compute_offsets;
 
-static const size_t littleEndian_ex_nums__offsets[16] = {
+static const size_t littleEndian_ex_nums__childrenOffsets[16] = {
     (const char *)&littleEndian_ex_nums__compute_offsets.le_bool16 - (const char *)&littleEndian_ex_nums__compute_offsets,
     (const char *)&littleEndian_ex_nums__compute_offsets.le_boolean - (const char *)&littleEndian_ex_nums__compute_offsets,
     (const char *)&littleEndian_ex_nums__compute_offsets.le_double - (const char *)&littleEndian_ex_nums__compute_offsets,
@@ -669,11 +669,11 @@ static const ERD littleEndian_ex_nums_ERD = {
     },
     COMPLEX, // typeCode
     16, // numChildren
-    littleEndian_ex_nums__offsets, // offsets
-    littleEndian_ex_nums__childrenERDs, // childrenERDs
-    (ERDParseSelf)&littleEndian_ex_nums__parseSelf, // parseSelf
-    (ERDUnparseSelf)&littleEndian_ex_nums__unparseSelf, // unparseSelf
-    {NULL} // initChoice
+    littleEndian_ex_nums__childrenOffsets,
+    littleEndian_ex_nums__childrenERDs,
+    (ERDParseSelf)&littleEndian_ex_nums__parseSelf,
+    (ERDUnparseSelf)&littleEndian_ex_nums__unparseSelf,
+    {.initChoice = NULL}
 };
 
 static const ERD boolean_false_fixed_ex_nums_ERD = {
@@ -758,7 +758,7 @@ static const ERD hexBinaryPrefixed_ab_fixed_ex_nums_ERD = {
 
 static const fixed_ex_nums_ fixed_ex_nums__compute_offsets;
 
-static const size_t fixed_ex_nums__offsets[8] = {
+static const size_t fixed_ex_nums__childrenOffsets[8] = {
     (const char *)&fixed_ex_nums__compute_offsets.boolean_false - (const char *)&fixed_ex_nums__compute_offsets,
     (const char *)&fixed_ex_nums__compute_offsets.boolean_true - (const char *)&fixed_ex_nums__compute_offsets,
     (const char *)&fixed_ex_nums__compute_offsets.double_3 - (const char *)&fixed_ex_nums__compute_offsets,
@@ -788,16 +788,16 @@ static const ERD fixed_ex_nums_ERD = {
     },
     COMPLEX, // typeCode
     8, // numChildren
-    fixed_ex_nums__offsets, // offsets
-    fixed_ex_nums__childrenERDs, // childrenERDs
-    (ERDParseSelf)&fixed_ex_nums__parseSelf, // parseSelf
-    (ERDUnparseSelf)&fixed_ex_nums__unparseSelf, // unparseSelf
-    {NULL} // initChoice
+    fixed_ex_nums__childrenOffsets,
+    fixed_ex_nums__childrenERDs,
+    (ERDParseSelf)&fixed_ex_nums__parseSelf,
+    (ERDUnparseSelf)&fixed_ex_nums__unparseSelf,
+    {.initChoice = NULL}
 };
 
 static const ex_nums_ ex_nums__compute_offsets;
 
-static const size_t ex_nums__offsets[4] = {
+static const size_t ex_nums__childrenOffsets[4] = {
     (const char *)&ex_nums__compute_offsets.array - (const char *)&ex_nums__compute_offsets,
     (const char *)&ex_nums__compute_offsets.bigEndian - (const char *)&ex_nums__compute_offsets,
     (const char *)&ex_nums__compute_offsets.littleEndian - (const char *)&ex_nums__compute_offsets,
@@ -819,11 +819,11 @@ static const ERD ex_nums_ERD = {
     },
     COMPLEX, // typeCode
     4, // numChildren
-    ex_nums__offsets, // offsets
-    ex_nums__childrenERDs, // childrenERDs
-    (ERDParseSelf)&ex_nums__parseSelf, // parseSelf
-    (ERDUnparseSelf)&ex_nums__unparseSelf, // unparseSelf
-    {NULL} // initChoice
+    ex_nums__childrenOffsets,
+    ex_nums__childrenERDs,
+    (ERDParseSelf)&ex_nums__parseSelf,
+    (ERDUnparseSelf)&ex_nums__unparseSelf,
+    {.initChoice = NULL}
 };
 
 // Initialize, parse, and unparse nodes of the infoset
@@ -1411,17 +1411,21 @@ ex_nums__unparseSelf(const ex_nums_ *instance, UState *ustate)
     if (ustate->error) return;
 }
 
-// Return a root element for parsing or unparsing the infoset
+// Get an infoset (optionally clearing it first) for parsing/walking
 
 InfosetBase *
-rootElement(void)
+get_infoset(bool clear_infoset)
 {
-    static bool initialized;
-    static ex_nums_ root;
-    if (!initialized)
+    static ex_nums_ infoset;
+
+    if (clear_infoset)
     {
-        ex_nums__initERD(&root, (InfosetBase *)&root);
-        initialized = true;
+        // If your infoset contains hexBinary prefixed length elements,
+        // you may want to walk infoset first to free their malloc'ed
+        // storage - we are not handling that case for now...
+        memset(&infoset, 0, sizeof(infoset));
+        ex_nums__initERD(&infoset, (InfosetBase *)&infoset);
     }
-    return &root._base;
+
+    return &infoset._base;
 }
diff --git a/daffodil-codegen-c/src/test/examples/padtest/generated_code.c b/daffodil-codegen-c/src/test/examples/padtest/generated_code.c
index 62d41b30e..7f859e89f 100644
--- a/daffodil-codegen-c/src/test/examples/padtest/generated_code.c
+++ b/daffodil-codegen-c/src/test/examples/padtest/generated_code.c
@@ -2,7 +2,7 @@
 #include "generated_code.h"
 #include <stdbool.h>    // for false, bool, true
 #include <stddef.h>     // for NULL, size_t
-#include <string.h>     // for memcmp
+#include <string.h>     // for memcmp, memset
 #include "errors.h"     // for Error, PState, UState, ERR_CHOICE_KEY, Error::(anonymous), UNUSED
 #include "parsers.h"    // for alloc_hexBinary, parse_hexBinary, parse_be_float, parse_be_int16, parse_validate_fixed, parse_be_bool32, parse_be_bool16, parse_be_int32, parse_be_uint16, parse_be_uint32, parse_le_bool32, parse_le_int64, parse_le_uint16, parse_le_uint8, parse_be_bool8, parse_be_double, parse_be_int64, parse_be_int8, parse_be_uint64, parse_be_uint8, parse_le_bool16, parse_le_bool8, parse_le_double, parse_le_float, parse_le_int16, parse_le_int32, parse_le_int8, parse_le_uint [...]
 #include "unparsers.h"  // for unparse_hexBinary, unparse_be_float, unparse_be_int16, unparse_validate_fixed, unparse_be_bool32, unparse_be_bool16, unparse_be_int32, unparse_be_uint16, unparse_be_uint32, unparse_le_bool32, unparse_le_int64, unparse_le_uint16, unparse_le_uint8, unparse_be_bool8, unparse_be_double, unparse_be_int64, unparse_be_int8, unparse_be_uint64, unparse_be_uint8, unparse_le_bool16, unparse_le_bool8, unparse_le_double, unparse_le_float, unparse_le_int16, unparse_le_in [...]
@@ -29,7 +29,7 @@ static const ERD opaque_padhexbinary_ERD = {
 
 static const padhexbinary_padtest_ padhexbinary_padtest__compute_offsets;
 
-static const size_t padhexbinary_padtest__offsets[1] = {
+static const size_t padhexbinary_padtest__childrenOffsets[1] = {
     (const char *)&padhexbinary_padtest__compute_offsets.opaque - (const char *)&padhexbinary_padtest__compute_offsets
 };
 
@@ -45,11 +45,11 @@ static const ERD padhexbinary_padtest_ERD = {
     },
     COMPLEX, // typeCode
     1, // numChildren
-    padhexbinary_padtest__offsets, // offsets
-    padhexbinary_padtest__childrenERDs, // childrenERDs
-    (ERDParseSelf)&padhexbinary_padtest__parseSelf, // parseSelf
-    (ERDUnparseSelf)&padhexbinary_padtest__unparseSelf, // unparseSelf
-    {NULL} // initChoice
+    padhexbinary_padtest__childrenOffsets,
+    padhexbinary_padtest__childrenERDs,
+    (ERDParseSelf)&padhexbinary_padtest__parseSelf,
+    (ERDUnparseSelf)&padhexbinary_padtest__unparseSelf,
+    {.initChoice = NULL}
 };
 
 static const ERD after_padtest_ERD = {
@@ -64,7 +64,7 @@ static const ERD after_padtest_ERD = {
 
 static const padtest_ padtest__compute_offsets;
 
-static const size_t padtest__offsets[2] = {
+static const size_t padtest__childrenOffsets[2] = {
     (const char *)&padtest__compute_offsets.padhexbinary - (const char *)&padtest__compute_offsets,
     (const char *)&padtest__compute_offsets.after - (const char *)&padtest__compute_offsets
 };
@@ -82,11 +82,11 @@ static const ERD padtest_ERD = {
     },
     COMPLEX, // typeCode
     2, // numChildren
-    padtest__offsets, // offsets
-    padtest__childrenERDs, // childrenERDs
-    (ERDParseSelf)&padtest__parseSelf, // parseSelf
-    (ERDUnparseSelf)&padtest__unparseSelf, // unparseSelf
-    {NULL} // initChoice
+    padtest__childrenOffsets,
+    padtest__childrenERDs,
+    (ERDParseSelf)&padtest__parseSelf,
+    (ERDUnparseSelf)&padtest__unparseSelf,
+    {.initChoice = NULL}
 };
 
 // Initialize, parse, and unparse nodes of the infoset
@@ -152,17 +152,21 @@ padtest__unparseSelf(const padtest_ *instance, UState *ustate)
     if (ustate->error) return;
 }
 
-// Return a root element for parsing or unparsing the infoset
+// Get an infoset (optionally clearing it first) for parsing/walking
 
 InfosetBase *
-rootElement(void)
+get_infoset(bool clear_infoset)
 {
-    static bool initialized;
-    static padtest_ root;
-    if (!initialized)
+    static padtest_ infoset;
+
+    if (clear_infoset)
     {
-        padtest__initERD(&root, (InfosetBase *)&root);
-        initialized = true;
+        // If your infoset contains hexBinary prefixed length elements,
+        // you may want to walk infoset first to free their malloc'ed
+        // storage - we are not handling that case for now...
+        memset(&infoset, 0, sizeof(infoset));
+        padtest__initERD(&infoset, (InfosetBase *)&infoset);
     }
-    return &root._base;
+
+    return &infoset._base;
 }
diff --git a/daffodil-codegen-c/src/test/examples/simple/generated_code.c b/daffodil-codegen-c/src/test/examples/simple/generated_code.c
new file mode 100644
index 000000000..5bde5a4cb
--- /dev/null
+++ b/daffodil-codegen-c/src/test/examples/simple/generated_code.c
@@ -0,0 +1,79 @@
+// clang-format off
+#include "generated_code.h"
+#include <stdbool.h>    // for false, bool, true
+#include <stddef.h>     // for NULL, size_t
+#include <string.h>     // for memcmp, memset
+#include "errors.h"     // for Error, PState, UState, ERR_CHOICE_KEY, Error::(anonymous), UNUSED
+#include "parsers.h"    // for alloc_hexBinary, parse_hexBinary, parse_be_float, parse_be_int16, parse_validate_fixed, parse_be_bool32, parse_be_bool16, parse_be_int32, parse_be_uint16, parse_be_uint32, parse_le_bool32, parse_le_int64, parse_le_uint16, parse_le_uint8, parse_be_bool8, parse_be_double, parse_be_int64, parse_be_int8, parse_be_uint64, parse_be_uint8, parse_le_bool16, parse_le_bool8, parse_le_double, parse_le_float, parse_le_int16, parse_le_int32, parse_le_int8, parse_le_uint [...]
+#include "unparsers.h"  // for unparse_hexBinary, unparse_be_float, unparse_be_int16, unparse_validate_fixed, unparse_be_bool32, unparse_be_bool16, unparse_be_int32, unparse_be_uint16, unparse_be_uint32, unparse_le_bool32, unparse_le_int64, unparse_le_uint16, unparse_le_uint8, unparse_be_bool8, unparse_be_double, unparse_be_int64, unparse_be_int8, unparse_be_uint64, unparse_be_uint8, unparse_le_bool16, unparse_le_bool8, unparse_le_double, unparse_le_float, unparse_le_int16, unparse_le_in [...]
+// clang-format on
+
+// Declare prototypes for easier compilation
+
+static void simple_byte__parseSelf(simple_byte_ *instance, PState *pstate);
+static void simple_byte__unparseSelf(const simple_byte_ *instance, UState *ustate);
+
+// Define metadata for the infoset
+
+static const simple_byte_ simple_byte__compute_offsets;
+
+static const size_t simple_byte__childrenOffsets[1] = {
+    (const char *)&simple_byte__compute_offsets.simple_byte - (const char *)&simple_byte__compute_offsets
+};
+
+static const ERD simple_byte_ERD = {
+    {
+        "si", // namedQName.prefix
+        "simple-byte", // namedQName.local
+        "urn:simple", // namedQName.ns
+    },
+    PRIMITIVE_INT8, // typeCode
+    0, // numChildren
+    simple_byte__childrenOffsets,
+    NULL, // childrenERDs
+    (ERDParseSelf)&simple_byte__parseSelf,
+    (ERDUnparseSelf)&simple_byte__unparseSelf,
+    {.initChoice = NULL}
+};
+
+// Initialize, parse, and unparse nodes of the infoset
+
+static void
+simple_byte__initERD(simple_byte_ *instance, InfosetBase *parent)
+{
+    instance->_base.erd = &simple_byte_ERD;
+    instance->_base.parent = parent;
+}
+
+static void
+simple_byte__parseSelf(simple_byte_ *instance, PState *pstate)
+{
+    parse_be_int8(&instance->simple_byte, 8, pstate);
+    if (pstate->error) return;
+}
+
+static void
+simple_byte__unparseSelf(const simple_byte_ *instance, UState *ustate)
+{
+    unparse_be_int8(instance->simple_byte, 8, ustate);
+    if (ustate->error) return;
+}
+
+// Get an infoset (optionally clearing it first) for parsing/walking
+
+InfosetBase *
+get_infoset(bool clear_infoset)
+{
+    static simple_byte_ infoset;
+
+    if (clear_infoset)
+    {
+        // If your infoset contains hexBinary prefixed length elements,
+        // you may want to walk infoset first to free their malloc'ed
+        // storage - we are not handling that case for now...
+        memset(&infoset, 0, sizeof(infoset));
+        simple_byte__initERD(&infoset, (InfosetBase *)&infoset);
+    }
+
+    return &infoset._base;
+}
diff --git a/daffodil-codegen-c/src/test/examples/simple/generated_code.h b/daffodil-codegen-c/src/test/examples/simple/generated_code.h
new file mode 100644
index 000000000..5f0ebb5c5
--- /dev/null
+++ b/daffodil-codegen-c/src/test/examples/simple/generated_code.h
@@ -0,0 +1,19 @@
+#ifndef GENERATED_CODE_H
+#define GENERATED_CODE_H
+
+// clang-format off
+#include <stdbool.h>  // for bool
+#include <stddef.h>   // for size_t
+#include <stdint.h>   // for uint8_t, int16_t, int32_t, int64_t, uint32_t, int8_t, uint16_t, uint64_t
+#include "infoset.h"  // for InfosetBase, HexBinary
+// clang-format on
+
+// Define infoset structures
+
+typedef struct simple_byte_
+{
+    InfosetBase _base;
+    int8_t      simple_byte;
+} simple_byte_;
+
+#endif // GENERATED_CODE_H
diff --git a/daffodil-codegen-c/src/test/examples/variablelen/generated_code.c b/daffodil-codegen-c/src/test/examples/variablelen/generated_code.c
index 94a5273fb..2e87beb13 100644
--- a/daffodil-codegen-c/src/test/examples/variablelen/generated_code.c
+++ b/daffodil-codegen-c/src/test/examples/variablelen/generated_code.c
@@ -2,7 +2,7 @@
 #include "generated_code.h"
 #include <stdbool.h>    // for false, bool, true
 #include <stddef.h>     // for NULL, size_t
-#include <string.h>     // for memcmp
+#include <string.h>     // for memcmp, memset
 #include "errors.h"     // for Error, PState, UState, ERR_CHOICE_KEY, Error::(anonymous), UNUSED
 #include "parsers.h"    // for alloc_hexBinary, parse_hexBinary, parse_be_float, parse_be_int16, parse_validate_fixed, parse_be_bool32, parse_be_bool16, parse_be_int32, parse_be_uint16, parse_be_uint32, parse_le_bool32, parse_le_int64, parse_le_uint16, parse_le_uint8, parse_be_bool8, parse_be_double, parse_be_int64, parse_be_int8, parse_be_uint64, parse_be_uint8, parse_le_bool16, parse_le_bool8, parse_le_double, parse_le_float, parse_le_int16, parse_le_int32, parse_le_int8, parse_le_uint [...]
 #include "unparsers.h"  // for unparse_hexBinary, unparse_be_float, unparse_be_int16, unparse_validate_fixed, unparse_be_bool32, unparse_be_bool16, unparse_be_int32, unparse_be_uint16, unparse_be_uint32, unparse_le_bool32, unparse_le_int64, unparse_le_uint16, unparse_le_uint8, unparse_be_bool8, unparse_be_double, unparse_be_int64, unparse_be_int8, unparse_be_uint64, unparse_be_uint8, unparse_le_bool16, unparse_le_bool8, unparse_le_double, unparse_le_float, unparse_le_int16, unparse_le_in [...]
@@ -53,7 +53,7 @@ static const ERD variablelen_expressionType_ERD = {
 
 static const expressionElement_ array_variablelen_expressionType_expressionElement__compute_offsets;
 
-static const size_t array_variablelen_expressionType_expressionElement__offsets[1] = {
+static const size_t array_variablelen_expressionType_expressionElement__childrenOffsets[1] = {
     (const char *)&array_variablelen_expressionType_expressionElement__compute_offsets.variablelen[1] - (const char *)&array_variablelen_expressionType_expressionElement__compute_offsets.variablelen[0]
 };
 
@@ -69,11 +69,11 @@ static const ERD array_variablelen_expressionType_expressionElement_ERD = {
     },
     ARRAY, // typeCode
     16, // maxOccurs
-    array_variablelen_expressionType_expressionElement__offsets, // offsets
-    array_variablelen_expressionType_expressionElement__childrenERDs, // childrenERDs
-    (ERDParseSelf)&array_variablelen_expressionType_expressionElement__parseSelf, // parseSelf
-    (ERDUnparseSelf)&array_variablelen_expressionType_expressionElement__unparseSelf, // unparseSelf
-    {.getArraySize = (GetArraySize)&array_variablelen_expressionType_expressionElement__getArraySize} // getArraySize
+    array_variablelen_expressionType_expressionElement__childrenOffsets,
+    array_variablelen_expressionType_expressionElement__childrenERDs,
+    (ERDParseSelf)&array_variablelen_expressionType_expressionElement__parseSelf,
+    (ERDUnparseSelf)&array_variablelen_expressionType_expressionElement__unparseSelf,
+    {.getArraySize = (GetArraySize)&array_variablelen_expressionType_expressionElement__getArraySize}
 };
 
 static const ERD after_expressionType_ERD = {
@@ -88,7 +88,7 @@ static const ERD after_expressionType_ERD = {
 
 static const expressionElement_ array_after_expressionType_expressionElement__compute_offsets;
 
-static const size_t array_after_expressionType_expressionElement__offsets[1] = {
+static const size_t array_after_expressionType_expressionElement__childrenOffsets[1] = {
     (const char *)&array_after_expressionType_expressionElement__compute_offsets.after[1] - (const char *)&array_after_expressionType_expressionElement__compute_offsets.after[0]
 };
 
@@ -104,16 +104,16 @@ static const ERD array_after_expressionType_expressionElement_ERD = {
     },
     ARRAY, // typeCode
     2, // maxOccurs
-    array_after_expressionType_expressionElement__offsets, // offsets
-    array_after_expressionType_expressionElement__childrenERDs, // childrenERDs
-    (ERDParseSelf)&array_after_expressionType_expressionElement__parseSelf, // parseSelf
-    (ERDUnparseSelf)&array_after_expressionType_expressionElement__unparseSelf, // unparseSelf
-    {.getArraySize = (GetArraySize)&array_after_expressionType_expressionElement__getArraySize} // getArraySize
+    array_after_expressionType_expressionElement__childrenOffsets,
+    array_after_expressionType_expressionElement__childrenERDs,
+    (ERDParseSelf)&array_after_expressionType_expressionElement__parseSelf,
+    (ERDUnparseSelf)&array_after_expressionType_expressionElement__unparseSelf,
+    {.getArraySize = (GetArraySize)&array_after_expressionType_expressionElement__getArraySize}
 };
 
 static const expressionElement_ expressionElement__compute_offsets;
 
-static const size_t expressionElement__offsets[4] = {
+static const size_t expressionElement__childrenOffsets[4] = {
     (const char *)&expressionElement__compute_offsets.before - (const char *)&expressionElement__compute_offsets,
     (const char *)&expressionElement__compute_offsets.variablelen_size - (const char *)&expressionElement__compute_offsets,
     (const char *)&expressionElement__compute_offsets.variablelen[0] - (const char *)&expressionElement__compute_offsets,
@@ -135,11 +135,11 @@ static const ERD expressionElement_ERD = {
     },
     COMPLEX, // typeCode
     4, // numChildren
-    expressionElement__offsets, // offsets
-    expressionElement__childrenERDs, // childrenERDs
-    (ERDParseSelf)&expressionElement__parseSelf, // parseSelf
-    (ERDUnparseSelf)&expressionElement__unparseSelf, // unparseSelf
-    {NULL} // initChoice
+    expressionElement__childrenOffsets,
+    expressionElement__childrenERDs,
+    (ERDParseSelf)&expressionElement__parseSelf,
+    (ERDUnparseSelf)&expressionElement__unparseSelf,
+    {.initChoice = NULL}
 };
 
 // Initialize, parse, and unparse nodes of the infoset
@@ -262,17 +262,21 @@ expressionElement__unparseSelf(const expressionElement_ *instance, UState *ustat
     if (ustate->error) return;
 }
 
-// Return a root element for parsing or unparsing the infoset
+// Get an infoset (optionally clearing it first) for parsing/walking
 
 InfosetBase *
-rootElement(void)
+get_infoset(bool clear_infoset)
 {
-    static bool initialized;
-    static expressionElement_ root;
-    if (!initialized)
+    static expressionElement_ infoset;
+
+    if (clear_infoset)
     {
-        expressionElement__initERD(&root, (InfosetBase *)&root);
-        initialized = true;
+        // If your infoset contains hexBinary prefixed length elements,
+        // you may want to walk infoset first to free their malloc'ed
+        // storage - we are not handling that case for now...
+        memset(&infoset, 0, sizeof(infoset));
+        expressionElement__initERD(&infoset, (InfosetBase *)&infoset);
     }
-    return &root._base;
+
+    return &infoset._base;
 }
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-boolean.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-boolean.dat
new file mode 100644
index 000000000..593f4708d
Binary files /dev/null and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-boolean.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-byte.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-byte.dat
new file mode 100644
index 000000000..f76dd238a
Binary files /dev/null and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-byte.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-double.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-double.dat
new file mode 100644
index 000000000..1b1cb4d44
Binary files /dev/null and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-double.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-float.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-float.dat
new file mode 100644
index 000000000..593f4708d
Binary files /dev/null and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-float.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-hexBinary.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-hexBinary.dat
new file mode 100644
index 000000000..593f4708d
Binary files /dev/null and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-hexBinary.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-hexBinaryPrefixed.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-hexBinaryPrefixed.dat
new file mode 100644
index 000000000..68782e0ef
Binary files /dev/null and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-hexBinaryPrefixed.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-int.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-int.dat
new file mode 100644
index 000000000..593f4708d
Binary files /dev/null and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-int.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-integer.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-integer.dat
new file mode 100644
index 000000000..593f4708d
Binary files /dev/null and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-integer.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-long.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-long.dat
new file mode 100644
index 000000000..1b1cb4d44
Binary files /dev/null and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-long.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-nonNegativeInteger.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-nonNegativeInteger.dat
new file mode 100644
index 000000000..593f4708d
Binary files /dev/null and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-nonNegativeInteger.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-short.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-short.dat
new file mode 100644
index 000000000..09f370e38
Binary files /dev/null and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-short.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-unsignedByte.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-unsignedByte.dat
new file mode 100644
index 000000000..f76dd238a
Binary files /dev/null and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-unsignedByte.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-unsignedInt.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-unsignedInt.dat
new file mode 100644
index 000000000..593f4708d
Binary files /dev/null and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-unsignedInt.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-unsignedLong.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-unsignedLong.dat
new file mode 100644
index 000000000..1b1cb4d44
Binary files /dev/null and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-unsignedLong.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-unsignedShort.dat b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-unsignedShort.dat
new file mode 100644
index 000000000..09f370e38
Binary files /dev/null and b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/data/simple-unsignedShort.dat differ
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-boolean.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-boolean.dat.xml
new file mode 100644
index 000000000..38dab6790
--- /dev/null
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-boolean.dat.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<si:simple-boolean xmlns:si="urn:simple">false</si:simple-boolean>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-byte.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-byte.dat.xml
new file mode 100644
index 000000000..93d82871f
--- /dev/null
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-byte.dat.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<si:simple-byte xmlns:si="urn:simple">0</si:simple-byte>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-double.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-double.dat.xml
new file mode 100644
index 000000000..7955ed0a8
--- /dev/null
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-double.dat.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<si:simple-double xmlns:si="urn:simple">0.0</si:simple-double>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-float.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-float.dat.xml
new file mode 100644
index 000000000..c33e2b057
--- /dev/null
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-float.dat.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<si:simple-float xmlns:si="urn:simple">0.0</si:simple-float>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-hexBinary.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-hexBinary.dat.xml
new file mode 100644
index 000000000..1aade3608
--- /dev/null
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-hexBinary.dat.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<si:simple-hexBinary xmlns:si="urn:simple">00000000</si:simple-hexBinary>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-hexBinaryPrefixed.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-hexBinaryPrefixed.dat.xml
new file mode 100644
index 000000000..e38aff897
--- /dev/null
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-hexBinaryPrefixed.dat.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<si:simple-hexBinaryPrefixed xmlns:si="urn:simple">00000000</si:simple-hexBinaryPrefixed>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-int.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-int.dat.xml
new file mode 100644
index 000000000..4fd827083
--- /dev/null
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-int.dat.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<si:simple-int xmlns:si="urn:simple">0</si:simple-int>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-integer.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-integer.dat.xml
new file mode 100644
index 000000000..55dfc6be8
--- /dev/null
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-integer.dat.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<si:simple-integer xmlns:si="urn:simple">0</si:simple-integer>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-long.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-long.dat.xml
new file mode 100644
index 000000000..955de4ed5
--- /dev/null
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-long.dat.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<si:simple-long xmlns:si="urn:simple">0</si:simple-long>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-nonNegativeInteger.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-nonNegativeInteger.dat.xml
new file mode 100644
index 000000000..571189f65
--- /dev/null
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-nonNegativeInteger.dat.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<si:simple-nonNegativeInteger xmlns:si="urn:simple">0</si:simple-nonNegativeInteger>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-short.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-short.dat.xml
new file mode 100644
index 000000000..ebc3c4e01
--- /dev/null
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-short.dat.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<si:simple-short xmlns:si="urn:simple">0</si:simple-short>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-unsignedByte.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-unsignedByte.dat.xml
new file mode 100644
index 000000000..0188c9491
--- /dev/null
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-unsignedByte.dat.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<si:simple-unsignedByte xmlns:si="urn:simple">0</si:simple-unsignedByte>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-unsignedInt.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-unsignedInt.dat.xml
new file mode 100644
index 000000000..137308bfd
--- /dev/null
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-unsignedInt.dat.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<si:simple-unsignedInt xmlns:si="urn:simple">0</si:simple-unsignedInt>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-unsignedLong.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-unsignedLong.dat.xml
new file mode 100644
index 000000000..0430c75f1
--- /dev/null
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-unsignedLong.dat.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<si:simple-unsignedLong xmlns:si="urn:simple">0</si:simple-unsignedLong>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-unsignedShort.dat.xml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-unsignedShort.dat.xml
new file mode 100644
index 000000000..6bdc4688c
--- /dev/null
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/infosets/simple-unsignedShort.dat.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<si:simple-unsignedShort xmlns:si="urn:simple">0</si:simple-unsignedShort>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/simple.dfdl.xsd b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/simple.dfdl.xsd
new file mode 100644
index 000000000..6c74fe040
--- /dev/null
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/simple.dfdl.xsd
@@ -0,0 +1,65 @@
+<?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
+  targetNamespace="urn:simple"
+  xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/"
+  xmlns:si="urn:simple"
+  xmlns:xs="http://www.w3.org/2001/XMLSchema"
+  xmlns="http://www.w3.org/2001/XMLSchema">
+
+  <!-- Binary representation properties -->
+
+  <include schemaLocation="org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
+  <annotation>
+    <appinfo source="http://www.ogf.org/dfdl/">
+      <dfdl:format
+        binaryBooleanFalseRep="0"
+        binaryBooleanTrueRep="1"
+        fillByte="%NUL;"
+        prefixIncludesPrefixLength="no"
+        ref="si:GeneralFormat"
+        representation="binary"/>
+    </appinfo>
+  </annotation>
+
+  <!-- Root elements (pick only one) -->
+
+  <element name="simple-boolean" type="xs:boolean"/>
+  <element name="simple-byte" type="xs:byte"/>
+  <element name="simple-double" type="xs:double"/>
+  <element name="simple-float" type="xs:float"/>
+  <element name="simple-hexBinary" type="xs:hexBinary" dfdl:length="4" dfdl:lengthKind="explicit" dfdl:lengthUnits="bytes"/>
+  <element name="simple-hexBinaryPrefixed" type="xs:hexBinary" dfdl:lengthKind="prefixed" dfdl:lengthUnits="bytes" dfdl:prefixLengthType="si:prefixedCount"/>
+  <element name="simple-int" type="xs:int"/>
+  <element name="simple-integer" type="xs:integer" dfdl:length="4" dfdl:lengthKind="explicit" dfdl:lengthUnits="bytes"/>
+  <element name="simple-long" type="xs:long"/>
+  <element name="simple-nonNegativeInteger" type="xs:nonNegativeInteger" dfdl:length="4" dfdl:lengthKind="explicit" dfdl:lengthUnits="bytes"/>
+  <element name="simple-short" type="xs:short"/>
+  <element name="simple-unsignedByte" type="xs:unsignedByte"/>
+  <element name="simple-unsignedInt" type="xs:unsignedInt"/>
+  <element name="simple-unsignedLong" type="xs:unsignedLong"/>
+  <element name="simple-unsignedShort" type="xs:unsignedShort"/>
+
+  <!-- Simple data types -->
+
+  <simpleType name="prefixedCount">
+    <restriction base="xs:unsignedShort"/>
+  </simpleType>
+
+</schema>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/simple.tdml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/simple.tdml
new file mode 100644
index 000000000..3732a2621
--- /dev/null
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/simple.tdml
@@ -0,0 +1,330 @@
+<?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.
+-->
+
+<!--
+    Run all tests with each backend
+    daffodil test -i -I daffodil simple.tdml
+    daffodil test -i -I daffodilC simple.tdml
+
+    Or you can debug specific backends in isolation, see below
+-->
+<tdml:testSuite
+  defaultRoundTrip="onePass"
+  xmlns:tdml="http://www.ibm.com/xmlns/dfdl/testData">
+
+  <!--
+      daffodil parse -r simple-boolean -s simple.dfdl.xsd -o c/simple-boolean.dat.xml data/simple-boolean.dat
+      daffodil unparse -r simple-boolean -s simple.dfdl.xsd -o c/simple-boolean.dat infosets/simple-boolean.dat.xml
+
+      daffodil generate c -r simple-boolean -s simple.dfdl.xsd && make -C c
+      c/daffodil parse -o c/simple-boolean.dat.xml data/simple-boolean.dat
+      c/daffodil unparse -o c/simple-boolean.dat infosets/simple-boolean.dat.xml
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-boolean"
+    root="simple-boolean">
+    <tdml:document>
+      <tdml:documentPart type="file">data/simple-boolean.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">infosets/simple-boolean.dat.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <!--
+      daffodil parse -r simple-byte -s simple.dfdl.xsd -o c/simple-byte.dat.xml data/simple-byte.dat
+      daffodil unparse -r simple-byte -s simple.dfdl.xsd -o c/simple-byte.dat infosets/simple-byte.dat.xml
+
+      daffodil generate c -r simple-byte -s simple.dfdl.xsd && make -C c
+      c/daffodil parse -o c/simple-byte.dat.xml data/simple-byte.dat
+      c/daffodil unparse -o c/simple-byte.dat infosets/simple-byte.dat.xml
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-byte"
+    root="simple-byte">
+    <tdml:document>
+      <tdml:documentPart type="file">data/simple-byte.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">infosets/simple-byte.dat.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <!--
+      daffodil parse -r simple-double -s simple.dfdl.xsd -o c/simple-double.dat.xml data/simple-double.dat
+      daffodil unparse -r simple-double -s simple.dfdl.xsd -o c/simple-double.dat infosets/simple-double.dat.xml
+
+      daffodil generate c -r simple-double -s simple.dfdl.xsd && make -C c
+      c/daffodil parse -o c/simple-double.dat.xml data/simple-double.dat
+      c/daffodil unparse -o c/simple-double.dat infosets/simple-double.dat.xml
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-double"
+    root="simple-double">
+    <tdml:document>
+      <tdml:documentPart type="file">data/simple-double.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">infosets/simple-double.dat.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <!--
+      daffodil parse -r simple-float -s simple.dfdl.xsd -o c/simple-float.dat.xml data/simple-float.dat
+      daffodil unparse -r simple-float -s simple.dfdl.xsd -o c/simple-float.dat infosets/simple-float.dat.xml
+
+      daffodil generate c -r simple-float -s simple.dfdl.xsd && make -C c
+      c/daffodil parse -o c/simple-float.dat.xml data/simple-float.dat
+      c/daffodil unparse -o c/simple-float.dat infosets/simple-float.dat.xml
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-float"
+    root="simple-float">
+    <tdml:document>
+      <tdml:documentPart type="file">data/simple-float.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">infosets/simple-float.dat.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <!--
+      daffodil parse -r simple-hexBinary -s simple.dfdl.xsd -o c/simple-hexBinary.dat.xml data/simple-hexBinary.dat
+      daffodil unparse -r simple-hexBinary -s simple.dfdl.xsd -o c/simple-hexBinary.dat infosets/simple-hexBinary.dat.xml
+
+      daffodil generate c -r simple-hexBinary -s simple.dfdl.xsd && make -C c
+      c/daffodil parse -o c/simple-hexBinary.dat.xml data/simple-hexBinary.dat
+      c/daffodil unparse -o c/simple-hexBinary.dat infosets/simple-hexBinary.dat.xml
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-hexBinary"
+    root="simple-hexBinary">
+    <tdml:document>
+      <tdml:documentPart type="file">data/simple-hexBinary.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">infosets/simple-hexBinary.dat.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <!--
+      daffodil parse -r simple-hexBinaryPrefixed -s simple.dfdl.xsd -o c/simple-hexBinaryPrefixed.dat.xml data/simple-hexBinaryPrefixed.dat
+      daffodil unparse -r simple-hexBinaryPrefixed -s simple.dfdl.xsd -o c/simple-hexBinaryPrefixed.dat infosets/simple-hexBinaryPrefixed.dat.xml
+
+      daffodil generate c -r simple-hexBinaryPrefixed -s simple.dfdl.xsd && make -C c
+      c/daffodil parse -o c/simple-hexBinaryPrefixed.dat.xml data/simple-hexBinaryPrefixed.dat
+      c/daffodil unparse -o c/simple-hexBinaryPrefixed.dat infosets/simple-hexBinaryPrefixed.dat.xml
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-hexBinaryPrefixed"
+    root="simple-hexBinaryPrefixed">
+    <tdml:document>
+      <tdml:documentPart type="file">data/simple-hexBinaryPrefixed.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">infosets/simple-hexBinaryPrefixed.dat.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <!--
+      daffodil parse -r simple-int -s simple.dfdl.xsd -o c/simple-int.dat.xml data/simple-int.dat
+      daffodil unparse -r simple-int -s simple.dfdl.xsd -o c/simple-int.dat infosets/simple-int.dat.xml
+
+      daffodil generate c -r simple-int -s simple.dfdl.xsd && make -C c
+      c/daffodil parse -o c/simple-int.dat.xml data/simple-int.dat
+      c/daffodil unparse -o c/simple-int.dat infosets/simple-int.dat.xml
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-int"
+    root="simple-int">
+    <tdml:document>
+      <tdml:documentPart type="file">data/simple-int.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">infosets/simple-int.dat.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <!--
+      daffodil parse -r simple-integer -s simple.dfdl.xsd -o c/simple-integer.dat.xml data/simple-integer.dat
+      daffodil unparse -r simple-integer -s simple.dfdl.xsd -o c/simple-integer.dat infosets/simple-integer.dat.xml
+
+      daffodil generate c -r simple-integer -s simple.dfdl.xsd && make -C c
+      c/daffodil parse -o c/simple-integer.dat.xml data/simple-integer.dat
+      c/daffodil unparse -o c/simple-integer.dat infosets/simple-integer.dat.xml
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-integer"
+    root="simple-integer">
+    <tdml:document>
+      <tdml:documentPart type="file">data/simple-integer.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">infosets/simple-integer.dat.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <!--
+      daffodil parse -r simple-long -s simple.dfdl.xsd -o c/simple-long.dat.xml data/simple-long.dat
+      daffodil unparse -r simple-long -s simple.dfdl.xsd -o c/simple-long.dat infosets/simple-long.dat.xml
+
+      daffodil generate c -r simple-long -s simple.dfdl.xsd && make -C c
+      c/daffodil parse -o c/simple-long.dat.xml data/simple-long.dat
+      c/daffodil unparse -o c/simple-long.dat infosets/simple-long.dat.xml
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-long"
+    root="simple-long">
+    <tdml:document>
+      <tdml:documentPart type="file">data/simple-long.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">infosets/simple-long.dat.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <!--
+      daffodil parse -r simple-nonNegativeInteger -s simple.dfdl.xsd -o c/simple-nonNegativeInteger.dat.xml data/simple-nonNegativeInteger.dat
+      daffodil unparse -r simple-nonNegativeInteger -s simple.dfdl.xsd -o c/simple-nonNegativeInteger.dat infosets/simple-nonNegativeInteger.dat.xml
+
+      daffodil generate c -r simple-nonNegativeInteger -s simple.dfdl.xsd && make -C c
+      c/daffodil parse -o c/simple-nonNegativeInteger.dat.xml data/simple-nonNegativeInteger.dat
+      c/daffodil unparse -o c/simple-nonNegativeInteger.dat infosets/simple-nonNegativeInteger.dat.xml
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-nonNegativeInteger"
+    root="simple-nonNegativeInteger">
+    <tdml:document>
+      <tdml:documentPart type="file">data/simple-nonNegativeInteger.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">infosets/simple-nonNegativeInteger.dat.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <!--
+      daffodil parse -r simple-short -s simple.dfdl.xsd -o c/simple-short.dat.xml data/simple-short.dat
+      daffodil unparse -r simple-short -s simple.dfdl.xsd -o c/simple-short.dat infosets/simple-short.dat.xml
+
+      daffodil generate c -r simple-short -s simple.dfdl.xsd && make -C c
+      c/daffodil parse -o c/simple-short.dat.xml data/simple-short.dat
+      c/daffodil unparse -o c/simple-short.dat infosets/simple-short.dat.xml
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-short"
+    root="simple-short">
+    <tdml:document>
+      <tdml:documentPart type="file">data/simple-short.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">infosets/simple-short.dat.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <!--
+      daffodil parse -r simple-unsignedByte -s simple.dfdl.xsd -o c/simple-unsignedByte.dat.xml data/simple-unsignedByte.dat
+      daffodil unparse -r simple-unsignedByte -s simple.dfdl.xsd -o c/simple-unsignedByte.dat infosets/simple-unsignedByte.dat.xml
+
+      daffodil generate c -r simple-unsignedByte -s simple.dfdl.xsd && make -C c
+      c/daffodil parse -o c/simple-unsignedByte.dat.xml data/simple-unsignedByte.dat
+      c/daffodil unparse -o c/simple-unsignedByte.dat infosets/simple-unsignedByte.dat.xml
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-unsignedByte"
+    root="simple-unsignedByte">
+    <tdml:document>
+      <tdml:documentPart type="file">data/simple-unsignedByte.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">infosets/simple-unsignedByte.dat.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <!--
+      daffodil parse -r simple-unsignedInt -s simple.dfdl.xsd -o c/simple-unsignedInt.dat.xml data/simple-unsignedInt.dat
+      daffodil unparse -r simple-unsignedInt -s simple.dfdl.xsd -o c/simple-unsignedInt.dat infosets/simple-unsignedInt.dat.xml
+
+      daffodil generate c -r simple-unsignedInt -s simple.dfdl.xsd && make -C c
+      c/daffodil parse -o c/simple-unsignedInt.dat.xml data/simple-unsignedInt.dat
+      c/daffodil unparse -o c/simple-unsignedInt.dat infosets/simple-unsignedInt.dat.xml
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-unsignedInt"
+    root="simple-unsignedInt">
+    <tdml:document>
+      <tdml:documentPart type="file">data/simple-unsignedInt.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">infosets/simple-unsignedInt.dat.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <!--
+      daffodil parse -r simple-unsignedLong -s simple.dfdl.xsd -o c/simple-unsignedLong.dat.xml data/simple-unsignedLong.dat
+      daffodil unparse -r simple-unsignedLong -s simple.dfdl.xsd -o c/simple-unsignedLong.dat infosets/simple-unsignedLong.dat.xml
+
+      daffodil generate c -r simple-unsignedLong -s simple.dfdl.xsd && make -C c
+      c/daffodil parse -o c/simple-unsignedLong.dat.xml data/simple-unsignedLong.dat
+      c/daffodil unparse -o c/simple-unsignedLong.dat infosets/simple-unsignedLong.dat.xml
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-unsignedLong"
+    root="simple-unsignedLong">
+    <tdml:document>
+      <tdml:documentPart type="file">data/simple-unsignedLong.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">infosets/simple-unsignedLong.dat.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <!--
+      daffodil parse -r simple-unsignedShort -s simple.dfdl.xsd -o c/simple-unsignedShort.dat.xml data/simple-unsignedShort.dat
+      daffodil unparse -r simple-unsignedShort -s simple.dfdl.xsd -o c/simple-unsignedShort.dat infosets/simple-unsignedShort.dat.xml
+
+      daffodil generate c -r simple-unsignedShort -s simple.dfdl.xsd && make -C c
+      c/daffodil parse -o c/simple-unsignedShort.dat.xml data/simple-unsignedShort.dat
+      c/daffodil unparse -o c/simple-unsignedShort.dat infosets/simple-unsignedShort.dat.xml
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-unsignedShort"
+    root="simple-unsignedShort">
+    <tdml:document>
+      <tdml:documentPart type="file">data/simple-unsignedShort.dat</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset type="file">infosets/simple-unsignedShort.dat.xml</tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+</tdml:testSuite>
diff --git a/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/simple_errors.tdml b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/simple_errors.tdml
new file mode 100644
index 000000000..10b45721a
--- /dev/null
+++ b/daffodil-codegen-c/src/test/resources/org/apache/daffodil/codegen/c/simple_errors.tdml
@@ -0,0 +1,158 @@
+<?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.
+-->
+
+<!--
+    Run all tests with each backend
+    daffodil test -i -I daffodil simple.err.tdml
+    daffodil test -i -I daffodilC simple.err.tdml
+-->
+<tdml:testSuite
+  defaultRoundTrip="none"
+  xmlns:tdml="http://www.ibm.com/xmlns/dfdl/testData">
+
+  <!--
+      Checks boolean must be 0 or 1, not 42
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-boolean-42"
+    root="simple-boolean">
+    <tdml:document>
+      <tdml:documentPart type="byte">0000002a</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>parse</tdml:error>
+      <tdml:error>boolean</tdml:error>
+      <tdml:error>42</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <!--
+      Checks byte with any left over bits causes parse error
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-byte-2b"
+    root="simple-byte">
+    <tdml:document>
+      <tdml:documentPart type="byte">0102</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>8 bit</tdml:error>
+      <tdml:error>remaining</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <!--
+      Checks double with only 4 bytes causes parse error
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-double-4b"
+    root="simple-double">
+    <tdml:document>
+      <tdml:documentPart type="byte">01020304</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>64 bit</tdml:error>
+      <tdml:error>32 available</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <!--
+      Checks float with NaN pattern is parsed as NaN
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-float-NaN"
+    root="simple-float">
+    <tdml:document>
+      <tdml:documentPart type="byte">7fc00000</tdml:documentPart>
+    </tdml:document>
+    <tdml:infoset>
+      <tdml:dfdlInfoset>
+        <si:simple-float xmlns:si="urn:simple">NaN</si:simple-float>
+      </tdml:dfdlInfoset>
+    </tdml:infoset>
+  </tdml:parserTestCase>
+
+  <!--
+      Checks hexBinary with too many bytes causes parse error
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-hexBinary-5b"
+    root="simple-hexBinary">
+    <tdml:document>
+      <tdml:documentPart type="byte">0102030405</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>8 bit</tdml:error>
+      <tdml:error>remaining</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <!--
+      Checks hexBinary with too few bytes causes parse error
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-hexBinaryPrefixed-4b"
+    root="simple-hexBinaryPrefixed">
+    <tdml:document>
+      <tdml:documentPart type="byte">01020304</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>2064 bit</tdml:error>
+      <tdml:error>16 available</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <!--
+      Checks int with 1 byte causes parse error
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-int-1b"
+    root="simple-int">
+    <tdml:document>
+      <tdml:documentPart type="byte">01</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>32 bit</tdml:error>
+      <tdml:error>8 available</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+  <!--
+      Checks unsignedShort with 4 bytes causes parse error
+  -->
+  <tdml:parserTestCase
+    model="simple.dfdl.xsd"
+    name="simple-unsignedShort-4b"
+    root="simple-unsignedShort">
+    <tdml:document>
+      <tdml:documentPart type="byte">01020304</tdml:documentPart>
+    </tdml:document>
+    <tdml:errors>
+      <tdml:error>16 bit</tdml:error>
+      <tdml:error>remaining</tdml:error>
+    </tdml:errors>
+  </tdml:parserTestCase>
+
+</tdml:testSuite>
diff --git a/daffodil-test/src/test/scala/org/apache/daffodil/codegen/c/TestSimple.scala b/daffodil-test/src/test/scala/org/apache/daffodil/codegen/c/TestSimple.scala
new file mode 100644
index 000000000..5b09f61b6
--- /dev/null
+++ b/daffodil-test/src/test/scala/org/apache/daffodil/codegen/c/TestSimple.scala
@@ -0,0 +1,53 @@
+/*
+ * 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.codegen.c
+
+import org.apache.daffodil.lib.api.TDMLImplementation
+import org.apache.daffodil.tdml.Runner
+
+import org.junit.AfterClass
+import org.junit.Test
+
+object TestSimple {
+  val testDir = "/org/apache/daffodil/codegen/c/"
+  val runner = Runner(testDir, "simple.tdml", TDMLImplementation.DaffodilC)
+
+  @AfterClass def shutDown(): Unit = { runner.reset() }
+}
+
+class TestSimple {
+  import TestSimple._
+
+  @Test def simple_boolean(): Unit = { runner.runOneTest("simple-boolean") }
+  @Test def simple_byte(): Unit = { runner.runOneTest("simple-byte") }
+  @Test def simple_double(): Unit = { runner.runOneTest("simple-double") }
+  @Test def simple_float(): Unit = { runner.runOneTest("simple-float") }
+  @Test def simple_hexBinary(): Unit = { runner.runOneTest("simple-hexBinary") }
+  @Test def simple_hexBinaryPrefixed(): Unit = { runner.runOneTest("simple-hexBinaryPrefixed") }
+  @Test def simple_int(): Unit = { runner.runOneTest("simple-int") }
+  @Test def simple_integer(): Unit = { runner.runOneTest("simple-integer") }
+  @Test def simple_long(): Unit = { runner.runOneTest("simple-long") }
+  @Test def simple_nonNegativeInteger(): Unit = {
+    runner.runOneTest("simple-nonNegativeInteger")
+  }
+  @Test def simple_short(): Unit = { runner.runOneTest("simple-short") }
+  @Test def simple_unsignedByte(): Unit = { runner.runOneTest("simple-unsignedByte") }
+  @Test def simple_unsignedInt(): Unit = { runner.runOneTest("simple-unsignedInt") }
+  @Test def simple_unsignedLong(): Unit = { runner.runOneTest("simple-unsignedLong") }
+  @Test def simple_unsignedShort(): Unit = { runner.runOneTest("simple-unsignedShort") }
+}
diff --git a/daffodil-test/src/test/scala/org/apache/daffodil/codegen/c/TestSimpleErrors.scala b/daffodil-test/src/test/scala/org/apache/daffodil/codegen/c/TestSimpleErrors.scala
new file mode 100644
index 000000000..942e3dc18
--- /dev/null
+++ b/daffodil-test/src/test/scala/org/apache/daffodil/codegen/c/TestSimpleErrors.scala
@@ -0,0 +1,46 @@
+/*
+ * 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.codegen.c
+
+import org.apache.daffodil.lib.api.TDMLImplementation
+import org.apache.daffodil.tdml.Runner
+
+import org.junit.AfterClass
+import org.junit.Test
+
+object TestSimpleErrors {
+  val testDir = "/org/apache/daffodil/codegen/c/"
+  val runner = Runner(testDir, "simple_errors.tdml", TDMLImplementation.DaffodilC)
+
+  @AfterClass def shutDown(): Unit = { runner.reset() }
+}
+
+class TestSimpleErrors {
+  import TestSimpleErrors._
+
+  @Test def simple_boolean_42(): Unit = { runner.runOneTest("simple-boolean-42") }
+  @Test def simple_byte_2b(): Unit = { runner.runOneTest("simple-byte-2b") }
+  @Test def simple_double_4b(): Unit = { runner.runOneTest("simple-double-4b") }
+  @Test def simple_float_NaN(): Unit = { runner.runOneTest("simple-float-NaN") }
+  @Test def simple_hexBinary_5b(): Unit = { runner.runOneTest("simple-hexBinary-5b") }
+  @Test def simple_hexBinaryPrefixed_4b(): Unit = {
+    runner.runOneTest("simple-hexBinaryPrefixed-4b")
+  }
+  @Test def simple_int_1b(): Unit = { runner.runOneTest("simple-int-1b") }
+  @Test def simple_unsignedShort_4b(): Unit = { runner.runOneTest("simple-unsignedShort-4b") }
+}