diff --git a/samples/quickstart-ints.cpp b/samples/quickstart-ints.cpp new file mode 100644 index 00000000..fb817f3c --- /dev/null +++ b/samples/quickstart-ints.cpp @@ -0,0 +1,159 @@ +// This file shows a quick example of parsing YAML to an int events +// buffer. Since this functionality is meant to implement in other +// programming languages, the code is kept very simple, and using only +// C-like idioms. + +// ryml can be used as a single header, or as a simple library: +#if defined(RYML_SINGLE_HEADER) // using the single header directly in the executable + #define RYML_SINGLE_HDR_DEFINE_NOW + #ifndef RYML_SINGLE_HEADER_INTS + #include + #else + #include + #endif +#elif defined(RYML_SINGLE_HEADER_LIB) // using the single header from a library + #ifndef RYML_SINGLE_HEADER_INTS + #include + #else + #include + #endif +#else +#include +#endif + +#ifndef RYML_SINGLE_HEADER_INTS +#include +#endif + + +// NOLINTBEGIN(hicpp-signed-bitwise) + +int main(int, const char *[]) +{ + using namespace c4::yml::extra::ievt; + auto PSTR_ = c4::yml::extra::ievt::PSTR; // PSTR does not work in windows + // YAML code to be parsed in place + char yaml[] = "do: a deer, a female deer\n" + "re: a drop of golden sun\n" + "mi: a name I call myself\n" + "fa: a long long way to run\n"; + // these are the event values we expect + const int expected_events[] = { + BSTR, + BDOC, + VAL_|BMAP|BLCK, + // + KEY_|SCLR|PLAI, 0, 2, // "do" + VAL_|SCLR|PLAI|PSTR_, 4, 21, // "a deer, a female deer" + // + KEY_|SCLR|PLAI|PSTR_, 26, 2, // "re" + VAL_|SCLR|PLAI|PSTR_, 30, 20, // "a drop of golden sun" + // + KEY_|SCLR|PLAI|PSTR_, 51, 2, // "mi" + VAL_|SCLR|PLAI|PSTR_, 55, 20, // "a name I call myself" + // + KEY_|SCLR|PLAI|PSTR_, 76, 2, // "fa" + VAL_|SCLR|PLAI|PSTR_, 80, 22, // "a long long way to run" + // + EMAP|PSTR_, + EDOC, + ESTR, + }; + + /* the output should be this: + * + * success! YAML requires event size 30, estimated=49 + * pos=0 event[0]: 0x1 + * pos=1 event[1]: 0x4 + * pos=2 event[2]: 0x110010 + * pos=3 event[3]: 0x80500 str=(0,2) 'do' + * pos=6 event[4]: 0x900500 str=(4,21) 'a deer, a female deer' + * pos=9 event[5]: 0x880500 str=(26,2) 're' + * pos=12 event[6]: 0x900500 str=(30,20) 'a drop of golden sun' + * pos=15 event[7]: 0x880500 str=(51,2) 'mi' + * pos=18 event[8]: 0x900500 str=(55,20) 'a name I call myself' + * pos=21 event[9]: 0x880500 str=(76,2) 'fa' + * pos=24 event[10]: 0x900500 str=(80,22) 'a long long way to run' + * pos=27 event[11]: 0x800020 + * pos=28 event[12]: 0x8 + * pos=29 event[13]: 0x2 + */ + + // buffer to where we will write the events + constexpr const int events_size = 100; + int events[events_size] = {}; + static_assert(events_size >= sizeof(expected_events)/sizeof(expected_events[0]), "buffer too small"); + // buffer for placing any scalars/tags that cannot be filtered + // in-place + char arena[100] = {}; + + + // ensure the estimation will succeed vs required size + int estimated_size = c4::yml::extra::estimate_events_ints_size(yaml); + if (estimated_size > events_size) + { + printf("the estimated size (%d) will not fit the events array (%d)\n", estimated_size, events_size); // LCOV_EXCL_LINE + return 1; // LCOV_EXCL_LINE + } + + // parse now. the parse should succeed (because the YAML above is + // legit), but if there were would be a parse error, we would get + // the default behavior which is abort on error, since we did not + // set up the error callbacks + c4::yml::extra::EventHandlerInts handler; + c4::yml::ParseEngine parser(&handler); + handler.reset(yaml, arena, events, estimated_size); // note we pass the estimated size! + parser.parse_in_place_ev("filename", yaml); + + // the YAML was successfully parsed, but it may happen that it + // requires more events than may fit in the buffer. so we need to + // check that it actually fits (this is mandatory): + if(!handler.fits_buffers()) + { + printf("error: buffers too small: required_evt=%d actual_evt=%d\n required_arena=%zu actual_arena=%zu\n", // LCOV_EXCL_LINE + handler.required_size_events(), estimated_size, handler.required_size_arena(), c4::to_csubstr(arena).len); // LCOV_EXCL_LINE + // WATCHOUT: if you want to retry the parse, you need to set + // up the source buffer again, because it is invalidated from + // being parsed in place. refer to the doxygen documentation + // for more details. + return 1; // LCOV_EXCL_LINE + } + + // done! + printf("success! YAML requires event size %d, estimated=%d (required_arena=%zu actual=%zu)\n", // LCOV_EXCL_LINE + handler.required_size_events(), estimated_size, handler.required_size_arena(), c4::to_csubstr(arena).len); // LCOV_EXCL_LINE + + // ensure the result is as expected + bool compare = true; + + // example iterating through the events array: compare and print + // the result + for (int pos = 0, evt = 0; pos < handler.required_size_events(); ++pos, ++evt) + { + bool status = (events[pos] == expected_events[pos]); + printf("pos=%d\tevent[%d]:\t0x%x", pos, evt, events[pos]); + if(events[pos] & WSTR) // the event has a string following it + { + int offset = events[pos + 1]; + int length = events[pos + 2]; + bool in_arena = (events[pos] & AREN); + // WATCHOUT! the string is NOT ZERO TERMINATED! + const char *ptr = in_arena ? arena : yaml; + const char *str = ptr + offset; + printf("\tstr=(%d,%d)\t'%.*s'", offset, length, length, str); + status = status && (offset == expected_events[pos + 1]); + status = status && (length == expected_events[pos + 2]); + pos += 2; // advance the two ints from the string + } + if(!status) + { + printf(" ... fail!"); // LCOV_EXCL_LINE + compare = false; // LCOV_EXCL_LINE + } + printf("\n"); + } + + return compare ? 0 : 1; +} + +// NOLINTEND(hicpp-signed-bitwise) diff --git a/samples/quickstart.cpp b/samples/quickstart.cpp index 9d3fe724..cd24bb9d 100644 --- a/samples/quickstart.cpp +++ b/samples/quickstart.cpp @@ -129,6 +129,9 @@ C4_SUPPRESS_WARNING_GCC_CLANG_PUSH C4_SUPPRESS_WARNING_GCC_CLANG("-Wcast-qual") C4_SUPPRESS_WARNING_GCC_CLANG("-Wold-style-cast") C4_SUPPRESS_WARNING_GCC("-Wuseless-cast") +#if __GNUC__ >= 6 +C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wnull-dereference") +#endif namespace sample { diff --git a/samples/singleheader-ints/CMakeLists.txt b/samples/singleheader-ints/CMakeLists.txt new file mode 100644 index 00000000..0274b0f2 --- /dev/null +++ b/samples/singleheader-ints/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.15 FATAL_ERROR) +project(ryml-quickstart-ints LANGUAGES CXX) + +# create a target to amalgamate ryml into a single header: +include(amalgamate.cmake) +amalgamate_ryml(SINGLE_HEADER_DIR SINGLE_HEADER) + +# now simply define the executable: +add_executable(ryml-quickstart-ints ../quickstart-ints.cpp ${SINGLE_HEADER}) +target_compile_features(ryml-quickstart-ints PUBLIC cxx_std_11) +target_compile_definitions(ryml-quickstart-ints PUBLIC -DRYML_SINGLE_HEADER) +target_compile_definitions(ryml-quickstart-ints PUBLIC -DRYML_SINGLE_HEADER_INTS) +target_include_directories(ryml-quickstart-ints PUBLIC "${SINGLE_HEADER_DIR}") + +add_custom_target(run ryml-quickstart-ints + COMMAND $ + DEPENDS ryml-quickstart-ints) diff --git a/samples/singleheader-ints/amalgamate.cmake b/samples/singleheader-ints/amalgamate.cmake new file mode 100644 index 00000000..9730c138 --- /dev/null +++ b/samples/singleheader-ints/amalgamate.cmake @@ -0,0 +1,18 @@ +find_package(Python3 COMPONENTS Interpreter) + +# amalgamate ryml to get the single header +function(amalgamate_ryml header_dir header_file) + set(rymldir "${CMAKE_CURRENT_LIST_DIR}/../..") + set(singleheaderdir "${rymldir}/src_singleheader") + set(singleheader "${singleheaderdir}/ryml_ints.hpp") + set(amscript "${rymldir}/tools/amalgamate.py") + file(GLOB_RECURSE srcfiles + LIST_DIRECTORIES FALSE + CONFIGURE_DEPENDS "${rymldir}/src" "${rymldir}/src_extra") + add_custom_command(OUTPUT "${singleheader}" + COMMAND "${Python3_EXECUTABLE}" "${amscript}" -e ints -- "${singleheader}" + COMMENT "${Python3_EXECUTABLE} ${amscript} -e ints -- ${singleheader}" + DEPENDS ${srcfiles} "${amscript}" "${rymldir}/ext/c4core/cmake/amalgamate_utils.py") + set(${header_dir} "${singleheaderdir}" PARENT_SCOPE) + set(${header_file} "${singleheader}" PARENT_SCOPE) +endfunction() diff --git a/samples/singleheader-ints/run.sh b/samples/singleheader-ints/run.sh new file mode 100755 index 00000000..fc2f7d85 --- /dev/null +++ b/samples/singleheader-ints/run.sh @@ -0,0 +1,10 @@ +#!/bin/bash -x + +# take the build type from the command line, or default to release +cfg=${1:-Release} +# make sure to run from where this file is +cd $(dirname $0) +# configure the sample +cmake -S . -B ./build/$cfg -DCMAKE_BUILD_TYPE=$cfg +# build and run the sample +cmake --build ./build/$cfg --config $cfg --target run diff --git a/samples/singleheaderlib-ints/CMakeLists.txt b/samples/singleheaderlib-ints/CMakeLists.txt new file mode 100644 index 00000000..ebdfd3bb --- /dev/null +++ b/samples/singleheaderlib-ints/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.15 FATAL_ERROR) +project(ryml-quickstart-ints LANGUAGES CXX) + +# create a target to amalgamate ryml into a single header: +include(../singleheader-ints/amalgamate.cmake) +amalgamate_ryml(SINGLE_HEADER_DIR SINGLE_HEADER) + +# add a library using the amalgamated header +add_library(ryml lib.cpp ${SINGLE_HEADER}) +target_compile_features(ryml PUBLIC cxx_std_11) +target_include_directories(ryml PUBLIC "${SINGLE_HEADER_DIR}") + +# now simply define the executable: +add_executable(ryml-quickstart-ints ../quickstart-ints.cpp) +target_link_libraries(ryml-quickstart-ints PRIVATE ryml) +target_compile_definitions(ryml-quickstart-ints PUBLIC -DRYML_SINGLE_HEADER_LIB) +target_compile_definitions(ryml-quickstart-ints PUBLIC -DRYML_SINGLE_HEADER_INTS) + +# adjustments for shared library +if(BUILD_SHARED_LIBS) + # RYML_SHARED should be propagated to targets consuming ryml + target_compile_definitions(ryml PUBLIC -DRYML_SHARED) +endif() + +add_custom_target(run ryml-quickstart-ints + COMMAND $ + DEPENDS ryml-quickstart-ints) diff --git a/samples/singleheaderlib-ints/lib.cpp b/samples/singleheaderlib-ints/lib.cpp new file mode 100644 index 00000000..9d7de747 --- /dev/null +++ b/samples/singleheaderlib-ints/lib.cpp @@ -0,0 +1,2 @@ +#define RYML_SINGLE_HDR_DEFINE_NOW +#include diff --git a/samples/singleheaderlib-ints/run_shared.sh b/samples/singleheaderlib-ints/run_shared.sh new file mode 100755 index 00000000..372a3677 --- /dev/null +++ b/samples/singleheaderlib-ints/run_shared.sh @@ -0,0 +1,10 @@ +#!/bin/bash -x + +# take the build type from the command line, or default to release +cfg=${1:-Release} +# make sure to run from where this file is +cd $(dirname $0) +# configure the sample +cmake -S . -B ./build/$cfg-shared -DCMAKE_BUILD_TYPE=$cfg -DBUILD_SHARED_LIBS=ON +# build and run the sample +cmake --build ./build/$cfg-shared --config $cfg --target run diff --git a/samples/singleheaderlib-ints/run_static.sh b/samples/singleheaderlib-ints/run_static.sh new file mode 100755 index 00000000..d0e57021 --- /dev/null +++ b/samples/singleheaderlib-ints/run_static.sh @@ -0,0 +1,10 @@ +#!/bin/bash -x + +# take the build type from the command line, or default to release +cfg=${1:-Release} +# make sure to run from where this file is +cd $(dirname $0) +# configure the sample +cmake -S . -B ./build/$cfg-static -DCMAKE_BUILD_TYPE=$cfg -DBUILD_SHARED_LIBS=OFF +# build and run the sample +cmake --build ./build/$cfg-static --config $cfg --target run diff --git a/tools/amalgamate.py b/tools/amalgamate.py index 215556c1..f62436ab 100644 --- a/tools/amalgamate.py +++ b/tools/amalgamate.py @@ -19,6 +19,7 @@ class Event(Enum): testsuite = "testsuite" ints = "ints" ints_utils = "ints_utils" + ints_to_testsuite = "ints_to_testsuite" all = "all" none = "none" def __str__(self): @@ -33,6 +34,7 @@ event_doc = { Event.testsuite: "enable the (extra) YAML test suite event handler", Event.ints: "enable the (extra) integer-based event handler", Event.ints_utils: "enable the (extra) integer-based event handler utils", + Event.ints_to_testsuite: "enable the (extra) integer events conversion to testsuite events", Event.all: "enable all event handlers", Event.none: "disable all event handlers", } @@ -138,7 +140,8 @@ INSTRUCTIONS: "src/c4/yml/event_handler_stack.hpp", am.onlyif(has_evt(Event.tree), "src/c4/yml/event_handler_tree.hpp"), am.onlyif(has_evt(Event.ints), "src_extra/c4/yml/extra/event_handler_ints.hpp"), - am.onlyif(has_evt(Event.ints_utils), "src_extra/c4/yml/extra/event_handler_ints_utils.hpp"), + am.onlyif(has_evt(Event.ints_utils), "src_extra/c4/yml/extra/ints_utils.hpp"), + am.onlyif(has_evt(Event.ints_to_testsuite), "src_extra/c4/yml/extra/ints_to_testsuite.hpp"), am.onlyif(has_evt(Event.testsuite), "src_extra/c4/yml/extra/string.hpp"), am.onlyif(has_evt(Event.testsuite), "src_extra/c4/yml/extra/event_handler_testsuite.hpp"), am.onlyif(has_evt(Event.ints_utils, Event.testsuite), "src_extra/c4/yml/extra/scalar.hpp"), @@ -158,8 +161,8 @@ INSTRUCTIONS: am.onlyif(has_evt(Event.tree), "src/c4/yml/tree.cpp"), am.onlyif(has_evt(Event.ints), "src_extra/c4/yml/extra/event_handler_ints.cpp"), am.onlyif(has_evt(Event.ints_utils, Event.testsuite), "src_extra/c4/yml/extra/scalar.cpp"), - am.onlyif(has_evt(Event.ints_utils), "src_extra/c4/yml/extra/event_handler_ints_utils.cpp"), - am.onlyif(has_evt(Event.testsuite), "src_extra/c4/yml/extra/event_handler_testsuite.cpp"), + am.onlyif(has_evt(Event.ints_utils), "src_extra/c4/yml/extra/ints_utils.cpp"), + am.onlyif(has_evt(Event.ints_to_testsuite), "src_extra/c4/yml/extra/ints_to_testsuite.cpp"), am.onlyif(has_evt(Event.tree), "src/c4/yml/reference_resolver.cpp"), am.onlyif(has_evt(Event.tree), "src/c4/yml/parse.cpp"), am.onlyif(has_evt(Event.tree), "src/c4/yml/node.cpp"),