This is an automated email from the ASF dual-hosted git repository. jinterrante pushed a commit to branch runtime2-2202 in repository https://gitbox.apache.org/repos/asf/daffodil.git
commit b84dd05592ceaad5ea254b877afcedbbd3f649fa Author: John Interrante <[email protected]> AuthorDate: Wed Apr 7 17:17:12 2021 -0400 Make some daffodil-runtime2 improvements Refactor duplicate code in Binary*CodeGenerator.scala files. Change PState/UState member field from a string error message to a struct error object. Make parse/unparse functions initialize that member field if any error happens. Move checks for errors from parse/unparse functions to their callers, e.g., "if (ustate->error) return;". Collect validation errors without stopping parsing or unparsing by initializing another PState/UState member field pointing to a validation struct with some fields. As last step, update all C files using include-what-you-use to document what's used from each include. Add instructions to README.md how to setup required C tool-chain elements on Linux and Windows. Generate daffodil-runtime2's name and version into generated_code.c automatically so we won't need to keep it up to date. Changelog: In README.md, fix or disable markdownlint warnings. Expand Build Requirements with instructions how to install C compiler, Mini-XML library, java, and sbt on Linux and Windows. Verify/update old hyperlinks and add new MSYS2 and clang hyperlinks. In build.sbt, add Compile / cCompiler and ccArchiveCommand settings as a guide to show developers how to override these default settings if they want sbt to call the C compiler with a different name than "cc". The sbt-cc plugin doesn't iterate through a list (${CC}, cc, gcc, clang, zig cc) to find a C compiler like our runtime2 code does. In daffodil_argp.c, remove argp_program_version's definition since we now generate it automatically within generated_code.c. In daffodil_main.c, add fflush_or_exit and change fopen_or_exit / fclose_or_open to call continue_or_exit with error message instead of calling exit directly. Make main print any validation diagnostics before printing any errors since they've been split into separate PState/UState member fields. Also make main call continue_or_exit instead of calling error directly and pass Error structs, not strings, to all continue_or_exit calls. In stack.c, make stack functions call continue_or_exit instead of calling error directly and pass Error structs, not strings, to all continue_or_exit calls. In xml_reader.c, make conversion functions return Error structs, not strings, to indicate errors converting strings to primitive datums. Make reader functions return Error structs, not strings, to indicate errors reading XML data. In xml_writer.c, make writer functions return Error structs, not strings, to indicate errors writing XML data. Remove redundant stack checking code already performed within stack fuctions. In errors.c (new file), use switch statement inside error_message function to centralize all error messages in one place for easier future internationalization. Use switch statement inside print_mybe_stop function to allow error messages to interpolate an extra detail (string or integer) if needed. Define a single static array that can be used to store validation diagnostics and define a function to print validation diagnostics. Implement continue_or_exit and eof_or_error functions here as well. In errors.h (new file), define enumerations for each type of error which could occur in C code. Define an Error struct to store an enumeration and optionally an extra detail (string or integer). Also define a Diagnostics struct to store an array of data validation errors which should be reported separately from parse errors. Move PState and UState structs here from infoset.h and give them member fields pointing to data validation and parse errors. Also declare need_diagnostics, print_diagnostics, continue_or_exit, eof_or_error prototypes and UNUSED macro in errors.h. In infoset.c, make walkInfosetNode and walkInfoset functions return Error struct, not string. Remove eof_or_error_msg function (now moved to errors.c with shorter name). In infoset.h, change InitChoiceRD and Visit* prototypes to return Error structs, not strings. Remove PState & UState structs, eof_or_error_msg prototype, and UNUSED macro (all moved to errors.h). In parsers.c, make parse_<endian>_<type> functions stop enclosing their code in "if (!pstate->error_msg)" statements and use Error structs, not strings. Callers are now expected to check for errors after each call immediately. Make parse_fill_bytes use read_stream_update_position helper macro too. Make parse_validate_fixed report errors in separate pstate->validati field, not pstate->error field. In unparsers.c, make unparse_<endian>_<type> functions stop enclosing their code in "if (!ustate->error_msg)" statements and use Error structs, not strings. Callers are now expected to check for errors after each call immediately. Make unparse_fill_bytes use write_stream_update_position helper macro too. Make unparse_validate_fixed report errors in separate ustate->validati field, not ustate->error field. In NestedUnion.c and ex_nums.c examples, update with freshly generated code to illustrate code generator changes (in particular, defining argp_program_version, adding an if statement after each parse/unparse call to check for an error and skip rest of calls immediately, and returning errors directly from initChoice functions with choice key that failed to match any branch key). In BinaryAbstractCodeGenerator.scala (new file), refactor common code that used to be duplicated in BinaryBooleanCodeGenerator.scala, BinaryFloatCodeGenerator.scala, and BinaryIntegerKnownLengthCodeGenerator.scala; now they pass parameters to binaryAbstractGenerateCode instead. In CodeGeneratorState.scala, make all necessary changes to report errors using structs instead of strings in generated code including initChoice functions, add if statements after each parse/unparse call, and generate the program name and version automatically. In build.properties, change sbt.version from 1.4.1 to 1.4.9 to ensure successful sbt build from scratch in a new Ubuntu or Windows VM (sbt was getting java.lang.NoClassDefFound: scala/Serializer). --- README.md | 134 +++++++++------ build.sbt | 2 + .../src/main/resources/c/libcli/daffodil_argp.c | 4 - .../src/main/resources/c/libcli/daffodil_main.c | 76 +++++---- .../src/main/resources/c/libcli/stack.c | 14 +- .../src/main/resources/c/libcli/xml_reader.c | 178 +++++++++++--------- .../src/main/resources/c/libcli/xml_reader.h | 2 +- .../src/main/resources/c/libcli/xml_writer.c | 109 +++++++----- .../src/main/resources/c/libcli/xml_writer.h | 2 +- .../src/main/resources/c/libruntime/errors.c | 183 +++++++++++++++++++++ .../src/main/resources/c/libruntime/errors.h | 117 +++++++++++++ .../src/main/resources/c/libruntime/infoset.c | 64 +++---- .../src/main/resources/c/libruntime/infoset.h | 65 ++------ .../src/main/resources/c/libruntime/parsers.c | 132 +++++++-------- .../src/main/resources/c/libruntime/parsers.h | 11 +- .../src/main/resources/c/libruntime/unparsers.c | 102 ++++++------ .../src/main/resources/c/libruntime/unparsers.h | 11 +- .../src/main/resources/examples/NestedUnion.c | 98 +++++++---- .../src/main/resources/examples/NestedUnion.h | 5 +- .../src/main/resources/examples/ex_nums.c | 123 +++++++++++++- .../src/main/resources/examples/ex_nums.h | 5 +- ...tor.scala => BinaryAbstractCodeGenerator.scala} | 37 +++-- .../generators/BinaryBooleanCodeGenerator.scala | 53 ++---- .../generators/BinaryFloatCodeGenerator.scala | 43 +---- .../BinaryIntegerKnownLengthCodeGenerator.scala | 49 +----- .../runtime2/generators/CodeGeneratorState.scala | 103 +++++++----- project/build.properties | 2 +- 27 files changed, 1063 insertions(+), 661 deletions(-) diff --git a/README.md b/README.md index 9097223..3b3ad76 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,9 @@ limitations under the License. --> +<!-- markdownlint-disable first-line-heading --> +<!-- markdownlint-disable line-length --> +<!-- markdownlint-disable no-inline-html --> [<img src="https://daffodil.apache.org/assets/themes/apache/img/apache-daffodil-logo.svg" height="85" align="left" alt="Apache Daffodil"/>][Website] [<img src="https://img.shields.io/github/workflow/status/apache/daffodil/Daffodil%20CI/master.svg" align="right"/>][GitHub Actions] <br clear="right" /> @@ -23,89 +26,114 @@ [<img src="https://img.shields.io/maven-central/v/org.apache.daffodil/daffodil-core_2.12.svg?color=brightgreen&label=version" align="right"/>][Releases] <br clear="both" /> -Apache Daffodil is an open-source implementation of the [DFDL specification] -that uses DFDL data descriptions to parse fixed format data into an infoset. -This infoset is commonly converted into XML or JSON to enable the use of -well-established XML or JSON technologies and libraries to consume, inspect, -and manipulate fixed format data in existing solutions. Daffodil is also -capable of serializing or "unparsing" data back to the original data format. -The DFDL infoset can also be converted directly to/from the data structures -carried by data processing frameworks so as to bypass any XML/JSON overheads. +Apache Daffodil is an open-source implementation of the [DFDL +specification] that uses DFDL data descriptions to parse fixed format +data into an infoset. This infoset is commonly converted into XML or +JSON to enable the use of well-established XML or JSON technologies +and libraries to consume, inspect, and manipulate fixed format data in +existing solutions. Daffodil is also capable of serializing or +"unparsing" data back to the original data format. The DFDL infoset +can also be converted directly to/from the data structures carried by +data processing frameworks so as to bypass any XML/JSON overheads. -For more information about Daffodil, see https://daffodil.apache.org/. +For more information about Daffodil, see the [Website]. ## Build Requirements * JDK 8 or higher * SBT 0.13.8 or higher -* C compiler (for daffodil-runtime2 only) -* Mini-XML Version 3.2 or higher (for daffodil-runtime2 only) +* C compiler C99 or higher +* Mini-XML Version 3.2 or higher + +Since Daffodil has a DFDL to C backend, you will need a C compiler +([gcc] or [clang]), the [Mini-XML] library, and possibly the GNU +[argp] library if your system's C library doesn't include it. You can +install gcc and libmxml as system packages on most Unix based +platforms with distribution-specific packager commands such as (Debian +and Ubuntu): + + # Just mentioning all other packages you might need too + sudo apt install build-essential curl git libmxml-dev + +You will need the Java Software Development Kit ([JDK]) and the Scala +Build Tool ([SBT]) to build Daffodil, run all tests, create packages, +and more. [SDK] offers an easy and uniform way to install both java +and sbt on any Unix based platform: + + curl -s "https://get.sdkman.io" | bash + sdk install java + sdk install sbt + +You can edit the Compile / cCompiler setting in build.sbt if you don't +want sbt to call your C compiler with "cc" as the driver command. + +On Windows, the easiest way to install gcc and libargp is to install +[MSYS2]'s collection of free tools and libraries although MSYS2 has no +package for libmxml which you'll need to build from source. First +install [MSYS2] following its website's installation instructions, +then run the following commands in a "MSYS2 MSYS" window: + + pacman -S gcc git libargp-devel make pkgconf + git clone https://github.com/michaelrsweet/mxml.git + cd mxml + ./configure --prefix=/usr --disable-shared --disable-threads + make + make install + +You also need to install [JDK} and [SBT] from their Windows +installation packages and define an environment variable using +Windows' control panel for editing environment variables. Define an +environment variable with the name `MSYS2_PATH_TYPE` and the value +`inherit`. Now when you open a new "MSYS2 MSYS" window from the Start +Menu, you will be able to type your sbt commands in the MSYS2 window +and both sbt and daffodil will be able to call the C compiler. ## Getting Started -You will need the full Java Software Development Kit ([JDK] or [SDK]), -not the Java Runtime Environment (JRE), to build Daffodil. You also -will need [SBT] to build Daffodil, run all tests, create packages, and -more. - -In order to build daffodil-runtime2, you will need a C compiler (for -example, [gcc]), the [Mini-XML] library, and possibly the [argp] -library if your system doesn't include it in its C library. - Below are some of the more common commands used for Daffodil development. ### Compile -```text -$ sbt compile -``` + sbt compile ### Tests Run all unit tests: -```text -$ sbt test -``` + sbt test Run all command line interface tests: -```text -$ sbt it:test -``` + sbt it:test ### Command Line Interface -Create Linux and Windows shell scripts in `daffodil-cli/target/universal/stage/bin/`. See -the [Command Line Interface] documentation for details on its usage: +Create Linux and Windows shell scripts in +`daffodil-cli/target/universal/stage/bin/`. See the [Command Line +Interface] documentation for details on its usage: -```btext -$ sbt daffodil-cli/stage -``` + sbt daffodil-cli/stage ### License Check -Generate an [Apache RAT] license check report located in ``target/rat.txt`` and error if -any unapproved licenses are found: +Generate an [Apache RAT] license check report located in +``target/rat.txt`` and error if any unapproved licenses are found: -```text -$ sbt ratCheck -``` + sbt ratCheck ### Test Coverage Report Generate an [sbt-scoverage] test coverage report located in ``target/scala-ver/scoverage-report/``: -```text -$ sbt clean coverage test it:test -$ sbt coverageAggregate -``` + sbt clean coverage test it:test + sbt coverageAggregate ## Getting Help For questions, we can be reached at the [email protected] or [email protected] mailing lists. Bugs can be reported via the [Daffodil JIRA]. [email protected] mailing lists. Bugs can be reported via the +[Daffodil JIRA]. ## License @@ -113,17 +141,19 @@ Apache Daffodil is licensed under the [Apache License, v2.0]. [Apache License, v2.0]: https://www.apache.org/licenses/LICENSE-2.0 [Apache RAT]: https://creadur.apache.org/rat/ -[CodeCov]: https://codecov.io/gh/apache/daffodil/ +[CodeCov]: https://app.codecov.io/gh/apache/daffodil [Command Line Interface]: https://daffodil.apache.org/cli/ -[DFDL specification]: http://www.ogf.org/dfdl -[Daffodil JIRA]: https://issues.apache.org/jira/projects/DAFFODIL +[DFDL specification]: https://daffodil.apache.org/docs/dfdl/ +[Daffodil JIRA]: https://issues.apache.org/jira/projects/DAFFODIL/ [Github Actions]: https://github.com/apache/daffodil/actions?query=branch%3Amaster+ -[JDK]: https://docs.oracle.com/en/java/javase/11/install/overview-jdk-installation.html +[JDK]: https://adoptopenjdk.net/ [Mini-XML]: https://www.msweet.org/mxml/ +[MSYS2]: https://www.msys2.org/ [Releases]: http://daffodil.apache.org/releases/ -[SBT]: http://www.scala-sbt.org -[SDK]: https://sdkman.io -[Website]: https://daffodil.apache.org +[SBT]: https://www.scala-sbt.org/ +[SDK]: https://sdkman.io/ +[Website]: https://daffodil.apache.org/ [argp]: https://packages.msys2.org/package/libargp-devel +[clang]: https://clang.llvm.org/get_started.html [gcc]: https://linuxize.com/post/how-to-install-gcc-on-ubuntu-20-04/ -[sbt-scoverage]: https://github.com/scoverage/sbt-scoverage +[sbt-scoverage]: https://github.com/scoverage/sbt-scoverage/ diff --git a/build.sbt b/build.sbt index b2fadab..2828483 100644 --- a/build.sbt +++ b/build.sbt @@ -68,6 +68,8 @@ lazy val runtime2 = Project("daffodil-runtime2", file("daffodil-runtime2 .settings(commonSettings) .settings(publishArtifact in (Compile, packageDoc) := false) .settings( + Compile / cCompiler := "cc", + Compile / ccArchiveCommand := "ar", Compile / ccTargets := ListSet(runtime2CFiles), Compile / cSources := Map( runtime2CFiles -> ( diff --git a/daffodil-runtime2/src/main/resources/c/libcli/daffodil_argp.c b/daffodil-runtime2/src/main/resources/c/libcli/daffodil_argp.c index ddc9b31..cbe33d4 100644 --- a/daffodil-runtime2/src/main/resources/c/libcli/daffodil_argp.c +++ b/daffodil-runtime2/src/main/resources/c/libcli/daffodil_argp.c @@ -21,10 +21,6 @@ #include <stdlib.h> // for putenv #include <string.h> // for strlen, strcmp -// Initialize our "daffodil" name and version - -const char *argp_program_version = "Apache Daffodil (runtime2) 0.1"; - // Initialize our "daffodil parse" CLI options struct daffodil_parse_cli daffodil_parse = { diff --git a/daffodil-runtime2/src/main/resources/c/libcli/daffodil_main.c b/daffodil-runtime2/src/main/resources/c/libcli/daffodil_main.c index fa53b57..03b1324 100644 --- a/daffodil-runtime2/src/main/resources/c/libcli/daffodil_main.c +++ b/daffodil-runtime2/src/main/resources/c/libcli/daffodil_main.c @@ -15,14 +15,13 @@ * limitations under the License. */ -#include "daffodil_argp.h" // for daffodil_cli, daffodil_parse, daffodil_parse_cli, daffodil_unparse, daffodil_unparse_cli, parse_daffodil_cli, DAFFODIL_PARSE, DAFFODIL_UNPARSE -#include "infoset.h" // for walkInfoset, InfosetBase, rootElement, ERD, PState, UState, VisitEventHandler +#include <stdio.h> // for NULL, perror, FILE, fclose, fflush, fopen, stdin, stdout +#include <string.h> // for strcmp +#include "daffodil_argp.h" // for daffodil_parse, daffodil_parse_cli, daffodil_unparse, daffodil_unparse_cli, daffodil_cli, parse_daffodil_cli, DAFFODIL_PARSE, DAFFODIL_UNPARSE +#include "errors.h" // for continue_or_exit, Error, print_diagnostics, PState, UState, ERR_FILE_CLOSE, ERR_FILE_FLUSH, ERR_FILE_OPEN, ERR_INFOSET_READ, ERR_INFOSET_WRITE +#include "infoset.h" // for walkInfoset, InfosetBase, rootElement, ERD, VisitEventHandler #include "xml_reader.h" // for xmlReaderMethods, XMLReader #include "xml_writer.h" // for xmlWriterMethods, XMLWriter -#include <error.h> // for error -#include <stdio.h> // for FILE, perror, fclose, fopen, stdin, stdout -#include <stdlib.h> // for exit, EXIT_FAILURE -#include <string.h> // for strcmp // Open a file or exit if it can't be opened @@ -34,33 +33,42 @@ fopen_or_exit(FILE *stream, const char *pathname, const char *mode) stream = fopen(pathname, mode); if (!stream) { - perror("Error opening file: "); - exit(EXIT_FAILURE); + perror("fopen"); + const Error error = {ERR_FILE_OPEN, {pathname}}; + continue_or_exit(&error); } } return stream; } -// Close a stream or exit if it can't be closed +// Flush all output to a file or exit if it can't be written, also +// print and exit if any previous error occurred or continue otherwise static void -fclose_or_exit(FILE *stream, FILE *stdin_stdout) +fflush_continue_or_exit(FILE *output, const Error *error) { - if (stream != stdin_stdout && fclose(stream) != 0) + if (fflush(output) != 0) { - perror("Error closing file: "); - exit(EXIT_FAILURE); + perror("fflush"); + if (!error) + { + const Error error = {ERR_FILE_FLUSH, {NULL}}; + continue_or_exit(&error); + } } + continue_or_exit(error); } -// Print an error and exit if an error occurred or continue otherwise +// Close a file or exit if it can't be closed static void -continue_or_exit(const char *error_msg) +fclose_or_exit(FILE *stream, FILE *stdin_or_stdout) { - if (error_msg) + if (stream != stdin_or_stdout && fclose(stream) != 0) { - error(EXIT_FAILURE, 0, "%s", error_msg); + perror("fclose"); + const Error error = {ERR_FILE_CLOSE, {NULL}}; + continue_or_exit(&error); } } @@ -85,23 +93,25 @@ 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}; + PState pstate = {input, 0, NULL, NULL}; root->erd->parseSelf(root, &pstate); - continue_or_exit(pstate.error_msg); + print_diagnostics(pstate.validati); + continue_or_exit(pstate.error); if (strcmp(daffodil_parse.infoset_converter, "xml") == 0) { // Visit the infoset and print XML from it. - XMLWriter xmlWriter = { + XMLWriter xmlWriter = { xmlWriterMethods, output, {NULL, NULL, 0}}; - const char *error_msg = + const Error *error = walkInfoset((VisitEventHandler *)&xmlWriter, root); - continue_or_exit(error_msg); + fflush_continue_or_exit(output, error); } else { - error(EXIT_FAILURE, 0, "Cannot write infoset type '%s'", - daffodil_parse.infoset_converter); + const Error error = {ERR_INFOSET_WRITE, + {daffodil_parse.infoset_converter}}; + continue_or_exit(&error); } } else if (daffodil_cli.subcommand == DAFFODIL_UNPARSE) @@ -113,22 +123,24 @@ main(int argc, char *argv[]) if (strcmp(daffodil_unparse.infoset_converter, "xml") == 0) { // Initialize our infoset's values from the XML data. - XMLReader xmlReader = { - xmlReaderMethods, input, root, NULL, NULL}; - const char *error_msg = + XMLReader xmlReader = {xmlReaderMethods, input, root, NULL, + NULL}; + const Error *error = walkInfoset((VisitEventHandler *)&xmlReader, root); - continue_or_exit(error_msg); + continue_or_exit(error); } else { - error(EXIT_FAILURE, 0, "Cannot read infoset type '%s'", - daffodil_unparse.infoset_converter); + const Error error = {ERR_INFOSET_READ, + {daffodil_unparse.infoset_converter}}; + continue_or_exit(&error); } // Unparse our infoset to the output file. - UState ustate = {output, 0, NULL}; + UState ustate = {output, 0, NULL, NULL}; root->erd->unparseSelf(root, &ustate); - continue_or_exit(ustate.error_msg); + print_diagnostics(ustate.validati); + continue_or_exit(ustate.error); } // Close our input and out files if we opened them. diff --git a/daffodil-runtime2/src/main/resources/c/libcli/stack.c b/daffodil-runtime2/src/main/resources/c/libcli/stack.c index 08c1561..8297bcd 100644 --- a/daffodil-runtime2/src/main/resources/c/libcli/stack.c +++ b/daffodil-runtime2/src/main/resources/c/libcli/stack.c @@ -16,10 +16,9 @@ */ #include "stack.h" -#include <error.h> // for error #include <stdbool.h> // for bool -#include <stddef.h> // for ptrdiff_t -#include <stdlib.h> // for EXIT_FAILURE +#include <stddef.h> // for NULL, ptrdiff_t +#include "errors.h" // for continue_or_exit, Error, ERR_STACK_EMPTY, ERR_STACK_OVERFLOW, ERR_STACK_UNDERFLOW // Initialize stack with preallocated array @@ -55,7 +54,8 @@ stack_pop(stack_t *p_stack) { if (stack_is_empty(p_stack)) { - error(EXIT_FAILURE, 0, "Stack underflow - program terminated"); + const Error error = {ERR_STACK_UNDERFLOW, {NULL}}; + continue_or_exit(&error); } return *(--p_stack->p_after); } @@ -67,7 +67,8 @@ stack_push(stack_t *p_stack, stack_item_t item) { if (stack_is_full(p_stack)) { - error(EXIT_FAILURE, 0, "Stack overflow - program terminated"); + const Error error = {ERR_STACK_OVERFLOW, {NULL}}; + continue_or_exit(&error); } *(p_stack->p_after++) = item; } @@ -79,7 +80,8 @@ stack_top(stack_t *p_stack) { if (stack_is_empty(p_stack)) { - error(EXIT_FAILURE, 0, "Stack empty - program terminated"); + const Error error = {ERR_STACK_EMPTY, {NULL}}; + continue_or_exit(&error); } return *(p_stack->p_after - 1); } diff --git a/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.c b/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.c index 3688933..57deee2 100644 --- a/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.c +++ b/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.c @@ -19,22 +19,23 @@ #include <assert.h> // for assert #include <errno.h> // for errno #include <inttypes.h> // for strtoimax, strtoumax -#include <mxml.h> // for mxmlWalkNext, mxmlGetType, mxmlGetElement, MXML_DESCEND, MXML_OPAQUE, mxmlDelete, mxmlGetOpaque, mxmlLoadFile, MXML_OPAQUE_CALLBACK +#include <mxml.h> // for mxmlWalkNext, mxmlGetElement, mxmlGetType, MXML_DESCEND, MXML_OPAQUE, mxmlDelete, mxmlGetOpaque, mxmlLoadFile, MXML_OPAQUE_CALLBACK #include <stdbool.h> // for bool, false, true #include <stdint.h> // for intmax_t, uintmax_t, int16_t, int32_t, int64_t, int8_t, uint16_t, uint32_t, uint64_t, uint8_t, INT16_MAX, INT16_MIN, INT32_MAX, INT32_MIN, INT64_MAX, INT64_MIN, INT8_MAX, INT8_MIN, UINT16_MAX, UINT32_MAX, UINT64_MAX, UINT8_MAX +#include <stdio.h> // for NULL #include <stdlib.h> // for strtod, strtof #include <string.h> // for strcmp, strlen, strncmp +#include "errors.h" // for Error, Error::(anonymous), ERR_STRTONUM_EMPTY, ERR_STRTONUM_NOT, ERR_XML_GONE, ERR_STRTOD_ERRNO, ERR_STRTOI_ERRNO, ERR_STRTONUM_RANGE, ERR_XML_MISMATCH, ERR_STRTOBOOL, ERR_XML_ERD, ERR_XML_INPUT, ERR_XML_LEFT, UNUSED // Convert an XML element's text to a boolean with error checking static bool -strtobool(const char *numptr, const char **errstrp) +strtobool(const char *numptr, const Error **errorptr) { // The lexical space of xs:boolean accepts true, false, 1, and 0 - bool value = false; - const char *error_msg = NULL; + bool value = false; - // Report any issues converting the string to a boolean + // Check for any errors converting the string to a boolean if (strcmp(numptr, "true") == 0) { value = true; @@ -53,10 +54,11 @@ strtobool(const char *numptr, const char **errstrp) } else { - error_msg = "Error converting XML data to boolean"; + static Error error = {ERR_STRTOBOOL, {NULL}}; + error.s = numptr; + *errorptr = &error; } - *errstrp = error_msg; return value; } @@ -64,7 +66,7 @@ strtobool(const char *numptr, const char **errstrp) // error checking) static double -strtodnum(const char *numptr, const char **errstrp) +strtodnum(const char *numptr, const Error **errorptr) { char *endptr = NULL; @@ -72,22 +74,24 @@ strtodnum(const char *numptr, const char **errstrp) errno = 0; const double value = strtod(numptr, &endptr); - // Report any issues converting the string to a number + // Check for any errors converting the string to a number if (errno != 0) { - *errstrp = "Error converting XML data to number"; + static Error error = {ERR_STRTOD_ERRNO, {NULL}}; + error.s = numptr; + *errorptr = &error; } else if (endptr == numptr) { - *errstrp = "Found no number in XML data"; + static Error error = {ERR_STRTONUM_EMPTY, {NULL}}; + error.s = numptr; + *errorptr = &error; } else if (*endptr != '\0') { - *errstrp = "Found non-number characters in XML data"; - } - else - { - *errstrp = NULL; + static Error error = {ERR_STRTONUM_NOT, {NULL}}; + error.s = numptr; + *errorptr = &error; } return value; @@ -97,7 +101,7 @@ strtodnum(const char *numptr, const char **errstrp) // error checking) static float -strtofnum(const char *numptr, const char **errstrp) +strtofnum(const char *numptr, const Error **errorptr) { char *endptr = NULL; @@ -105,33 +109,35 @@ strtofnum(const char *numptr, const char **errstrp) errno = 0; const float value = strtof(numptr, &endptr); - // Report any issues converting the string to a number + // Check for any errors converting the string to a number if (errno != 0) { - *errstrp = "Error converting XML data to number"; + static Error error = {ERR_STRTOD_ERRNO, {NULL}}; + error.s = numptr; + *errorptr = &error; } else if (endptr == numptr) { - *errstrp = "Found no number in XML data"; + static Error error = {ERR_STRTONUM_EMPTY, {NULL}}; + error.s = numptr; + *errorptr = &error; } else if (*endptr != '\0') { - *errstrp = "Found non-number characters in XML data"; - } - else - { - *errstrp = NULL; + static Error error = {ERR_STRTONUM_NOT, {NULL}}; + error.s = numptr; + *errorptr = &error; } return value; } -// Convert an XML element's text to a signed integer (BSD function not -// widely available, so call strtoimax with our own error checking) +// Convert an XML element's text to a signed integer (call strtoimax +// with our own error checking) static intmax_t strtonum(const char *numptr, intmax_t minval, intmax_t maxval, - const char **errstrp) + const Error **errorptr) { char *endptr = NULL; assert(minval < maxval); @@ -140,26 +146,30 @@ strtonum(const char *numptr, intmax_t minval, intmax_t maxval, errno = 0; const intmax_t value = strtoimax(numptr, &endptr, 10); - // Report any issues converting the string to a number + // Check for any errors converting the string to a number if (errno != 0) { - *errstrp = "Error converting XML data to integer"; + static Error error = {ERR_STRTOI_ERRNO, {NULL}}; + error.s = numptr; + *errorptr = &error; } else if (endptr == numptr) { - *errstrp = "Found no number in XML data"; + static Error error = {ERR_STRTONUM_EMPTY, {NULL}}; + error.s = numptr; + *errorptr = &error; } else if (*endptr != '\0') { - *errstrp = "Found non-number characters in XML data"; + static Error error = {ERR_STRTONUM_NOT, {NULL}}; + error.s = numptr; + *errorptr = &error; } else if (value < minval || value > maxval) { - *errstrp = "Number in XML data out of range"; - } - else - { - *errstrp = NULL; + static Error error = {ERR_STRTONUM_RANGE, {NULL}}; + error.s = numptr; + *errorptr = &error; } return value; @@ -169,7 +179,7 @@ strtonum(const char *numptr, intmax_t minval, intmax_t maxval, // with our own error checking) static uintmax_t -strtounum(const char *numptr, uintmax_t maxval, const char **errstrp) +strtounum(const char *numptr, uintmax_t maxval, const Error **errorptr) { char *endptr = NULL; @@ -177,26 +187,30 @@ strtounum(const char *numptr, uintmax_t maxval, const char **errstrp) errno = 0; const uintmax_t value = strtoumax(numptr, &endptr, 10); - // Report any issues converting the string to a number + // Check for any errors converting the string to a number if (errno != 0) { - *errstrp = "Error converting XML data to integer"; + static Error error = {ERR_STRTOI_ERRNO, {NULL}}; + error.s = numptr; + *errorptr = &error; } else if (endptr == numptr) { - *errstrp = "Found no number in XML data"; + static Error error = {ERR_STRTONUM_EMPTY, {NULL}}; + error.s = numptr; + *errorptr = &error; } else if (*endptr != '\0') { - *errstrp = "Found non-number characters in XML data"; + static Error error = {ERR_STRTONUM_NOT, {NULL}}; + error.s = numptr; + *errorptr = &error; } else if (value > maxval) { - *errstrp = "Number in XML data out of range"; - } - else - { - *errstrp = NULL; + static Error error = {ERR_STRTONUM_RANGE, {NULL}}; + error.s = numptr; + *errorptr = &error; } return value; @@ -204,7 +218,7 @@ strtounum(const char *numptr, uintmax_t maxval, const char **errstrp) // Read XML data from file before walking infoset -static const char * +static const Error * xmlStartDocument(XMLReader *reader) { // Load the XML data into memory @@ -212,7 +226,8 @@ xmlStartDocument(XMLReader *reader) reader->node = reader->xml; if (!reader->node) { - return "Unable to read XML data from input file"; + static Error error = {ERR_XML_INPUT, {NULL}}; + return &error; } // Consume the <?xml line if there is one @@ -237,12 +252,13 @@ xmlStartDocument(XMLReader *reader) } while (mxmlGetType(reader->node) == MXML_OPAQUE); } - return reader->node ? NULL : "Ran out of XML data"; + static Error error = {ERR_XML_GONE, {NULL}}; + return reader->node ? NULL : &error; } // Delete XML data after walking infoset -static const char * +static const Error * xmlEndDocument(XMLReader *reader) { // Consume any remaining newlines or whitespace @@ -255,7 +271,9 @@ xmlEndDocument(XMLReader *reader) if (reader->node) { // This code path exits the program - no need to call mxmlDelete - return "Did not consume all of the XML data"; + static Error error = {ERR_XML_LEFT, {NULL}}; + error.s = mxmlGetElement(reader->node); + return &error; } // Free the storage allocated to hold the XML data @@ -267,7 +285,7 @@ xmlEndDocument(XMLReader *reader) // Continue walking both XML data and infoset in lockstep -static const char * +static const Error * xmlStartComplex(XMLReader *reader, const InfosetBase *base) { // Consume any newlines or whitespace before the element @@ -284,30 +302,31 @@ xmlStartComplex(XMLReader *reader, const InfosetBase *base) // Check whether we are walking both XML data and infoset in lockstep if (name_from_xml && name_from_erd) { - return strcmp(name_from_xml, name_from_erd) == 0 - ? NULL - : "Found mismatch between XML data and infoset"; + static Error error = {ERR_XML_MISMATCH, {NULL}}; + error.s = name_from_erd; + return strcmp(name_from_xml, name_from_erd) == 0 ? NULL : &error; } else { - return "Ran out of XML data"; + static Error error = {ERR_XML_GONE, {NULL}}; + return &error; } } // Consume XML data only on start events, not end events -static const char * +static const Error * xmlEndComplex(XMLReader *reader, const InfosetBase *base) { UNUSED(reader); // because nothing to read - UNUSED(base); // because nothing to check + UNUSED(base); // because nothing to check return NULL; } // Read a boolean, 32-bit or 64-bit real number, or 8, 16, 32, or // 64-bit signed or unsigned integer from XML data -static const char * +static const Error * xmlNumberElem(XMLReader *reader, const ERD *erd, void *number) { // Consume any newlines or whitespace before the element @@ -327,70 +346,75 @@ xmlNumberElem(XMLReader *reader, const ERD *erd, void *number) { if (strcmp(name_from_xml, name_from_erd) == 0) { + static Error error_erd = {ERR_XML_ERD, {NULL}}; + // Check for any errors getting the number - const char *errstr = NULL; + const Error *error = NULL; // Handle varying bit lengths of both signed & unsigned numbers const enum TypeCode typeCode = erd->typeCode; switch (typeCode) { case PRIMITIVE_BOOLEAN: - *(bool *)number = strtobool(number_from_xml, &errstr); + *(bool *)number = strtobool(number_from_xml, &error); break; case PRIMITIVE_FLOAT: - *(float *)number = strtofnum(number_from_xml, &errstr); + *(float *)number = strtofnum(number_from_xml, &error); break; case PRIMITIVE_DOUBLE: - *(double *)number = strtodnum(number_from_xml, &errstr); + *(double *)number = strtodnum(number_from_xml, &error); break; case PRIMITIVE_INT16: *(int16_t *)number = (int16_t)strtonum( - number_from_xml, INT16_MIN, INT16_MAX, &errstr); + number_from_xml, INT16_MIN, INT16_MAX, &error); break; case PRIMITIVE_INT32: *(int32_t *)number = (int32_t)strtonum( - number_from_xml, INT32_MIN, INT32_MAX, &errstr); + number_from_xml, INT32_MIN, INT32_MAX, &error); break; case PRIMITIVE_INT64: *(int64_t *)number = (int64_t)strtonum( - number_from_xml, INT64_MIN, INT64_MAX, &errstr); + number_from_xml, INT64_MIN, INT64_MAX, &error); break; case PRIMITIVE_INT8: *(int8_t *)number = (int8_t)strtonum(number_from_xml, INT8_MIN, - INT8_MAX, &errstr); + INT8_MAX, &error); break; case PRIMITIVE_UINT16: *(uint16_t *)number = - (uint16_t)strtounum(number_from_xml, UINT16_MAX, &errstr); + (uint16_t)strtounum(number_from_xml, UINT16_MAX, &error); break; case PRIMITIVE_UINT32: *(uint32_t *)number = - (uint32_t)strtounum(number_from_xml, UINT32_MAX, &errstr); + (uint32_t)strtounum(number_from_xml, UINT32_MAX, &error); break; case PRIMITIVE_UINT64: *(uint64_t *)number = - (uint64_t)strtounum(number_from_xml, UINT64_MAX, &errstr); + (uint64_t)strtounum(number_from_xml, UINT64_MAX, &error); break; case PRIMITIVE_UINT8: *(uint8_t *)number = - (uint8_t)strtounum(number_from_xml, UINT8_MAX, &errstr); + (uint8_t)strtounum(number_from_xml, UINT8_MAX, &error); break; default: - errstr = "Unexpected ERD typeCode while reading number from " - "XML data"; + error_erd.d64 = typeCode; + error = &error_erd; break; } - return errstr; + return error; } else { - return "Found mismatch between XML data and infoset"; + static Error error = {ERR_XML_MISMATCH, {NULL}}; + error.s = name_from_erd; + return &error; } } else { - return "Ran out of XML data"; + static Error error = {ERR_XML_GONE, {NULL}}; + return &error; } } diff --git a/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.h b/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.h index 57ce1bd..952d39e 100644 --- a/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.h +++ b/daffodil-runtime2/src/main/resources/c/libcli/xml_reader.h @@ -18,9 +18,9 @@ #ifndef XML_READER_H #define XML_READER_H -#include "infoset.h" // for VisitEventHandler, InfosetBase #include <mxml.h> // for mxml_node_t #include <stdio.h> // for FILE +#include "infoset.h" // for VisitEventHandler, InfosetBase // XMLReader - infoset visitor with methods to read XML diff --git a/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.c b/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.c index 4f609ce..11d36ba 100644 --- a/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.c +++ b/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.c @@ -16,83 +16,93 @@ */ #include "xml_writer.h" -#include "stack.h" // for stack_is_empty, stack_pop, stack_push, stack_top, stack_init, stack_is_full #include <assert.h> // for assert -#include <mxml.h> // for mxmlNewOpaquef, mxml_node_t, mxmlElementSetAttr, mxmlNewElement, mxmlDelete, mxmlNewXML, mxmlSaveFile, MXML_NO_CALLBACK +#include <mxml.h> // for mxmlNewOpaquef, mxml_node_t, mxmlElementSetAttr, mxmlGetOpaque, mxmlNewElement, mxmlDelete, mxmlGetElement, mxmlNewXML, mxmlSaveFile, MXML_NO_CALLBACK #include <stdbool.h> // for bool #include <stdint.h> // for int16_t, int32_t, int64_t, int8_t, uint16_t, uint32_t, uint64_t, uint8_t -#include <stdio.h> // for NULL, fflush +#include <stdio.h> // for NULL #include <string.h> // for strcmp +#include "errors.h" // for Error, ERR_XML_DECL, ERR_XML_ELEMENT, ERR_XML_WRITE, Error::(anonymous) +#include "stack.h" // for stack_is_empty, stack_pop, stack_push, stack_top, stack_init -// Push new XML document on stack. This function is not -// thread-safe since it uses static storage. +// Push new XML document on stack (note the stack is stored in a +// static array which could overflow and stop the program; it also +// means none of those functions are thread-safe) -static const char * +static const Error * xmlStartDocument(XMLWriter *writer) { -#define MAX_DEPTH 100 + enum + { + MAX_DEPTH = 100 + }; static mxml_node_t *array[MAX_DEPTH]; stack_init(&writer->stack, array, MAX_DEPTH); mxml_node_t *xml = mxmlNewXML("1.0"); - stack_push(&writer->stack, xml); - return xml ? NULL : "Error making new XML declaration"; + if (xml) + { + stack_push(&writer->stack, xml); + return NULL; + } + else + { + static Error error = {ERR_XML_DECL, {NULL}}; + return &error; + } } -// Pop completed XML document off stack and write it to stream +// Pop completed XML document off stack and write it to stream (note +// stack underflow will stop program) -static const char * +static const Error * xmlEndDocument(XMLWriter *writer) { mxml_node_t *xml = stack_pop(&writer->stack); assert(stack_is_empty(&writer->stack)); + int status = mxmlSaveFile(xml, writer->stream, MXML_NO_CALLBACK); if (status < 0) { - return "Error writing XML document"; + static Error error = {ERR_XML_WRITE, {NULL}}; + return &error; } - status = fflush(writer->stream); mxmlDelete(xml); - return status == 0 ? NULL : "Error flushing stream"; + return NULL; } -// Push new complex element on stack +// Push new complex element on stack (note stack overflow will stop +// program) -static const char * +static const Error * xmlStartComplex(XMLWriter *writer, const InfosetBase *base) { - mxml_node_t *complex = NULL; - if (!stack_is_full(&writer->stack)) + mxml_node_t *parent = stack_top(&writer->stack); + const char * name = get_erd_name(base->erd); + const char * xmlns = get_erd_xmlns(base->erd); + mxml_node_t *complex = mxmlNewElement(parent, name); + if (xmlns) { - mxml_node_t *parent = stack_top(&writer->stack); - const char * name = get_erd_name(base->erd); - const char * xmlns = get_erd_xmlns(base->erd); - complex = mxmlNewElement(parent, name); - if (xmlns) - { - const char *ns = get_erd_ns(base->erd); - mxmlElementSetAttr(complex, xmlns, ns); - } - stack_push(&writer->stack, complex); + const char *ns = get_erd_ns(base->erd); + mxmlElementSetAttr(complex, xmlns, ns); } - return complex ? NULL : "Error making new complex element"; + stack_push(&writer->stack, complex); + return NULL; } -// Pop completed complex element off stack +// Pop completed complex element off stack (note stack underflow will +// stop program) -static const char * +static const Error * xmlEndComplex(XMLWriter *writer, const InfosetBase *base) { - mxml_node_t *complex = NULL; - if (!stack_is_empty(&writer->stack)) - { - complex = stack_pop(&writer->stack); + mxml_node_t *complex = stack_pop(&writer->stack); - const char *name_from_xml = mxmlGetElement(complex); - const char *name_from_erd = get_erd_name(base->erd); - assert(strcmp(name_from_xml, name_from_erd) == 0); - } - return complex ? NULL : "Underflowed the XML stack"; + const char *name_from_xml = mxmlGetElement(complex); + const char *name_from_erd = get_erd_name(base->erd); + assert(strcmp(name_from_xml, name_from_erd) == 0); + + return NULL; } // Fix a real number to conform to xsd:float syntax if needed @@ -107,14 +117,15 @@ fixNumberIfNeeded(const char *text) modifyInPlace[1] = 'a'; } // These are not required by xsd:float, only to match runtime1 better - // - Strip + from <f>E+<e> to get <f>E<e> (not worth it) - // - Add .0 to 1 to get 1.0 (not worth it) + // - Strip + from <f>E+<e> to get <f>E<e> + // - Add .0 to 1 to get 1.0 + // It would be better to compare floats as numbers, not strings, though } // Write a boolean, 32-bit or 64-bit real number, or 8, 16, 32, or // 64-bit signed or unsigned integer as an XML element's value -static const char * +static const Error * xmlNumberElem(XMLWriter *writer, const ERD *erd, const void *number) { mxml_node_t *parent = stack_top(&writer->stack); @@ -177,8 +188,16 @@ xmlNumberElem(XMLWriter *writer, const ERD *erd, const void *number) break; } - return (simple && text) ? NULL - : "Error making new simple numerical element"; + if (simple && text) + { + return NULL; + } + else + { + static Error error = {ERR_XML_ELEMENT, {NULL}}; + error.s = name; + return &error; + } } // Initialize a struct with our visitor event handler methods diff --git a/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.h b/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.h index c49e2f0..00fe45a 100644 --- a/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.h +++ b/daffodil-runtime2/src/main/resources/c/libcli/xml_writer.h @@ -18,9 +18,9 @@ #ifndef XML_WRITER_H #define XML_WRITER_H +#include <stdio.h> // for FILE #include "infoset.h" // for VisitEventHandler #include "stack.h" // for stack_t -#include <stdio.h> // for FILE // XMLWriter - infoset visitor with methods to output XML diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/errors.c b/daffodil-runtime2/src/main/resources/c/libruntime/errors.c new file mode 100644 index 0000000..d624b0a --- /dev/null +++ b/daffodil-runtime2/src/main/resources/c/libruntime/errors.c @@ -0,0 +1,183 @@ +/* + * 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 "errors.h" +#include <error.h> // for error +#include <inttypes.h> // for PRId64 +#include <stdio.h> // for NULL, feof, ferror, FILE, size_t +#include <stdlib.h> // for EXIT_FAILURE + +// error_message - return an internationalized error message + +static const char * +error_message(enum ErrorCode code) +{ + switch (code) + { + case ERR_CHOICE_KEY: + return "no match between choice dispatch key %" PRId64 + " and any branch key"; + case ERR_FILE_CLOSE: + return "error closing file"; + case ERR_FILE_FLUSH: + return "error flushing stream to file"; + case ERR_FILE_OPEN: + return "error opening file '%s'"; + case ERR_FIXED_VALUE: + return "value of element '%s' does not match value of its " + "'fixed' attribute"; + case ERR_INFOSET_READ: + return "cannot read infoset type '%s'"; + case ERR_INFOSET_WRITE: + return "cannot write infoset type '%s'"; + case ERR_PARSE_BOOL: + return "error parsing binary value %" PRId64 " as either true or false"; + case ERR_STACK_EMPTY: + return "stack empty, stopping program"; + case ERR_STACK_OVERFLOW: + return "stack overflow, stopping program"; + case ERR_STACK_UNDERFLOW: + return "stack underflow, stopping program"; + case ERR_STREAM_EOF: + return "EOF in stream, stopping program"; + case ERR_STREAM_ERROR: + return "error in stream, stopping program"; + case ERR_STRTOBOOL: + return "error converting XML data '%s' to boolean"; + case ERR_STRTOD_ERRNO: + return "error converting XML data '%s' to number"; + case ERR_STRTOI_ERRNO: + return "error converting XML data '%s' to integer"; + case ERR_STRTONUM_EMPTY: + return "found no number in XML data '%s'"; + case ERR_STRTONUM_NOT: + return "found non-number characters in XML data '%s'"; + case ERR_STRTONUM_RANGE: + return "number in XML data '%s' out of range"; + case ERR_XML_DECL: + return "error making new XML declaration"; + case ERR_XML_ELEMENT: + return "error making new XML element '%s'"; + case ERR_XML_ERD: + return "unexpected ERD typeCode %" PRId64 " while reading XML data"; + case ERR_XML_GONE: + return "ran out of XML data"; + case ERR_XML_INPUT: + return "unable to read XML data from input file"; + case ERR_XML_LEFT: + return "did not consume all of the XML data, '%s' left"; + case ERR_XML_MISMATCH: + return "found mismatch between XML data and infoset '%s'"; + case ERR_XML_WRITE: + return "error writing XML document"; + default: + return "unrecognized error code, shouldn't happen"; + } +} + +// print_maybe_stop - print a message and maybe stop the program + +static void +print_maybe_stop(const Error *err, int status) +{ + const int errnum = 0; + const char *format = "%s"; + const char *msg = error_message(err->code); + + switch (err->code) + { + case ERR_FILE_OPEN: + case ERR_FIXED_VALUE: + case ERR_INFOSET_READ: + case ERR_INFOSET_WRITE: + case ERR_STRTOBOOL: + case ERR_STRTOD_ERRNO: + case ERR_STRTOI_ERRNO: + case ERR_STRTONUM_EMPTY: + case ERR_STRTONUM_NOT: + case ERR_STRTONUM_RANGE: + case ERR_XML_ELEMENT: + case ERR_XML_LEFT: + case ERR_XML_MISMATCH: + error(status, errnum, msg, err->s); + break; + case ERR_CHOICE_KEY: + case ERR_PARSE_BOOL: + case ERR_XML_ERD: + error(status, errnum, msg, err->d64); + break; + default: + error(status, errnum, format, msg); + break; + } +} + +// need_diagnostics - return pointer to validation diagnostics + +Diagnostics * +need_diagnostics(void) +{ + static Diagnostics validati; + return &validati; +} + +// print_diagnostics - print any validation diagnostics + +void +print_diagnostics(const Diagnostics *validati) +{ + if (validati) + { + for (size_t i = 0; i < validati->length; i++) + { + const Error *error = &validati->array[i]; + print_maybe_stop(error, 0); + } + } +} + +// continue_or_exit - print and exit if an error occurred or continue otherwise + +void +continue_or_exit(const Error *error) +{ + if (error) + { + print_maybe_stop(error, EXIT_FAILURE); + } +} + +// eof_or_error - return an error if a stream has its eof or error indicator set + +const Error * +eof_or_error(FILE *stream) +{ + if (feof(stream)) + { + static Error error = {ERR_STREAM_EOF, {NULL}}; + return &error; + } + else if (ferror(stream)) + { + static Error error = {ERR_STREAM_ERROR, {NULL}}; + return &error; + } + else + { + return NULL; + } +} diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/errors.h b/daffodil-runtime2/src/main/resources/c/libruntime/errors.h new file mode 100644 index 0000000..da55fb4 --- /dev/null +++ b/daffodil-runtime2/src/main/resources/c/libruntime/errors.h @@ -0,0 +1,117 @@ +/* + * 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. + */ + +#ifndef ERRORS_H +#define ERRORS_H + +#include <stdio.h> // for FILE, size_t +#include <stdint.h> // for int64_t + +// ErrorCode - types of errors which could occur + +enum ErrorCode +{ + ERR_CHOICE_KEY, + ERR_FILE_CLOSE, + ERR_FILE_FLUSH, + ERR_FILE_OPEN, + ERR_FIXED_VALUE, + ERR_INFOSET_READ, + ERR_INFOSET_WRITE, + ERR_PARSE_BOOL, + ERR_STACK_EMPTY, + ERR_STACK_OVERFLOW, + ERR_STACK_UNDERFLOW, + ERR_STREAM_EOF, + ERR_STREAM_ERROR, + ERR_STRTOBOOL, + ERR_STRTOD_ERRNO, + ERR_STRTOI_ERRNO, + ERR_STRTONUM_EMPTY, + ERR_STRTONUM_NOT, + ERR_STRTONUM_RANGE, + ERR_XML_DECL, + ERR_XML_ELEMENT, + ERR_XML_ERD, + ERR_XML_GONE, + ERR_XML_INPUT, + ERR_XML_LEFT, + ERR_XML_MISMATCH, + ERR_XML_WRITE +}; + +// Error - specific error occuring now + +typedef struct Error +{ + enum ErrorCode code; + union + { + const char *s; // for %s + int64_t d64; // for %d64 + }; +} Error; + +// Diagnostics - array of validation errors + +typedef struct Diagnostics +{ + Error array[100]; + size_t length; +} Diagnostics; + +// PState - mutable state while parsing data + +typedef struct PState +{ + FILE * stream; // input to read data from + size_t position; // 0-based position in stream + Diagnostics *validati; // any validation diagnostics + const Error *error; // any error which stops program +} PState; + +// UState - mutable state while unparsing infoset + +typedef struct UState +{ + FILE * stream; // output to write data to + size_t position; // 0-based position in stream + Diagnostics *validati; // any validation diagnostics + const Error *error; // any error which stops program +} UState; + +// need_diagnostics - return pointer to validation diagnostics + +extern Diagnostics *need_diagnostics(void); + +// print_diagnostics - print any validation diagnostics + +extern void print_diagnostics(const Diagnostics *validati); + +// continue_or_exit - print and exit if an error occurred or continue otherwise + +extern void continue_or_exit(const Error *error); + +// eof_or_error - return an error if a stream has its eof or error indicator set + +extern const Error *eof_or_error(FILE *stream); + +// UNUSED - suppress compiler warning about unused variable + +#define UNUSED(x) (void)(x) + +#endif // ERRORS_H diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/infoset.c b/daffodil-runtime2/src/main/resources/c/libruntime/infoset.c index 4f102f1..8c52eb2 100644 --- a/daffodil-runtime2/src/main/resources/c/libruntime/infoset.c +++ b/daffodil-runtime2/src/main/resources/c/libruntime/infoset.c @@ -17,6 +17,7 @@ #include "infoset.h" #include <string.h> // for memccpy +#include "errors.h" // for Error, ERR_WALK_KEY // get_erd_name, get_erd_xmlns, get_erd_ns - get name and xmlns // attribute/value from ERD to use on XML element @@ -113,15 +114,15 @@ get_erd_ns(const ERD *erd) // walkInfosetNode - recursively walk an infoset node and call // VisitEventHandler methods -static const char * +static const Error * walkInfosetNode(const VisitEventHandler *handler, const InfosetBase *infoNode) { - const char *error_msg = NULL; + const Error *error = NULL; // Start visiting the node - if (!error_msg) + if (!error) { - error_msg = handler->visitStartComplex(handler, infoNode); + error = handler->visitStartComplex(handler, infoNode); } // Walk the node's children recursively @@ -129,7 +130,7 @@ walkInfosetNode(const VisitEventHandler *handler, const InfosetBase *infoNode) const ERD **const childrenERDs = infoNode->erd->childrenERDs; const size_t * offsets = infoNode->erd->offsets; - for (size_t i = 0; i < count && !error_msg; i++) + for (size_t i = 0; i < count && !error; i++) { const size_t offset = offsets[i]; const ERD * childERD = childrenERDs[i]; @@ -144,14 +145,10 @@ walkInfosetNode(const VisitEventHandler *handler, const InfosetBase *infoNode) { case CHOICE: // Point next ERD to choice of alternative elements' ERDs - if (!infoNode->erd->initChoice(infoNode, rootElement())) - { - error_msg = "Walk error: no match between choice dispatch key " - "and any branch key"; - } + error = infoNode->erd->initChoice(infoNode, rootElement()); break; case COMPLEX: - error_msg = walkInfosetNode(handler, childNode); + error = walkInfosetNode(handler, childNode); break; case PRIMITIVE_BOOLEAN: case PRIMITIVE_DOUBLE: @@ -164,58 +161,39 @@ walkInfosetNode(const VisitEventHandler *handler, const InfosetBase *infoNode) case PRIMITIVE_UINT32: case PRIMITIVE_UINT64: case PRIMITIVE_UINT8: - error_msg = handler->visitNumberElem(handler, childERD, number); + error = handler->visitNumberElem(handler, childERD, number); break; } } // End visiting the node - if (!error_msg) + if (!error) { - error_msg = handler->visitEndComplex(handler, infoNode); + error = handler->visitEndComplex(handler, infoNode); } - return error_msg; + return error; } // walkInfoset - walk an infoset and call VisitEventHandler methods -const char * +const Error * walkInfoset(const VisitEventHandler *handler, const InfosetBase *infoset) { - const char *error_msg = NULL; + const Error *error = NULL; - if (!error_msg) + if (!error) { - error_msg = handler->visitStartDocument(handler); + error = handler->visitStartDocument(handler); } - if (!error_msg) + if (!error) { - error_msg = walkInfosetNode(handler, infoset); + error = walkInfosetNode(handler, infoset); } - if (!error_msg) + if (!error) { - error_msg = handler->visitEndDocument(handler); + error = handler->visitEndDocument(handler); } - return error_msg; -} - -// eof_or_error_msg - check if a stream has its eof or error indicator set - -const char * -eof_or_error_msg(FILE *stream) -{ - if (feof(stream)) - { - return "Found eof indicator in stream, stopping now"; - } - else if (ferror(stream)) - { - return "Found error indicator in stream, stopping now"; - } - else - { - return NULL; - } + return error; } diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/infoset.h b/daffodil-runtime2/src/main/resources/c/libruntime/infoset.h index bc4315e..77bc57f 100644 --- a/daffodil-runtime2/src/main/resources/c/libruntime/infoset.h +++ b/daffodil-runtime2/src/main/resources/c/libruntime/infoset.h @@ -18,32 +18,29 @@ #ifndef INFOSET_H #define INFOSET_H -#include <stdbool.h> // for bool -#include <stddef.h> // for size_t -#include <stdio.h> // for FILE +#include <stddef.h> // for size_t +#include "errors.h" // for Error, PState, UState // Prototypes needed for compilation typedef struct ElementRuntimeData ERD; typedef struct InfosetBase InfosetBase; -typedef struct PState PState; -typedef struct UState UState; 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 (*InitChoiceRD)(const InfosetBase *infoNode, - const InfosetBase *rootElement); - -typedef const char *(*VisitStartDocument)(const VisitEventHandler *handler); -typedef const char *(*VisitEndDocument)(const VisitEventHandler *handler); -typedef const char *(*VisitStartComplex)(const VisitEventHandler *handler, - const InfosetBase * base); -typedef const char *(*VisitEndComplex)(const VisitEventHandler *handler, - const InfosetBase * base); -typedef const char *(*VisitNumberElem)(const VisitEventHandler *handler, - const ERD *erd, const void *number); +typedef const Error *(*InitChoiceRD)(const InfosetBase *infoNode, + const InfosetBase *rootElement); + +typedef const Error *(*VisitStartDocument)(const VisitEventHandler *handler); +typedef const Error *(*VisitEndDocument)(const VisitEventHandler *handler); +typedef const Error *(*VisitStartComplex)(const VisitEventHandler *handler, + const InfosetBase * base); +typedef const Error *(*VisitEndComplex)(const VisitEventHandler *handler, + const InfosetBase * base); +typedef const Error *(*VisitNumberElem)(const VisitEventHandler *handler, + const ERD *erd, const void *number); // NamedQName - name of an infoset element @@ -54,7 +51,7 @@ typedef struct NamedQName const char *ns; // namespace URI (optional, may be NULL) } NamedQName; -// TypeCode - type of an infoset element +// TypeCode - types of infoset elements enum TypeCode { @@ -96,24 +93,6 @@ typedef struct InfosetBase const ERD *erd; } InfosetBase; -// PState - mutable state while parsing data - -typedef struct PState -{ - FILE * stream; // input to read data from - size_t position; // 0-based position in stream - const char *error_msg; // to stop if an error happens -} PState; - -// UState - mutable state while unparsing infoset - -typedef struct UState -{ - FILE * stream; // output to write data to - size_t position; // 0-based position in stream - const char *error_msg; // to stop if an error happens -} UState; - // VisitEventHandler - methods to be called when walking an infoset typedef struct VisitEventHandler @@ -140,19 +119,7 @@ extern InfosetBase *rootElement(void); // walkInfoset - walk an infoset and call VisitEventHandler methods -extern const char *walkInfoset(const VisitEventHandler *handler, - const InfosetBase * infoset); - -// eof_or_error_msg - check if a stream has its eof or error indicator set - -extern const char *eof_or_error_msg(FILE *stream); - -// NO_CHOICE - define value stored in uninitialized _choice field - -static const size_t NO_CHOICE = (size_t)-1; - -// UNUSED - suppress compiler warning about unused variable - -#define UNUSED(x) (void)(x) +extern const Error *walkInfoset(const VisitEventHandler *handler, + const InfosetBase * infoset); #endif // INFOSET_H diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/parsers.c b/daffodil-runtime2/src/main/resources/c/libruntime/parsers.c index db3a783..c9040da 100644 --- a/daffodil-runtime2/src/main/resources/c/libruntime/parsers.c +++ b/daffodil-runtime2/src/main/resources/c/libruntime/parsers.c @@ -16,14 +16,14 @@ */ #include "parsers.h" -#include <endian.h> // for be32toh, be64toh, le32toh, le64toh, be16toh, le16toh -#include <stdbool.h> // for bool -#include <stdio.h> // for fread, size_t +#include <endian.h> // for be32toh, le32toh, be16toh, be64toh, le16toh, le64toh +#include <stdbool.h> // for bool, false, true +#include <stdio.h> // for fread +#include "errors.h" // for PState, eof_or_error, Error, ERR_PARSE_BOOL, Error::(anonymous), Diagnostics, need_diagnostics, ERR_FIXED_VALUE -// Macros that are not defined by <endian.h> +// Macros not defined by <endian.h> which we need for uniformity #define be8toh(var) var - #define le8toh(var) var // Helper macro to reduce duplication of C code reading stream, @@ -34,7 +34,8 @@ pstate->position += count; \ if (count < sizeof(buffer)) \ { \ - pstate->error_msg = eof_or_error_msg(pstate->stream); \ + pstate->error = eof_or_error(pstate->stream); \ + if (pstate->error) return; \ } // Macros to define parse_<endian>_<type> functions @@ -43,70 +44,63 @@ void parse_##endian##_bool##bits(bool *number, int64_t true_rep, \ uint32_t false_rep, PState *pstate) \ { \ - if (!pstate->error_msg) \ + union \ { \ - union \ - { \ - char c_val[sizeof(uint##bits##_t)]; \ - uint##bits##_t i_val; \ - } buffer; \ + char c_val[sizeof(uint##bits##_t)]; \ + uint##bits##_t i_val; \ + } buffer; \ \ - read_stream_update_position; \ - buffer.i_val = endian##bits##toh(buffer.i_val); \ - if (true_rep < 0) \ - { \ - *number = (buffer.i_val != false_rep); \ - } \ - else if (buffer.i_val == (uint32_t)true_rep) \ - { \ - *number = true; \ - } \ - else if (buffer.i_val == false_rep) \ - { \ - *number = false; \ - } \ - else \ - { \ - pstate->error_msg = "Unable to parse boolean"; \ - } \ + read_stream_update_position; \ + buffer.i_val = endian##bits##toh(buffer.i_val); \ + if (true_rep < 0) \ + { \ + *number = (buffer.i_val != false_rep); \ + } \ + else if (buffer.i_val == (uint32_t)true_rep) \ + { \ + *number = true; \ + } \ + else if (buffer.i_val == false_rep) \ + { \ + *number = false; \ + } \ + else \ + { \ + static Error error = {ERR_PARSE_BOOL, {NULL}}; \ + error.d64 = (int64_t)buffer.i_val; \ + pstate->error = &error; \ } \ } #define define_parse_endian_real(endian, type, bits) \ void parse_##endian##_##type(type *number, PState *pstate) \ { \ - if (!pstate->error_msg) \ + union \ { \ - union \ - { \ - char c_val[sizeof(type)]; \ - type f_val; \ - uint##bits##_t i_val; \ - } buffer; \ + char c_val[sizeof(type)]; \ + type f_val; \ + uint##bits##_t i_val; \ + } buffer; \ \ - read_stream_update_position; \ - buffer.i_val = endian##bits##toh(buffer.i_val); \ - *number = buffer.f_val; \ - } \ + read_stream_update_position; \ + buffer.i_val = endian##bits##toh(buffer.i_val); \ + *number = buffer.f_val; \ } #define define_parse_endian_integer(endian, type, bits) \ void parse_##endian##_##type##bits(type##bits##_t *number, PState *pstate) \ { \ - if (!pstate->error_msg) \ + union \ { \ - union \ - { \ - char c_val[sizeof(type##bits##_t)]; \ - type##bits##_t i_val; \ - } buffer; \ + char c_val[sizeof(type##bits##_t)]; \ + type##bits##_t i_val; \ + } buffer; \ \ - read_stream_update_position; \ - *number = endian##bits##toh(buffer.i_val); \ - } \ + read_stream_update_position; \ + *number = endian##bits##toh(buffer.i_val); \ } -// Define functions to parse binary real numbers and integers +// Parse binary booleans, real numbers, and integers define_parse_endian_bool(be, 16); define_parse_endian_bool(be, 32); @@ -142,36 +136,38 @@ define_parse_endian_integer(le, uint, 32) define_parse_endian_integer(le, uint, 64) define_parse_endian_integer(le, uint, 8) -// Define function to parse fill bytes until end position is reached +// Parse fill bytes until end position is reached void parse_fill_bytes(size_t end_position, PState *pstate) { - while (!pstate->error_msg && pstate->position < end_position) + union { - char buffer; - size_t count = fread(&buffer, 1, sizeof(buffer), pstate->stream); + char c_val[1]; + } buffer; - pstate->position += count; - if (count < sizeof(buffer)) - { - pstate->error_msg = eof_or_error_msg(pstate->stream); - } + while (pstate->position < end_position) + { + read_stream_update_position; } } -// Define function to validate number is same as fixed value after parse +// Validate parsed number is same as fixed value void parse_validate_fixed(bool same, const char *element, PState *pstate) { - UNUSED(element); // because managing strings hard in embedded C - if (!pstate->error_msg && !same) + if (!same) { - // Error message would be easier to assemble and - // internationalize if we used an error struct with multiple - // fields instead of a const char string. - pstate->error_msg = "Parse: Value of element does not match value of " - "its 'fixed' attribute"; + Diagnostics *validati = need_diagnostics(); + pstate->validati = validati; + + if (validati->length < + sizeof(validati->array) / sizeof(*validati->array)) + { + Error *error = &validati->array[validati->length++]; + error->code = ERR_FIXED_VALUE; + error->s = element; + } } } diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/parsers.h b/daffodil-runtime2/src/main/resources/c/libruntime/parsers.h index 42b8d9a..c005d94 100644 --- a/daffodil-runtime2/src/main/resources/c/libruntime/parsers.h +++ b/daffodil-runtime2/src/main/resources/c/libruntime/parsers.h @@ -18,11 +18,12 @@ #ifndef PARSERS_H #define PARSERS_H -#include "infoset.h" // for PState #include <stdbool.h> // for bool -#include <stdint.h> // for int16_t, int32_t, int64_t, int8_t, uint16_t, uint32_t, uint64_t, uint8_t +#include <stddef.h> // for size_t +#include <stdint.h> // for int64_t, uint32_t, int16_t, int32_t, int8_t, uint16_t, uint64_t, uint8_t +#include "errors.h" // for PState -// Functions to parse binary booleans, real numbers, and integers +// Parse binary booleans, real numbers, and integers extern void parse_be_bool16(bool *number, int64_t true_rep, uint32_t false_rep, PState *pstate); @@ -64,11 +65,11 @@ extern void parse_le_uint32(uint32_t *number, PState *pstate); extern void parse_le_uint64(uint64_t *number, PState *pstate); extern void parse_le_uint8(uint8_t *number, PState *pstate); -// Function to parse fill bytes until end position is reached +// Parse fill bytes until end position is reached extern void parse_fill_bytes(size_t end_position, PState *pstate); -// Function to validate number is same as fixed value after parse +// Validate parsed number is same as fixed value extern void parse_validate_fixed(bool same, const char *element, PState *pstate); diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.c b/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.c index 90bdd17..de65700 100644 --- a/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.c +++ b/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.c @@ -16,14 +16,14 @@ */ #include "unparsers.h" -#include <endian.h> // for htobe32, htobe64, htole32, htole64, htobe16, htole16 +#include <endian.h> // for htobe32, htole32, htobe16, htobe64, htole16, htole64 #include <stdbool.h> // for bool -#include <stdio.h> // for fwrite, size_t +#include <stdio.h> // for fwrite +#include "errors.h" // for UState, eof_or_error, Diagnostics, Error, need_diagnostics, ERR_FIXED_VALUE, Error::(anonymous) -// Macros that are not defined by <endian.h> +// Macros not defined by <endian.h> which we need for uniformity #define htobe8(var) var - #define htole8(var) var // Helper macro to reduce duplication of C code writing stream, @@ -34,7 +34,8 @@ ustate->position += count; \ if (count < sizeof(buffer)) \ { \ - ustate->error_msg = eof_or_error_msg(ustate->stream); \ + ustate->error = eof_or_error(ustate->stream); \ + if (ustate->error) return; \ } // Macros to define unparse_<endian>_<type> functions @@ -43,55 +44,46 @@ void unparse_##endian##_bool##bits(bool number, uint32_t true_rep, \ uint32_t false_rep, UState *ustate) \ { \ - if (!ustate->error_msg) \ + union \ { \ - union \ - { \ - char c_val[sizeof(uint##bits##_t)]; \ - uint##bits##_t i_val; \ - } buffer; \ + char c_val[sizeof(uint##bits##_t)]; \ + uint##bits##_t i_val; \ + } buffer; \ \ - buffer.i_val = hto##endian##bits(number ? true_rep : false_rep); \ - write_stream_update_position; \ - } \ + buffer.i_val = hto##endian##bits(number ? true_rep : false_rep); \ + write_stream_update_position; \ } #define define_unparse_endian_real(endian, type, bits) \ void unparse_##endian##_##type(type number, UState *ustate) \ { \ - if (!ustate->error_msg) \ + union \ { \ - union \ - { \ - char c_val[sizeof(type)]; \ - type f_val; \ - uint##bits##_t i_val; \ - } buffer; \ + char c_val[sizeof(type)]; \ + type f_val; \ + uint##bits##_t i_val; \ + } buffer; \ \ - buffer.f_val = number; \ - buffer.i_val = hto##endian##bits(buffer.i_val); \ - write_stream_update_position; \ - } \ + buffer.f_val = number; \ + buffer.i_val = hto##endian##bits(buffer.i_val); \ + write_stream_update_position; \ } #define define_unparse_endian_integer(endian, type, bits) \ void unparse_##endian##_##type##bits(type##bits##_t number, \ UState * ustate) \ { \ - if (!ustate->error_msg) \ + union \ { \ - union \ - { \ - char c_val[sizeof(type##bits##_t)]; \ - type##bits##_t i_val; \ - } buffer; \ + char c_val[sizeof(type##bits##_t)]; \ + type##bits##_t i_val; \ + } buffer; \ \ - buffer.i_val = hto##endian##bits(number); \ - write_stream_update_position; \ - } \ + buffer.i_val = hto##endian##bits(number); \ + write_stream_update_position; \ } -// Define functions to unparse binary real numbers and integers +// Unparse binary booleans, real numbers, and integers define_unparse_endian_bool(be, 16); define_unparse_endian_bool(be, 32); @@ -127,35 +119,41 @@ define_unparse_endian_integer(le, uint, 32) define_unparse_endian_integer(le, uint, 64) define_unparse_endian_integer(le, uint, 8) -// Define function to unparse fill bytes until end position is reached +// Unparse fill bytes until end position is reached void -unparse_fill_bytes(size_t end_position, const char fill_byte, UState *ustate) +unparse_fill_bytes(size_t end_position, const char fill_byte, + UState *ustate) { - while (!ustate->error_msg && ustate->position < end_position) + union { - size_t count = fwrite(&fill_byte, 1, sizeof(fill_byte), ustate->stream); + char c_val[1]; + } buffer; - ustate->position += count; - if (count < sizeof(fill_byte)) - { - ustate->error_msg = eof_or_error_msg(ustate->stream); - } + buffer.c_val[0] = fill_byte; + + while (ustate->position < end_position) + { + write_stream_update_position; } } -// Define function to validate number is same as fixed value during unparse +// Validate unparsed number is same as fixed value void unparse_validate_fixed(bool same, const char *element, UState *ustate) { - if (!ustate->error_msg && !same) + if (!same) { - // Error message would be easier to assemble and - // internationalize if we used an error struct with multiple - // fields instead of a const char string. - ustate->error_msg = "Unparse: Value of element does not match value of " - "its 'fixed' attribute"; - UNUSED(element); // unused because managing strings hard in embedded C + Diagnostics *validati = need_diagnostics(); + ustate->validati = validati; + + if (validati->length < + sizeof(validati->array) / sizeof(*validati->array)) + { + Error *error = &validati->array[validati->length++]; + error->code = ERR_FIXED_VALUE; + error->s = element; + } } } diff --git a/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.h b/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.h index b676f7a..4b5f5ab 100644 --- a/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.h +++ b/daffodil-runtime2/src/main/resources/c/libruntime/unparsers.h @@ -18,11 +18,12 @@ #ifndef UNPARSERS_H #define UNPARSERS_H -#include "infoset.h" // for UState #include <stdbool.h> // for bool -#include <stdint.h> // for int16_t, int32_t, int64_t, int8_t, uint16_t, uint32_t, uint64_t, uint8_t +#include <stddef.h> // for size_t +#include <stdint.h> // for uint32_t, int16_t, int32_t, int64_t, int8_t, uint16_t, uint64_t, uint8_t +#include "errors.h" // for UState -// Functions to unparse binary booleans, real numbers, and integers +// Unparse binary booleans, real numbers, and integers extern void unparse_be_bool16(bool number, uint32_t true_rep, uint32_t false_rep, UState *ustate); @@ -64,12 +65,12 @@ extern void unparse_le_uint32(uint32_t number, UState *ustate); extern void unparse_le_uint64(uint64_t number, UState *ustate); extern void unparse_le_uint8(uint8_t number, UState *ustate); -// Function to unparse fill bytes until end position is reached +// Unparse fill bytes until end position is reached extern void unparse_fill_bytes(size_t end_position, const char fill_byte, UState *ustate); -// Function to validate number is same as fixed value during unparse +// Validate unparsed number is same as fixed value extern void unparse_validate_fixed(bool same, const char *element, UState *ustate); diff --git a/daffodil-runtime2/src/main/resources/examples/NestedUnion.c b/daffodil-runtime2/src/main/resources/examples/NestedUnion.c index da4a0a4..f5f1f89 100644 --- a/daffodil-runtime2/src/main/resources/examples/NestedUnion.c +++ b/daffodil-runtime2/src/main/resources/examples/NestedUnion.c @@ -16,13 +16,18 @@ */ #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 <stdbool.h> // for bool, true, false #include <stddef.h> // for NULL, size_t +#include "errors.h" // for Error, PState, UState, ERR_CHOICE_KEY, UNUSED +#include "parsers.h" // for parse_be_float, parse_be_int16, parse_be_bool32, parse_validate_fixed, parse_be_bool16, parse_be_int32, parse_be_uint32, parse_le_bool32, parse_le_int64, parse_le_uint8, parse_be_bool8, parse_be_double, parse_be_int64, parse_be_int8, parse_be_uint16, 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_uint16, parse_le_uint32, parse_le_uint64 +#include "unparsers.h" // for unparse_be_float, unparse_be_int16, unparse_be_bool32, unparse_validate_fixed, unparse_be_bool16, unparse_be_int32, unparse_be_uint32, unparse_le_bool32, unparse_le_int64, unparse_le_uint8, unparse_be_bool8, unparse_be_double, unparse_be_int64, unparse_be_int8, unparse_be_uint16, unparse_be_uint64, unparse_be_uint8, unparse_le_bool16, unparse_le_bool8, unparse_le_double, unparse_le_float, unparse_le_int16, unparse_le_int32, unparse_le_int8, unparse_le_uint1 [...] -// Prototypes needed for compilation +// Initialize our program's name and version + +const char *argp_program_version = "daffodil-runtime2 3.1.0-SNAPSHOT"; + +// Declare prototypes for easier compilation static void foo_initSelf(foo *instance); static void foo_parseSelf(foo *instance, PState *pstate); @@ -31,14 +36,14 @@ 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 const Error *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 +// Define metadata for the infoset static const ERD tag_NestedUnionType_ERD = { { @@ -238,7 +243,7 @@ static const ERD NestedUnion_ERD = { NULL // initChoice }; -// Return a root element to be used for parsing or unparsing +// Return a root element for parsing or unparsing the infoset InfosetBase * rootElement(void) @@ -253,7 +258,7 @@ rootElement(void) return &root._base; } -// Methods to initialize, parse, and unparse infoset nodes +// Initialize, parse, and unparse nodes of the infoset static void foo_initSelf(foo *instance) @@ -268,16 +273,22 @@ static void foo_parseSelf(foo *instance, PState *pstate) { parse_be_int32(&instance->a, pstate); + if (pstate->error) return; parse_be_int32(&instance->b, pstate); + if (pstate->error) return; parse_be_int32(&instance->c, pstate); + if (pstate->error) return; } static void foo_unparseSelf(const foo *instance, UState *ustate) { unparse_be_int32(instance->a, ustate); + if (ustate->error) return; unparse_be_int32(instance->b, ustate); + if (ustate->error) return; unparse_be_int32(instance->c, ustate); + if (ustate->error) return; } static void @@ -293,30 +304,38 @@ static void bar_parseSelf(bar *instance, PState *pstate) { parse_be_double(&instance->x, pstate); + if (pstate->error) return; parse_be_double(&instance->y, pstate); + if (pstate->error) return; parse_be_double(&instance->z, pstate); + if (pstate->error) return; } static void bar_unparseSelf(const bar *instance, UState *ustate) { unparse_be_double(instance->x, ustate); + if (ustate->error) return; unparse_be_double(instance->y, ustate); + if (ustate->error) return; unparse_be_double(instance->z, ustate); + if (ustate->error) return; } static void data_initSelf(data *instance) { instance->_base.erd = &data_NestedUnionType_ERD; - instance->_choice = NO_CHOICE; + instance->_choice = 0xFFFFFFFFFFFFFFFF; foo_initSelf(&instance->foo); bar_initSelf(&instance->bar); } -static bool +static const Error * data_initChoice(data *instance, const NestedUnion *rootElement) { + static Error error = {ERR_CHOICE_KEY, {NULL}}; + int64_t key = rootElement->tag; switch (key) { @@ -329,60 +348,69 @@ data_initChoice(data *instance, const NestedUnion *rootElement) instance->_choice = 1; break; default: - instance->_choice = NO_CHOICE; - break; + error.d64 = key; + return &error; } - 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; - } + // Point next ERD to choice of alternative elements' ERDs + 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 NULL; } static void data_parseSelf(data *instance, PState *pstate) { - instance->_base.erd->initChoice(&instance->_base, rootElement()); + static Error error = {ERR_CHOICE_KEY, {NULL}}; + + pstate->error = instance->_base.erd->initChoice(&instance->_base, rootElement()); + if (pstate->error) return; + switch (instance->_choice) { case 0: foo_parseSelf(&instance->foo, pstate); + if (pstate->error) return; break; case 1: bar_parseSelf(&instance->bar, pstate); + if (pstate->error) return; break; default: - pstate->error_msg = - "Parse error: no match between choice dispatch key and any branch key"; - break; + // Should never happen because initChoice would return an error first + error.d64 = (int64_t)instance->_choice; + pstate->error = &error; + return; } } static void data_unparseSelf(const data *instance, UState *ustate) { - instance->_base.erd->initChoice(&instance->_base, rootElement()); + static Error error = {ERR_CHOICE_KEY, {NULL}}; + + ustate->error = instance->_base.erd->initChoice(&instance->_base, rootElement()); + if (ustate->error) return; + switch (instance->_choice) { case 0: foo_unparseSelf(&instance->foo, ustate); + if (ustate->error) return; break; case 1: bar_unparseSelf(&instance->bar, ustate); + if (ustate->error) return; break; default: - ustate->error_msg = - "Unparse error: no match between choice dispatch key and any branch key"; - break; + // Should never happen because initChoice would return an error first + error.d64 = (int64_t)instance->_choice; + ustate->error = &error; + return; } } @@ -398,13 +426,17 @@ static void NestedUnion_parseSelf(NestedUnion *instance, PState *pstate) { parse_be_int32(&instance->tag, pstate); + if (pstate->error) return; data_parseSelf(&instance->data, pstate); + if (pstate->error) return; } static void NestedUnion_unparseSelf(const NestedUnion *instance, UState *ustate) { unparse_be_int32(instance->tag, ustate); + if (ustate->error) return; data_unparseSelf(&instance->data, ustate); + if (ustate->error) return; } diff --git a/daffodil-runtime2/src/main/resources/examples/NestedUnion.h b/daffodil-runtime2/src/main/resources/examples/NestedUnion.h index 620e1b5..b729693 100644 --- a/daffodil-runtime2/src/main/resources/examples/NestedUnion.h +++ b/daffodil-runtime2/src/main/resources/examples/NestedUnion.h @@ -18,9 +18,10 @@ #ifndef GENERATED_CODE_H #define GENERATED_CODE_H -#include "infoset.h" // for InfosetBase #include <stdbool.h> // for bool -#include <stdint.h> // for int16_t, int32_t, int64_t, int8_t, uint16_t, uint32_t, uint64_t, uint8_t +#include <stddef.h> // for size_t +#include <stdint.h> // for int16_t, int32_t, int64_t, uint32_t, uint8_t, int8_t, uint16_t, uint64_t +#include "infoset.h" // for InfosetBase // Define infoset structures diff --git a/daffodil-runtime2/src/main/resources/examples/ex_nums.c b/daffodil-runtime2/src/main/resources/examples/ex_nums.c index 88b7247..b64e7f6 100644 --- a/daffodil-runtime2/src/main/resources/examples/ex_nums.c +++ b/daffodil-runtime2/src/main/resources/examples/ex_nums.c @@ -16,13 +16,18 @@ */ #include "ex_nums.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 <stdbool.h> // for bool, true, false #include <stddef.h> // for NULL, size_t +#include "errors.h" // for Error, PState, UState, ERR_CHOICE_KEY, UNUSED +#include "parsers.h" // for parse_be_float, parse_be_int16, parse_be_bool32, parse_validate_fixed, parse_be_bool16, parse_be_int32, parse_be_uint32, parse_le_bool32, parse_le_int64, parse_le_uint8, parse_be_bool8, parse_be_double, parse_be_int64, parse_be_int8, parse_be_uint16, 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_uint16, parse_le_uint32, parse_le_uint64 +#include "unparsers.h" // for unparse_be_float, unparse_be_int16, unparse_be_bool32, unparse_validate_fixed, unparse_be_bool16, unparse_be_int32, unparse_be_uint32, unparse_le_bool32, unparse_le_int64, unparse_le_uint8, unparse_be_bool8, unparse_be_double, unparse_be_int64, unparse_be_int8, unparse_be_uint16, unparse_be_uint64, unparse_be_uint8, unparse_le_bool16, unparse_le_bool8, unparse_le_double, unparse_le_float, unparse_le_int16, unparse_le_int32, unparse_le_int8, unparse_le_uint1 [...] -// Prototypes needed for compilation +// Initialize our program's name and version + +const char *argp_program_version = "daffodil-runtime2 3.1.0-SNAPSHOT"; + +// Declare prototypes for easier compilation static void array_initSelf(array *instance); static void array_parseSelf(array *instance, PState *pstate); @@ -40,7 +45,7 @@ static void ex_nums_initSelf(ex_nums *instance); static void ex_nums_parseSelf(ex_nums *instance, PState *pstate); static void ex_nums_unparseSelf(const ex_nums *instance, UState *ustate); -// Metadata singletons +// Define metadata for the infoset static const ERD be_bool16_array_ex_nums_ERD = { { @@ -648,7 +653,7 @@ static const ERD ex_nums_ERD = { NULL // initChoice }; -// Return a root element to be used for parsing or unparsing +// Return a root element for parsing or unparsing the infoset InfosetBase * rootElement(void) @@ -663,7 +668,7 @@ rootElement(void) return &root._base; } -// Methods to initialize, parse, and unparse infoset nodes +// Initialize, parse, and unparse nodes of the infoset static void array_initSelf(array *instance) @@ -683,26 +688,42 @@ static void array_parseSelf(array *instance, PState *pstate) { parse_be_bool16(&instance->be_bool16[0], -1, 0, pstate); + if (pstate->error) return; parse_be_bool16(&instance->be_bool16[1], -1, 0, pstate); + if (pstate->error) return; parse_be_float(&instance->be_float[0], pstate); + if (pstate->error) return; parse_be_float(&instance->be_float[1], pstate); + if (pstate->error) return; parse_be_float(&instance->be_float[2], pstate); + if (pstate->error) return; parse_be_int16(&instance->be_int16[0], pstate); + if (pstate->error) return; parse_be_int16(&instance->be_int16[1], pstate); + if (pstate->error) return; parse_be_int16(&instance->be_int16[2], pstate); + if (pstate->error) return; } static void array_unparseSelf(const array *instance, UState *ustate) { unparse_be_bool16(instance->be_bool16[0], ~0, 0, ustate); + if (ustate->error) return; unparse_be_bool16(instance->be_bool16[1], ~0, 0, ustate); + if (ustate->error) return; unparse_be_float(instance->be_float[0], ustate); + if (ustate->error) return; unparse_be_float(instance->be_float[1], ustate); + if (ustate->error) return; unparse_be_float(instance->be_float[2], ustate); + if (ustate->error) return; unparse_be_int16(instance->be_int16[0], ustate); + if (ustate->error) return; unparse_be_int16(instance->be_int16[1], ustate); + if (ustate->error) return; unparse_be_int16(instance->be_int16[2], ustate); + if (ustate->error) return; } static void @@ -731,42 +752,74 @@ static void bigEndian_parseSelf(bigEndian *instance, PState *pstate) { parse_be_bool16(&instance->be_bool16, 1, 0, pstate); + if (pstate->error) return; parse_be_bool32(&instance->be_bool32, -1, 0, pstate); + if (pstate->error) return; parse_be_bool8(&instance->be_bool8, -1, 0, pstate); + if (pstate->error) return; parse_be_bool32(&instance->be_boolean, -1, 0, pstate); + if (pstate->error) return; parse_be_double(&instance->be_double, pstate); + if (pstate->error) return; parse_be_float(&instance->be_float, pstate); + if (pstate->error) return; parse_be_int16(&instance->be_int16, pstate); + if (pstate->error) return; parse_be_int32(&instance->be_int32, pstate); + if (pstate->error) return; parse_be_int64(&instance->be_int64, pstate); + if (pstate->error) return; parse_be_int8(&instance->be_int8, pstate); + if (pstate->error) return; parse_be_int16(&instance->be_integer16, pstate); + if (pstate->error) return; parse_be_uint16(&instance->be_uint16, pstate); + if (pstate->error) return; parse_be_uint32(&instance->be_uint32, pstate); + if (pstate->error) return; parse_be_uint64(&instance->be_uint64, pstate); + if (pstate->error) return; parse_be_uint8(&instance->be_uint8, pstate); + if (pstate->error) return; parse_be_uint32(&instance->be_nonNegativeInteger32, pstate); + if (pstate->error) return; } static void bigEndian_unparseSelf(const bigEndian *instance, UState *ustate) { unparse_be_bool16(instance->be_bool16, 1, 0, ustate); + if (ustate->error) return; unparse_be_bool32(instance->be_bool32, ~0, 0, ustate); + if (ustate->error) return; unparse_be_bool8(instance->be_bool8, ~0, 0, ustate); + if (ustate->error) return; unparse_be_bool32(instance->be_boolean, ~0, 0, ustate); + if (ustate->error) return; unparse_be_double(instance->be_double, ustate); + if (ustate->error) return; unparse_be_float(instance->be_float, ustate); + if (ustate->error) return; unparse_be_int16(instance->be_int16, ustate); + if (ustate->error) return; unparse_be_int32(instance->be_int32, ustate); + if (ustate->error) return; unparse_be_int64(instance->be_int64, ustate); + if (ustate->error) return; unparse_be_int8(instance->be_int8, ustate); + if (ustate->error) return; unparse_be_int16(instance->be_integer16, ustate); + if (ustate->error) return; unparse_be_uint16(instance->be_uint16, ustate); + if (ustate->error) return; unparse_be_uint32(instance->be_uint32, ustate); + if (ustate->error) return; unparse_be_uint64(instance->be_uint64, ustate); + if (ustate->error) return; unparse_be_uint8(instance->be_uint8, ustate); + if (ustate->error) return; unparse_be_uint32(instance->be_nonNegativeInteger32, ustate); + if (ustate->error) return; } static void @@ -795,42 +848,74 @@ static void littleEndian_parseSelf(littleEndian *instance, PState *pstate) { parse_le_bool16(&instance->le_bool16, 1, 0, pstate); + if (pstate->error) return; parse_le_bool32(&instance->le_bool32, -1, 0, pstate); + if (pstate->error) return; parse_le_bool8(&instance->le_bool8, -1, 0, pstate); + if (pstate->error) return; parse_le_bool32(&instance->le_boolean, -1, 0, pstate); + if (pstate->error) return; parse_le_double(&instance->le_double, pstate); + if (pstate->error) return; parse_le_float(&instance->le_float, pstate); + if (pstate->error) return; parse_le_int16(&instance->le_int16, pstate); + if (pstate->error) return; parse_le_int32(&instance->le_int32, pstate); + if (pstate->error) return; parse_le_int64(&instance->le_int64, pstate); + if (pstate->error) return; parse_le_int8(&instance->le_int8, pstate); + if (pstate->error) return; parse_le_int64(&instance->le_integer64, pstate); + if (pstate->error) return; parse_le_uint16(&instance->le_uint16, pstate); + if (pstate->error) return; parse_le_uint32(&instance->le_uint32, pstate); + if (pstate->error) return; parse_le_uint64(&instance->le_uint64, pstate); + if (pstate->error) return; parse_le_uint8(&instance->le_uint8, pstate); + if (pstate->error) return; parse_le_uint8(&instance->le_nonNegativeInteger8, pstate); + if (pstate->error) return; } static void littleEndian_unparseSelf(const littleEndian *instance, UState *ustate) { unparse_le_bool16(instance->le_bool16, 1, 0, ustate); + if (ustate->error) return; unparse_le_bool32(instance->le_bool32, ~0, 0, ustate); + if (ustate->error) return; unparse_le_bool8(instance->le_bool8, ~0, 0, ustate); + if (ustate->error) return; unparse_le_bool32(instance->le_boolean, ~0, 0, ustate); + if (ustate->error) return; unparse_le_double(instance->le_double, ustate); + if (ustate->error) return; unparse_le_float(instance->le_float, ustate); + if (ustate->error) return; unparse_le_int16(instance->le_int16, ustate); + if (ustate->error) return; unparse_le_int32(instance->le_int32, ustate); + if (ustate->error) return; unparse_le_int64(instance->le_int64, ustate); + if (ustate->error) return; unparse_le_int8(instance->le_int8, ustate); + if (ustate->error) return; unparse_le_int64(instance->le_integer64, ustate); + if (ustate->error) return; unparse_le_uint16(instance->le_uint16, ustate); + if (ustate->error) return; unparse_le_uint32(instance->le_uint32, ustate); + if (ustate->error) return; unparse_le_uint64(instance->le_uint64, ustate); + if (ustate->error) return; unparse_le_uint8(instance->le_uint8, ustate); + if (ustate->error) return; unparse_le_uint8(instance->le_nonNegativeInteger8, ustate); + if (ustate->error) return; } static void @@ -847,26 +932,42 @@ static void fixed_parseSelf(fixed *instance, PState *pstate) { parse_be_bool32(&instance->boolean_false, -1, 0, pstate); + if (pstate->error) return; parse_validate_fixed(instance->boolean_false == false, "boolean_false", pstate); + if (pstate->error) return; parse_be_bool32(&instance->boolean_true, -1, 0, pstate); + if (pstate->error) return; parse_validate_fixed(instance->boolean_true == true, "boolean_true", pstate); + if (pstate->error) return; parse_be_float(&instance->float_1_5, pstate); + if (pstate->error) return; parse_validate_fixed(instance->float_1_5 == 1.5, "float_1_5", pstate); + if (pstate->error) return; parse_be_int32(&instance->int_32, pstate); + if (pstate->error) return; parse_validate_fixed(instance->int_32 == 32, "int_32", pstate); + if (pstate->error) return; } static void fixed_unparseSelf(const fixed *instance, UState *ustate) { unparse_be_bool32(instance->boolean_false, ~0, 0, ustate); + if (ustate->error) return; unparse_validate_fixed(instance->boolean_false == false, "boolean_false", ustate); + if (ustate->error) return; unparse_be_bool32(instance->boolean_true, ~0, 0, ustate); + if (ustate->error) return; unparse_validate_fixed(instance->boolean_true == true, "boolean_true", ustate); + if (ustate->error) return; unparse_be_float(instance->float_1_5, ustate); + if (ustate->error) return; unparse_validate_fixed(instance->float_1_5 == 1.5, "float_1_5", ustate); + if (ustate->error) return; unparse_be_int32(instance->int_32, ustate); + if (ustate->error) return; unparse_validate_fixed(instance->int_32 == 32, "int_32", ustate); + if (ustate->error) return; } static void @@ -883,17 +984,25 @@ static void ex_nums_parseSelf(ex_nums *instance, PState *pstate) { array_parseSelf(&instance->array, pstate); + if (pstate->error) return; bigEndian_parseSelf(&instance->bigEndian, pstate); + if (pstate->error) return; littleEndian_parseSelf(&instance->littleEndian, pstate); + if (pstate->error) return; fixed_parseSelf(&instance->fixed, pstate); + if (pstate->error) return; } static void ex_nums_unparseSelf(const ex_nums *instance, UState *ustate) { array_unparseSelf(&instance->array, ustate); + if (ustate->error) return; bigEndian_unparseSelf(&instance->bigEndian, ustate); + if (ustate->error) return; littleEndian_unparseSelf(&instance->littleEndian, ustate); + if (ustate->error) return; fixed_unparseSelf(&instance->fixed, ustate); + if (ustate->error) return; } diff --git a/daffodil-runtime2/src/main/resources/examples/ex_nums.h b/daffodil-runtime2/src/main/resources/examples/ex_nums.h index ae33283..937c93c 100644 --- a/daffodil-runtime2/src/main/resources/examples/ex_nums.h +++ b/daffodil-runtime2/src/main/resources/examples/ex_nums.h @@ -18,9 +18,10 @@ #ifndef GENERATED_CODE_H #define GENERATED_CODE_H -#include "infoset.h" // for InfosetBase #include <stdbool.h> // for bool -#include <stdint.h> // for int16_t, int32_t, int64_t, int8_t, uint16_t, uint32_t, uint64_t, uint8_t +#include <stddef.h> // for size_t +#include <stdint.h> // for int16_t, int32_t, int64_t, uint32_t, uint8_t, int8_t, uint16_t, uint64_t +#include "infoset.h" // for InfosetBase // Define infoset structures diff --git a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryFloatCodeGenerator.scala b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryAbstractCodeGenerator.scala similarity index 58% copy from daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryFloatCodeGenerator.scala copy to daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryAbstractCodeGenerator.scala index 332ba66..f5aa79b 100644 --- a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryFloatCodeGenerator.scala +++ b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryAbstractCodeGenerator.scala @@ -18,41 +18,46 @@ package org.apache.daffodil.runtime2.generators import org.apache.daffodil.dsom.ElementBase +import org.apache.daffodil.schema.annotation.props.gen.BitOrder import org.apache.daffodil.schema.annotation.props.gen.ByteOrder import org.apache.daffodil.schema.annotation.props.gen.OccursCountKind -trait BinaryFloatCodeGenerator { +trait BinaryAbstractCodeGenerator { + + def binaryAbstractGenerateCode(e: ElementBase, initialValue: String, prim: String, + parseArgs: String, unparseArgs: String, cgState: CodeGeneratorState): Unit = { - def binaryFloatGenerateCode(e: ElementBase, lengthInBits: Int, cgState: CodeGeneratorState): Unit = { // For the time being this is a very limited back end. // So there are some restrictions to enforce. - assert(lengthInBits == 32 || lengthInBits == 64) - val byteOrder: ByteOrder = { - e.schemaDefinitionUnless(e.byteOrderEv.isConstant, "Runtime dfdl:byteOrder expressions not supported.") - val bo = e.byteOrderEv.constValue - bo - } + e.schemaDefinitionUnless(e.bitOrder eq BitOrder.MostSignificantBitFirst, "Only dfdl:bitOrder 'mostSignificantBitFirst' is supported.") + e.schemaDefinitionUnless(e.byteOrderEv.isConstant, "Runtime dfdl:byteOrder expressions not supported.") + e.schemaDefinitionUnless(e.elementLengthInBitsEv.isConstant, "Runtime dfdl:length expressions not supported.") - // Use a NAN to mark our field as uninitialized in case parsing or unparsing - // fails to set the field. - val initialValue = "NAN" val fieldName = e.namedQName.local + val byteOrder = e.byteOrderEv.constValue val conv = if (byteOrder eq ByteOrder.BigEndian) "be" else "le" - val prim = if (lengthInBits == 32) "float" else "double" val arraySize = if (e.occursCountKind == OccursCountKind.Fixed) e.maxOccurs else 0 val fixed = e.xml.attribute("fixed") val fixedValue = if (fixed.isDefined) fixed.get.text else "" def addStatements(deref: String): Unit = { val initStatement = s" instance->$fieldName$deref = $initialValue;" - val parseStatement = s" parse_${conv}_$prim(&instance->$fieldName$deref, pstate);" - val unparseStatement = s" unparse_${conv}_$prim(instance->$fieldName$deref, ustate);" + val parseStatement = + s""" parse_${conv}_$prim(&instance->$fieldName$deref, $parseArgs); + | if (pstate->error) return;""".stripMargin + val unparseStatement = + s""" unparse_${conv}_$prim(instance->$fieldName$deref, $unparseArgs); + | if (ustate->error) return;""".stripMargin cgState.addSimpleTypeStatements(initStatement, parseStatement, unparseStatement) if (fixedValue.nonEmpty) { val init2 = "" - val parse2 = s""" parse_validate_fixed(instance->$fieldName$deref == $fixedValue, "$fieldName", pstate);""" - val unparse2 = s""" unparse_validate_fixed(instance->$fieldName$deref == $fixedValue, "$fieldName", ustate);""" + val parse2 = + s""" parse_validate_fixed(instance->$fieldName$deref == $fixedValue, "$fieldName", pstate); + | if (pstate->error) return;""".stripMargin + val unparse2 = + s""" unparse_validate_fixed(instance->$fieldName$deref == $fixedValue, "$fieldName", ustate); + | if (ustate->error) return;""".stripMargin cgState.addSimpleTypeStatements(init2, parse2, unparse2) } } diff --git a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryBooleanCodeGenerator.scala b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryBooleanCodeGenerator.scala index e5a7d3a..32c4bbe 100644 --- a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryBooleanCodeGenerator.scala +++ b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryBooleanCodeGenerator.scala @@ -19,60 +19,27 @@ package org.apache.daffodil.runtime2.generators import org.apache.daffodil.dsom.ElementBase import org.apache.daffodil.exceptions.Assert -import org.apache.daffodil.schema.annotation.props.gen.BitOrder -import org.apache.daffodil.schema.annotation.props.gen.ByteOrder -import org.apache.daffodil.schema.annotation.props.gen.OccursCountKind import passera.unsigned.ULong -trait BinaryBooleanCodeGenerator { +trait BinaryBooleanCodeGenerator extends BinaryAbstractCodeGenerator { def binaryBooleanGenerateCode(e: ElementBase, cgState: CodeGeneratorState): Unit = { - // For the time being this is a very limited back end. - // So there are some restrictions to enforce. - e.schemaDefinitionUnless(e.bitOrder eq BitOrder.MostSignificantBitFirst, "Only dfdl:bitOrder 'mostSignificantBitFirst' is supported.") - val byteOrder: ByteOrder = { - e.schemaDefinitionUnless(e.byteOrderEv.isConstant, "Runtime dfdl:byteOrder expressions not supported.") - e.byteOrderEv.constValue - } - val lengthInBits: Long = { - e.schemaDefinitionUnless(e.elementLengthInBitsEv.isConstant, "Runtime dfdl:length expressions not supported.") - val len = e.elementLengthInBitsEv.constValue.get - len match { - case 8 | 16 | 32 => len - case _ => e.SDE("Boolean lengths other than 8, 16, or 32 bits are not supported.") - } - } Assert.invariant(e.binaryBooleanTrueRep.isEmpty || e.binaryBooleanTrueRep.getULong >= ULong(0)) Assert.invariant(e.binaryBooleanFalseRep >= ULong(0)) + Assert.invariant(e.elementLengthInBitsEv.isConstant) - val initialValue = "true" - val fieldName = e.namedQName.local - val conv = if (byteOrder eq ByteOrder.BigEndian) "be" else "le" + val lengthInBits = e.elementLengthInBitsEv.constValue.get + val initialValue = lengthInBits match { + case 8 | 16 | 32 => "true" + case _ => e.SDE("Boolean lengths other than 8, 16, or 32 bits are not supported.") + } val prim = s"bool$lengthInBits" val trueRep = if (e.binaryBooleanTrueRep.isDefined) e.binaryBooleanTrueRep.getULong else -1 val falseRep = e.binaryBooleanFalseRep + val parseArgs = s"$trueRep, $falseRep, pstate" val unparseTrueRep = if (e.binaryBooleanTrueRep.isDefined) s"$trueRep" else s"~$falseRep" - val arraySize = if (e.occursCountKind == OccursCountKind.Fixed) e.maxOccurs else 0 - val fixed = e.xml.attribute("fixed") - val fixedValue = if (fixed.isDefined) fixed.get.text else "" + val unparseArgs = s"$unparseTrueRep, $falseRep, ustate" - def addStatements(deref: String): Unit = { - val initStatement = s" instance->$fieldName$deref = $initialValue;" - val parseStatement = s" parse_${conv}_$prim(&instance->$fieldName$deref, $trueRep, $falseRep, pstate);" - val unparseStatement = s" unparse_${conv}_$prim(instance->$fieldName$deref, $unparseTrueRep, $falseRep, ustate);" - cgState.addSimpleTypeStatements(initStatement, parseStatement, unparseStatement) - - if (fixedValue.nonEmpty) { - val init2 = "" - val parse2 = s""" parse_validate_fixed(instance->$fieldName$deref == $fixedValue, "$fieldName", pstate);""" - val unparse2 = s""" unparse_validate_fixed(instance->$fieldName$deref == $fixedValue, "$fieldName", ustate);""" - cgState.addSimpleTypeStatements(init2, parse2, unparse2) - } - } - if (arraySize > 0) - for (i <- 0 until arraySize) - addStatements(s"[$i]") - else - addStatements("") + binaryAbstractGenerateCode(e, initialValue, prim, parseArgs, unparseArgs, cgState) } } diff --git a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryFloatCodeGenerator.scala b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryFloatCodeGenerator.scala index 332ba66..3d071b6 100644 --- a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryFloatCodeGenerator.scala +++ b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryFloatCodeGenerator.scala @@ -18,48 +18,21 @@ package org.apache.daffodil.runtime2.generators import org.apache.daffodil.dsom.ElementBase -import org.apache.daffodil.schema.annotation.props.gen.ByteOrder -import org.apache.daffodil.schema.annotation.props.gen.OccursCountKind -trait BinaryFloatCodeGenerator { +trait BinaryFloatCodeGenerator extends BinaryAbstractCodeGenerator { def binaryFloatGenerateCode(e: ElementBase, lengthInBits: Int, cgState: CodeGeneratorState): Unit = { - // For the time being this is a very limited back end. - // So there are some restrictions to enforce. - assert(lengthInBits == 32 || lengthInBits == 64) - val byteOrder: ByteOrder = { - e.schemaDefinitionUnless(e.byteOrderEv.isConstant, "Runtime dfdl:byteOrder expressions not supported.") - val bo = e.byteOrderEv.constValue - bo - } // Use a NAN to mark our field as uninitialized in case parsing or unparsing // fails to set the field. - val initialValue = "NAN" - val fieldName = e.namedQName.local - val conv = if (byteOrder eq ByteOrder.BigEndian) "be" else "le" + val initialValue = lengthInBits match { + case 32 | 64 => "NAN" + case _ => e.SDE("Floating point lengths other than 32 or 64 bits are not supported.") + } val prim = if (lengthInBits == 32) "float" else "double" - val arraySize = if (e.occursCountKind == OccursCountKind.Fixed) e.maxOccurs else 0 - val fixed = e.xml.attribute("fixed") - val fixedValue = if (fixed.isDefined) fixed.get.text else "" + val parseArgs = "pstate" + val unparseArgs = "ustate" - def addStatements(deref: String): Unit = { - val initStatement = s" instance->$fieldName$deref = $initialValue;" - val parseStatement = s" parse_${conv}_$prim(&instance->$fieldName$deref, pstate);" - val unparseStatement = s" unparse_${conv}_$prim(instance->$fieldName$deref, ustate);" - cgState.addSimpleTypeStatements(initStatement, parseStatement, unparseStatement) - - if (fixedValue.nonEmpty) { - val init2 = "" - val parse2 = s""" parse_validate_fixed(instance->$fieldName$deref == $fixedValue, "$fieldName", pstate);""" - val unparse2 = s""" unparse_validate_fixed(instance->$fieldName$deref == $fixedValue, "$fieldName", ustate);""" - cgState.addSimpleTypeStatements(init2, parse2, unparse2) - } - } - if (arraySize > 0) - for (i <- 0 until arraySize) - addStatements(s"[$i]") - else - addStatements("") + binaryAbstractGenerateCode(e, initialValue, prim, parseArgs, unparseArgs, cgState) } } diff --git a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryIntegerKnownLengthCodeGenerator.scala b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryIntegerKnownLengthCodeGenerator.scala index 9761b3b..0c39979 100644 --- a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryIntegerKnownLengthCodeGenerator.scala +++ b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/BinaryIntegerKnownLengthCodeGenerator.scala @@ -18,60 +18,25 @@ package org.apache.daffodil.runtime2.generators import org.apache.daffodil.grammar.primitives.BinaryIntegerKnownLength -import org.apache.daffodil.schema.annotation.props.gen.OccursCountKind -import org.apache.daffodil.schema.annotation.props.gen.{ BitOrder, ByteOrder } -trait BinaryIntegerKnownLengthCodeGenerator { +trait BinaryIntegerKnownLengthCodeGenerator extends BinaryAbstractCodeGenerator { def binaryIntegerKnownLengthGenerateCode(g: BinaryIntegerKnownLength, cgState: CodeGeneratorState): Unit = { - // For the time being this is a very limited back end. - // So there are some restrictions to enforce. - val e = g.e - val lengthInBits: Long = { - e.schemaDefinitionUnless(e.elementLengthInBitsEv.isConstant, "Runtime dfdl:length expressions not supported.") - val len = e.elementLengthInBitsEv.constValue.get - len - } - e.schemaDefinitionUnless(e.bitOrder eq BitOrder.MostSignificantBitFirst, "Only dfdl:bitOrder 'mostSignificantBitFirst' is supported.") - val byteOrder: ByteOrder = { - e.schemaDefinitionUnless(e.byteOrderEv.isConstant, "Runtime dfdl:byteOrder expressions not supported.") - val bo = e.byteOrderEv.constValue - bo - } // Use an unusual memory bit pattern (magic debug value) to mark our field // as uninitialized in case parsing or unparsing fails to set the field. - val initialValue = lengthInBits match { + val e = g.e + val initialValue = g.lengthInBits match { case 8 => "0xCC" case 16 => "0xCCCC" case 32 => "0xCCCCCCCC" case 64 => "0xCCCCCCCCCCCCCCCC" case _ => e.SDE("Integer lengths other than 8, 16, 32, or 64 bits are not supported.") } - val fieldName = e.namedQName.local - val conv = if (byteOrder eq ByteOrder.BigEndian) "be" else "le" - val prim = if (g.signed) s"int${lengthInBits}" else s"uint${lengthInBits}" - val arraySize = if (e.occursCountKind == OccursCountKind.Fixed) e.maxOccurs else 0 - val fixed = e.xml.attribute("fixed") - val fixedValue = if (fixed.isDefined) fixed.get.text else "" - - def addStatements(deref: String): Unit = { - val initStatement = s" instance->$fieldName$deref = $initialValue;" - val parseStatement = s" parse_${conv}_$prim(&instance->$fieldName$deref, pstate);" - val unparseStatement = s" unparse_${conv}_$prim(instance->$fieldName$deref, ustate);" - cgState.addSimpleTypeStatements(initStatement, parseStatement, unparseStatement) + val prim = if (g.signed) s"int${g.lengthInBits}" else s"uint${g.lengthInBits}" + val parseArgs = "pstate" + val unparseArgs = "ustate" - if (fixedValue.nonEmpty) { - val init2 = "" - val parse2 = s""" parse_validate_fixed(instance->$fieldName$deref == $fixedValue, "$fieldName", pstate);""" - val unparse2 = s""" unparse_validate_fixed(instance->$fieldName$deref == $fixedValue, "$fieldName", ustate);""" - cgState.addSimpleTypeStatements(init2, parse2, unparse2) - } - } - if (arraySize > 0) - for (i <- 0 until arraySize) - addStatements(s"[$i]") - else - addStatements("") + binaryAbstractGenerateCode(e, initialValue, prim, parseArgs, unparseArgs, cgState) } } diff --git a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala index 0fd7922..577a542 100644 --- a/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala +++ b/daffodil-runtime2/src/main/scala/org/apache/daffodil/runtime2/generators/CodeGeneratorState.scala @@ -85,12 +85,12 @@ class CodeGeneratorState { val hasChoice = structs.top.initChoiceStatements.nonEmpty val root = structs.elems.last.C val prototypeInitChoice = if (hasChoice) - s"\nstatic bool ${C}_initChoice($C *instance, const $root *rootElement);" + s"\nstatic const Error *${C}_initChoice($C *instance, const $root *rootElement);" else "" val implementInitChoice = if (hasChoice) s""" - |static bool + |static const Error * |${C}_initChoice($C *instance, const $root *rootElement) |{ |$initChoiceStatements @@ -222,17 +222,27 @@ class CodeGeneratorState { |""".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 initStatement = s" instance->_choice = 0xFFFFFFFFFFFFFFFF;" val initChoiceStatement = - s""" int64_t key = rootElement->$dispatchField; + s""" static Error error = {ERR_CHOICE_KEY, {NULL}}; + | + | int64_t key = rootElement->$dispatchField; | switch (key) | {""".stripMargin val parseStatement = - s""" instance->_base.erd->initChoice(&instance->_base, rootElement()); + s""" static Error error = {ERR_CHOICE_KEY, {NULL}}; + | + | pstate->error = instance->_base.erd->initChoice(&instance->_base, rootElement()); + | if (pstate->error) return; + | | switch (instance->_choice) | {""".stripMargin val unparseStatement = - s""" instance->_base.erd->initChoice(&instance->_base, rootElement()); + s""" static Error error = {ERR_CHOICE_KEY, {NULL}}; + | + | ustate->error = instance->_base.erd->initChoice(&instance->_base, rootElement()); + | if (ustate->error) return; + | | switch (instance->_choice) | {""".stripMargin @@ -254,34 +264,31 @@ class CodeGeneratorState { val declaration = s" };" val initChoiceStatement = s""" default: - | instance->_choice = NO_CHOICE; - | break; + | error.d64 = key; + | return &error; | } | - | 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 + | // Point next ERD to choice of alternative elements' ERDs + | 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 NULL;""".stripMargin val parseStatement = s""" default: - | pstate->error_msg = - | "Parse error: no match between choice dispatch key and any branch key"; - | break; + | // Should never happen because initChoice would return an error first + | error.d64 = (int64_t)instance->_choice; + | pstate->error = &error; + | return; | }""".stripMargin val unparseStatement = s""" default: - | ustate->error_msg = - | "Unparse error: no match between choice dispatch key and any branch key"; - | break; + | // Should never happen because initChoice would return an error first + | error.d64 = (int64_t)instance->_choice; + | ustate->error = &error; + | return; | }""".stripMargin structs.top.declarations += declaration @@ -293,8 +300,12 @@ class CodeGeneratorState { // Implement padding if complex type has an explicit length if (context.maybeFixedLengthInBits.isDefined && context.maybeFixedLengthInBits.get > 0) { val octalFillByte = context.fillByteEv.constValue.toByte.toOctalString - val parseStatement = s" parse_fill_bytes(end_position, pstate);" - val unparseStatement = s" unparse_fill_bytes(end_position, '\\$octalFillByte', ustate);" + val parseStatement = + s""" parse_fill_bytes(end_position, pstate); + | if (pstate->error) return;""".stripMargin + val unparseStatement = + s""" unparse_fill_bytes(end_position, '\\$octalFillByte', ustate); + | if (ustate->error) return;""".stripMargin structs.top.parserStatements += parseStatement structs.top.unparserStatements += unparseStatement @@ -382,8 +393,12 @@ class CodeGeneratorState { def addStatements(deref: String): Unit = { val moreIndent = if (hasChoice) " " else "" val initStatement = s" ${C}_initSelf(&instance->$e$deref);" - val parseStatement = s" $moreIndent${C}_parseSelf(&instance->$e$deref, pstate);" - val unparseStatement = s" $moreIndent${C}_unparseSelf(&instance->$e$deref, ustate);" + val parseStatement = + s"""$moreIndent ${C}_parseSelf(&instance->$e$deref, pstate); + |$moreIndent if (pstate->error) return;""".stripMargin + val unparseStatement = + s"""$moreIndent ${C}_unparseSelf(&instance->$e$deref, ustate); + |$moreIndent if (ustate->error) return;""".stripMargin structs.top.initStatements += initStatement structs.top.parserStatements += parseStatement @@ -544,9 +559,10 @@ class CodeGeneratorState { s"""#ifndef GENERATED_CODE_H |#define GENERATED_CODE_H | - |#include "infoset.h" // for InfosetBase |#include <stdbool.h> // for bool - |#include <stdint.h> // for int16_t, int32_t, int64_t, int8_t, uint16_t, uint32_t, uint64_t, uint8_t + |#include <stddef.h> // for size_t + |#include <stdint.h> // for int16_t, int32_t, int64_t, uint32_t, uint8_t, int8_t, uint16_t, uint64_t + |#include "infoset.h" // for InfosetBase |// Define infoset structures | @@ -557,25 +573,32 @@ class CodeGeneratorState { } def generateCodeFile(rootElementName: String): String = { + val program = this.getClass.getPackage.getImplementationTitle + val version = this.getClass.getPackage.getImplementationVersion val prototypes = this.prototypes.mkString("\n") val erds = this.erds.mkString("\n") val finalImplementation = this.finalImplementation.mkString("\n") val code = s"""#include "generated_code.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 <stdbool.h> // for bool, true, false |#include <stddef.h> // for NULL, size_t + |#include "errors.h" // for Error, PState, UState, ERR_CHOICE_KEY, UNUSED + |#include "parsers.h" // for parse_be_float, parse_be_int16, parse_be_bool32, parse_validate_fixed, parse_be_bool16, parse_be_int32, parse_be_uint32, parse_le_bool32, parse_le_int64, parse_le_uint8, parse_be_bool8, parse_be_double, parse_be_int64, parse_be_int8, parse_be_uint16, 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_uint16, parse_le_uint32, parse_le_uint64 + |#include "unparsers.h" // for unparse_be_float, unparse_be_int16, unparse_be_bool32, unparse_validate_fixed, unparse_be_bool16, unparse_be_int32, unparse_be_uint32, unparse_le_bool32, unparse_le_int64, unparse_le_uint8, unparse_be_bool8, unparse_be_double, unparse_be_int64, unparse_be_int8, unparse_be_uint16, unparse_be_uint64, unparse_be_uint8, unparse_le_bool16, unparse_le_bool8, unparse_le_double, unparse_le_float, unparse_le_int16, unparse_le_int32, unparse_le_int8, unpars [...] + | + |// Initialize our program's name and version + | + |const char *argp_program_version = "$program $version"; | - |// Prototypes needed for compilation + |// Declare prototypes for easier compilation | |$prototypes | - |// Metadata singletons + |// Define metadata for the infoset | |$erds - |// Return a root element to be used for parsing or unparsing + |// Return a root element for parsing or unparsing the infoset | |InfosetBase * |rootElement(void) @@ -590,7 +613,7 @@ class CodeGeneratorState { | return &root._base; |} | - |// Methods to initialize, parse, and unparse infoset nodes + |// Initialize, parse, and unparse nodes of the infoset | |$finalImplementation |""".stripMargin diff --git a/project/build.properties b/project/build.properties index 18f5ff8..0e52c79 100644 --- a/project/build.properties +++ b/project/build.properties @@ -15,4 +15,4 @@ * limitations under the License. */ -sbt.version=1.4.1 +sbt.version=1.4.9
