tuxji commented on a change in pull request #488:
URL: https://github.com/apache/incubator-daffodil/pull/488#discussion_r574832636
##########
File path:
daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala
##########
@@ -162,10 +322,31 @@ class CodeGeneratorState {
def addComplexTypeStatements(child: ElementBase): Unit = {
val C = localName(child)
val e = child.name
+ val hasChoice = structs.top.initChoiceStatements.nonEmpty
Review comment:
By a sequence with an anonymous choice, do you mean a complex type which
looks like this?
```xml
<xs:complexType name="NestedUnionType">
<xs:sequence>
<xs:element name="tag" type="idl:int32"/>
<xs:choice dfdl:choiceDispatchKey="{xs:string(./tag)}">
<xs:element name="foo" type="idl:FooType" dfdl:choiceBranchKey="1
2"/>
<xs:element name="bar" type="idl:BarType" dfdl:choiceBranchKey="3
4"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
```
I tried generating the C code and found out that the generator didn't
recognize the anonymous choice was present - it generated a struct with all
members like below,
```c
typedef struct NestedUnion
{
InfosetBase _base;
int32_t tag;
foo foo;
bar bar;
} NestedUnion;
```
I could make the generator look for the anonymous choice and generate a
struct with a nested anonymous union like this,
```c
typedef struct NestedUnion
{
InfosetBase _base;
int32_t tag;
size_t _choice; // choice of which union field to use
union
{
foo foo;
bar bar;
};
} NestedUnion;
```
The _choice member field serves an important role in the ERD structure.
Since we have a _choice member field, we have a corresponding entry for it in
childrenERDs and it has its own ERD structure. When we walk that ERD and see
it has a CHOICE type code, that tells us it's time to call the initChoice
function to initialize the **union** child's ERD structure so it points to the
ERD of the right alternative element. The _choice element always should
immediately precede the union element in the ERD structure, so if we inline it
into the parent C struct due to an anonymous choice, we also should make sure
it has a unique name (_choice1 instead of _choice) in case we need a _choice2
for another anonymous choice following the first choice. It might be possible
to remove the _choice member field to save space, but we would need to move the
CHOICE type code to the union child's ERD. That ERD would no longer point to
the union child's choice of alternative element ERDs; we would need
a way to walk the union child's choice of alternative elements and ERDs
everywhere that `instance->_choice` currently gets used. However, if Mike has
a better suggestion how to design the choice runtime structure (CRD), then we
could dispense with _choice.
Yes, a name collision issue would arise in sequences with multiple
alternative elements with the same name. However, Daffodil won't even allow
such a schema to compile. When I tried changing bar to foo in NestedUnionType
in nested.dfdl.xsd, Daffodil reported two schema definition errors:
```text
[error] Schema Definition Error: Error loading schema due to
org.xml.sax.SAXParseException; systemId: file:.../nested.dfdl.xsd; lineNumber:
70; columnNumber: 25; cos-element-consistent: Error for type
'#AnonType_dataNestedUnionType'. Multiple elements with name 'foo', with
different types, appear in the model group.
[error] Schema Definition Error: Error loading schema due to
org.xml.sax.SAXParseException; systemId: file:.../nested.dfdl.xsd; lineNumber:
70; columnNumber: 25; cos-nonambig: foo and foo (or elements from their
substitution group) violate "Unique Particle Attribution". During validation
against this schema, ambiguity would be created for those two particles.
```
When I changed the second foo to have the same FooType, the first error
disappeared but the second error remained.
##########
File path:
daffodil-runtime2/src/test/resources/org/apache/daffodil/runtime2/ingress_xdcc_bw.tdml
##########
@@ -0,0 +1,196 @@
+<?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.
+-->
+
+<tdml:testSuite
+ defaultConfig="config-runtime2"
+ defaultImplementations="daffodil daffodil-runtime2"
+ defaultRoundTrip="none"
+ description="TDML tests for ingress_xdcc_bw"
+ xmlns:daf="urn:ogf:dfdl:2013:imp:daffodil.apache.org:2018:ext"
+ xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/"
+ xmlns:tdml="http://www.ibm.com/xmlns/dfdl/testData">
+
+ <tdml:defineConfig name="config-runtime1">
+ <daf:tunables>
+ <daf:tdmlImplementation>daffodil</daf:tdmlImplementation>
+ </daf:tunables>
+ </tdml:defineConfig>
+
+ <tdml:defineConfig name="config-runtime2">
+ <daf:tunables>
+ <daf:tdmlImplementation>daffodil-runtime2</daf:tdmlImplementation>
+ </daf:tunables>
+ </tdml:defineConfig>
+
+ <tdml:parserTestCase
+ description="ingress_xdcc_bw parse test 111"
+ model="ingress_xdcc_bw.dfdl.xsd"
+ name="ingress_xdcc_bw_parse_111"
+ root="GapsPDU">
+ <tdml:document>
+ <tdml:documentPart
type="file">ingress_xdcc_bw_parse_111.dat</tdml:documentPart>
+ </tdml:document>
+ <tdml:infoset>
+ <tdml:dfdlInfoset
type="file">ingress_xdcc_bw_unparse_111.xml</tdml:dfdlInfoset>
+ </tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:unparserTestCase
+ description="ingress_xdcc_bw unparse test 111"
+ model="ingress_xdcc_bw.dfdl.xsd"
+ name="ingress_xdcc_bw_unparse_111"
+ root="GapsPDU">
+ <tdml:infoset>
+ <tdml:dfdlInfoset
type="file">ingress_xdcc_bw_unparse_111.xml</tdml:dfdlInfoset>
+ </tdml:infoset>
+ <tdml:document>
+ <tdml:documentPart
type="file">ingress_xdcc_bw_parse_111.dat</tdml:documentPart>
+ </tdml:document>
+ </tdml:unparserTestCase>
+
+ <tdml:parserTestCase
+ description="ingress_xdcc_bw parse test 112"
+ model="ingress_xdcc_bw.dfdl.xsd"
+ name="ingress_xdcc_bw_parse_112"
+ root="GapsPDU">
+ <tdml:document>
+ <tdml:documentPart
type="file">ingress_xdcc_bw_parse_112.dat</tdml:documentPart>
+ </tdml:document>
+ <tdml:infoset>
+ <tdml:dfdlInfoset
type="file">ingress_xdcc_bw_unparse_112.xml</tdml:dfdlInfoset>
+ </tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:unparserTestCase
+ description="ingress_xdcc_bw unparse test 112"
+ model="ingress_xdcc_bw.dfdl.xsd"
+ name="ingress_xdcc_bw_unparse_112"
+ root="GapsPDU">
+ <tdml:infoset>
+ <tdml:dfdlInfoset
type="file">ingress_xdcc_bw_unparse_112.xml</tdml:dfdlInfoset>
+ </tdml:infoset>
+ <tdml:document>
+ <tdml:documentPart
type="file">ingress_xdcc_bw_parse_112.dat</tdml:documentPart>
+ </tdml:document>
+ </tdml:unparserTestCase>
+
+ <tdml:parserTestCase
+ description="ingress_xdcc_bw parse test 113"
+ model="ingress_xdcc_bw.dfdl.xsd"
+ name="ingress_xdcc_bw_parse_113"
+ root="GapsPDU">
+ <tdml:document>
+ <tdml:documentPart
type="file">ingress_xdcc_bw_parse_113.dat</tdml:documentPart>
+ </tdml:document>
+ <tdml:infoset>
+ <tdml:dfdlInfoset
type="file">ingress_xdcc_bw_unparse_113.xml</tdml:dfdlInfoset>
+ </tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:unparserTestCase
+ description="ingress_xdcc_bw unparse test 113"
+ model="ingress_xdcc_bw.dfdl.xsd"
+ name="ingress_xdcc_bw_unparse_113"
+ root="GapsPDU">
+ <tdml:infoset>
+ <tdml:dfdlInfoset
type="file">ingress_xdcc_bw_unparse_113.xml</tdml:dfdlInfoset>
+ </tdml:infoset>
+ <tdml:document>
+ <tdml:documentPart
type="file">ingress_xdcc_bw_parse_113.dat</tdml:documentPart>
+ </tdml:document>
+ </tdml:unparserTestCase>
+
+ <tdml:parserTestCase
+ description="ingress_xdcc_bw parse test 114"
+ model="ingress_xdcc_bw.dfdl.xsd"
+ name="ingress_xdcc_bw_parse_114"
+ root="GapsPDU">
+ <tdml:document>
+ <tdml:documentPart
type="file">ingress_xdcc_bw_parse_114.dat</tdml:documentPart>
+ </tdml:document>
+ <tdml:infoset>
+ <tdml:dfdlInfoset
type="file">ingress_xdcc_bw_unparse_114.xml</tdml:dfdlInfoset>
+ </tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:unparserTestCase
+ description="ingress_xdcc_bw unparse test 114"
+ model="ingress_xdcc_bw.dfdl.xsd"
+ name="ingress_xdcc_bw_unparse_114"
+ root="GapsPDU">
+ <tdml:infoset>
+ <tdml:dfdlInfoset
type="file">ingress_xdcc_bw_unparse_114.xml</tdml:dfdlInfoset>
+ </tdml:infoset>
+ <tdml:document>
+ <tdml:documentPart
type="file">ingress_xdcc_bw_parse_114.dat</tdml:documentPart>
+ </tdml:document>
+ </tdml:unparserTestCase>
+
+ <tdml:parserTestCase
+ description="ingress_xdcc_bw parse test 115"
+ model="ingress_xdcc_bw.dfdl.xsd"
+ name="ingress_xdcc_bw_parse_115"
+ root="GapsPDU">
+ <tdml:document>
+ <tdml:documentPart
type="file">ingress_xdcc_bw_parse_115.dat</tdml:documentPart>
+ </tdml:document>
+ <tdml:infoset>
+ <tdml:dfdlInfoset
type="file">ingress_xdcc_bw_unparse_115.xml</tdml:dfdlInfoset>
+ </tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:unparserTestCase
+ description="ingress_xdcc_bw unparse test 115"
+ model="ingress_xdcc_bw.dfdl.xsd"
+ name="ingress_xdcc_bw_unparse_115"
+ root="GapsPDU">
+ <tdml:infoset>
+ <tdml:dfdlInfoset
type="file">ingress_xdcc_bw_unparse_115.xml</tdml:dfdlInfoset>
+ </tdml:infoset>
+ <tdml:document>
+ <tdml:documentPart
type="file">ingress_xdcc_bw_parse_115.dat</tdml:documentPart>
+ </tdml:document>
+ </tdml:unparserTestCase>
+
+ <tdml:parserTestCase
+ description="ingress_xdcc_bw parse test 116"
+ model="ingress_xdcc_bw.dfdl.xsd"
+ name="ingress_xdcc_bw_parse_116"
+ root="GapsPDU">
+ <tdml:document>
+ <tdml:documentPart
type="file">ingress_xdcc_bw_parse_116.dat</tdml:documentPart>
+ </tdml:document>
+ <tdml:infoset>
+ <tdml:dfdlInfoset
type="file">ingress_xdcc_bw_unparse_116.xml</tdml:dfdlInfoset>
+ </tdml:infoset>
+ </tdml:parserTestCase>
+
+ <tdml:unparserTestCase
+ description="ingress_xdcc_bw unparse test 116"
+ model="ingress_xdcc_bw.dfdl.xsd"
+ name="ingress_xdcc_bw_unparse_116"
+ root="GapsPDU">
+ <tdml:infoset>
+ <tdml:dfdlInfoset
type="file">ingress_xdcc_bw_unparse_116.xml</tdml:dfdlInfoset>
+ </tdml:infoset>
+ <tdml:document>
+ <tdml:documentPart
type="file">ingress_xdcc_bw_parse_116.dat</tdml:documentPart>
+ </tdml:document>
+ </tdml:unparserTestCase>
+
+</tdml:testSuite>
Review comment:
I can move the schemas and test cases for collaborators' use cases which
just use runtime2 for code generation (not developing or testing runtime2
itself) to some other place such as
daffodil-test/src/test/{resources,scala}/org/apache/daffodil/runtime2. It
might be better to do it in another pull request that only moves things around,
not changes things. I want to keep those schemas and test cases to ensure
their use cases keep working as expected. Let me know if a different directory
name or place sounds better. I would like to see us start running runtime2 on
as many of daffodil's own tests as possible too, which may make those
collaborators' schemas and test cases unnecessary to keep around in the long
term.
##########
File path: daffodil-runtime2/src/main/resources/c/libruntime/infoset.h
##########
@@ -38,6 +33,8 @@ typedef struct VisitEventHandler VisitEventHandler;
typedef void (*ERDInitSelf)(InfosetBase *infoNode);
typedef void (*ERDParseSelf)(InfosetBase *infoNode, PState *pstate);
typedef void (*ERDUnparseSelf)(const InfosetBase *infoNode, UState *ustate);
+typedef bool (*ERDInitChoice)(const InfosetBase *infoNode,
Review comment:
Actually, the loop inside infoset.c's walkInfosetNode function calls
initChoice to update the ERD structure to point to a choice of alternative
child ERDs when walking a choice group's node, so initChoice must modify the
following element's ERD structure (`childNode->erd`) as well as the infoset's
CRD structure (`instance->_choice`). Nevertheless, I've changed the type name
ERDInitChoice to InitChoiceRD and added a comment to infoset.c to clarify
initChoice's role.
```c
case CHOICE:
// Point next ERD to choice of alternative elements' ERDs
if (!infoNode->erd->initChoice(infoNode, rootElement()))
error_msg =
```
##########
File path: daffodil-runtime2/src/main/resources/c/libruntime/infoset.h
##########
@@ -81,34 +79,35 @@ typedef struct ElementRuntimeData
const NamedQName namedQName;
const enum TypeCode typeCode;
const size_t numChildren;
- const ptrdiff_t * offsets;
+ const size_t * offsets;
const ERD ** childrenERDs;
const ERDInitSelf initSelf;
const ERDParseSelf parseSelf;
const ERDUnparseSelf unparseSelf;
+ const ERDInitChoice initChoice;
} ERD;
-// InfosetBase - representation of an infoset element
+// InfosetBase - metadata of an infoset element
typedef struct InfosetBase
{
const ERD *erd;
} InfosetBase;
-// PState - parser state while parsing input
+// PState - mutable state while parsing data
typedef struct PState
{
- FILE * stream; // input to read from
+ FILE * stream; // input to read data from
const char *error_msg; // to stop if an error happens
} PState;
-// UState - unparser state while unparsing infoset
+// UState - mutable state while unparsing infoset
typedef struct UState
{
- FILE * stream; // output to write to
+ FILE * stream; // output to write infoset to
Review comment:
Yes, "output to write data to" makes more sense, changed.
##########
File path:
daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala
##########
@@ -105,41 +129,178 @@ class CodeGeneratorState {
qnameInit
}
+ /**
+ * We want to convert a choiceDispatchKey expression into C struct dot
+ * notation (rootElement->[subElement.field]) which will access the C
+ * struct field containing the choiceDispatchKey's runtime value.
+ *
+ * We make some assumptions to make generating the dot notation easier:
+ * - the expression starts with '{xs:string( and ends with )}'
+ * - the expression returns the value of a previous element without
+ * changing the value in any way (except converting it to xs:string)
+ * - both the expression and the C code use only local names (for now...)
+ * - we can map the context node's path to a Unix-like slash path
+ * - all dpath operations look like Unix-like relative paths (../tag)
+ * - we can normalize the new path and convert it to C struct dot notation
+ * - we can store the accessed value in an int64_t local variable safely
+ */
+ private def choiceDispatchField(context: ElementBase): String = {
+ // We want to use SchemaComponent.scPath but it's private so duplicate it
here (for now...)
+ def scPath(sc: SchemaComponent): Seq[SchemaComponent] =
sc.optLexicalParent.map { scPath }.getOrElse(Nil) :+ sc
+ val localNames = scPath(context).map {
+ case er: AbstractElementRef => er.refQName.local
+ case e: ElementBase => e.namedQName.local
+ case ed: GlobalElementDecl => ed.namedQName.local
+ case _ => ""
+ }
+ val absoluteSlashPath = localNames.mkString("/")
+ val dispatchSlashPath = context.complexType.modelGroup match {
+ case choice: Choice if choice.isDirectDispatch =>
+ val expr = choice.choiceDispatchKeyEv.expr.toBriefXML()
+ val before = "'{xs:string("
+ val after = ")}'"
+ val relativePath = if (expr.startsWith(before) && expr.endsWith(after))
+ expr.substring(before.length, expr.length - after.length) else expr
+ val normalizedURI = new URI(absoluteSlashPath + "/" +
relativePath).normalize
+ normalizedURI.getPath.substring(1)
+ case _ => ""
+ }
+ // Strip namespace prefixes since C code uses only local names (for now...)
+ val localDispatchSlashPath = dispatchSlashPath.replaceAll("/[^:]+:", "/")
+ val res = localDispatchSlashPath.replace('/', '.')
+ res
+ }
Review comment:
Yes, I would like to see us add code generate support to the DPath
compiler. I agree this code feels fragile and we probably will need the DPath
expression compiler once people start using dispatch expressions that don't
match the supported format so far.
##########
File path: daffodil-runtime2/src/main/resources/c/libcli/daffodil_argp.c
##########
@@ -62,6 +63,9 @@ static const struct argp parse_argp = {
parse_handler, // function to get these CLI options
parse_args_doc, // short usage documentation
parse_doc, // long help documentation
+ 0, // children
Review comment:
I've replaced those comments with longer comments but programmers still
will have to study argp_help.c in the argp library's source code and read the
GNU gettext library's documentation before they can use those parameters.
```c
0, // child argps parsed after this argp
0, // function to replace help messages
0 // domain name for translation lookup
```
##########
File path: daffodil-runtime2/src/main/resources/c/libruntime/infoset.c
##########
@@ -159,6 +159,13 @@ walkInfosetNode(const VisitEventHandler *handler, const
InfosetBase *infoNode)
error_msg =
handler->visitNumberElem(handler, childERD, numLocation);
break;
+ case CHOICE:
+ if (!infoNode->erd->initChoice(infoNode, rootElement()))
Review comment:
NestedUnion.c in this change set has an example initChoice function but
GitHub might have hidden its diff in your window at the time you did your
search. We call initChoice to update the following union child element's ERD
structure (`childNode->erd`) as well as the infoset's CRD structure
(`instance->_choice`) whenever we walk the infoset in walkInfosetNode
(infoset.c), data_parseSelf (NestedUnion.c), or data_unparseSelf
(NestedUnion.c). Here's the corresponding code in the example initChoice
(childNode points to the union child following the _choice field):
```c
if (instance->_choice != NO_CHOICE)
{
const size_t choice = instance->_choice + 1; // skip the _choice
field
const size_t offset = instance->_base.erd->offsets[choice];
const ERD * childERD = instance->_base.erd->childrenERDs[choice];
InfosetBase *childNode = (InfosetBase *)((const char *)instance +
offset);
childNode->erd = childERD;
return true;
}
```
You're right, I've been assuming that the entire infonode is a single choice
since I didn't consider anonymous choices needing to be inlined into the parent
C struct. The _choice field has its entry in childrenERDs and its own ERD
structure naming it, so we can be more flexible instead of hardcoding
`instance->_choice` in CodeGeneratorState.scala. If you can give me a DFDL
schema with multiple choice groups, I'll make that improvement in a future pull
request.
##########
File path: daffodil-runtime2/src/main/resources/c/libruntime/infoset.h
##########
@@ -81,34 +79,35 @@ typedef struct ElementRuntimeData
const NamedQName namedQName;
const enum TypeCode typeCode;
const size_t numChildren;
- const ptrdiff_t * offsets;
+ const size_t * offsets;
Review comment:
Those thoughts motivated me to change offsets to size_t:
1) Offsets are always positive deltas from the infoset's base to following
fields
2) If I use size_t instead of ptrdiff_t, the delta can be twice as large
and never becomes negative due to overflow
3) Some C functions we call receive or return size_t rather than ptrdiff_t
##########
File path: daffodil-runtime2/src/main/resources/examples/NestedUnion.c
##########
@@ -0,0 +1,408 @@
+/*
+ * 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.
+ */
+
+#include "NestedUnion.h"
+#include "parsers.h" // for parse_be_double, parse_be_float,
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_double,
parse_le_float, 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_double, unparse_be_float,
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_double, unparse_le_float, 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 <math.h> // for NAN
+#include <stdbool.h> // for bool, false, true
+#include <stddef.h> // for NULL, size_t
+
+// Prototypes needed for compilation
+
+static void foo_initSelf(foo *instance);
+static void foo_parseSelf(foo *instance, PState *pstate);
+static void foo_unparseSelf(const foo *instance, UState *ustate);
+static void bar_initSelf(bar *instance);
+static void bar_parseSelf(bar *instance, PState *pstate);
+static void bar_unparseSelf(const bar *instance, UState *ustate);
+static void data_initSelf(data *instance);
+static bool data_initChoice(data *instance, const NestedUnion *rootElement);
+static void data_parseSelf(data *instance, PState *pstate);
+static void data_unparseSelf(const data *instance, UState *ustate);
+static void NestedUnion_initSelf(NestedUnion *instance);
+static void NestedUnion_parseSelf(NestedUnion *instance, PState *pstate);
+static void NestedUnion_unparseSelf(const NestedUnion *instance, UState
*ustate);
+
+// Metadata singletons
+
+static const ERD tag_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "tag", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_INT32, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD _choice_data_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "_choice", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ CHOICE, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD a_FooType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "a", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_INT32, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD b_FooType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "b", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_INT32, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD c_FooType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "c", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_INT32, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const foo foo_compute_offsets;
+
+static const size_t foo_offsets[3] = {
+ (const char *)&foo_compute_offsets.a - (const char *)&foo_compute_offsets,
+ (const char *)&foo_compute_offsets.b - (const char *)&foo_compute_offsets,
+ (const char *)&foo_compute_offsets.c - (const char *)&foo_compute_offsets
+};
+
+static const ERD *foo_childrenERDs[3] = {
+ &a_FooType_ERD,
+ &b_FooType_ERD,
+ &c_FooType_ERD
+};
+
+static const ERD foo_data_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "foo", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ COMPLEX, // typeCode
+ 3, // numChildren
+ foo_offsets, // offsets
+ foo_childrenERDs, // childrenERDs
+ (ERDInitSelf)&foo_initSelf, // initSelf
+ (ERDParseSelf)&foo_parseSelf, // parseSelf
+ (ERDUnparseSelf)&foo_unparseSelf, // unparseSelf
+ NULL // initChoice
+};
+
+static const ERD x_BarType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "x", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_DOUBLE, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD y_BarType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "y", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_DOUBLE, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD z_BarType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "z", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_DOUBLE, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const bar bar_compute_offsets;
+
+static const size_t bar_offsets[3] = {
+ (const char *)&bar_compute_offsets.x - (const char *)&bar_compute_offsets,
+ (const char *)&bar_compute_offsets.y - (const char *)&bar_compute_offsets,
+ (const char *)&bar_compute_offsets.z - (const char *)&bar_compute_offsets
+};
+
+static const ERD *bar_childrenERDs[3] = {
+ &x_BarType_ERD,
+ &y_BarType_ERD,
+ &z_BarType_ERD
+};
+
+static const ERD bar_data_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "bar", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ COMPLEX, // typeCode
+ 3, // numChildren
+ bar_offsets, // offsets
+ bar_childrenERDs, // childrenERDs
+ (ERDInitSelf)&bar_initSelf, // initSelf
+ (ERDParseSelf)&bar_parseSelf, // parseSelf
+ (ERDUnparseSelf)&bar_unparseSelf, // unparseSelf
+ NULL // initChoice
+};
+
+static const data data_compute_offsets;
+
+static const size_t data_offsets[3] = {
+ (const char *)&data_compute_offsets._choice - (const char
*)&data_compute_offsets,
+ (const char *)&data_compute_offsets.foo - (const char
*)&data_compute_offsets,
+ (const char *)&data_compute_offsets.bar - (const char
*)&data_compute_offsets
+};
+
+static const ERD *data_childrenERDs[3] = {
+ &_choice_data_NestedUnionType_ERD,
+ &foo_data_NestedUnionType_ERD,
+ &bar_data_NestedUnionType_ERD
+};
+
+static const ERD data_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "data", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ COMPLEX, // typeCode
+ 2, // numChildren
+ data_offsets, // offsets
+ data_childrenERDs, // childrenERDs
Review comment:
You're looking at data_childrenERDs which has 3 (not 2) child ERDs in
it. The 1st child ERD is for the _choice field preceding the choice group and
the 2nd & 3rd child ERDs are for each of the 2 alternative elements in data's
choice group. I made the assumption that a choice group's ERD would have no
other child ERDs in it, but we can have more child elements preceding /
following the _choice field and union field if we inline anonymous choice
groups into the parent group.
##########
File path: daffodil-runtime2/src/main/resources/examples/ex_nums.c
##########
@@ -660,10 +559,10 @@ littleEndian_unparseSelf(const littleEndian *instance,
UState *ustate)
static void
ex_nums_initSelf(ex_nums *instance)
{
+ instance->_base.erd = &ex_nums_ERD;
array_initSelf(&instance->array);
bigEndian_initSelf(&instance->bigEndian);
littleEndian_initSelf(&instance->littleEndian);
- instance->_base.erd = &ex_nums__ERD;
}
Review comment:
Test cases don't use those src/main/resources/examples files. I keep
them close to the rest of the main C files because they serve as examples of
what the code generator will create as well as scaffolding for further changes
to be made. For example, I manually edited ex_nums.h and ex_nums.c to include
arrays, figured out what the C code for parsing and unparsing arrays should
look like, and finally made corresponding changes to the code generator so I
could replace ex_nums.[ch] with the generator's automatically generated code.
I also included NestedUnion.[ch] in the pull request to 1) show everyone what
generated code for choice groups looks like, and 2) let us make changes to that
code manually in the future if necessary.
Saying handCrafted isn't quite the right name. Every example is generated
by running "daffodil generate c" on a corresponding DFDL schema in the test
resources. Sometimes I hand craft changes to those examples, but I end up
replacing them with the generated code in my pull request to let the examples
show what the generated code really looks like. I do have to rename the files
(including the #include <generated_code.h> header) and add the Apache copyright
each time I replace the files.
##########
File path: daffodil-runtime2/src/main/resources/examples/ex_nums.c
##########
@@ -269,186 +211,137 @@ static const ptrdiff_t bigEndian_offsets[10] = {
};
static const ERD *bigEndian_childrenERDs[10] = {
- &be_double_bigEndian_ex_nums__ERD,
- &be_float_bigEndian_ex_nums__ERD,
- &be_uint64_bigEndian_ex_nums__ERD,
- &be_uint32_bigEndian_ex_nums__ERD,
- &be_uint16_bigEndian_ex_nums__ERD,
- &be_uint8_bigEndian_ex_nums__ERD,
- &be_int64_bigEndian_ex_nums__ERD,
- &be_int32_bigEndian_ex_nums__ERD,
- &be_int16_bigEndian_ex_nums__ERD,
- &be_int8_bigEndian_ex_nums__ERD
-};
-
-static const ERD bigEndian_ex_nums__ERD = {
+ &be_double_bigEndian_ex_nums_ERD,
+ &be_float_bigEndian_ex_nums_ERD,
+ &be_uint64_bigEndian_ex_nums_ERD,
+ &be_uint32_bigEndian_ex_nums_ERD,
+ &be_uint16_bigEndian_ex_nums_ERD,
+ &be_uint8_bigEndian_ex_nums_ERD,
+ &be_int64_bigEndian_ex_nums_ERD,
+ &be_int32_bigEndian_ex_nums_ERD,
+ &be_int16_bigEndian_ex_nums_ERD,
+ &be_int8_bigEndian_ex_nums_ERD
+};
+
+static const ERD bigEndian_ex_nums_ERD = {
{
NULL, // namedQName.prefix
"bigEndian", // namedQName.local
NULL, // namedQName.ns
},
- COMPLEX, // typeCode
- 10, // numChildren
- bigEndian_offsets, // offsets
- bigEndian_childrenERDs, // childrenERDs
- (ERDInitSelf)&bigEndian_initSelf, // initSelf
- (ERDParseSelf)&bigEndian_parseSelf, // parseSelf
+ COMPLEX, // typeCode
+ 10, // numChildren
+ bigEndian_offsets, // offsets
+ bigEndian_childrenERDs, // childrenERDs
+ (ERDInitSelf)&bigEndian_initSelf, // initSelf
+ (ERDParseSelf)&bigEndian_parseSelf, // parseSelf
(ERDUnparseSelf)&bigEndian_unparseSelf, // unparseSelf
+ NULL // initChoice
};
-static const ERD le_uint64_littleEndian_ex_nums__ERD = {
+static const ERD le_uint64_littleEndian_ex_nums_ERD = {
{
NULL, // namedQName.prefix
"le_uint64", // namedQName.local
NULL, // namedQName.ns
},
PRIMITIVE_UINT64, // typeCode
- 0, // numChildren
- NULL, // offsets
- NULL, // childrenERDs
- NULL, // initSelf
- NULL, // parseSelf
- NULL, // unparseSelf
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
};
-static const ERD le_uint32_littleEndian_ex_nums__ERD = {
+static const ERD le_uint32_littleEndian_ex_nums_ERD = {
{
NULL, // namedQName.prefix
"le_uint32", // namedQName.local
NULL, // namedQName.ns
},
PRIMITIVE_UINT32, // typeCode
- 0, // numChildren
- NULL, // offsets
- NULL, // childrenERDs
- NULL, // initSelf
- NULL, // parseSelf
- NULL, // unparseSelf
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
};
-static const ERD le_uint16_littleEndian_ex_nums__ERD = {
+static const ERD le_uint16_littleEndian_ex_nums_ERD = {
{
NULL, // namedQName.prefix
"le_uint16", // namedQName.local
NULL, // namedQName.ns
},
PRIMITIVE_UINT16, // typeCode
- 0, // numChildren
- NULL, // offsets
- NULL, // childrenERDs
- NULL, // initSelf
- NULL, // parseSelf
- NULL, // unparseSelf
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
};
-static const ERD le_uint8_littleEndian_ex_nums__ERD = {
+static const ERD le_uint8_littleEndian_ex_nums_ERD = {
{
NULL, // namedQName.prefix
"le_uint8", // namedQName.local
NULL, // namedQName.ns
},
PRIMITIVE_UINT8, // typeCode
- 0, // numChildren
- NULL, // offsets
- NULL, // childrenERDs
- NULL, // initSelf
- NULL, // parseSelf
- NULL, // unparseSelf
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
};
-static const ERD le_int64_littleEndian_ex_nums__ERD = {
+static const ERD le_int64_littleEndian_ex_nums_ERD = {
{
NULL, // namedQName.prefix
"le_int64", // namedQName.local
NULL, // namedQName.ns
},
PRIMITIVE_INT64, // typeCode
- 0, // numChildren
- NULL, // offsets
- NULL, // childrenERDs
- NULL, // initSelf
- NULL, // parseSelf
- NULL, // unparseSelf
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
};
-static const ERD le_int32_littleEndian_ex_nums__ERD = {
+static const ERD le_int32_littleEndian_ex_nums_ERD = {
{
NULL, // namedQName.prefix
"le_int32", // namedQName.local
NULL, // namedQName.ns
},
PRIMITIVE_INT32, // typeCode
- 0, // numChildren
- NULL, // offsets
- NULL, // childrenERDs
- NULL, // initSelf
- NULL, // parseSelf
- NULL, // unparseSelf
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
};
-static const ERD le_int16_littleEndian_ex_nums__ERD = {
+static const ERD le_int16_littleEndian_ex_nums_ERD = {
{
NULL, // namedQName.prefix
"le_int16", // namedQName.local
NULL, // namedQName.ns
},
PRIMITIVE_INT16, // typeCode
- 0, // numChildren
- NULL, // offsets
- NULL, // childrenERDs
- NULL, // initSelf
- NULL, // parseSelf
- NULL, // unparseSelf
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
};
-static const ERD le_int8_littleEndian_ex_nums__ERD = {
+static const ERD le_int8_littleEndian_ex_nums_ERD = {
{
NULL, // namedQName.prefix
"le_int8", // namedQName.local
NULL, // namedQName.ns
},
PRIMITIVE_INT8, // typeCode
- 0, // numChildren
- NULL, // offsets
- NULL, // childrenERDs
- NULL, // initSelf
- NULL, // parseSelf
- NULL, // unparseSelf
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
};
-static const ERD le_float_littleEndian_ex_nums__ERD = {
+static const ERD le_float_littleEndian_ex_nums_ERD = {
{
NULL, // namedQName.prefix
"le_float", // namedQName.local
NULL, // namedQName.ns
},
PRIMITIVE_FLOAT, // typeCode
- 0, // numChildren
- NULL, // offsets
- NULL, // childrenERDs
- NULL, // initSelf
- NULL, // parseSelf
- NULL, // unparseSelf
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
};
-static const ERD le_double_littleEndian_ex_nums__ERD = {
+static const ERD le_double_littleEndian_ex_nums_ERD = {
{
NULL, // namedQName.prefix
"le_double", // namedQName.local
NULL, // namedQName.ns
},
PRIMITIVE_DOUBLE, // typeCode
- 0, // numChildren
- NULL, // offsets
- NULL, // childrenERDs
- NULL, // initSelf
- NULL, // parseSelf
- NULL, // unparseSelf
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
};
static const littleEndian littleEndian_compute_offsets;
-static const ptrdiff_t littleEndian_offsets[10] = {
+static const size_t littleEndian_offsets[10] = {
Review comment:
Are you saying that we could merge some offsets tables such as
bigEndian_offsets and littleEndian_offsets together to save space since they
have identical contents? The DFDL schema has separate bigEndian and
littleEndian elements so the C code generator generates everything for them
separately.
##########
File path: daffodil-runtime2/src/main/resources/examples/ex_nums.c
##########
@@ -80,182 +71,133 @@ static const ptrdiff_t array_offsets[6] = {
};
static const ERD *array_childrenERDs[6] = {
- &be_int16_array_ex_nums__ERD,
- &be_int16_array_ex_nums__ERD,
- &be_int16_array_ex_nums__ERD,
- &be_float_array_ex_nums__ERD,
- &be_float_array_ex_nums__ERD,
- &be_float_array_ex_nums__ERD
+ &be_int16_array_ex_nums_ERD,
+ &be_int16_array_ex_nums_ERD,
+ &be_int16_array_ex_nums_ERD,
+ &be_float_array_ex_nums_ERD,
+ &be_float_array_ex_nums_ERD,
+ &be_float_array_ex_nums_ERD
};
-static const ERD array_ex_nums__ERD = {
+static const ERD array_ex_nums_ERD = {
{
NULL, // namedQName.prefix
"array", // namedQName.local
NULL, // namedQName.ns
},
- COMPLEX, // typeCode
- 6, // numChildren
- array_offsets, // offsets
- array_childrenERDs, // childrenERDs
- (ERDInitSelf)&array_initSelf, // initSelf
- (ERDParseSelf)&array_parseSelf, // parseSelf
+ COMPLEX, // typeCode
+ 6, // numChildren
Review comment:
Agreed, do you want me to make those changes in a future pull request or
can you? I would love to get some help :).
##########
File path: daffodil-runtime2/src/main/resources/examples/ex_nums.c
##########
@@ -80,182 +71,133 @@ static const ptrdiff_t array_offsets[6] = {
};
static const ERD *array_childrenERDs[6] = {
- &be_int16_array_ex_nums__ERD,
- &be_int16_array_ex_nums__ERD,
- &be_int16_array_ex_nums__ERD,
- &be_float_array_ex_nums__ERD,
- &be_float_array_ex_nums__ERD,
- &be_float_array_ex_nums__ERD
+ &be_int16_array_ex_nums_ERD,
Review comment:
I tried to handle arrays differently than scalars when looping over
child elements in infoset.c's walkInfosetNode function. However, I realized if
I expanded arrays inline within childrenERDs, the loop wouldn't need any
special case treatment of arrays. Remember that the C language has less
self-reflection capability than Java or Scala which makes it difficult to find
the actual size of arrays using the `sizeof()` operator (arrays lose their
compile time size information at runtime). If we find a place to put an
explicit array size somewhere in the ERD structure, I agree we could avoid
expanding arrays inline. I don't expect our collaborators to use very large
arrays in their wire protocol packets, but once we start supporting other use
cases that require very large arrays, we'll need to change the ERD structure.
##########
File path: daffodil-runtime2/src/main/resources/examples/NestedUnion.c
##########
@@ -0,0 +1,408 @@
+/*
+ * 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.
+ */
+
+#include "NestedUnion.h"
+#include "parsers.h" // for parse_be_double, parse_be_float,
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_double,
parse_le_float, 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_double, unparse_be_float,
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_double, unparse_le_float, 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 <math.h> // for NAN
+#include <stdbool.h> // for bool, false, true
+#include <stddef.h> // for NULL, size_t
+
+// Prototypes needed for compilation
+
+static void foo_initSelf(foo *instance);
+static void foo_parseSelf(foo *instance, PState *pstate);
+static void foo_unparseSelf(const foo *instance, UState *ustate);
+static void bar_initSelf(bar *instance);
+static void bar_parseSelf(bar *instance, PState *pstate);
+static void bar_unparseSelf(const bar *instance, UState *ustate);
+static void data_initSelf(data *instance);
+static bool data_initChoice(data *instance, const NestedUnion *rootElement);
+static void data_parseSelf(data *instance, PState *pstate);
+static void data_unparseSelf(const data *instance, UState *ustate);
+static void NestedUnion_initSelf(NestedUnion *instance);
+static void NestedUnion_parseSelf(NestedUnion *instance, PState *pstate);
+static void NestedUnion_unparseSelf(const NestedUnion *instance, UState
*ustate);
+
+// Metadata singletons
+
+static const ERD tag_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "tag", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_INT32, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD _choice_data_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "_choice", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ CHOICE, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD a_FooType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "a", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_INT32, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD b_FooType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "b", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_INT32, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD c_FooType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "c", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_INT32, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const foo foo_compute_offsets;
+
+static const size_t foo_offsets[3] = {
+ (const char *)&foo_compute_offsets.a - (const char *)&foo_compute_offsets,
+ (const char *)&foo_compute_offsets.b - (const char *)&foo_compute_offsets,
+ (const char *)&foo_compute_offsets.c - (const char *)&foo_compute_offsets
+};
+
+static const ERD *foo_childrenERDs[3] = {
+ &a_FooType_ERD,
+ &b_FooType_ERD,
+ &c_FooType_ERD
+};
+
+static const ERD foo_data_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "foo", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ COMPLEX, // typeCode
+ 3, // numChildren
+ foo_offsets, // offsets
+ foo_childrenERDs, // childrenERDs
+ (ERDInitSelf)&foo_initSelf, // initSelf
+ (ERDParseSelf)&foo_parseSelf, // parseSelf
+ (ERDUnparseSelf)&foo_unparseSelf, // unparseSelf
+ NULL // initChoice
+};
+
+static const ERD x_BarType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "x", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_DOUBLE, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD y_BarType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "y", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_DOUBLE, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD z_BarType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "z", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_DOUBLE, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const bar bar_compute_offsets;
+
+static const size_t bar_offsets[3] = {
+ (const char *)&bar_compute_offsets.x - (const char *)&bar_compute_offsets,
+ (const char *)&bar_compute_offsets.y - (const char *)&bar_compute_offsets,
+ (const char *)&bar_compute_offsets.z - (const char *)&bar_compute_offsets
+};
+
+static const ERD *bar_childrenERDs[3] = {
+ &x_BarType_ERD,
+ &y_BarType_ERD,
+ &z_BarType_ERD
+};
+
+static const ERD bar_data_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "bar", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ COMPLEX, // typeCode
+ 3, // numChildren
+ bar_offsets, // offsets
+ bar_childrenERDs, // childrenERDs
+ (ERDInitSelf)&bar_initSelf, // initSelf
+ (ERDParseSelf)&bar_parseSelf, // parseSelf
+ (ERDUnparseSelf)&bar_unparseSelf, // unparseSelf
+ NULL // initChoice
+};
+
+static const data data_compute_offsets;
+
+static const size_t data_offsets[3] = {
+ (const char *)&data_compute_offsets._choice - (const char
*)&data_compute_offsets,
+ (const char *)&data_compute_offsets.foo - (const char
*)&data_compute_offsets,
+ (const char *)&data_compute_offsets.bar - (const char
*)&data_compute_offsets
+};
+
+static const ERD *data_childrenERDs[3] = {
+ &_choice_data_NestedUnionType_ERD,
+ &foo_data_NestedUnionType_ERD,
+ &bar_data_NestedUnionType_ERD
+};
+
+static const ERD data_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "data", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ COMPLEX, // typeCode
+ 2, // numChildren
+ data_offsets, // offsets
+ data_childrenERDs, // childrenERDs
+ (ERDInitSelf)&data_initSelf, // initSelf
+ (ERDParseSelf)&data_parseSelf, // parseSelf
+ (ERDUnparseSelf)&data_unparseSelf, // unparseSelf
+ (ERDInitChoice)&data_initChoice // initChoice
Review comment:
The only ERD which has a non-null initChoice is a choice group which
must choose a single child element from a set of alternative child elements.
Is that your understanding, or are you thinking that the ERD for a _choice
field would have a non-null initSelf/initChoice? Right now the ERD for a
_choice field is a CHOICE type with only a name defined for it and we always
assign the NO_CHOICE constant to it when initializing it in its parent's
initSelf:
```c
static const ERD _choice_data_NestedUnionType_ERD = {
{
NULL, // namedQName.prefix
"_choice", // namedQName.local
NULL, // namedQName.ns
},
CHOICE, // typeCode
0, NULL, NULL, NULL, NULL, NULL, NULL
};
```
It would be nice if we could merge initSelf and initChoice, but I see a
couple of problems. One problem is that initSelf and initChoice take different
parameters. The C code we generate from the choiceDispatchKey expression needs
to start from the root element's base in order to return the value of the
correct dispatch key field. The second and more serious problem is that
initSelf overwrites *every* C struct member field with "uninitialized" constant
values. We don't ever want to call initSelf again after initializing an
infoset root instance, especially not when we are reading data into the
infoset, walking the infoset, or writing data from the infoset. An initSelf
for a choice group calls initSelf on each of the alternative elements to set
their fields to uninitialized constant values, while initChoice changes only
the value in the _choice member field and the ERD pointer value in the
following union child's _base.erd member field.
##########
File path: daffodil-runtime2/src/main/resources/examples/NestedUnion.c
##########
@@ -0,0 +1,408 @@
+/*
+ * 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.
+ */
+
+#include "NestedUnion.h"
+#include "parsers.h" // for parse_be_double, parse_be_float,
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_double,
parse_le_float, 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_double, unparse_be_float,
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_double, unparse_le_float, 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 <math.h> // for NAN
+#include <stdbool.h> // for bool, false, true
+#include <stddef.h> // for NULL, size_t
+
+// Prototypes needed for compilation
+
+static void foo_initSelf(foo *instance);
+static void foo_parseSelf(foo *instance, PState *pstate);
+static void foo_unparseSelf(const foo *instance, UState *ustate);
+static void bar_initSelf(bar *instance);
+static void bar_parseSelf(bar *instance, PState *pstate);
+static void bar_unparseSelf(const bar *instance, UState *ustate);
+static void data_initSelf(data *instance);
+static bool data_initChoice(data *instance, const NestedUnion *rootElement);
+static void data_parseSelf(data *instance, PState *pstate);
+static void data_unparseSelf(const data *instance, UState *ustate);
+static void NestedUnion_initSelf(NestedUnion *instance);
+static void NestedUnion_parseSelf(NestedUnion *instance, PState *pstate);
+static void NestedUnion_unparseSelf(const NestedUnion *instance, UState
*ustate);
+
+// Metadata singletons
+
+static const ERD tag_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "tag", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_INT32, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD _choice_data_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "_choice", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ CHOICE, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD a_FooType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "a", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_INT32, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD b_FooType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "b", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_INT32, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD c_FooType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "c", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_INT32, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const foo foo_compute_offsets;
+
+static const size_t foo_offsets[3] = {
+ (const char *)&foo_compute_offsets.a - (const char *)&foo_compute_offsets,
+ (const char *)&foo_compute_offsets.b - (const char *)&foo_compute_offsets,
+ (const char *)&foo_compute_offsets.c - (const char *)&foo_compute_offsets
+};
+
+static const ERD *foo_childrenERDs[3] = {
+ &a_FooType_ERD,
+ &b_FooType_ERD,
+ &c_FooType_ERD
+};
+
+static const ERD foo_data_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "foo", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ COMPLEX, // typeCode
+ 3, // numChildren
+ foo_offsets, // offsets
+ foo_childrenERDs, // childrenERDs
+ (ERDInitSelf)&foo_initSelf, // initSelf
+ (ERDParseSelf)&foo_parseSelf, // parseSelf
+ (ERDUnparseSelf)&foo_unparseSelf, // unparseSelf
+ NULL // initChoice
+};
+
+static const ERD x_BarType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "x", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_DOUBLE, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD y_BarType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "y", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_DOUBLE, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD z_BarType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "z", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_DOUBLE, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const bar bar_compute_offsets;
+
+static const size_t bar_offsets[3] = {
+ (const char *)&bar_compute_offsets.x - (const char *)&bar_compute_offsets,
+ (const char *)&bar_compute_offsets.y - (const char *)&bar_compute_offsets,
+ (const char *)&bar_compute_offsets.z - (const char *)&bar_compute_offsets
+};
+
+static const ERD *bar_childrenERDs[3] = {
+ &x_BarType_ERD,
+ &y_BarType_ERD,
+ &z_BarType_ERD
+};
+
+static const ERD bar_data_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "bar", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ COMPLEX, // typeCode
+ 3, // numChildren
+ bar_offsets, // offsets
+ bar_childrenERDs, // childrenERDs
+ (ERDInitSelf)&bar_initSelf, // initSelf
+ (ERDParseSelf)&bar_parseSelf, // parseSelf
+ (ERDUnparseSelf)&bar_unparseSelf, // unparseSelf
+ NULL // initChoice
+};
+
+static const data data_compute_offsets;
+
+static const size_t data_offsets[3] = {
+ (const char *)&data_compute_offsets._choice - (const char
*)&data_compute_offsets,
+ (const char *)&data_compute_offsets.foo - (const char
*)&data_compute_offsets,
+ (const char *)&data_compute_offsets.bar - (const char
*)&data_compute_offsets
+};
+
+static const ERD *data_childrenERDs[3] = {
+ &_choice_data_NestedUnionType_ERD,
+ &foo_data_NestedUnionType_ERD,
+ &bar_data_NestedUnionType_ERD
+};
+
+static const ERD data_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "data", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ COMPLEX, // typeCode
+ 2, // numChildren
+ data_offsets, // offsets
+ data_childrenERDs, // childrenERDs
+ (ERDInitSelf)&data_initSelf, // initSelf
+ (ERDParseSelf)&data_parseSelf, // parseSelf
+ (ERDUnparseSelf)&data_unparseSelf, // unparseSelf
+ (ERDInitChoice)&data_initChoice // initChoice
+};
+
+static const NestedUnion NestedUnion_compute_offsets;
+
+static const size_t NestedUnion_offsets[2] = {
+ (const char *)&NestedUnion_compute_offsets.tag - (const char
*)&NestedUnion_compute_offsets,
+ (const char *)&NestedUnion_compute_offsets.data - (const char
*)&NestedUnion_compute_offsets
+};
+
+static const ERD *NestedUnion_childrenERDs[2] = {
+ &tag_NestedUnionType_ERD,
+ &data_NestedUnionType_ERD
+};
+
+static const ERD NestedUnion_ERD = {
+ {
+ "idl", // namedQName.prefix
+ "NestedUnion", // namedQName.local
+ "urn:idl:1.0", // namedQName.ns
+ },
+ COMPLEX, // typeCode
+ 2, // numChildren
+ NestedUnion_offsets, // offsets
+ NestedUnion_childrenERDs, // childrenERDs
+ (ERDInitSelf)&NestedUnion_initSelf, // initSelf
+ (ERDParseSelf)&NestedUnion_parseSelf, // parseSelf
+ (ERDUnparseSelf)&NestedUnion_unparseSelf, // unparseSelf
+ NULL // initChoice
+};
+
+// Return a root element to be used for parsing or unparsing
+
+InfosetBase *
+rootElement(void)
+{
+ static bool initialized;
+ static NestedUnion root;
+ if (!initialized)
+ {
+ NestedUnion_initSelf(&root);
+ initialized = true;
+ }
+ return &root._base;
+}
+
+// Methods to initialize, parse, and unparse infoset nodes
+
+static void
+foo_initSelf(foo *instance)
+{
+ instance->_base.erd = &foo_data_NestedUnionType_ERD;
+ instance->a = 0xCCCCCCCC;
+ instance->b = 0xCCCCCCCC;
+ instance->c = 0xCCCCCCCC;
+}
+
+static void
+foo_parseSelf(foo *instance, PState *pstate)
+{
+ parse_be_int32(&instance->a, pstate);
+ parse_be_int32(&instance->b, pstate);
+ parse_be_int32(&instance->c, pstate);
+}
+
+static void
+foo_unparseSelf(const foo *instance, UState *ustate)
+{
+ unparse_be_int32(instance->a, ustate);
+ unparse_be_int32(instance->b, ustate);
+ unparse_be_int32(instance->c, ustate);
+}
+
+static void
+bar_initSelf(bar *instance)
+{
+ instance->_base.erd = &bar_data_NestedUnionType_ERD;
+ instance->x = NAN;
+ instance->y = NAN;
+ instance->z = NAN;
+}
+
+static void
+bar_parseSelf(bar *instance, PState *pstate)
+{
+ parse_be_double(&instance->x, pstate);
+ parse_be_double(&instance->y, pstate);
+ parse_be_double(&instance->z, pstate);
+}
+
+static void
+bar_unparseSelf(const bar *instance, UState *ustate)
+{
+ unparse_be_double(instance->x, ustate);
+ unparse_be_double(instance->y, ustate);
+ unparse_be_double(instance->z, ustate);
+}
+
+static void
+data_initSelf(data *instance)
+{
+ instance->_base.erd = &data_NestedUnionType_ERD;
+ instance->_choice = NO_CHOICE;
+ foo_initSelf(&instance->foo);
+ bar_initSelf(&instance->bar);
+}
+
+static bool
+data_initChoice(data *instance, const NestedUnion *rootElement)
+{
+ int64_t key = rootElement->tag;
+ switch (key)
+ {
+ case 1:
+ case 2:
+ instance->_choice = 0;
+ break;
+ case 3:
+ case 4:
+ instance->_choice = 1;
+ break;
+ default:
+ instance->_choice = NO_CHOICE;
+ break;
+ }
+
+ if (instance->_choice != NO_CHOICE)
+ {
+ const size_t choice = instance->_choice + 1; // skip the _choice field
+ const size_t offset = instance->_base.erd->offsets[choice];
Review comment:
You're looking at the initChoice function's code following a dispatch
key switch statement. The preceding switch statement has just computed the
choice value used to index into the offsets & ERDs of the choice group's
children and the code you're now looking at is about to update the ERD pointer
value in the union child's _base.erd member field.
##########
File path:
daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala
##########
@@ -105,41 +129,178 @@ class CodeGeneratorState {
qnameInit
}
+ /**
+ * We want to convert a choiceDispatchKey expression into C struct dot
+ * notation (rootElement->[subElement.field]) which will access the C
+ * struct field containing the choiceDispatchKey's runtime value.
+ *
+ * We make some assumptions to make generating the dot notation easier:
+ * - the expression starts with '{xs:string( and ends with )}'
+ * - the expression returns the value of a previous element without
+ * changing the value in any way (except converting it to xs:string)
+ * - both the expression and the C code use only local names (for now...)
+ * - we can map the context node's path to a Unix-like slash path
+ * - all dpath operations look like Unix-like relative paths (../tag)
+ * - we can normalize the new path and convert it to C struct dot notation
+ * - we can store the accessed value in an int64_t local variable safely
+ */
+ private def choiceDispatchField(context: ElementBase): String = {
+ // We want to use SchemaComponent.scPath but it's private so duplicate it
here (for now...)
+ def scPath(sc: SchemaComponent): Seq[SchemaComponent] =
sc.optLexicalParent.map { scPath }.getOrElse(Nil) :+ sc
+ val localNames = scPath(context).map {
+ case er: AbstractElementRef => er.refQName.local
+ case e: ElementBase => e.namedQName.local
+ case ed: GlobalElementDecl => ed.namedQName.local
+ case _ => ""
+ }
+ val absoluteSlashPath = localNames.mkString("/")
+ val dispatchSlashPath = context.complexType.modelGroup match {
+ case choice: Choice if choice.isDirectDispatch =>
+ val expr = choice.choiceDispatchKeyEv.expr.toBriefXML()
+ val before = "'{xs:string("
+ val after = ")}'"
+ val relativePath = if (expr.startsWith(before) && expr.endsWith(after))
+ expr.substring(before.length, expr.length - after.length) else expr
+ val normalizedURI = new URI(absoluteSlashPath + "/" +
relativePath).normalize
+ normalizedURI.getPath.substring(1)
+ case _ => ""
+ }
+ // Strip namespace prefixes since C code uses only local names (for now...)
+ val localDispatchSlashPath = dispatchSlashPath.replaceAll("/[^:]+:", "/")
+ val res = localDispatchSlashPath.replace('/', '.')
+ res
+ }
+
+ def addBeforeSwitchStatements(context: ElementBase): Unit = {
+ val erd = erdName(context)
+ val initStatement = s" instance->_base.erd = &$erd;"
+
+ structs.top.initStatements += initStatement
+
+ val dispatchField = choiceDispatchField(context)
+ if (dispatchField.nonEmpty) {
+ val C = localName(context)
+ val declaration =
+ s""" size_t _choice; // choice of which union field to use
+ | union
+ | {""".stripMargin
+ val erdDef =
+ s"""static const ERD _choice_$erd = {
+ | {
+ | NULL, // namedQName.prefix
+ | "_choice", // namedQName.local
+ | NULL, // namedQName.ns
+ | },
+ | CHOICE, // typeCode
+ | 0, NULL, NULL, NULL, NULL, NULL, NULL
+ |};
+ |""".stripMargin
+ val offsetComputation = s" (const char
*)&${C}_compute_offsets._choice - (const char *)&${C}_compute_offsets"
+ val erdComputation = s" &_choice_$erd"
+ val initStatement = s" instance->_choice = NO_CHOICE;"
+ val initChoiceStatement =
+ s""" int64_t key = rootElement->$dispatchField;
+ | switch (key)
+ | {""".stripMargin
+ val parseStatement =
+ s""" instance->_base.erd->initChoice(&instance->_base,
rootElement());
+ | switch (instance->_choice)
+ | {""".stripMargin
+ val unparseStatement =
+ s""" instance->_base.erd->initChoice(&instance->_base,
rootElement());
+ | switch (instance->_choice)
+ | {""".stripMargin
+
+ erds += erdDef
+ structs.top.declarations += declaration
+ structs.top.offsetComputations += offsetComputation
+ structs.top.erdComputations += erdComputation
+ structs.top.initStatements += initStatement
+ structs.top.initChoiceStatements += initChoiceStatement
+ structs.top.parserStatements += parseStatement
+ structs.top.unparserStatements += unparseStatement
+ }
+ }
+
+ def addAfterSwitchStatements(): Unit = {
+ if (structs.top.initChoiceStatements.nonEmpty) {
+ val declaration = s" };"
+ val initChoiceStatement =
+ s""" default:
+ | instance->_choice = NO_CHOICE;
+ | break;
+ | }
+ |
+ | if (instance->_choice != NO_CHOICE)
+ | {
+ | const size_t choice = instance->_choice + 1; // skip the
_choice field
+ | const size_t offset = instance->_base.erd->offsets[choice];
+ | const ERD * childERD =
instance->_base.erd->childrenERDs[choice];
+ | InfosetBase *childNode = (InfosetBase *)((const char
*)instance + offset);
+ | childNode->erd = childERD;
+ | return true;
+ | }
+ | else
+ | {
+ | return false;
+ | }""".stripMargin
+ val parseStatement =
+ s""" default:
+ | pstate->error_msg = "node's _choice field was not
initialized during parsing";
Review comment:
This place is where we report a processing error at runtime. It's not
where we report an error that comes from users making mistakes writing schemas,
although it's possible that a failure of the implementation could allow some
errors to result in processing errors that should have been reported as schema
definition errors sooner.
##########
File path:
daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala
##########
@@ -105,41 +129,178 @@ class CodeGeneratorState {
qnameInit
}
+ /**
+ * We want to convert a choiceDispatchKey expression into C struct dot
+ * notation (rootElement->[subElement.field]) which will access the C
+ * struct field containing the choiceDispatchKey's runtime value.
+ *
Review comment:
Yes, let's modify the DPath expression compiler to generate C code in a
future pull request.
##########
File path: daffodil-runtime2/src/main/resources/examples/NestedUnion.c
##########
@@ -0,0 +1,408 @@
+/*
+ * 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.
+ */
+
+#include "NestedUnion.h"
+#include "parsers.h" // for parse_be_double, parse_be_float,
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_double,
parse_le_float, 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_double, unparse_be_float,
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_double, unparse_le_float, 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 <math.h> // for NAN
+#include <stdbool.h> // for bool, false, true
+#include <stddef.h> // for NULL, size_t
+
+// Prototypes needed for compilation
+
+static void foo_initSelf(foo *instance);
+static void foo_parseSelf(foo *instance, PState *pstate);
+static void foo_unparseSelf(const foo *instance, UState *ustate);
+static void bar_initSelf(bar *instance);
+static void bar_parseSelf(bar *instance, PState *pstate);
+static void bar_unparseSelf(const bar *instance, UState *ustate);
+static void data_initSelf(data *instance);
+static bool data_initChoice(data *instance, const NestedUnion *rootElement);
+static void data_parseSelf(data *instance, PState *pstate);
+static void data_unparseSelf(const data *instance, UState *ustate);
+static void NestedUnion_initSelf(NestedUnion *instance);
+static void NestedUnion_parseSelf(NestedUnion *instance, PState *pstate);
+static void NestedUnion_unparseSelf(const NestedUnion *instance, UState
*ustate);
+
+// Metadata singletons
+
+static const ERD tag_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "tag", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_INT32, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD _choice_data_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "_choice", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ CHOICE, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD a_FooType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "a", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_INT32, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD b_FooType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "b", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_INT32, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD c_FooType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "c", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_INT32, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const foo foo_compute_offsets;
+
+static const size_t foo_offsets[3] = {
+ (const char *)&foo_compute_offsets.a - (const char *)&foo_compute_offsets,
+ (const char *)&foo_compute_offsets.b - (const char *)&foo_compute_offsets,
+ (const char *)&foo_compute_offsets.c - (const char *)&foo_compute_offsets
+};
+
+static const ERD *foo_childrenERDs[3] = {
+ &a_FooType_ERD,
+ &b_FooType_ERD,
+ &c_FooType_ERD
+};
+
+static const ERD foo_data_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "foo", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ COMPLEX, // typeCode
+ 3, // numChildren
+ foo_offsets, // offsets
+ foo_childrenERDs, // childrenERDs
+ (ERDInitSelf)&foo_initSelf, // initSelf
+ (ERDParseSelf)&foo_parseSelf, // parseSelf
+ (ERDUnparseSelf)&foo_unparseSelf, // unparseSelf
+ NULL // initChoice
+};
+
+static const ERD x_BarType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "x", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_DOUBLE, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD y_BarType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "y", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_DOUBLE, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const ERD z_BarType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "z", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ PRIMITIVE_DOUBLE, // typeCode
+ 0, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const bar bar_compute_offsets;
+
+static const size_t bar_offsets[3] = {
+ (const char *)&bar_compute_offsets.x - (const char *)&bar_compute_offsets,
+ (const char *)&bar_compute_offsets.y - (const char *)&bar_compute_offsets,
+ (const char *)&bar_compute_offsets.z - (const char *)&bar_compute_offsets
+};
+
+static const ERD *bar_childrenERDs[3] = {
+ &x_BarType_ERD,
+ &y_BarType_ERD,
+ &z_BarType_ERD
+};
+
+static const ERD bar_data_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "bar", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ COMPLEX, // typeCode
+ 3, // numChildren
+ bar_offsets, // offsets
+ bar_childrenERDs, // childrenERDs
+ (ERDInitSelf)&bar_initSelf, // initSelf
+ (ERDParseSelf)&bar_parseSelf, // parseSelf
+ (ERDUnparseSelf)&bar_unparseSelf, // unparseSelf
+ NULL // initChoice
+};
+
+static const data data_compute_offsets;
+
+static const size_t data_offsets[3] = {
+ (const char *)&data_compute_offsets._choice - (const char
*)&data_compute_offsets,
+ (const char *)&data_compute_offsets.foo - (const char
*)&data_compute_offsets,
+ (const char *)&data_compute_offsets.bar - (const char
*)&data_compute_offsets
+};
+
+static const ERD *data_childrenERDs[3] = {
+ &_choice_data_NestedUnionType_ERD,
+ &foo_data_NestedUnionType_ERD,
+ &bar_data_NestedUnionType_ERD
+};
+
+static const ERD data_NestedUnionType_ERD = {
+ {
+ NULL, // namedQName.prefix
+ "data", // namedQName.local
+ NULL, // namedQName.ns
+ },
+ COMPLEX, // typeCode
+ 2, // numChildren
+ data_offsets, // offsets
+ data_childrenERDs, // childrenERDs
+ (ERDInitSelf)&data_initSelf, // initSelf
+ (ERDParseSelf)&data_parseSelf, // parseSelf
+ (ERDUnparseSelf)&data_unparseSelf, // unparseSelf
+ (ERDInitChoice)&data_initChoice // initChoice
+};
+
+static const NestedUnion NestedUnion_compute_offsets;
+
+static const size_t NestedUnion_offsets[2] = {
+ (const char *)&NestedUnion_compute_offsets.tag - (const char
*)&NestedUnion_compute_offsets,
+ (const char *)&NestedUnion_compute_offsets.data - (const char
*)&NestedUnion_compute_offsets
+};
+
+static const ERD *NestedUnion_childrenERDs[2] = {
+ &tag_NestedUnionType_ERD,
+ &data_NestedUnionType_ERD
+};
+
+static const ERD NestedUnion_ERD = {
+ {
+ "idl", // namedQName.prefix
+ "NestedUnion", // namedQName.local
+ "urn:idl:1.0", // namedQName.ns
+ },
+ COMPLEX, // typeCode
+ 2, // numChildren
+ NestedUnion_offsets, // offsets
+ NestedUnion_childrenERDs, // childrenERDs
+ (ERDInitSelf)&NestedUnion_initSelf, // initSelf
+ (ERDParseSelf)&NestedUnion_parseSelf, // parseSelf
+ (ERDUnparseSelf)&NestedUnion_unparseSelf, // unparseSelf
+ NULL // initChoice
+};
+
+// Return a root element to be used for parsing or unparsing
+
+InfosetBase *
+rootElement(void)
+{
+ static bool initialized;
+ static NestedUnion root;
+ if (!initialized)
+ {
+ NestedUnion_initSelf(&root);
+ initialized = true;
+ }
+ return &root._base;
+}
+
+// Methods to initialize, parse, and unparse infoset nodes
+
+static void
+foo_initSelf(foo *instance)
+{
+ instance->_base.erd = &foo_data_NestedUnionType_ERD;
+ instance->a = 0xCCCCCCCC;
+ instance->b = 0xCCCCCCCC;
+ instance->c = 0xCCCCCCCC;
+}
+
+static void
+foo_parseSelf(foo *instance, PState *pstate)
+{
+ parse_be_int32(&instance->a, pstate);
+ parse_be_int32(&instance->b, pstate);
+ parse_be_int32(&instance->c, pstate);
+}
+
+static void
+foo_unparseSelf(const foo *instance, UState *ustate)
+{
+ unparse_be_int32(instance->a, ustate);
+ unparse_be_int32(instance->b, ustate);
+ unparse_be_int32(instance->c, ustate);
+}
+
+static void
+bar_initSelf(bar *instance)
+{
+ instance->_base.erd = &bar_data_NestedUnionType_ERD;
+ instance->x = NAN;
+ instance->y = NAN;
+ instance->z = NAN;
+}
+
+static void
+bar_parseSelf(bar *instance, PState *pstate)
+{
+ parse_be_double(&instance->x, pstate);
+ parse_be_double(&instance->y, pstate);
+ parse_be_double(&instance->z, pstate);
+}
+
+static void
+bar_unparseSelf(const bar *instance, UState *ustate)
+{
+ unparse_be_double(instance->x, ustate);
+ unparse_be_double(instance->y, ustate);
+ unparse_be_double(instance->z, ustate);
+}
+
+static void
+data_initSelf(data *instance)
+{
+ instance->_base.erd = &data_NestedUnionType_ERD;
+ instance->_choice = NO_CHOICE;
+ foo_initSelf(&instance->foo);
+ bar_initSelf(&instance->bar);
+}
+
+static bool
+data_initChoice(data *instance, const NestedUnion *rootElement)
+{
+ int64_t key = rootElement->tag;
+ switch (key)
+ {
+ case 1:
+ case 2:
+ instance->_choice = 0;
+ break;
+ case 3:
+ case 4:
+ instance->_choice = 1;
+ break;
+ default:
+ instance->_choice = NO_CHOICE;
+ break;
+ }
+
+ if (instance->_choice != NO_CHOICE)
+ {
+ const size_t choice = instance->_choice + 1; // skip the _choice field
+ const size_t offset = instance->_base.erd->offsets[choice];
+ const ERD * childERD = instance->_base.erd->childrenERDs[choice];
+ InfosetBase *childNode = (InfosetBase *)((const char *)instance +
offset);
+ childNode->erd = childERD;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+static void
+data_parseSelf(data *instance, PState *pstate)
+{
+ instance->_base.erd->initChoice(&instance->_base, rootElement());
+ switch (instance->_choice)
+ {
+ case 0:
+ foo_parseSelf(&instance->foo, pstate);
+ break;
+ case 1:
+ bar_parseSelf(&instance->bar, pstate);
+ break;
+ default:
+ pstate->error_msg = "node's _choice field was not initialized during
parsing";
Review comment:
Actually, there's a way to cause a processing error during parse. Note
that a person using the DFDL schema could store a value in the dispatch key
field which has no valid mapping to any branch key of the choice group's
alternative child elements. Daffodil's runtime1 reports a processing error
when this happens in a binary data file, "ChoiceDispatchNoMatch: Parse Error:
Choice dispatch key (0) failed to match any of the branch keys". Note
Daffodil's runtime1 unparses an infoset containing an invalid dispatch key
value without reporting a processing error (it doesn't check whether the
mapping is valid). However, I think it's fine for Daffodil's runtime2 to
perform more strict checking during parse, unparse, and walk. I've revised
each error message to be more clear what the problem is as well:
```c
pstate->error_msg =
"Parse error: no match between choice dispatch key and any branch
key";
```
##########
File path: daffodil-runtime2/src/main/resources/examples/ex_nums.c
##########
@@ -568,6 +467,7 @@ array_unparseSelf(const array *instance, UState *ustate)
static void
bigEndian_initSelf(bigEndian *instance)
Review comment:
I wanted two separate elements in the TestRuntime2 DFDL schema where all
the integers and floating point numbers were either bigEndian or littleEndian
to ensure more complete test coverage of the generated C code. I explicitly
named these elements "bigEndian" and "littleEndian" but the names could have
been "Bar" and "Foo" instead.
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]