diff --git a/README.md b/README.md index 83da3db..53aa7ea 100644 --- a/README.md +++ b/README.md @@ -248,15 +248,15 @@ We can check that a JSON/HDF5 file complies with the **uzuki** specification: ```cpp #include "uzuki2/uzuki2.hpp" -uzuki2::validate_hdf5(h5_file_path, h5_group_name); -uzuki2::validate_json(json_file_path); +uzuki2::hdf5::validate(h5_file_path, h5_group_name); +uzuki2::json::validate(json_file_path); ``` This will raise an error if any violations of the specification are observed. If a non-zero expected number of external objects is present: ```cpp -uzuki2::validate_hdf5(h5_file_path, h5_group_name, num_externals); +uzuki2::hdf5::validate(h5_file_path, h5_group_name, num_externals); ``` Advanced users can also use the **uzuki2** parser to load the list into memory. @@ -266,10 +266,10 @@ which can be used to load the HDF5 contents into `std::vector`s for easier downs ```cpp DefaultExternals ext(nexpected); -auto ptr = uzuki2::parse_hdf5(file_path, group_name, ext); +auto ptr = uzuki2::hdf5::parse(file_path, group_name, ext); ``` -Also see the [reference documentation](https://ltla.github.io/uzuki2) for more details. +Also see the [reference documentation](https://artifactdb.github.io/uzuki2) for more details. ### Building projects @@ -291,7 +291,7 @@ FetchContent_MakeAvailable(uzuki2) Then you can link to **uzuki2** to make the headers available during compilation: -``` +```cmake # For executables: target_link_libraries(myexe uzuki2) diff --git a/docs/Doxyfile b/docs/Doxyfile index c07358e..0b9613e 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -790,8 +790,8 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = ../include/uzuki2/parse_hdf5.hpp \ - ../include/uzuki2/parse_json.hpp \ +INPUT = ../include/uzuki2/parse_json.hpp \ + ../include/uzuki2/parse_hdf5.hpp \ ../include/uzuki2/interfaces.hpp \ ../include/uzuki2/uzuki2.hpp \ ../README.md diff --git a/include/uzuki2/parse_hdf5.hpp b/include/uzuki2/parse_hdf5.hpp index c63b4fa..57771c1 100644 --- a/include/uzuki2/parse_hdf5.hpp +++ b/include/uzuki2/parse_hdf5.hpp @@ -25,10 +25,18 @@ namespace uzuki2 { /** - * @cond + * @namespace uzuki2::hdf5 + * @brief Parse an R list from a HDF5 file. + * + * The hierarchical nature of HDF5 allows it to naturally store nested list structures. + * It supports random access of list components, which provides some optimization opportunities for parsing large lists. + * However, it incurs a large overhead per list element; for small lists, users may prefer to use a JSON file instead (see `json`). */ namespace hdf5 { +/** + * @cond + */ inline std::string load_string_attribute(const H5::Attribute& attr, const std::string& field, const std::string& path) { if (attr.getTypeClass() != H5T_STRING || attr.getSpace().getSimpleExtentNdims() != 0) { throw std::runtime_error(std::string("'") + field + "' attribute should be a scalar string at '" + path + "'"); @@ -483,173 +491,161 @@ std::shared_ptr parse_inner(const H5::Group& handle, Externals& ext, const return output; } - -} /** * @endcond */ /** - * @brief Parse HDF5 file contents using the **uzuki2** specification. + * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. + * @tparam Externals Class describing how to resolve external references for type `EXTERNAL`. * - * The hierarchical nature of HDF5 allows it to naturally store nested list structures. - * It supports random access of list components, which provides some optimization opportunities for parsing large lists. - * However, it incurs a large overhead per list element; for small lists, users may prefer to use a JSON file instead (see `JsonParser`). + * @param handle Handle for a HDF5 group corresponding to the list. + * @param name Name of the HDF5 group corresponding to `handle`. + * Only used for error messages. + * @param ext Instance of an external reference resolver class. + * + * @return Pointer to the root `Base` object. + * Depending on `Provisioner`, this may contain references to all nested objects. + * + * Any invalid representations in `contents` will cause an error to be thrown. + * + * @section provisioner-contract Provisioner requirements + * The `Provisioner` class is expected to provide the following static methods: + * + * - `Nothing* new_Nothing()`, which returns a new instance of a `Nothing` subclass. + * - `Other* new_Other(void* p)`, which returns a new instance of a `Other` subclass. + * `p` is a pointer to an "external" object, generated by calling `ext.get()` (see below). + * - `List* new_List(size_t l, bool n)`, which returns a new instance of a `List` with length `l`. + * If `n = true`, names are present and will be added via `List::set_name()`. + * - `IntegerVector* new_Integer(size_t l, bool n, bool s)`, which returns a new instance of an `IntegerVector` subclass of length `l`. + * If `n = true`, names are present and will be added via `Vector::set_name()`. + * If `s = true` and `l = 1`, the value was represented on file as a scalar integer. + * - `NumberVector* new_Number(size_t l, bool n, bool s)`, which returns a new instance of a `NumberVector` subclass of length `l`. + * If `n = true`, names are present and will be added via `Vector::set_name()`. + * If `s = true` and `l = 1`, the value was represented on file as a scalar float. + * - `StringVector* new_String(size_t l, bool n, bool s, StringVector::Format f)`, which returns a new instance of a `StringVector` subclass of length `l` with format `f`. + * If `n = true`, names are present and will be added via `Vector::set_name()`. + * If `s = true` and `l = 1`, the value was represented on file as a scalar string. + * - `BooleanVector* new_Boolean(size_t l, bool n, bool s)`, which returns a new instance of a `BooleanVector` subclass of length `l`. + * If `n = true`, names are present and will be added via `Vector::set_name()`. + * If `s = true` and `l = 1`, the value was represented on file as a scalar boolean. + * - `Factor* new_Factor(size_t l, bool n, bool s, size_t ll, bool o)`, which returns a new instance of a `Factor` subclass of length `l` and with `ll` unique levels. + * If `n = true`, names are present and will be added via `Vector::set_name()`. + * If `s = true` and `l = 1`, the lone index was represented on file as a scalar integer. + * If `o = true`, the levels should be assumed to be sorted. + * + * @section external-contract Externals requirements + * The `Externals` class is expected to provide the following `const` methods: + * + * - `void* get(size_t i) const`, which returns a pointer to an "external" object, given the index of that object. + * This will be stored in the corresponding `Other` subclass generated by `Provisioner::new_Other`. + * - `size_t size()`, which returns the number of available external references. */ -class Hdf5Parser { -public: - /** - * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. - * @tparam Externals Class describing how to resolve external references for type `EXTERNAL`. - * - * @param handle Handle for a HDF5 group corresponding to the list. - * @param name Name of the HDF5 group corresponding to `handle`. - * Only used for error messages. - * @param ext Instance of an external reference resolver class. - * - * @return Pointer to the root `Base` object. - * Depending on `Provisioner`, this may contain references to all nested objects. - * - * Any invalid representations in `contents` will cause an error to be thrown. - * - * @section provisioner-contract Provisioner requirements - * The `Provisioner` class is expected to provide the following static methods: - * - * - `Nothing* new_Nothing()`, which returns a new instance of a `Nothing` subclass. - * - `Other* new_Other(void* p)`, which returns a new instance of a `Other` subclass. - * `p` is a pointer to an "external" object, generated by calling `ext.get()` (see below). - * - `List* new_List(size_t l, bool n)`, which returns a new instance of a `List` with length `l`. - * If `n = true`, names are present and will be added via `List::set_name()`. - * - `IntegerVector* new_Integer(size_t l, bool n, bool s)`, which returns a new instance of an `IntegerVector` subclass of length `l`. - * If `n = true`, names are present and will be added via `Vector::set_name()`. - * If `s = true` and `l = 1`, the value was represented on file as a scalar integer. - * - `NumberVector* new_Number(size_t l, bool n, bool s)`, which returns a new instance of a `NumberVector` subclass of length `l`. - * If `n = true`, names are present and will be added via `Vector::set_name()`. - * If `s = true` and `l = 1`, the value was represented on file as a scalar float. - * - `StringVector* new_String(size_t l, bool n, bool s, StringVector::Format f)`, which returns a new instance of a `StringVector` subclass of length `l` with format `f`. - * If `n = true`, names are present and will be added via `Vector::set_name()`. - * If `s = true` and `l = 1`, the value was represented on file as a scalar string. - * - `BooleanVector* new_Boolean(size_t l, bool n, bool s)`, which returns a new instance of a `BooleanVector` subclass of length `l`. - * If `n = true`, names are present and will be added via `Vector::set_name()`. - * If `s = true` and `l = 1`, the value was represented on file as a scalar boolean. - * - `Factor* new_Factor(size_t l, bool n, bool s, size_t ll, bool o)`, which returns a new instance of a `Factor` subclass of length `l` and with `ll` unique levels. - * If `n = true`, names are present and will be added via `Vector::set_name()`. - * If `s = true` and `l = 1`, the lone index was represented on file as a scalar integer. - * If `o = true`, the levels should be assumed to be sorted. - * - * @section external-contract Externals requirements - * The `Externals` class is expected to provide the following `const` methods: - * - * - `void* get(size_t i) const`, which returns a pointer to an "external" object, given the index of that object. - * This will be stored in the corresponding `Other` subclass generated by `Provisioner::new_Other`. - * - `size_t size()`, which returns the number of available external references. - */ - template - std::shared_ptr parse(const H5::Group& handle, const std::string& name, Externals ext) { - Version version; - if (handle.attrExists("uzuki_version")) { - auto ver_str = hdf5::load_string_attribute(handle.openAttribute("uzuki_version"), "uzuki_version", name); - version = parse_version_string(ver_str); - } - - ExternalTracker etrack(std::move(ext)); - auto ptr = hdf5::parse_inner(handle, etrack, name, version); - etrack.validate(); - return ptr; +template +std::shared_ptr parse(const H5::Group& handle, const std::string& name, Externals ext) { + Version version; + if (handle.attrExists("uzuki_version")) { + auto ver_str = load_string_attribute(handle.openAttribute("uzuki_version"), "uzuki_version", name); + version = parse_version_string(ver_str); } - /** - * Parse HDF5 file contents using the **uzuki2** specification, given the group handle. - * It is assumed that there are no external references. - * - * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. - * - * @param handle Handle for a HDF5 group corresponding to the list. - * @param name Name of the HDF5 group corresponding to `handle`. - * Only used for error messages. - * - * @return Pointer to the root `Base` object. - * Depending on `Provisioner`, this may contain references to all nested objects. - * - * Any invalid representations in `contents` will cause an error to be thrown. - */ - template - std::shared_ptr parse(const H5::Group& handle, const std::string& name) { - return parse(handle, name, uzuki2::DummyExternals(0)); - } + ExternalTracker etrack(std::move(ext)); + auto ptr = parse_inner(handle, etrack, name, version); + etrack.validate(); + return ptr; +} -public: - /** - * Parse HDF5 file contents using the **uzuki2** specification, given the file path. - * - * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. - * @tparam Externals Class describing how to resolve external references for type `EXTERNAL`. - * - * @param file Path to a HDF5 file. - * @param name Name of the HDF5 group containing the list in `file`. - * @param ext Instance of an external reference resolver class. - * - * @return Pointer to the root `Base` object. - * Depending on `Provisioner`, this may contain references to all nested objects. - * - * Any invalid representations in `contents` will cause an error to be thrown. - */ - template - std::shared_ptr parse(const std::string& file, const std::string& name, Externals ext) { - H5::H5File handle(file, H5F_ACC_RDONLY); - return parse(handle.openGroup(name), name, std::move(ext)); - } +/** + * Parse HDF5 file contents using the **uzuki2** specification, given the group handle. + * It is assumed that there are no external references. + * + * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. + * + * @param handle Handle for a HDF5 group corresponding to the list. + * @param name Name of the HDF5 group corresponding to `handle`. + * Only used for error messages. + * + * @return Pointer to the root `Base` object. + * Depending on `Provisioner`, this may contain references to all nested objects. + * + * Any invalid representations in `contents` will cause an error to be thrown. + */ +template +std::shared_ptr parse(const H5::Group& handle, const std::string& name) { + return parse(handle, name, uzuki2::DummyExternals(0)); +} - /** - * Parse HDF5 file contents using the **uzuki2** specification, given the file path. - * It is assumed that there are no external references. - * - * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. - * - * @param file Path to a HDF5 file. - * @param name Name of the HDF5 group containing the list in `file`. - * - * @return Pointer to the root `Base` object. - * Depending on `Provisioner`, this may contain references to all nested objects. - * - * Any invalid representations in `contents` will cause an error to be thrown. - */ - template - std::shared_ptr parse(const std::string& file, const std::string& name) { - H5::H5File handle(file, H5F_ACC_RDONLY); - return parse(handle.openGroup(name), name, uzuki2::DummyExternals(0)); - } +/** + * Parse HDF5 file contents using the **uzuki2** specification, given the file path. + * + * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. + * @tparam Externals Class describing how to resolve external references for type `EXTERNAL`. + * + * @param file Path to a HDF5 file. + * @param name Name of the HDF5 group containing the list in `file`. + * @param ext Instance of an external reference resolver class. + * + * @return Pointer to the root `Base` object. + * Depending on `Provisioner`, this may contain references to all nested objects. + * + * Any invalid representations in `contents` will cause an error to be thrown. + */ +template +std::shared_ptr parse(const std::string& file, const std::string& name, Externals ext) { + H5::H5File handle(file, H5F_ACC_RDONLY); + return parse(handle.openGroup(name), name, std::move(ext)); +} -public: - /** - * Validate HDF5 file contents against the **uzuki2** specification, given the group handle. - * Any invalid representations will cause an error to be thrown. - * - * @param handle Handle for a HDF5 group corresponding to the list. - * @param name Name of the HDF5 group corresponding to `handle`. - * Only used for error messages. - * @param num_external Expected number of external references. - */ - void validate(const H5::Group& handle, const std::string& name, int num_external = 0) { - DummyExternals ext(num_external); - parse(handle, name, ext); - return; - } +/** + * Parse HDF5 file contents using the **uzuki2** specification, given the file path. + * It is assumed that there are no external references. + * + * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. + * + * @param file Path to a HDF5 file. + * @param name Name of the HDF5 group containing the list in `file`. + * + * @return Pointer to the root `Base` object. + * Depending on `Provisioner`, this may contain references to all nested objects. + * + * Any invalid representations in `contents` will cause an error to be thrown. + */ +template +std::shared_ptr parse(const std::string& file, const std::string& name) { + H5::H5File handle(file, H5F_ACC_RDONLY); + return parse(handle.openGroup(name), name, uzuki2::DummyExternals(0)); +} - /** - * Validate HDF5 file contents against the **uzuki2** specification, given the file path. - * Any invalid representations will cause an error to be thrown. - * - * @param file Path to a HDF5 file. - * @param name Name of the HDF5 group containing the list in `file`. - * @param num_external Expected number of external references. - */ - void validate(const std::string& file, const std::string& name, int num_external = 0) { - DummyExternals ext(num_external); - parse(file, name, ext); - return; - } -}; +/** + * Validate HDF5 file contents against the **uzuki2** specification, given the group handle. + * Any invalid representations will cause an error to be thrown. + * + * @param handle Handle for a HDF5 group corresponding to the list. + * @param name Name of the HDF5 group corresponding to `handle`. + * Only used for error messages. + * @param num_external Expected number of external references. + */ +inline void validate(const H5::Group& handle, const std::string& name, int num_external = 0) { + DummyExternals ext(num_external); + parse(handle, name, ext); + return; +} + +/** + * Validate HDF5 file contents against the **uzuki2** specification, given the file path. + * Any invalid representations will cause an error to be thrown. + * + * @param file Path to a HDF5 file. + * @param name Name of the HDF5 group containing the list in `file`. + * @param num_external Expected number of external references. + */ +inline void validate(const std::string& file, const std::string& name, int num_external = 0) { + DummyExternals ext(num_external); + parse(file, name, ext); + return; +} + +} } diff --git a/include/uzuki2/parse_json.hpp b/include/uzuki2/parse_json.hpp index e332918..cdd2f53 100644 --- a/include/uzuki2/parse_json.hpp +++ b/include/uzuki2/parse_json.hpp @@ -29,10 +29,18 @@ namespace uzuki2 { /** - * @cond + * @namespace uzuki2::json + * @brief Parse an R list from a JSON file. + * + * JSON provides an alternative to the HDF5 format handled by `hdf5::parse()` and friends. + * JSON is simpler to parse and has less formatting-related overhead. + * However, it does not support random access and discards some precision for floating-point numbers. */ namespace json { +/** + * @cond + */ inline const std::vector >& extract_array( const std::unordered_map >& properties, const std::string& name, @@ -374,220 +382,221 @@ std::shared_ptr parse_object(const millijson::Base* contents, Externals& e return output; } - -} /** * @endcond */ /** - * @brief Parse JSON file contents using the **uzuki2** specification. - * - * JSON provides an alternative to the HDF5 format that is handled by `Hdf5Parser`. - * JSON is simpler to parse and has less formatting-related overhead. - * However, it does not support random access and discards some precision for floating-point numbers. + * @brief Options for JSON file parsing. */ -class JsonParser { -public: +struct Options { /** * Whether parsing should be done in parallel to file I/O. * If true, an extra thread is used to avoid blocking I/O operations. */ bool parallel = false; +}; - /** - * Parse JSON file contents using the **uzuki2** specification, given an arbitrary input source of bytes. - * - * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. - * See `Hdf5Parser::parse()` for more details. - * @tparam Externals Class describing how to resolve external references for type `EXTERNAL`. - * See `Hdf5Parser::parse()` for more details. - * - * @param reader Instance of a `byteme::Reader` providing the contents of the JSON file. - * @param ext Instance of an external reference resolver class. - * - * @return Pointer to the root `Base` object. - * Depending on `Provisioner`, this may contain references to all nested objects. - * - * Any invalid representations in `reader` will cause an error to be thrown. - */ - template - ParsedList parse(byteme::Reader& reader, Externals ext) { - std::shared_ptr contents; - if (parallel) { - byteme::PerByte bytestream(&reader); - contents = millijson::parse(bytestream); - } else { - byteme::PerByteParallel bytestream(&reader); - contents = millijson::parse(bytestream); - } +/** + * Parse JSON file contents using the **uzuki2** specification, given an arbitrary input source of bytes. + * + * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. + * See `hdf5::parse()` for more details. + * @tparam Externals Class describing how to resolve external references for type `EXTERNAL`. + * See `hdf5::parse()` for more details. + * + * @param reader Instance of a `byteme::Reader` providing the contents of the JSON file. + * @param ext Instance of an external reference resolver class. + * @param options Options for parsing. + * + * @return Pointer to the root `Base` object. + * Depending on `Provisioner`, this may contain references to all nested objects. + * + * Any invalid representations in `reader` will cause an error to be thrown. + */ +template +ParsedList parse(byteme::Reader& reader, Externals ext, Options options = Options()) { + std::shared_ptr contents; + if (options.parallel) { + byteme::PerByte bytestream(&reader); + contents = millijson::parse(bytestream); + } else { + byteme::PerByteParallel bytestream(&reader); + contents = millijson::parse(bytestream); + } - Version version; - if (contents->type() == millijson::OBJECT) { - auto optr = static_cast(contents.get()); - const auto& map = optr->values; - auto vIt = map.find("version"); - if (vIt != map.end()) { - if (vIt->second->type() != millijson::STRING) { - throw std::runtime_error("expected a string in 'version'"); - } - auto vptr = static_cast(vIt->second.get()); - version = parse_version_string(vptr->value); + Version version; + if (contents->type() == millijson::OBJECT) { + auto optr = static_cast(contents.get()); + const auto& map = optr->values; + auto vIt = map.find("version"); + if (vIt != map.end()) { + if (vIt->second->type() != millijson::STRING) { + throw std::runtime_error("expected a string in 'version'"); } + auto vptr = static_cast(vIt->second.get()); + version = parse_version_string(vptr->value); } - - ExternalTracker etrack(std::move(ext)); - auto output = json::parse_object(contents.get(), etrack, "", version); - etrack.validate(); - return ParsedList(std::move(output), std::move(version)); } - /** - * Overload of `parse()` assuming that there are no external references. - * - * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. - * See `Hdf5Parser::parse()` for more details. - * - * @param reader Instance of a `byteme::Reader` providing the contents of the JSON file. - * - * @return Pointer to the root `Base` object. - * Depending on `Provisioner`, this may contain references to all nested objects. - * - * Any invalid representations in `reader` will cause an error to be thrown. - */ - template - ParsedList parse(byteme::Reader& reader) { - DummyExternals ext(0); - return parse(reader, std::move(ext)); - } + ExternalTracker etrack(std::move(ext)); + auto output = parse_object(contents.get(), etrack, "", version); + etrack.validate(); + return ParsedList(std::move(output), std::move(version)); +} -public: - /** - * Parse JSON file contents using the **uzuki2** specification, given the file path. - * - * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. - * See `Hdf5Parser::parse()` for more details. - * @tparam Externals Class describing how to resolve external references for type `EXTERNAL`. - * See `Hdf5Parser::parse()` for more details. - * - * @param file Path to a (possibly Gzip-compressed) JSON file. - * @param ext Instance of an external reference resolver class. - * - * @return Pointer to the root `Base` object. - * Depending on `Provisioner`, this may contain references to all nested objects. - * - * Any invalid representations in `reader` will cause an error to be thrown. - */ - template - ParsedList parse_file(const std::string& file, Externals ext) { - byteme::SomeFileReader reader(file.c_str()); - return parse(reader, std::move(ext)); - } +/** + * Overload of `json::parse()` assuming that there are no external references. + * + * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. + * See `hdf5::parse()` for more details. + * + * @param reader Instance of a `byteme::Reader` providing the contents of the JSON file. + * @param options Options for parsing. + * + * @return Pointer to the root `Base` object. + * Depending on `Provisioner`, this may contain references to all nested objects. + * + * Any invalid representations in `reader` will cause an error to be thrown. + */ +template +ParsedList parse(byteme::Reader& reader, Options options = Options()) { + DummyExternals ext(0); + return parse(reader, std::move(ext), std::move(options)); +} - /** - * Overload of `parse_file()` assuming that there are no external references. - * - * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. - * See `Hdf5Parser::parse()` for more details. - * - * @param file Path to a (possibly Gzip-compressed) JSON file. - * - * @return Pointer to the root `Base` object. - * Depending on `Provisioner`, this may contain references to all nested objects. - * - * Any invalid representations in `reader` will cause an error to be thrown. - */ - template - ParsedList parse_file(const std::string& file) { - DummyExternals ext(0); - return parse_file(file, std::move(ext)); - } +/** + * Parse JSON file contents using the **uzuki2** specification, given the file path. + * + * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. + * See `hdf5::parse()` for more details. + * @tparam Externals Class describing how to resolve external references for type `EXTERNAL`. + * See `hdf5::parse()` for more details. + * + * @param file Path to a (possibly Gzip-compressed) JSON file. + * @param ext Instance of an external reference resolver class. + * @param options Options for parsing. + * + * @return Pointer to the root `Base` object. + * Depending on `Provisioner`, this may contain references to all nested objects. + * + * Any invalid representations in `reader` will cause an error to be thrown. + */ +template +ParsedList parse_file(const std::string& file, Externals ext, Options options = Options()) { + byteme::SomeFileReader reader(file.c_str()); + return parse(reader, std::move(ext), std::move(options)); +} -public: - /** - * Parse a buffer containing JSON file contents using the **uzuki2** specification. - * - * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. - * See `Hdf5Parser::parse()` for more details. - * @tparam Externals Class describing how to resolve external references for type `EXTERNAL`. - * See `Hdf5Parser::parse()` for more details. - * - * @param[in] buffer Pointer to an array containing the JSON file contents (possibly Gzip/Zlib-compressed). - * @param len Length of the buffer in bytes. - * @param ext Instance of an external reference resolver class. - * - * @return Pointer to the root `Base` object. - * Depending on `Provisioner`, this may contain references to all nested objects. - * - * Any invalid representations in `reader` will cause an error to be thrown. - */ - template - ParsedList parse_buffer(const unsigned char* buffer, size_t len, Externals ext) { - byteme::SomeBufferReader reader(buffer, len); - return parse(reader, std::move(ext)); - } +/** + * Overload of `json::parse_file()` assuming that there are no external references. + * + * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. + * See `hdf5::parse()` for more details. + * + * @param file Path to a (possibly Gzip-compressed) JSON file. + * @param options Options for parsing. + * + * @return Pointer to the root `Base` object. + * Depending on `Provisioner`, this may contain references to all nested objects. + * + * Any invalid representations in `reader` will cause an error to be thrown. + */ +template +ParsedList parse_file(const std::string& file, Options options = Options()) { + DummyExternals ext(0); + return parse_file(file, std::move(ext), std::move(options)); +} - /** - * Overload of `parse_buffer()` assuming that there are no external references. - * - * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. - * See `Hdf5Parser::parse()` for more details. - * - * @param[in] buffer Pointer to an array containing the JSON file contents (possibly Gzip/Zlib-compressed). - * @param len Length of the buffer in bytes. - * - * @return Pointer to the root `Base` object. - * Depending on `Provisioner`, this may contain references to all nested objects. - * - * Any invalid representations in `reader` will cause an error to be thrown. - */ - template - ParsedList parse_buffer(const unsigned char* buffer, size_t len) { - DummyExternals ext(0); - return parse_buffer(buffer, len, std::move(ext)); - } +/** + * Parse a buffer containing JSON file contents using the **uzuki2** specification. + * + * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. + * See `hdf5::parse()` for more details. + * @tparam Externals Class describing how to resolve external references for type `EXTERNAL`. + * See `hdf5::parse()` for more details. + * + * @param[in] buffer Pointer to an array containing the JSON file contents (possibly Gzip/Zlib-compressed). + * @param len Length of the buffer in bytes. + * @param ext Instance of an external reference resolver class. + * @param options Options for parsing. + * + * @return Pointer to the root `Base` object. + * Depending on `Provisioner`, this may contain references to all nested objects. + * + * Any invalid representations in `reader` will cause an error to be thrown. + */ +template +ParsedList parse_buffer(const unsigned char* buffer, size_t len, Externals ext, Options options = Options()) { + byteme::SomeBufferReader reader(buffer, len); + return parse(reader, std::move(ext), std::move(options)); +} -public: - /** - * Validate JSON file contents against the **uzuki2** specification, given a source of bytes. - * Any invalid representations will cause an error to be thrown. - * - * @param reader Instance of a `byteme::Reader` providing the contents of the JSON file. - * @param num_external Expected number of external references. - */ - void validate(byteme::Reader& reader, int num_external = 0) { - DummyExternals ext(num_external); - parse(reader, std::move(ext)); - return; - } +/** + * Overload of `json::parse_buffer()` assuming that there are no external references. + * + * @tparam Provisioner A class namespace defining static methods for creating new `Base` objects. + * See `hdf5::parse()` for more details. + * + * @param[in] buffer Pointer to an array containing the JSON file contents (possibly Gzip/Zlib-compressed). + * @param len Length of the buffer in bytes. + * @param options Options for parsing. + * + * @return Pointer to the root `Base` object. + * Depending on `Provisioner`, this may contain references to all nested objects. + * + * Any invalid representations in `reader` will cause an error to be thrown. + */ +template +ParsedList parse_buffer(const unsigned char* buffer, size_t len, Options options = Options()) { + DummyExternals ext(0); + return parse_buffer(buffer, len, std::move(ext), std::move(options)); +} - /** - * Validate JSON file contents against the **uzuki2** specification, given a path to the file. - * Any invalid representations will cause an error to be thrown. - * - * @param file Path to a (possible Gzip-compressed) JSON file. - * @param num_external Expected number of external references. - */ - void validate_file(const std::string& file, int num_external = 0) { - DummyExternals ext(num_external); - parse_file(file, std::move(ext)); - return; - } +/** + * Validate JSON file contents against the **uzuki2** specification, given a source of bytes. + * Any invalid representations will cause an error to be thrown. + * + * @param reader Instance of a `byteme::Reader` providing the contents of the JSON file. + * @param num_external Expected number of external references. + * @param options Options for parsing. + */ +inline void validate(byteme::Reader& reader, int num_external = 0, Options options = Options()) { + DummyExternals ext(num_external); + parse(reader, std::move(ext), std::move(options)); + return; +} - /** - * Validate JSON file contents against the **uzuki2** specification, given a buffer containing the file contents. - * Any invalid representations will cause an error to be thrown. - * - * @param[in] buffer Pointer to an array containing the JSON file contents (possibly Gzip/Zlib-compressed). - * @param len Length of the buffer in bytes. - * @param num_external Expected number of external references. - */ - void validate_buffer(const unsigned char* buffer, size_t len, int num_external = 0) { - DummyExternals ext(num_external); - parse_buffer(buffer, len, std::move(ext)); - return; - } -}; +/** + * Validate JSON file contents against the **uzuki2** specification, given a path to the file. + * Any invalid representations will cause an error to be thrown. + * + * @param file Path to a (possible Gzip-compressed) JSON file. + * @param num_external Expected number of external references. + * @param options Options for parsing. + */ +inline void validate_file(const std::string& file, int num_external = 0, Options options = Options()) { + DummyExternals ext(num_external); + parse_file(file, std::move(ext), std::move(options)); + return; +} + +/** + * Validate JSON file contents against the **uzuki2** specification, given a buffer containing the file contents. + * Any invalid representations will cause an error to be thrown. + * + * @param[in] buffer Pointer to an array containing the JSON file contents (possibly Gzip/Zlib-compressed). + * @param len Length of the buffer in bytes. + * @param num_external Expected number of external references. + * @param options Options for parsing. + */ +inline void validate_buffer(const unsigned char* buffer, size_t len, int num_external = 0, Options options = Options()) { + DummyExternals ext(num_external); + parse_buffer(buffer, len, std::move(ext), std::move(options)); + return; +} + +} } diff --git a/tests/src/external.cpp b/tests/src/external.cpp index 52fb369..a958e85 100644 --- a/tests/src/external.cpp +++ b/tests/src/external.cpp @@ -17,7 +17,7 @@ TEST(Hdf5ExternalTest, SimpleLoading) { } { DefaultExternals ext(1); - auto parsed = uzuki2::Hdf5Parser().parse(path, "foo", ext); + auto parsed = uzuki2::hdf5::parse(path, "foo", ext); EXPECT_EQ(parsed->type(), uzuki2::EXTERNAL); auto stuff = static_cast(parsed.get()); @@ -36,7 +36,7 @@ TEST(Hdf5ExternalTest, SimpleLoading) { } { DefaultExternals ext(2); - auto parsed = uzuki2::Hdf5Parser().parse(path, "foo", ext); + auto parsed = uzuki2::hdf5::parse(path, "foo", ext); EXPECT_EQ(parsed->type(), uzuki2::LIST); auto list = static_cast(parsed.get()); @@ -52,7 +52,7 @@ void expect_hdf5_external_error(std::string path, std::string name, std::string H5::H5File file(path, H5F_ACC_RDONLY); EXPECT_ANY_THROW({ try { - uzuki2::Hdf5Parser().validate(file.openGroup(name), name, num_expected); + uzuki2::hdf5::validate(file.openGroup(name), name, num_expected); } catch (std::exception& e) { EXPECT_THAT(e.what(), ::testing::HasSubstr(msg)); throw; @@ -105,7 +105,7 @@ TEST(Hdf5ExternalTest, CheckErrors) { auto load_json_with_externals(std::string x, int num_externals) { DefaultExternals ext(num_externals); - return uzuki2::JsonParser().parse_buffer(reinterpret_cast(x.c_str()), x.size(), ext); + return uzuki2::json::parse_buffer(reinterpret_cast(x.c_str()), x.size(), ext); } TEST(JsonExternalTest, SimpleLoading) { @@ -134,7 +134,7 @@ TEST(JsonExternalTest, SimpleLoading) { void expect_json_external_error(std::string x, std::string msg, int num_expected) { EXPECT_ANY_THROW({ try { - uzuki2::JsonParser().validate_buffer(reinterpret_cast(x.c_str()), x.size(), num_expected); + uzuki2::json::validate_buffer(reinterpret_cast(x.c_str()), x.size(), num_expected); } catch (std::exception& e) { EXPECT_THAT(e.what(), ::testing::HasSubstr(msg)); throw; diff --git a/tests/src/misc.cpp b/tests/src/misc.cpp index 48cbe26..aed0eab 100644 --- a/tests/src/misc.cpp +++ b/tests/src/misc.cpp @@ -76,8 +76,8 @@ TEST_P(JsonFileTest, Chunking) { auto path = "TEST.json"; auto param = GetParam(); size_t block_size = std::get<0>(param); - uzuki2::JsonParser parser; - parser.parallel = std::get<1>(param); + uzuki2::json::Options opt; + opt.parallel = std::get<1>(param); { std::ofstream ohandle(path); @@ -87,7 +87,7 @@ TEST_P(JsonFileTest, Chunking) { // Checking parsing is fine with different block types. { byteme::SomeFileReader reader(path, block_size); - auto parsed = parser.parse(reader); + auto parsed = uzuki2::json::parse(reader, opt); EXPECT_EQ(parsed->type(), uzuki2::LIST); auto stuff = static_cast(parsed.get()); @@ -108,7 +108,7 @@ TEST_P(JsonFileTest, Chunking) { // Checking that validation works fine. { byteme::SomeFileReader reader(path, block_size); - EXPECT_NO_THROW(parser.validate(reader)); + EXPECT_NO_THROW(uzuki2::json::validate(reader)); } // Checking that the overload works with external references. @@ -120,7 +120,7 @@ TEST_P(JsonFileTest, Chunking) { { byteme::SomeFileReader reader(path, block_size); DefaultExternals ext(2); - auto parsed = parser.parse(reader, std::move(ext)); + auto parsed = uzuki2::json::parse(reader, std::move(ext)); EXPECT_EQ(parsed->type(), uzuki2::LIST); auto stuff = static_cast(parsed.get()); @@ -135,7 +135,7 @@ TEST_P(JsonFileTest, Chunking) { // Checking that validation works fine. { byteme::SomeFileReader reader(path, block_size); - EXPECT_NO_THROW(parser.validate(reader, 2)); + EXPECT_NO_THROW(uzuki2::json::validate(reader, 2)); } } @@ -150,7 +150,6 @@ INSTANTIATE_TEST_SUITE_P( TEST(JsonFileTest, CheckMethods) { std::string path = "TEST.json"; - uzuki2::JsonParser parser; // Making sure the parse_file methods work correctly. { @@ -158,9 +157,9 @@ TEST(JsonFileTest, CheckMethods) { ohandle << "{ \"type\":\"list\",\n \"values\": [ { \"type\": \"nothing\" }, { \"type\": \"integer\", \"values\": [ 1, 2, 3 ] } ], \n\t\"names\": [ \"X\", \"Y\" ] }"; } { - auto parsed = parser.parse_file(path); + auto parsed = uzuki2::json::parse_file(path); EXPECT_EQ(parsed->type(), uzuki2::LIST); - EXPECT_NO_THROW(parser.validate_file(path)); + EXPECT_NO_THROW(uzuki2::json::validate_file(path)); } // Again, with some externals. @@ -169,9 +168,9 @@ TEST(JsonFileTest, CheckMethods) { ohandle << "{ \"type\":\"list\",\n \"values\": [ { \"type\": \"external\", \"index\": 0 }, { \"type\": \"external\", \"index\": 1 } ] }"; } { - auto parsed = parser.parse_file(path, DefaultExternals(2)); + auto parsed = uzuki2::json::parse_file(path, DefaultExternals(2)); EXPECT_EQ(parsed->type(), uzuki2::LIST); - EXPECT_NO_THROW(parser.validate_file(path, 2)); + EXPECT_NO_THROW(uzuki2::json::validate_file(path, 2)); } } diff --git a/tests/src/utils.h b/tests/src/utils.h index a3a6f2c..667ec8c 100644 --- a/tests/src/utils.h +++ b/tests/src/utils.h @@ -139,19 +139,19 @@ inline H5::DataSet create_dataset(const H5::Group& parent, const std::string& na } inline auto load_hdf5(std::string name, std::string group) { - return uzuki2::Hdf5Parser().parse(name, group); + return uzuki2::hdf5::parse(name, group); } inline auto load_json(std::string x, bool parallel = false) { - uzuki2::JsonParser parser; - parser.parallel = parallel; - return parser.parse_buffer(reinterpret_cast(x.c_str()), x.size()); + uzuki2::json::Options opt; + opt.parallel = parallel; + return uzuki2::json::parse_buffer(reinterpret_cast(x.c_str()), x.size(), opt); } inline void expect_hdf5_error(std::string file, std::string name, std::string msg) { EXPECT_ANY_THROW({ try { - uzuki2::Hdf5Parser().validate(file, name); + uzuki2::hdf5::validate(file, name); } catch (std::exception& e) { EXPECT_THAT(e.what(), ::testing::HasSubstr(msg)); throw; @@ -162,7 +162,7 @@ inline void expect_hdf5_error(std::string file, std::string name, std::string ms inline void expect_json_error(std::string json, std::string msg) { EXPECT_ANY_THROW({ try { - uzuki2::JsonParser().validate_buffer(reinterpret_cast(json.c_str()), json.size()); + uzuki2::json::validate_buffer(reinterpret_cast(json.c_str()), json.size()); } catch (std::exception& e) { EXPECT_THAT(e.what(), ::testing::HasSubstr(msg)); throw;