diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2725307..80b154d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -29,6 +29,8 @@ jobs: - name: Configure CMake run: > CC=clang cmake -B ${{env.build_dir}} + -DCOWL_READERS='functional;obf' + -DCOWL_WRITERS='functional;obf' -DCOWL_LTO=OFF -DCOWL_CLANG_TIDY=ON -DCMAKE_COMPILE_WARNING_AS_ERROR=ON @@ -72,6 +74,8 @@ jobs: -DULIB_SANITIZERS=ON -DULIB_LIBRARY_TYPE=${{matrix.lib_type}} -DCOWL_LIBRARY_TYPE=${{matrix.lib_type}} + -DCOWL_READERS='functional;obf' + -DCOWL_WRITERS='functional;obf' -DCOWL_ENTITY_IDS=ON -DCOWL_CPP_TESTS=ON -DCMAKE_COMPILE_WARNING_AS_ERROR=ON diff --git a/include/cowl_reader.h b/include/cowl_reader.h index e5e42f9..91493fd 100644 --- a/include/cowl_reader.h +++ b/include/cowl_reader.h @@ -62,6 +62,19 @@ CowlReader cowl_reader_functional(void); #endif // COWL_READER_FUNCTIONAL +#ifdef COWL_READER_OBF + +/** + * Returns the OBF syntax reader. + * + * @return OBF syntax reader. + */ +COWL_API +COWL_CONST +CowlReader cowl_reader_obf(void); + +#endif // COWL_READER_OBF + /// @} COWL_END_DECLS diff --git a/include/cowl_writer.h b/include/cowl_writer.h index daf1cfb..298d0da 100644 --- a/include/cowl_writer.h +++ b/include/cowl_writer.h @@ -124,6 +124,19 @@ CowlWriter cowl_writer_functional(void); #endif // COWL_WRITER_FUNCTIONAL +#ifdef COWL_WRITER_OBF + +/** + * Returns the OBF syntax writer. + * + * @return OBF syntax writer. + */ +COWL_API +COWL_CONST +CowlWriter cowl_writer_obf(void); + +#endif // COWL_WRITER_OBF + /** * Checks whether the writer supports stream writing. * diff --git a/src/formats/obf/cowl_obf_constants.h b/src/formats/obf/cowl_obf_constants.h new file mode 100644 index 0000000..3923c03 --- /dev/null +++ b/src/formats/obf/cowl_obf_constants.h @@ -0,0 +1,42 @@ +/** + * @author Valerio Di Ceglie + * + * @copyright Copyright (c) 2024 SisInf Lab, Polytechnic University of Bari + * @copyright + * @copyright SPDX-License-Identifier: EPL-2.0 + * + * @file + */ + +#ifndef COWL_OBF_CONSTANTS_H +#define COWL_OBF_CONSTANTS_H + +typedef enum CowlOBFLiteralType { + COWL_OLT_TYPED_LITERAL = 0, + COWL_OLT_STRING_LITERAL, + COWL_OLT_STRING_LITERAL_LANGUAGE +} CowlOBFLiteralType; + +#define COWL_OBF_FORMAT_VERSION 1U + +#define COWL_OBF_HEADER_IMPORT_OFFSET 0U +#define COWL_OBF_HEADER_ANNOTATION_OFFSET 1U +#define COWL_OBF_HEADER_VERSION_OFFSET 2U +#define COWL_OBF_HEADER_IRI_OFFSET 3U +#define COWL_OBF_HEADER_FORMAT_OFFSET 4U +#define COWL_OBF_HEADER_ANNOTATIONS_BITMASK (1U << COWL_OBF_HEADER_ANNOTATION_OFFSET) +#define COWL_OBF_HEADER_VERSION_BITMASK (1U << COWL_OBF_HEADER_VERSION_OFFSET) +#define COWL_OBF_HEADER_IRI_BITMASK (1U << COWL_OBF_HEADER_IRI_OFFSET) +#define COWL_OBF_HEADER_IMPORT_BITMASK (1U) + +#define COWL_OBF_AXIOM_TYPE_SIZE 6U +#define COWL_OBF_AXIOM_ANNOT_OFFSET COWL_OBF_AXIOM_TYPE_SIZE +#define COWL_OBF_AXIOM_UTLITY_OFFSET (COWL_OBF_AXIOM_ANNOT_OFFSET + 1U) +#define COWL_OBF_AXIOM_TYPE_BITMASK ((1U << COWL_OBF_AXIOM_TYPE_SIZE) - 1U) +#define COWL_OBF_AXIOM_ANNOT_BITMASK (1U << COWL_OBF_AXIOM_ANNOT_OFFSET) +#define COWL_OBF_AXIOM_UTILITY_BITMASK (1U << COWL_OBF_AXIOM_UTLITY_OFFSET) + +#define COWL_OBF_LITERAL_TYPE_BITMASK 0x03 +#define COWL_OBF_STRING_COMPRESSION_OFFSET 2U + +#endif // COWL_OBF_CONSTANTS_H diff --git a/src/formats/obf/cowl_obf_map.c b/src/formats/obf/cowl_obf_map.c new file mode 100644 index 0000000..66738f4 --- /dev/null +++ b/src/formats/obf/cowl_obf_map.c @@ -0,0 +1,168 @@ +/** + * @author Valerio Di Ceglie + * + * @copyright Copyright (c) 2024 SisInf Lab, Polytechnic University of Bari + * @copyright + * @copyright SPDX-License-Identifier: EPL-2.0 + * + * @file + */ + +#include "cowl_obf_map.h" +#include "cowl.h" +#include "ulib.h" +#include +#include + +static inline void *uintptr_to_void(uintptr_t ptr) { + // NOLINTBEGIN(performance-no-int-to-ptr) + return (void *)ptr; + // NOLINTEND(performance-no-int-to-ptr) +} + +static cowl_ret populate_standard_prefixes(CowlOBFMap *map) { + const CowlVocab *vocab = cowl_vocab(); + // Update COWL_OBF_STANDARD_PREFIX_COUNT if you change the number of standard prefixes. + CowlString *ns[] = { + vocab->rdf->ns, vocab->rdfs->ns, vocab->xsd->ns, vocab->owl->ns, vocab->xml->ns, + }; + CowlString *prefix[] = { + vocab->rdf->prefix, vocab->rdfs->prefix, vocab->xsd->prefix, + vocab->owl->prefix, vocab->xml->prefix, + }; + cowl_ret ret = COWL_OK; + for (ulib_uint i = 0; i < COWL_OBF_STANDARD_PREFIX_COUNT; ++i) { + if ((ret = cowl_obf_map_add_prefix(map, ns[i], prefix[i]))) break; + } + return ret; +} + +cowl_ret cowl_obf_map_init(CowlOBFMap *map) { + map->_idx_to_prefix = uvec(CowlObjectPtr); + map->_idx_to_ns = uvec(CowlObjectPtr); + map->_prefix_to_idx = uhmap(CowlObjectTable); + map->_idx_to_id = uvec(CowlObjectPtr); + map->_id_to_idx = uhmap(CowlObjectTable); + cowl_ret ret = populate_standard_prefixes(map); + if (ret) cowl_obf_map_deinit(map); + return ret; +} + +void cowl_obf_map_deinit(CowlOBFMap *map) { + uvec_foreach (CowlObjectPtr, &map->_idx_to_prefix, e) { + cowl_release(*e.item); + } + uvec_foreach (CowlObjectPtr, &map->_idx_to_ns, e) { + cowl_release(*e.item); + } + uvec_foreach (CowlObjectPtr, &map->_idx_to_id, e) { + cowl_release(*e.item); + } + uvec_deinit(CowlObjectPtr, &map->_idx_to_id); + uvec_deinit(CowlObjectPtr, &map->_idx_to_prefix); + uvec_deinit(CowlObjectPtr, &map->_idx_to_ns); + uhash_deinit(CowlObjectTable, &map->_prefix_to_idx); + uhash_deinit(CowlObjectTable, &map->_id_to_idx); +} + +UVec(CowlObjectPtr) const *cowl_obf_map_get_ids(CowlOBFMap const *map) { + return &map->_idx_to_id; +} + +UVec(CowlObjectPtr) const *cowl_obf_map_get_prefixes(CowlOBFMap const *map) { + return &map->_idx_to_prefix; +} + +UVec(CowlObjectPtr) const *cowl_obf_map_get_namespaces(CowlOBFMap const *map) { + return &map->_idx_to_ns; +} + +CowlString *cowl_obf_map_get_ns(CowlOBFMap const *map, ulib_uint idx) { + return uvec_get(CowlObjectPtr, &map->_idx_to_ns, idx); +} + +ulib_uint cowl_obf_map_get_ns_idx(CowlOBFMap const *map, CowlString *namespace) { + uintptr_t idx = (uintptr_t)uhmap_get(CowlObjectTable, &map->_prefix_to_idx, namespace, + uintptr_to_void(COWL_OBF_NOT_FOUND)); + return (ulib_uint)idx; +} + +CowlIRIOrAnonInd *cowl_obf_map_get_id(CowlOBFMap const *map, ulib_uint idx) { + return uvec_get(CowlObjectPtr, &map->_idx_to_id, idx); +} + +ulib_uint cowl_obf_map_get_id_idx(CowlOBFMap const *map, CowlIRIOrAnonInd *id) { + uintptr_t idx = (uintptr_t)uhmap_get(CowlObjectTable, &map->_id_to_idx, id, + uintptr_to_void(COWL_OBF_NOT_FOUND)); + return (ulib_uint)idx; +} + +cowl_ret cowl_obf_map_add_prefix(CowlOBFMap *map, CowlString *ns, CowlString *prefix) { + ulib_uint tbl_idx; + uhash_ret ret; + ret = uhash_put(CowlObjectTable, &map->_prefix_to_idx, ns, &tbl_idx); + if (ret == UHASH_ERR) return COWL_ERR_MEM; + if (ret == UHASH_PRESENT) return COWL_OK; + if (uvec_push(CowlObjectPtr, &map->_idx_to_prefix, prefix)) goto err_prefix; + if (uvec_push(CowlObjectPtr, &map->_idx_to_ns, ns)) goto err_ns; + + ulib_uint index = uvec_count(CowlObjectPtr, &map->_idx_to_prefix) - 1; + uhash_value(CowlObjectTable, &map->_prefix_to_idx, tbl_idx) = uintptr_to_void(index); + cowl_retain(ns); + if (prefix) cowl_retain(prefix); + + return COWL_OK; + +err_ns: + uvec_pop(CowlObjectPtr, &map->_idx_to_prefix, NULL); +err_prefix: + uhash_delete(CowlObjectTable, &map->_prefix_to_idx, tbl_idx); + return COWL_ERR_MEM; +} + +static inline cowl_ret add_iri_ns(CowlOBFMap *map, CowlIRI *iri) { + return cowl_obf_map_add_prefix(map, cowl_iri_get_ns(iri), NULL); +} + +cowl_ret cowl_obf_map_add_primitive_fast(CowlOBFMap *map, CowlAnyPrimitive *primitive) { + CowlIRIOrAnonInd *id = cowl_get_iri(primitive); + + if (id) { + if (add_iri_ns(map, id)) return COWL_ERR_MEM; + } else { + id = primitive; + } + + if (uvec_push(CowlObjectPtr, &map->_idx_to_id, id)) { + return COWL_ERR_MEM; + } + + cowl_retain(id); + return COWL_OK; +} + +cowl_ret cowl_obf_map_add_primitive(CowlOBFMap *map, CowlAnyPrimitive *primitive) { + CowlIRIOrAnonInd *id = cowl_get_iri(primitive); + bool id_is_iri = true; + + if (!id) { + id_is_iri = false; + id = primitive; + } + + ulib_uint tbl_idx; + uhash_ret ret = uhash_put(CowlObjectTable, &map->_id_to_idx, id, &tbl_idx); + if (ret == UHASH_ERR) return COWL_ERR_MEM; + if (ret == UHASH_PRESENT) return COWL_OK; + + if (uvec_push(CowlObjectPtr, &map->_idx_to_id, id)) { + uhash_delete(CowlObjectTable, &map->_id_to_idx, tbl_idx); + return COWL_ERR_MEM; + } + + ulib_uint index = uvec_count(CowlObjectPtr, &map->_idx_to_id) - 1; + uhash_value(CowlObjectTable, &map->_id_to_idx, tbl_idx) = uintptr_to_void(index); + cowl_retain(id); + + return id_is_iri ? add_iri_ns(map, id) : COWL_OK; +} diff --git a/src/formats/obf/cowl_obf_map.h b/src/formats/obf/cowl_obf_map.h new file mode 100644 index 0000000..954a1b2 --- /dev/null +++ b/src/formats/obf/cowl_obf_map.h @@ -0,0 +1,49 @@ +/** + * @author Valerio Di Ceglie + * + * @copyright Copyright (c) 2024 SisInf Lab, Polytechnic University of Bari + * @copyright + * @copyright SPDX-License-Identifier: EPL-2.0 + * + * @file + */ + +#ifndef COWL_OBF_MAP_H +#define COWL_OBF_MAP_H + +#include "cowl.h" +#include "ulib.h" + +typedef struct CowlOBFMap { + UVec(CowlObjectPtr) _idx_to_prefix; + UVec(CowlObjectPtr) _idx_to_ns; + UHash(CowlObjectTable) _prefix_to_idx; + UVec(CowlObjectPtr) _idx_to_id; + UHash(CowlObjectTable) _id_to_idx; +} CowlOBFMap; + +typedef void CowlIRIOrAnonInd; + +#define COWL_OBF_NOT_FOUND ((ulib_uint)(-1)) +#define COWL_OBF_STANDARD_PREFIX_COUNT 5 + +cowl_ret cowl_obf_map_init(CowlOBFMap *map); +void cowl_obf_map_deinit(CowlOBFMap *map); + +UVec(CowlObjectPtr) const *cowl_obf_map_get_ids(CowlOBFMap const *map); +UVec(CowlObjectPtr) const *cowl_obf_map_get_prefixes(CowlOBFMap const *map); +UVec(CowlObjectPtr) const *cowl_obf_map_get_namespaces(CowlOBFMap const *map); + +CowlString *cowl_obf_map_get_ns(CowlOBFMap const *map, ulib_uint idx); +ulib_uint cowl_obf_map_get_ns_idx(CowlOBFMap const *map, CowlString *namespace); + +CowlIRIOrAnonInd *cowl_obf_map_get_id(CowlOBFMap const *map, ulib_uint idx); +ulib_uint cowl_obf_map_get_id_idx(CowlOBFMap const *map, CowlIRIOrAnonInd *id); + +cowl_ret cowl_obf_map_add_primitive(CowlOBFMap *map, CowlAnyPrimitive *primitive); +cowl_ret cowl_obf_map_add_prefix(CowlOBFMap *map, CowlString *ns, CowlString *prefix); + +// Only use when decoding, this won't populate the primitive -> index map. +cowl_ret cowl_obf_map_add_primitive_fast(CowlOBFMap *map, CowlAnyPrimitive *primitive); + +#endif // COWL_OBF_MAP_H diff --git a/src/formats/obf/reader/CMakeLists.txt b/src/formats/obf/reader/CMakeLists.txt new file mode 100644 index 0000000..0a355e5 --- /dev/null +++ b/src/formats/obf/reader/CMakeLists.txt @@ -0,0 +1,13 @@ +# Sources + +set(COWL_READER_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}") +file(GLOB COWL_READER_SOURCES CONFIGURE_DEPENDS "${COWL_READER_SRC_DIR}/*.c") +list(APPEND COWL_SOURCES ${COWL_READER_SOURCES}) + +cmake_path(GET COWL_READER_SRC_DIR PARENT_PATH COWL_READER_SRC_DIR) +file(GLOB COWL_READER_SOURCES CONFIGURE_DEPENDS "${COWL_READER_SRC_DIR}/*.c") +list(APPEND COWL_SOURCES ${COWL_READER_SOURCES}) + +# Propagate changes in the parent project + +set(COWL_SOURCES ${COWL_SOURCES} PARENT_SCOPE) diff --git a/src/formats/obf/reader/cowl_obf_decoder.c b/src/formats/obf/reader/cowl_obf_decoder.c new file mode 100644 index 0000000..4227d01 --- /dev/null +++ b/src/formats/obf/reader/cowl_obf_decoder.c @@ -0,0 +1,1064 @@ +/** + * @author Valerio Di Ceglie + * + * @copyright Copyright (c) 2024 SisInf Lab, Polytechnic University of Bari + * @copyright + * @copyright SPDX-License-Identifier: EPL-2.0 + * + * @file + */ + +#include "../cowl_obf_constants.h" +#include "../cowl_obf_map.h" + +#include "cowl.h" +#include "cowl_vector_private.h" +#include "ulib.h" +#include + +typedef struct CowlOBFDecoder { + CowlOBFMap map; + UIStream *stream; + CowlIStream *istream; + CowlString *default_prefix; + CowlString *empty_prefix; + cowl_ret state; +} CowlOBFDecoder; + +static inline cowl_ret decoder_init(CowlOBFDecoder *d, UIStream *stream, CowlIStream *istream) { + d->stream = stream; + d->istream = istream; + d->default_prefix = cowl_string_from_static(":"); + d->empty_prefix = cowl_string_empty(); + d->state = COWL_OK; + return cowl_obf_map_init(&d->map); +} + +static inline void decoder_deinit(CowlOBFDecoder *d) { + cowl_release(d->default_prefix); + cowl_release(d->empty_prefix); + cowl_obf_map_deinit(&d->map); +} + +static inline bool decoder_error(CowlOBFDecoder *d) { + return d->state || d->stream->state; +} + +static inline bool decoder_ok(CowlOBFDecoder *d) { + return !decoder_error(d); +} + +static inline cowl_ret decoder_state(CowlOBFDecoder *d) { + return d->state ? d->state : cowl_ret_from_ustream(d->stream->state); +} + +static inline bool read_bytes(CowlOBFDecoder *d, void *data, ulib_uint length) { + return uistream_read(d->stream, data, length, NULL); +} + +static inline bool read_varint(CowlOBFDecoder *d, ulib_uint *value) { + return uistream_read_varint(d->stream, value, NULL); +} + +static CowlAnyClsExp *decode_cls_exp(CowlOBFDecoder *d); +static CowlAnyDataRange *decode_data_range(CowlOBFDecoder *d); +static CowlIndividual *decode_individual(CowlOBFDecoder *d); +static CowlObjPropExp *decode_obj_prop_exp(CowlOBFDecoder *d); +static CowlDataPropExp *decode_data_prop_exp(CowlOBFDecoder *d); +static CowlFacetRestr *decode_facet_restr(CowlOBFDecoder *d); +static CowlLiteral *decode_literal(CowlOBFDecoder *d); +static CowlAnnotation *decode_annotation(CowlOBFDecoder *d); + +static inline CowlAny *handle_null(CowlOBFDecoder *d, CowlAny *obj) { + if (!obj) d->state = COWL_ERR_MEM; + return obj; +} + +static CowlAny *cls_exp_item(CowlOBFDecoder *d) { + return decode_cls_exp(d); +} + +static CowlAny *data_range_item(CowlOBFDecoder *d) { + return decode_data_range(d); +} + +static CowlAny *individual_item(CowlOBFDecoder *d) { + return decode_individual(d); +} + +static CowlAny *obj_prop_exp_item(CowlOBFDecoder *d) { + return decode_obj_prop_exp(d); +} + +static CowlAny *data_prop_exp_item(CowlOBFDecoder *d) { + return decode_data_prop_exp(d); +} + +static CowlAny *facet_restr_item(CowlOBFDecoder *d) { + return decode_facet_restr(d); +} + +static CowlAny *literal_item(CowlOBFDecoder *d) { + return decode_literal(d); +} + +static CowlAny *annotation_item(CowlOBFDecoder *d) { + return decode_annotation(d); +} + +static CowlVector *decode_vector(CowlOBFDecoder *d, CowlAny *(*decode_item)(CowlOBFDecoder *)) { + ulib_uint length; + if (read_varint(d, &length)) return NULL; + + CowlVector *vec = cowl_vector_empty(); + if (!vec) goto err; + if (cowl_vector_reserve(vec, length)) goto err; + + for (ulib_uint i = 0; i < length; ++i) { + CowlAny *item = decode_item(d); + if (!item || cowl_vector_push(vec, item)) goto err; + } + if (cowl_vector_shrink(vec)) goto err; + + return vec; + +err: + d->state = COWL_ERR_MEM; + cowl_release(vec); + return NULL; +} + +static UString decode_ustring(CowlOBFDecoder *d) { + ulib_uint length; + if (read_varint(d, &length)) return ustring_null; + + UString string; + char *data = ustring(&string, length); + + if (!data) { + d->state = COWL_ERR_MEM; + return ustring_null; + } + + if (read_bytes(d, data, length)) { + ustring_deinit(&string); + return ustring_null; + } + + return string; +} + +static CowlString *decode_string(CowlOBFDecoder *d) { + UString ustring = decode_ustring(d); + if (ustring_is_null(ustring)) return NULL; + return handle_null(d, cowl_string(ustring)); +} + +static CowlString *decode_string_intern(CowlOBFDecoder *d) { + UString ustring = decode_ustring(d); + if (ustring_is_null(ustring)) return NULL; + return handle_null(d, cowl_string_opt(ustring, COWL_SO_INTERN)); +} + +static CowlIRIOrAnonInd *get_id(CowlOBFDecoder *d, ulib_uint offset) { + CowlIRIOrAnonInd *id = cowl_obf_map_get_id(&d->map, offset); + if (!id) d->state = COWL_ERR; + return id; +} + +static CowlIRIOrAnonInd *get_iri_or_anon(CowlOBFDecoder *d, ulib_uint offset) { + CowlIRIOrAnonInd *id = get_id(d, offset); + return id ? cowl_retain(id) : NULL; +} + +static inline CowlIRI *get_iri(CowlOBFDecoder *d, ulib_uint offset) { + return get_iri_or_anon(d, offset); +} + +static CowlClass *get_class(CowlOBFDecoder *d, ulib_uint offset) { + CowlIRIOrAnonInd *id = get_id(d, offset); + if (!id) return NULL; + return handle_null(d, cowl_class(id)); +} + +static CowlDatatype *get_datatype(CowlOBFDecoder *d, ulib_uint offset) { + CowlIRIOrAnonInd *iri = get_id(d, offset); + if (!iri) return NULL; + return handle_null(d, cowl_datatype(iri)); +} + +static CowlNamedInd *get_named_ind(CowlOBFDecoder *d, ulib_uint offset) { + CowlIRIOrAnonInd *iri = get_id(d, offset); + if (!iri) return NULL; + return handle_null(d, cowl_named_ind(iri)); +} + +static CowlObjProp *get_obj_prop(CowlOBFDecoder *d, ulib_uint offset) { + CowlIRIOrAnonInd *iri = get_id(d, offset); + if (!iri) return NULL; + return handle_null(d, cowl_obj_prop(iri)); +} + +static CowlDataProp *get_data_prop(CowlOBFDecoder *d, ulib_uint offset) { + CowlIRIOrAnonInd *iri = get_id(d, offset); + if (!iri) return NULL; + return handle_null(d, cowl_data_prop(iri)); +} + +static CowlAnnotProp *get_annot_prop(CowlOBFDecoder *d, ulib_uint offset) { + CowlIRIOrAnonInd *iri = get_id(d, offset); + if (!iri) return NULL; + return handle_null(d, cowl_annot_prop(iri)); +} + +static CowlIndividual *get_individual(CowlOBFDecoder *d, ulib_uint offset) { + CowlAny *id = get_id(d, offset); + if (!id) return NULL; + return cowl_is_individual(id) ? cowl_retain(id) : handle_null(d, cowl_named_ind(id)); +} + +static CowlIRI *decode_iri(CowlOBFDecoder *d) { + ulib_uint offset; + if (read_varint(d, &offset)) return NULL; + return get_iri(d, offset); +} + +static CowlIRIOrAnonInd *decode_iri_or_anon(CowlOBFDecoder *d) { + ulib_uint offset; + if (read_varint(d, &offset)) return NULL; + return get_iri_or_anon(d, offset); +} + +static CowlAnyEntity *decode_entity(CowlOBFDecoder *d, CowlEntityType type) { + ulib_uint offset; + if (read_varint(d, &offset)) return NULL; + switch (type) { + case COWL_ET_CLASS: return get_class(d, offset); + case COWL_ET_ANNOT_PROP: return get_annot_prop(d, offset); + case COWL_ET_DATA_PROP: return get_data_prop(d, offset); + case COWL_ET_DATATYPE: return get_datatype(d, offset); + case COWL_ET_NAMED_IND: return get_named_ind(d, offset); + case COWL_ET_OBJ_PROP: return get_obj_prop(d, offset); + default: break; + } + return NULL; +} + +static CowlLiteral *decode_literal_typed(CowlOBFDecoder *d, CowlString *value) { + CowlIRI *iri = decode_iri(d); + if (!iri) return NULL; + + CowlLiteral *literal = NULL; + CowlDatatype *dt = cowl_datatype(iri); + if (dt) literal = handle_null(d, cowl_literal(dt, value, NULL)); + + cowl_release_all(iri, dt); + return literal; +} + +static CowlLiteral *decode_literal_string(CowlOBFDecoder *d, CowlString *value) { + return handle_null(d, cowl_literal(NULL, value, NULL)); +} + +static CowlLiteral *decode_literal_lang(CowlOBFDecoder *d, CowlString *value) { + CowlString *lang = decode_string(d); + if (!lang) return NULL; + CowlLiteral *literal = handle_null(d, cowl_literal(NULL, value, lang)); + cowl_release(lang); + return literal; +} + +static CowlLiteral *decode_literal(CowlOBFDecoder *d) { + ulib_byte header; + if (read_bytes(d, &header, sizeof(header))) return NULL; + + // TODO: add compression logic + CowlOBFLiteralType type = (CowlOBFLiteralType)(header & COWL_OBF_LITERAL_TYPE_BITMASK); + // ulib_uint compression = header >> COWL_OBF_STRING_COMPRESSION_OFFSET; + + CowlString *value = decode_string(d); + if (!value) return NULL; + + CowlLiteral *literal = NULL; + + switch (type) { + case COWL_OLT_TYPED_LITERAL: literal = decode_literal_typed(d, value); break; + case COWL_OLT_STRING_LITERAL: literal = decode_literal_string(d, value); break; + case COWL_OLT_STRING_LITERAL_LANGUAGE: literal = decode_literal_lang(d, value); break; + default: break; + } + + cowl_release(value); + return literal; +} + +static CowlAnnotProp *decode_annot_prop(CowlOBFDecoder *d) { + ulib_uint offset; + if (read_varint(d, &offset)) return NULL; + return get_annot_prop(d, offset); +} + +static CowlAnnotValue *decode_annot_value(CowlOBFDecoder *d) { + ulib_uint value; + if (read_varint(d, &value)) return NULL; + return value ? get_iri_or_anon(d, value - 1) : decode_literal(d); +} + +static CowlAnnotation *decode_annotation(CowlOBFDecoder *d) { + CowlIRI *iri = NULL; + CowlAnnotProp *prop = NULL; + CowlAnnotValue *value = NULL; + CowlVector *annots = NULL; + CowlAnnotation *annot = NULL; + + ulib_uint has_no_annot; + if (read_varint(d, &has_no_annot)) return NULL; + iri = has_no_annot ? get_iri(d, has_no_annot - 1) : decode_iri(d); + if (!iri) goto end; + if (!(prop = cowl_annot_prop(iri))) goto end; + if (!(value = decode_annot_value(d))) goto end; + if (!has_no_annot && !(annots = decode_vector(d, annotation_item))) goto end; + annot = cowl_annotation(prop, value, annots); + +end: + if (!annot) d->state = COWL_ERR_MEM; + cowl_release_all(iri, prop, value, annots); + return annot; +} + +static CowlIndividual *decode_individual(CowlOBFDecoder *d) { + ulib_uint offset; + if (read_varint(d, &offset)) return NULL; + return get_individual(d, offset); +} + +static CowlObjProp *decode_obj_prop(CowlOBFDecoder *d) { + ulib_uint offset; + if (read_varint(d, &offset)) return NULL; + return get_obj_prop(d, offset); +} + +static CowlInvObjProp *decode_inv_obj_prop(CowlOBFDecoder *d) { + CowlObjProp *prop = decode_obj_prop(d); + if (!prop) return NULL; + CowlInvObjProp *inv = handle_null(d, cowl_inv_obj_prop(prop)); + cowl_release(prop); + return inv; +} + +static CowlObjPropExp *decode_obj_prop_exp(CowlOBFDecoder *d) { + ulib_uint offset; + if (read_varint(d, &offset)) return NULL; + if (offset == 0) return (CowlObjPropExp *)decode_inv_obj_prop(d); + return (CowlObjPropExp *)get_obj_prop(d, offset - 1); +} + +static CowlDataPropExp *decode_data_prop_exp(CowlOBFDecoder *d) { + ulib_uint offset; + if (read_varint(d, &offset)) return NULL; + return (CowlDataPropExp *)get_data_prop(d, offset); +} + +static CowlFacetRestr *decode_facet_restr(CowlOBFDecoder *d) { + CowlFacetRestr *restr = NULL; + CowlIRI *iri = decode_iri(d); + CowlAny *literal = decode_literal(d); + if (iri && literal) restr = handle_null(d, cowl_facet_restr(iri, literal)); + cowl_release_all(iri, literal); + return restr; +} + +static CowlClass *decode_class(CowlOBFDecoder *d) { + ulib_uint offset; + if (read_varint(d, &offset)) return NULL; + return get_class(d, offset); +} + +static CowlObjQuant *decode_obj_quant(CowlOBFDecoder *d, CowlQuantType type) { + CowlObjQuant *ce = NULL; + CowlObjPropExp *prop = decode_obj_prop_exp(d); + CowlClsExp *filler = decode_cls_exp(d); + if (decoder_ok(d)) ce = handle_null(d, cowl_obj_quant(type, prop, filler)); + cowl_release_all(prop, filler); + return ce; +} + +static CowlObjCard *decode_obj_card(CowlOBFDecoder *d, CowlCardType type) { + CowlObjCard *ce = NULL; + ulib_uint cardinality; + if (read_varint(d, &cardinality)) return NULL; + + CowlObjPropExp *prop = decode_obj_prop_exp(d); + CowlClsExp *filler = NULL; + bool has_filler = cardinality & 0x01; + cardinality >>= 1; + if (has_filler) filler = decode_cls_exp(d); + + if (decoder_ok(d)) ce = handle_null(d, cowl_obj_card(type, prop, filler, cardinality)); + cowl_release_all(prop, filler); + return ce; +} + +static CowlObjHasValue *decode_obj_has_value(CowlOBFDecoder *d) { + CowlObjHasValue *ce = NULL; + CowlObjPropExp *prop = decode_obj_prop_exp(d); + CowlIndividual *ind = decode_individual(d); + if (decoder_ok(d)) ce = handle_null(d, cowl_obj_has_value(prop, ind)); + cowl_release_all(prop, ind); + return ce; +} + +static CowlObjHasSelf *decode_obj_has_self(CowlOBFDecoder *d) { + CowlObjHasSelf *ce = NULL; + CowlObjPropExp *prop = decode_obj_prop_exp(d); + if (decoder_ok(d)) ce = handle_null(d, cowl_obj_has_self(prop)); + cowl_release(prop); + return ce; +} + +static CowlDataQuant *decode_data_quant(CowlOBFDecoder *d, CowlQuantType type) { + CowlDataQuant *ce = NULL; + CowlDataPropExp *prop = decode_data_prop_exp(d); + CowlDataRange *filler = decode_data_range(d); + if (decoder_ok(d)) ce = handle_null(d, cowl_data_quant(type, prop, filler)); + cowl_release_all(prop, filler); + return ce; +} + +static CowlDataCard *decode_data_card(CowlOBFDecoder *d, CowlCardType type) { + CowlDataCard *ce = NULL; + ulib_uint cardinality; + if (read_varint(d, &cardinality)) return NULL; + + CowlDataPropExp *prop = decode_data_prop_exp(d); + CowlDataRange *filler = NULL; + bool has_filler = cardinality & 0x01; + cardinality >>= 1; + if (has_filler) filler = decode_data_range(d); + + if (decoder_ok(d)) ce = handle_null(d, cowl_data_card(type, prop, filler, cardinality)); + cowl_release_all(prop, filler); + return ce; +} + +static CowlDataHasValue *decode_data_has_value(CowlOBFDecoder *d) { + CowlDataHasValue *ce = NULL; + CowlDataPropExp *prop = decode_data_prop_exp(d); + CowlAny *literal = decode_literal(d); + if (decoder_ok(d)) ce = handle_null(d, cowl_data_has_value(prop, literal)); + cowl_release_all(prop, literal); + return ce; +} + +static CowlNAryBool *decode_nary_cls_exp(CowlOBFDecoder *d, CowlNAryType cls_exp_type) { + CowlNAryBool *ce = NULL; + CowlVector *operands = decode_vector(d, cls_exp_item); + if (decoder_ok(d)) ce = handle_null(d, cowl_nary_bool(cls_exp_type, operands)); + cowl_release(operands); + return ce; +} + +static CowlObjCompl *decode_obj_compl(CowlOBFDecoder *d) { + CowlObjCompl *ce = NULL; + CowlClsExp *operand = decode_cls_exp(d); + if (decoder_ok(d)) ce = handle_null(d, cowl_obj_compl(operand)); + cowl_release(operand); + return ce; +} + +static CowlObjOneOf *decode_obj_one_of(CowlOBFDecoder *d) { + CowlObjOneOf *ce = NULL; + CowlVector *inds = decode_vector(d, individual_item); + if (decoder_ok(d)) ce = handle_null(d, cowl_obj_one_of(inds)); + cowl_release(inds); + return ce; +} + +static CowlAnyClsExp *decode_cls_exp(CowlOBFDecoder *d) { + ulib_uint type; + if (read_varint(d, &type)) return NULL; + + if (type >= COWL_CET_COUNT - 1) { + return get_class(d, type - COWL_CET_COUNT + 1); + } + + switch ((CowlClsExpType)(type + 1)) { + case COWL_CET_OBJ_SOME: return decode_obj_quant(d, COWL_QT_SOME); + case COWL_CET_OBJ_ALL: return decode_obj_quant(d, COWL_QT_ALL); + case COWL_CET_OBJ_MIN_CARD: return decode_obj_card(d, COWL_CT_MIN); + case COWL_CET_OBJ_MAX_CARD: return decode_obj_card(d, COWL_CT_MAX); + case COWL_CET_OBJ_EXACT_CARD: return decode_obj_card(d, COWL_CT_EXACT); + case COWL_CET_OBJ_HAS_VALUE: return decode_obj_has_value(d); + case COWL_CET_OBJ_HAS_SELF: return decode_obj_has_self(d); + case COWL_CET_DATA_SOME: return decode_data_quant(d, COWL_QT_SOME); + case COWL_CET_DATA_ALL: return decode_data_quant(d, COWL_QT_ALL); + case COWL_CET_DATA_MIN_CARD: return decode_data_card(d, COWL_CT_MIN); + case COWL_CET_DATA_MAX_CARD: return decode_data_card(d, COWL_CT_MAX); + case COWL_CET_DATA_EXACT_CARD: return decode_data_card(d, COWL_CT_EXACT); + case COWL_CET_DATA_HAS_VALUE: return decode_data_has_value(d); + case COWL_CET_OBJ_INTERSECT: return decode_nary_cls_exp(d, COWL_NT_INTERSECT); + case COWL_CET_OBJ_UNION: return decode_nary_cls_exp(d, COWL_NT_UNION); + case COWL_CET_OBJ_COMPL: return decode_obj_compl(d); + case COWL_CET_OBJ_ONE_OF: return decode_obj_one_of(d); + default: break; + } + + return NULL; +} + +static CowlDatatype *decode_datatype(CowlOBFDecoder *d) { + ulib_uint offset; + if (read_varint(d, &offset)) return NULL; + return get_datatype(d, offset); +} + +static CowlNAryData *decode_nary_data(CowlOBFDecoder *d, CowlNAryType type) { + CowlNAryData *dr = NULL; + CowlVector *operands = decode_vector(d, data_range_item); + if (decoder_ok(d)) dr = handle_null(d, cowl_nary_data(type, operands)); + cowl_release(operands); + return dr; +} + +static CowlDataCompl *decode_data_compl(CowlOBFDecoder *d) { + CowlDataCompl *dr = NULL; + CowlDataRange *operand = decode_data_range(d); + if (decoder_ok(d)) dr = handle_null(d, cowl_data_compl(operand)); + cowl_release(operand); + return dr; +} + +static CowlDataOneOf *decode_data_one_of(CowlOBFDecoder *d) { + CowlDataOneOf *dr = NULL; + CowlVector *operands = decode_vector(d, literal_item); + if (decoder_ok(d)) dr = handle_null(d, cowl_data_one_of(operands)); + cowl_release(operands); + return dr; +} + +static CowlDatatypeRestr *decode_datatype_restr(CowlOBFDecoder *d) { + CowlDatatypeRestr *dr = NULL; + CowlDatatype *dt = decode_datatype(d); + CowlVector *restrs = decode_vector(d, facet_restr_item); + if (decoder_ok(d)) dr = handle_null(d, cowl_datatype_restr(dt, restrs)); + cowl_release_all(dt, restrs); + return dr; +} + +static CowlAnyDataRange *decode_data_range(CowlOBFDecoder *d) { + ulib_uint type; + if (read_varint(d, &type)) return NULL; + + if (type >= COWL_DRT_COUNT - 1) { + return get_datatype(d, type - COWL_DRT_COUNT + 1); + } + + switch ((CowlDataRangeType)type + 1) { + case COWL_DRT_DATATYPE_RESTR: return decode_datatype_restr(d); + case COWL_DRT_DATA_INTERSECT: return decode_nary_data(d, COWL_NT_INTERSECT); + case COWL_DRT_DATA_UNION: return decode_nary_data(d, COWL_NT_UNION); + case COWL_DRT_DATA_COMPL: return decode_data_compl(d); + case COWL_DRT_DATA_ONE_OF: return decode_data_one_of(d); + default: break; + } + + return NULL; +} + +static CowlDeclAxiom * +decode_declaration_axiom(CowlOBFDecoder *d, CowlEntityType entity_type, bool has_annot) { + CowlDeclAxiom *axiom = NULL; + CowlAnyEntity *entity = decode_entity(d, entity_type); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_decl_axiom(entity, annot)); + cowl_release_all(entity, annot); + return axiom; +} + +static CowlNAryClsAxiom * +decode_nary_class_axiom(CowlOBFDecoder *d, CowlNAryAxiomType type, bool has_annot) { + CowlNAryClsAxiom *axiom = NULL; + CowlVector *op = decode_vector(d, cls_exp_item); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_nary_cls_axiom(type, op, annot)); + cowl_release_all(op, annot); + return axiom; +} + +static CowlDisjUnionAxiom *decode_disj_union_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlDisjUnionAxiom *axiom = NULL; + CowlClass *cls = decode_class(d); + CowlVector *disj = decode_vector(d, cls_exp_item); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_disj_union_axiom(cls, disj, annot)); + cowl_release_all(cls, disj, annot); + return axiom; +} + +static CowlSubClsAxiom *decode_sub_cls_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlSubClsAxiom *axiom = NULL; + CowlClsExp *sub = decode_cls_exp(d); + CowlClsExp *sup = decode_cls_exp(d); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_sub_cls_axiom(sub, sup, annot)); + cowl_release_all(sub, sup, annot); + return axiom; +} + +static CowlClsAssertAxiom *decode_cls_assert_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlClsAssertAxiom *axiom = NULL; + CowlClsExp *ce = decode_cls_exp(d); + CowlIndividual *ind = decode_individual(d); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_cls_assert_axiom(ce, ind, annot)); + cowl_release_all(ce, ind, annot); + return axiom; +} + +static CowlNAryIndAxiom * +decode_nary_ind_axiom(CowlOBFDecoder *d, CowlNAryAxiomType type, bool has_annot) { + CowlNAryIndAxiom *axiom = NULL; + CowlVector *inds = decode_vector(d, individual_item); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_nary_ind_axiom(type, inds, annot)); + cowl_release_all(inds, annot); + return axiom; +} + +static CowlNAryObjPropAxiom * +decode_nary_obj_prop_axiom(CowlOBFDecoder *d, CowlNAryAxiomType type, bool has_annot) { + CowlNAryObjPropAxiom *axiom = NULL; + CowlVector *props = decode_vector(d, obj_prop_exp_item); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_nary_obj_prop_axiom(type, props, annot)); + cowl_release_all(props, annot); + return axiom; +} + +static CowlObjPropCharAxiom * +decode_obj_prop_char_axiom(CowlOBFDecoder *d, CowlCharAxiomType type, bool has_annot) { + CowlObjPropCharAxiom *axiom = NULL; + CowlObjPropExp *prop = decode_obj_prop_exp(d); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_obj_prop_char_axiom(type, prop, annot)); + cowl_release_all(prop, annot); + return axiom; +} + +static CowlObjPropDomainAxiom *decode_obj_prop_domain_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlObjPropDomainAxiom *axiom = NULL; + CowlObjPropExp *prop = decode_obj_prop_exp(d); + CowlClsExp *domain = decode_cls_exp(d); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_obj_prop_domain_axiom(prop, domain, annot)); + cowl_release_all(prop, domain, annot); + return axiom; +} + +static CowlObjPropRangeAxiom *decode_obj_prop_range_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlObjPropRangeAxiom *axiom = NULL; + CowlObjPropExp *prop = decode_obj_prop_exp(d); + CowlClsExp *range = decode_cls_exp(d); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_obj_prop_range_axiom(prop, range, annot)); + cowl_release_all(prop, range, annot); + return axiom; +} + +static CowlSubDataPropAxiom *decode_sub_data_prop_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlSubDataPropAxiom *axiom = NULL; + CowlDataPropExp *sub = decode_data_prop_exp(d); + CowlDataPropExp *sup = decode_data_prop_exp(d); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_sub_data_prop_axiom(sub, sup, annot)); + cowl_release_all(sub, sup, annot); + return axiom; +} + +static CowlNAryDataPropAxiom * +decode_nary_data_prop_axiom(CowlOBFDecoder *d, CowlNAryAxiomType type, bool has_annot) { + CowlNAryDataPropAxiom *axiom = NULL; + CowlVector *props = decode_vector(d, data_prop_exp_item); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_nary_data_prop_axiom(type, props, annot)); + cowl_release_all(props, annot); + return axiom; +} + +static CowlFuncDataPropAxiom *decode_func_data_prop_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlFuncDataPropAxiom *axiom = NULL; + CowlDataPropExp *prop = decode_data_prop_exp(d); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_func_data_prop_axiom(prop, annot)); + cowl_release_all(prop, annot); + return axiom; +} + +static CowlDataPropDomainAxiom *decode_data_prop_domain_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlDataPropDomainAxiom *axiom = NULL; + CowlDataPropExp *prop = decode_data_prop_exp(d); + CowlClsExp *domain = decode_cls_exp(d); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_data_prop_domain_axiom(prop, domain, annot)); + cowl_release_all(prop, domain, annot); + return axiom; +} + +static CowlObjPropAssertAxiom *decode_neg_obj_prop_assert_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlObjPropAssertAxiom *axiom = NULL; + CowlObjPropExp *prop = decode_obj_prop_exp(d); + CowlIndividual *subject = decode_individual(d); + CowlIndividual *object = decode_individual(d); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) + axiom = handle_null(d, cowl_neg_obj_prop_assert_axiom(prop, subject, object, annot)); + cowl_release_all(prop, subject, object, annot); + return axiom; +} + +static CowlDataPropAssertAxiom *decode_data_prop_assert_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlDataPropAssertAxiom *axiom = NULL; + CowlDataPropExp *prop = decode_data_prop_exp(d); + CowlIndividual *subj = decode_individual(d); + CowlLiteral *obj = decode_literal(d); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_data_prop_assert_axiom(prop, subj, obj, annot)); + cowl_release_all(prop, obj, subj, annot); + return axiom; +} + +static CowlDataPropAssertAxiom * +decode_neg_data_prop_assert_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlDataPropAssertAxiom *axiom = NULL; + CowlDataPropExp *prop = decode_data_prop_exp(d); + CowlIndividual *subj = decode_individual(d); + CowlLiteral *obj = decode_literal(d); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) + axiom = handle_null(d, cowl_neg_data_prop_assert_axiom(prop, subj, obj, annot)); + cowl_release_all(prop, subj, obj, annot); + return axiom; +} + +static CowlSubObjPropAxiom * +decode_sub_obj_prop_axiom(CowlOBFDecoder *d, bool has_u_bit, bool has_annot) { + CowlAny *sub = NULL; + CowlObjPropExp *sup = NULL; + CowlVector *annot = NULL; + CowlSubObjPropAxiom *axiom = NULL; + if (has_u_bit) { + sub = decode_vector(d, obj_prop_exp_item); + sup = decode_obj_prop_exp(d); + if (has_annot) annot = decode_vector(d, annotation_item); + axiom = cowl_sub_obj_prop_chain_axiom(sub, sup, annot); + } else { + sub = decode_obj_prop_exp(d); + sup = decode_obj_prop_exp(d); + if (has_annot) annot = decode_vector(d, annotation_item); + axiom = cowl_sub_obj_prop_axiom(sub, sup, annot); + } + if (decoder_ok(d)) axiom = handle_null(d, axiom); + cowl_release_all(sub, sup, annot); + return axiom; +} + +static CowlInvObjPropAxiom *decode_inv_obj_prop_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlInvObjPropAxiom *axiom = NULL; + CowlObjPropExp *first = decode_obj_prop_exp(d); + CowlObjPropExp *second = decode_obj_prop_exp(d); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_inv_obj_prop_axiom(first, second, annot)); + cowl_release_all(first, second, annot); + return axiom; +} + +static CowlObjPropAssertAxiom *decode_obj_prop_assert_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlObjPropAssertAxiom *axiom = NULL; + CowlObjPropExp *prop = decode_obj_prop_exp(d); + CowlIndividual *subj = decode_individual(d); + CowlIndividual *obj = decode_individual(d); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_obj_prop_assert_axiom(prop, subj, obj, annot)); + cowl_release_all(prop, subj, obj, annot); + return axiom; +} + +static CowlDataPropRangeAxiom *decode_data_prop_range_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlDataPropRangeAxiom *axiom = NULL; + CowlDataPropExp *prop = decode_data_prop_exp(d); + CowlDataRange *dr = decode_data_range(d); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_data_prop_range_axiom(prop, dr, annot)); + cowl_release_all(prop, dr, annot); + return axiom; +} + +static CowlHasKeyAxiom *decode_has_key_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlHasKeyAxiom *axiom = NULL; + CowlClsExp *ce = decode_cls_exp(d); + CowlVector *op = decode_vector(d, obj_prop_exp_item); + CowlVector *dp = decode_vector(d, data_prop_exp_item); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_has_key_axiom(ce, op, dp, annot)); + cowl_release_all(ce, op, dp, annot); + return axiom; +} + +static CowlAnnotAssertAxiom *decode_annot_assert_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlAnnotAssertAxiom *axiom = NULL; + CowlAnnotProp *prop = decode_annot_prop(d); + CowlIRIOrAnonInd *subj = decode_iri_or_anon(d); + CowlAnyAnnotValue *value = decode_annot_value(d); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_annot_assert_axiom(prop, subj, value, annot)); + cowl_release_all(prop, value, subj, annot); + return axiom; +} + +static CowlSubAnnotPropAxiom *decode_sub_annot_prop_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlSubAnnotPropAxiom *axiom = NULL; + CowlAnnotProp *sub = decode_annot_prop(d); + CowlAnnotProp *sup = decode_annot_prop(d); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_sub_annot_prop_axiom(sub, sup, annot)); + cowl_release_all(sub, sup, annot); + return axiom; +} + +static CowlAnnotPropDomainAxiom *decode_annot_prop_domain_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlAnnotPropDomainAxiom *axiom = NULL; + CowlAnnotProp *prop = decode_annot_prop(d); + CowlIRI *domain = decode_iri(d); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_annot_prop_domain_axiom(prop, domain, annot)); + cowl_release_all(prop, domain, annot); + return axiom; +} + +static CowlAnnotPropRangeAxiom *decode_annot_prop_range_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlAnnotPropRangeAxiom *axiom = NULL; + CowlAnnotProp *prop = decode_annot_prop(d); + CowlIRI *range = decode_iri(d); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_annot_prop_range_axiom(prop, range, annot)); + cowl_release_all(prop, range, annot); + return axiom; +} + +static CowlDatatypeDefAxiom *decode_datatype_def_axiom(CowlOBFDecoder *d, bool has_annot) { + CowlDatatypeDefAxiom *axiom = NULL; + CowlDatatype *dt = decode_datatype(d); + CowlDataRange *dr = decode_data_range(d); + CowlVector *annot = has_annot ? decode_vector(d, annotation_item) : NULL; + if (decoder_ok(d)) axiom = handle_null(d, cowl_datatype_def_axiom(dt, dr, annot)); + cowl_release_all(dt, dr, annot); + return axiom; +} + +static CowlAnyAxiom *decode_axiom(CowlOBFDecoder *d) { + ulib_byte header; + size_t read; + if (uistream_read(d->stream, &header, sizeof(header), &read) || read == 0) return NULL; + + ulib_byte type = header & COWL_OBF_AXIOM_TYPE_BITMASK; + ulib_byte has_annot = header & COWL_OBF_AXIOM_ANNOT_BITMASK; + ulib_byte has_u_bit = header & COWL_OBF_AXIOM_UTILITY_BITMASK; + + if (type < COWL_ET_COUNT) { + CowlEntityType entity_type = (CowlEntityType)type; + return decode_declaration_axiom(d, entity_type, has_annot); + } + + switch ((CowlAxiomType)(type - COWL_ET_COUNT)) { + case COWL_AT_DATATYPE_DEF: return decode_datatype_def_axiom(d, has_annot); + case COWL_AT_SUB_CLASS: return decode_sub_cls_axiom(d, has_annot); + case COWL_AT_EQUIV_CLASSES: return decode_nary_class_axiom(d, COWL_NAT_EQUIV, has_annot); + case COWL_AT_DISJ_CLASSES: return decode_nary_class_axiom(d, COWL_NAT_DISJ, has_annot); + case COWL_AT_DISJ_UNION: return decode_disj_union_axiom(d, has_annot); + case COWL_AT_CLASS_ASSERT: return decode_cls_assert_axiom(d, has_annot); + case COWL_AT_SAME_IND: return decode_nary_ind_axiom(d, COWL_NAT_SAME, has_annot); + case COWL_AT_DIFF_IND: return decode_nary_ind_axiom(d, COWL_NAT_DIFF, has_annot); + case COWL_AT_OBJ_PROP_ASSERT: return decode_obj_prop_assert_axiom(d, has_annot); + case COWL_AT_NEG_OBJ_PROP_ASSERT: return decode_neg_obj_prop_assert_axiom(d, has_annot); + case COWL_AT_DATA_PROP_ASSERT: return decode_data_prop_assert_axiom(d, has_annot); + case COWL_AT_NEG_DATA_PROP_ASSERT: return decode_neg_data_prop_assert_axiom(d, has_annot); + case COWL_AT_SUB_OBJ_PROP: return decode_sub_obj_prop_axiom(d, has_u_bit, has_annot); + case COWL_AT_INV_OBJ_PROP: return decode_inv_obj_prop_axiom(d, has_annot); + case COWL_AT_EQUIV_OBJ_PROP: + return decode_nary_obj_prop_axiom(d, COWL_NAT_EQUIV, has_annot); + case COWL_AT_DISJ_OBJ_PROP: return decode_nary_obj_prop_axiom(d, COWL_NAT_DISJ, has_annot); + case COWL_AT_FUNC_OBJ_PROP: return decode_obj_prop_char_axiom(d, COWL_CAT_FUNC, has_annot); + case COWL_AT_INV_FUNC_OBJ_PROP: + return decode_obj_prop_char_axiom(d, COWL_CAT_INV_FUNC, has_annot); + case COWL_AT_SYMM_OBJ_PROP: return decode_obj_prop_char_axiom(d, COWL_CAT_SYMM, has_annot); + case COWL_AT_ASYMM_OBJ_PROP: + return decode_obj_prop_char_axiom(d, COWL_CAT_ASYMM, has_annot); + case COWL_AT_TRANS_OBJ_PROP: + return decode_obj_prop_char_axiom(d, COWL_CAT_TRANS, has_annot); + case COWL_AT_REFL_OBJ_PROP: return decode_obj_prop_char_axiom(d, COWL_CAT_REFL, has_annot); + case COWL_AT_IRREFL_OBJ_PROP: + return decode_obj_prop_char_axiom(d, COWL_CAT_IRREFL, has_annot); + case COWL_AT_OBJ_PROP_DOMAIN: return decode_obj_prop_domain_axiom(d, has_annot); + case COWL_AT_OBJ_PROP_RANGE: return decode_obj_prop_range_axiom(d, has_annot); + case COWL_AT_SUB_DATA_PROP: return decode_sub_data_prop_axiom(d, has_annot); + case COWL_AT_EQUIV_DATA_PROP: + return decode_nary_data_prop_axiom(d, COWL_NAT_EQUIV, has_annot); + case COWL_AT_DISJ_DATA_PROP: + return decode_nary_data_prop_axiom(d, COWL_NAT_DISJ, has_annot); + case COWL_AT_FUNC_DATA_PROP: return decode_func_data_prop_axiom(d, has_annot); + case COWL_AT_DATA_PROP_DOMAIN: return decode_data_prop_domain_axiom(d, has_annot); + case COWL_AT_DATA_PROP_RANGE: return decode_data_prop_range_axiom(d, has_annot); + case COWL_AT_HAS_KEY: return decode_has_key_axiom(d, has_annot); + case COWL_AT_ANNOT_ASSERT: return decode_annot_assert_axiom(d, has_annot); + case COWL_AT_SUB_ANNOT_PROP: return decode_sub_annot_prop_axiom(d, has_annot); + case COWL_AT_ANNOT_PROP_DOMAIN: return decode_annot_prop_domain_axiom(d, has_annot); + case COWL_AT_ANNOT_PROP_RANGE: return decode_annot_prop_range_axiom(d, has_annot); + default: break; + } + + return NULL; +} + +static void decode_axioms(CowlOBFDecoder *d) { + CowlAxiom *axiom; + while (decoder_ok(d) && (axiom = decode_axiom(d))) { + d->state = cowl_istream_handle_axiom(d->istream, axiom); + cowl_release(axiom); + } +} + +static void register_prefix_decl(CowlOBFDecoder *d, CowlString *prefix, CowlString *ns) { + if (cowl_equals(prefix, d->default_prefix)) prefix = d->empty_prefix; + if ((d->state = cowl_obf_map_add_prefix(&d->map, ns, prefix)) == COWL_OK) { + CowlSymTable *st = cowl_istream_get_sym_table(d->istream); + d->state = cowl_sym_table_register_prefix(st, prefix, ns, false); + } +} + +static void decode_prefix_decl(CowlOBFDecoder *d) { + CowlString *prefix = decode_string(d); + CowlString *ns = decode_string_intern(d); + if (prefix && ns) register_prefix_decl(d, prefix, ns); + cowl_release_all(prefix, ns); +} + +static void decode_prefix_decls(CowlOBFDecoder *d) { + ulib_uint length; + if (read_varint(d, &length)) return; + for (ulib_uint i = 0; decoder_ok(d) && i < length; ++i) { + decode_prefix_decl(d); + } +} + +static void decode_id_decl(CowlOBFDecoder *d) { + ulib_uint type; + if (read_varint(d, &type)) return; + + CowlString *string = decode_string(d); + if (!string) return; + + CowlIRIOrAnonInd *id; + + if (type == 0) { + id = cowl_anon_ind(string); + } else { + id = cowl_iri(cowl_obf_map_get_ns(&d->map, type - 1), string); + } + + if (!id || cowl_obf_map_add_primitive_fast(&d->map, id)) { + d->state = COWL_ERR_MEM; + } + + cowl_release_all(string, id); +} + +static void decode_id_decls(CowlOBFDecoder *d) { + ulib_uint length; + if (read_varint(d, &length)) return; + for (ulib_uint i = 0; decoder_ok(d) && i < length; ++i) { + decode_id_decl(d); + } +} + +static void decode_ontology_iri(CowlOBFDecoder *d) { + CowlIRI *iri = decode_iri(d); + if (!iri) return; + d->state = cowl_istream_handle_iri(d->istream, iri); + cowl_release(iri); +} + +static void decode_version_iri(CowlOBFDecoder *d) { + CowlIRI *version = decode_iri(d); + if (!version) return; + d->state = cowl_istream_handle_version(d->istream, version); + cowl_release(version); +} + +static void decode_annotations(CowlOBFDecoder *d) { + ulib_uint length; + if (read_varint(d, &length)) return; + for (ulib_uint i = 0; decoder_ok(d) && i < length; ++i) { + CowlAnnotation *annot = decode_annotation(d); + if (!annot) return; + d->state = cowl_istream_handle_annot(d->istream, annot); + cowl_release(annot); + } +} + +static void decode_imports(CowlOBFDecoder *d) { + ulib_uint length; + if (read_varint(d, &length)) return; + for (ulib_uint i = 0; decoder_ok(d) && i < length; ++i) { + CowlIRI *import = decode_iri(d); + if (!import) return; + d->state = cowl_istream_handle_import(d->istream, import); + cowl_release(import); + } +} + +static void decode_header(CowlOBFDecoder *d) { + ulib_byte header; + if (read_bytes(d, &header, sizeof(header))) return; + + decode_prefix_decls(d); + if (decoder_error(d)) return; + + decode_id_decls(d); + if (decoder_error(d)) return; + + if (header & COWL_OBF_HEADER_IRI_BITMASK) { + decode_ontology_iri(d); + if (decoder_error(d)) return; + } + + if (header & COWL_OBF_HEADER_VERSION_BITMASK) { + decode_version_iri(d); + if (decoder_error(d)) return; + } + + if (header & COWL_OBF_HEADER_ANNOTATIONS_BITMASK) { + decode_annotations(d); + if (decoder_error(d)) return; + } + + if (header & COWL_OBF_HEADER_IMPORT_BITMASK) { + decode_imports(d); + if (decoder_error(d)) return; + } +} + +static cowl_ret cowl_obf_read_ontology(UIStream *stream, CowlIStream *istream) { + CowlOBFDecoder d = {}; + if (decoder_init(&d, stream, istream)) return COWL_ERR_MEM; + decode_header(&d); + if (decoder_ok(&d)) decode_axioms(&d); + decoder_deinit(&d); + return decoder_state(&d); +} + +CowlReader cowl_reader_obf(void) { + return (CowlReader){ + .name = "obf", + .read = cowl_obf_read_ontology, + }; +} diff --git a/src/formats/obf/writer/CMakeLists.txt b/src/formats/obf/writer/CMakeLists.txt new file mode 100644 index 0000000..2f2a889 --- /dev/null +++ b/src/formats/obf/writer/CMakeLists.txt @@ -0,0 +1,13 @@ +# Sources + +set(COWL_WRITER_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}") +file(GLOB COWL_WRITER_SOURCES CONFIGURE_DEPENDS "${COWL_WRITER_SRC_DIR}/*.c") +list(APPEND COWL_SOURCES ${COWL_WRITER_SOURCES}) + +cmake_path(GET COWL_WRITER_SRC_DIR PARENT_PATH COWL_WRITER_SRC_DIR) +file(GLOB COWL_WRITER_SOURCES CONFIGURE_DEPENDS "${COWL_WRITER_SRC_DIR}/*.c") +list(APPEND COWL_SOURCES ${COWL_WRITER_SOURCES}) + +# Propagate changes in the parent project + +set(COWL_SOURCES ${COWL_SOURCES} PARENT_SCOPE) diff --git a/src/formats/obf/writer/cowl_obf_encoder.c b/src/formats/obf/writer/cowl_obf_encoder.c new file mode 100644 index 0000000..96f38b5 --- /dev/null +++ b/src/formats/obf/writer/cowl_obf_encoder.c @@ -0,0 +1,517 @@ +/** + * @author Valerio Di Ceglie + * + * @copyright Copyright (c) 2024 SisInf Lab, Polytechnic University of Bari + * @copyright + * @copyright SPDX-License-Identifier: EPL-2.0 + * + * @file + */ + +#include "../cowl_obf_constants.h" +#include "../cowl_obf_map.h" + +#include "cowl.h" +#include "cowl_impl.h" +#include "ulib.h" +#include + +typedef struct CowlOBFEncoder { + CowlOBFMap map; + UOStream *stream; + cowl_ret state; +} CowlOBFEncoder; + +static bool for_each_primitive(void *ctx, CowlAnyPrimitive *primitive) { + CowlOBFEncoder *e = ctx; + return cowl_obf_map_add_primitive(&e->map, primitive) == COWL_OK; +} + +static cowl_ret populate_owl_map(CowlOBFEncoder *e, CowlOntology *onto) { + if (cowl_obf_map_init(&e->map)) goto err; + + CowlSymTable *st = cowl_ontology_get_sym_table(onto); + CowlTable *prefix_ns_map = cowl_sym_table_get_prefix_ns_map(st, false); + + cowl_table_foreach (prefix_ns_map, entry) { + if (cowl_obf_map_add_prefix(&e->map, *entry.val, *entry.key)) goto err; + } + + CowlIRI *iri = cowl_ontology_get_iri(onto); + if (iri && cowl_obf_map_add_primitive(&e->map, iri)) goto err; + + iri = cowl_ontology_get_version(onto); + if (iri && cowl_obf_map_add_primitive(&e->map, iri)) goto err; + + CowlIterator iter = { e, for_each_primitive }; + if (!cowl_ontology_iterate_primitives(onto, COWL_PF_ALL, &iter, false)) goto err; + if (!cowl_ontology_iterate_import_iris(onto, &iter, false)) goto err; + + return COWL_OK; + +err: + cowl_obf_map_deinit(&e->map); + return COWL_ERR_MEM; +} + +static cowl_ret encoder_init(CowlOBFEncoder *e, UOStream *stream, CowlOntology *onto) { + e->stream = stream; + e->state = COWL_OK; + return populate_owl_map(e, onto); +} + +static void encoder_deinit(CowlOBFEncoder *e) { + cowl_obf_map_deinit(&e->map); +} + +static inline bool encoder_error(CowlOBFEncoder *e) { + return e->state || e->stream->state; +} + +static inline bool encoder_ok(CowlOBFEncoder *e) { + return !encoder_error(e); +} + +static inline cowl_ret encoder_state(CowlOBFEncoder *e) { + return e->state ? e->state : cowl_ret_from_ustream(e->stream->state); +} + +static void write_bytes(CowlOBFEncoder *e, void const *data, ulib_uint length) { + uostream_write(e->stream, data, length, NULL); +} + +static void write_varint(CowlOBFEncoder *e, ulib_uint value) { + uostream_write_varint(e->stream, value, NULL); +} + +static void encode_object(CowlOBFEncoder *e, CowlAny *obj); + +static void encode_cstring(CowlOBFEncoder *e, char const *string, ulib_uint length) { + write_varint(e, length); + if (length) write_bytes(e, string, length); +} + +static void encode_ustring(CowlOBFEncoder *e, UString const *string) { + encode_cstring(e, ustring_data(*string), ustring_length(*string)); +} + +static void encode_string(CowlOBFEncoder *e, CowlString *string) { + encode_ustring(e, cowl_string_get_raw(string)); +} + +static void encode_id_offset(CowlOBFEncoder *e, CowlIRIOrAnonInd *id, ulib_uint offset) { + ulib_uint idx = cowl_obf_map_get_id_idx(&e->map, id); + if (idx == COWL_OBF_NOT_FOUND) { + // Should never happen if the map is correctly populated. + e->state = COWL_ERR; + return; + } + write_varint(e, idx + offset); +} + +static void encode_id(CowlOBFEncoder *e, CowlIRIOrAnonInd *id) { + encode_id_offset(e, id, 0); +} + +static void encode_uvec(CowlOBFEncoder *e, UVec(CowlObjectPtr) const *vec, + void (*encode)(CowlOBFEncoder *, void *)) { + write_varint(e, uvec_count(CowlObjectPtr, vec)); + uvec_foreach (CowlObjectPtr, vec, obj) { + encode(e, *obj.item); + } +} + +static void encode_vector(CowlOBFEncoder *e, CowlVector *vec) { + encode_uvec(e, cowl_vector_get_data(vec), encode_object); +} + +static void encode_datatype(CowlOBFEncoder *e, CowlDatatype *datatype, bool is_data_range) { + ulib_uint offset = is_data_range ? COWL_DRT_COUNT - 1 : 0; + encode_id_offset(e, cowl_datatype_get_iri(datatype), offset); +} + +static CowlOBFLiteralType literal_type(CowlDatatype *dt, CowlString *lang) { + if (lang) return COWL_OLT_STRING_LITERAL_LANGUAGE; + if (dt == cowl_xsd_vocab()->dt.string) return COWL_OLT_STRING_LITERAL; + return COWL_OLT_TYPED_LITERAL; +} + +static void encode_literal(CowlOBFEncoder *e, CowlLiteral *literal, ulib_byte compression) { + CowlDatatype *dt = cowl_literal_get_datatype(literal); + CowlString *value = cowl_literal_get_value(literal); + CowlString *lang = cowl_literal_get_lang(literal); + + CowlOBFLiteralType type = literal_type(dt, lang); + ulib_byte header = (compression << COWL_OBF_STRING_COMPRESSION_OFFSET) | type; + write_bytes(e, &header, sizeof(header)); + + encode_string(e, value); + switch (type) { + case COWL_OLT_TYPED_LITERAL: encode_datatype(e, dt, false); break; + case COWL_OLT_STRING_LITERAL_LANGUAGE: encode_string(e, lang); break; + default: break; + } +} + +static void encode_facet_restr(CowlOBFEncoder *e, CowlFacetRestr *restr) { + encode_id(e, cowl_facet_restr_get_facet(restr)); + encode_literal(e, cowl_facet_restr_get_value(restr), 0); +} + +static void encode_named_ind(CowlOBFEncoder *e, CowlNamedInd *ind) { + encode_id(e, cowl_named_ind_get_iri(ind)); +} + +static void encode_class(CowlOBFEncoder *e, CowlClass *class, bool is_expression) { + ulib_uint offset = is_expression ? COWL_CET_COUNT - 1 : 0; + encode_id_offset(e, cowl_class_get_iri(class), offset); +} + +static void encode_obj_prop(CowlOBFEncoder *e, CowlObjProp *prop, bool is_expression) { + ulib_uint offset = is_expression ? 1 : 0; + encode_id_offset(e, cowl_obj_prop_get_iri(prop), offset); +} + +static void encode_inv_obj_prop(CowlOBFEncoder *e, CowlAnyObjPropExp *prop) { + write_varint(e, 0); + encode_obj_prop(e, cowl_inv_obj_prop_get_prop(prop), false); +} + +static void encode_data_prop(CowlOBFEncoder *e, CowlDataProp *prop) { + encode_id(e, cowl_data_prop_get_iri(prop)); +} + +static void encode_annot_prop(CowlOBFEncoder *e, CowlAnnotProp *prop) { + encode_id(e, cowl_annot_prop_get_iri(prop)); +} + +static void encode_annot_value(CowlOBFEncoder *e, CowlAnyAnnotValue *value) { + if (cowl_annot_value_get_type(value) != COWL_AVT_LITERAL) { + encode_id_offset(e, value, 1); + return; + } + // Literal. + write_varint(e, 0); + encode_literal(e, value, 0); +} + +static void encode_annotation(CowlOBFEncoder *e, CowlAnnotation *annot) { + CowlIRI *iri = cowl_annot_prop_get_iri(cowl_annotation_get_prop(annot)); + CowlVector *annotations = cowl_annotation_get_annot(annot); + + if (annotations) { + write_varint(e, 0); + encode_id(e, iri); + } else { + encode_id_offset(e, iri, 1); + } + + encode_annot_value(e, cowl_annotation_get_value(annot)); + if (annotations) encode_vector(e, annotations); +} + +static void encode_card_restr(CowlOBFEncoder *e, CowlAny *restr) { + CowlClsExpType type = cowl_cls_exp_get_type(restr) - 1; + write_varint(e, type); + + CowlAny *opt = cowl_get_opt_field(restr); + ulib_uint card = (cowl_get_uint_field(restr) << 1) | !!opt; + write_varint(e, card); + + unsigned count; + CowlAny **fields = cowl_get_fields(restr, &count); + + for (unsigned i = 0; i < count; ++i) { + encode_object(e, fields[i]); + } + + if (opt) encode_object(e, opt); +} + +static void encode_decl_axiom(CowlOBFEncoder *e, CowlDeclAxiom *axiom) { + CowlAnyEntity *entity = cowl_decl_axiom_get_entity(axiom); + CowlVector *annot = cowl_axiom_get_annot(axiom); + + ulib_byte header = (ulib_byte)(COWL_AT_DECL + cowl_entity_get_type(entity)); + if (annot) header |= (ulib_byte)(1U << COWL_OBF_AXIOM_ANNOT_OFFSET); + + write_bytes(e, &header, sizeof(header)); + encode_id(e, cowl_entity_get_iri(entity)); + if (annot) encode_vector(e, annot); +} + +ulib_byte axiom_header(CowlObjectType type, bool annot, bool utility) { + ulib_byte header = (type - COWL_OT_FIRST_A) + COWL_ET_COUNT; + header |= utility << COWL_OBF_AXIOM_UTLITY_OFFSET; + header |= annot << COWL_OBF_AXIOM_ANNOT_OFFSET; + return header; +} + +ulib_byte cls_exp_header(CowlObjectType type) { + return (type - COWL_OT_FIRST_CE) - 1; +} + +ulib_byte data_range_header(CowlObjectType type) { + return (type - COWL_OT_FIRST_DR) - 1; +} + +static void encode_construct_body(CowlOBFEncoder *e, CowlAny *obj) { + unsigned count; + CowlAny **fields = cowl_get_fields(obj, &count); + + for (unsigned i = 0; i < count; i++) { + encode_object(e, fields[i]); + } + + CowlAny *opt = cowl_get_opt_field(obj); + if (opt) encode_object(e, opt); +} + +static void encode_axiom_default(CowlOBFEncoder *e, CowlAnyAxiom *axiom) { + ulib_byte header = axiom_header(cowl_get_type(axiom), cowl_axiom_get_annot(axiom), false); + write_bytes(e, &header, sizeof(header)); + encode_construct_body(e, axiom); +} + +static void encode_cls_exp_default(CowlOBFEncoder *e, CowlAnyClsExp *exp) { + write_varint(e, cls_exp_header(cowl_get_type(exp))); + encode_construct_body(e, exp); +} + +static void encode_data_range_default(CowlOBFEncoder *e, CowlAnyDataRange *range) { + write_varint(e, data_range_header(cowl_get_type(range))); + encode_construct_body(e, range); +} + +static void encode_datatype_def_axiom(CowlOBFEncoder *e, CowlDatatypeDefAxiom *axiom) { + CowlDatatype *dt = cowl_datatype_def_axiom_get_datatype(axiom); + CowlDataRange *dr = cowl_datatype_def_axiom_get_range(axiom); + CowlVector *annot = cowl_axiom_get_annot(axiom); + + ulib_byte header = axiom_header(COWL_OT_A_DATATYPE_DEF, annot, false); + write_bytes(e, &header, sizeof(header)); + encode_datatype(e, dt, false); + encode_object(e, dr); + if (annot) encode_vector(e, annot); +} + +static void encode_sub_obj_prop_axiom(CowlOBFEncoder *e, CowlSubObjPropAxiom *axiom) { + bool is_chain = cowl_get_type(cowl_sub_obj_prop_axiom_get_sub(axiom)) == COWL_OT_VECTOR; + ulib_byte header = axiom_header(COWL_OT_A_SUB_OBJ_PROP, cowl_axiom_get_annot(axiom), is_chain); + write_bytes(e, &header, sizeof(header)); + encode_construct_body(e, axiom); +} + +static void encode_obj_quant(CowlOBFEncoder *e, CowlObjectType type, CowlObjQuant *exp) { + write_varint(e, cls_exp_header(type)); + encode_object(e, cowl_obj_quant_get_prop(exp)); + encode_object(e, cowl_obj_quant_get_filler(exp)); +} + +static void encode_data_quant(CowlOBFEncoder *e, CowlObjectType type, CowlDataQuant *exp) { + write_varint(e, cls_exp_header(type)); + encode_object(e, cowl_data_quant_get_prop(exp)); + encode_object(e, cowl_data_quant_get_range(exp)); +} + +static void encode_datatype_restr(CowlOBFEncoder *e, CowlDatatypeRestr *restr) { + write_varint(e, data_range_header(COWL_OT_DR_DATATYPE_RESTR)); + encode_datatype(e, cowl_datatype_restr_get_datatype(restr), false); + encode_vector(e, cowl_datatype_restr_get_restrictions(restr)); +} + +static void encode_annot_assert_axiom(CowlOBFEncoder *e, CowlAnnotAssertAxiom *axiom) { + CowlVector *annot = cowl_axiom_get_annot(axiom); + ulib_byte header = axiom_header(COWL_OT_A_ANNOT_ASSERT, annot, false); + write_bytes(e, &header, sizeof(header)); + + encode_annot_prop(e, cowl_annot_assert_axiom_get_prop(axiom)); + encode_id(e, cowl_annot_assert_axiom_get_subject(axiom)); + encode_annot_value(e, cowl_annot_assert_axiom_get_value(axiom)); + if (annot) encode_vector(e, annot); +} + +static void encode_disj_union_axiom(CowlOBFEncoder *e, CowlDisjUnionAxiom *axiom) { + CowlVector *annot = cowl_axiom_get_annot(axiom); + ulib_byte header = axiom_header(COWL_OT_A_DISJ_UNION, annot, false); + write_bytes(e, &header, sizeof(header)); + + encode_class(e, cowl_disj_union_axiom_get_class(axiom), false); + encode_vector(e, cowl_disj_union_axiom_get_disjoints(axiom)); + if (annot) encode_vector(e, annot); +} + +static void encode_object(CowlOBFEncoder *e, CowlAny *obj) { + CowlObjectType type = cowl_get_type(obj); + switch (type) { + case COWL_OT_STRING: encode_string(e, obj); break; + case COWL_OT_VECTOR: encode_vector(e, obj); break; + case COWL_OT_IRI: encode_id(e, obj); break; + case COWL_OT_LITERAL: encode_literal(e, obj, 0); break; + case COWL_OT_FACET_RESTR: encode_facet_restr(e, obj); break; + case COWL_OT_ANNOTATION: encode_annotation(e, obj); break; + case COWL_OT_ANNOT_PROP: encode_annot_prop(e, obj); break; + case COWL_OT_CE_CLASS: encode_class(e, obj, true); break; + case COWL_OT_DPE_DATA_PROP: encode_data_prop(e, obj); break; + case COWL_OT_DR_DATATYPE: encode_datatype(e, obj, true); break; + case COWL_OT_DR_DATATYPE_RESTR: encode_datatype_restr(e, obj); break; + case COWL_OT_I_NAMED: encode_named_ind(e, obj); break; + case COWL_OT_OPE_OBJ_PROP: encode_obj_prop(e, obj, true); break; + case COWL_OT_OPE_INV_OBJ_PROP: encode_inv_obj_prop(e, obj); break; + case COWL_OT_I_ANONYMOUS: encode_id(e, obj); break; + case COWL_OT_A_DECL: encode_decl_axiom(e, obj); break; + case COWL_OT_A_DATATYPE_DEF: encode_datatype_def_axiom(e, obj); break; + case COWL_OT_A_DISJ_UNION: encode_disj_union_axiom(e, obj); break; + case COWL_OT_A_SUB_OBJ_PROP: encode_sub_obj_prop_axiom(e, obj); break; + case COWL_OT_A_ANNOT_ASSERT: encode_annot_assert_axiom(e, obj); break; + case COWL_OT_CE_OBJ_SOME: + case COWL_OT_CE_OBJ_ALL: encode_obj_quant(e, type, obj); break; + case COWL_OT_CE_DATA_SOME: + case COWL_OT_CE_DATA_ALL: encode_data_quant(e, type, obj); break; + case COWL_OT_CE_OBJ_MIN_CARD: + case COWL_OT_CE_OBJ_MAX_CARD: + case COWL_OT_CE_OBJ_EXACT_CARD: + case COWL_OT_CE_DATA_MIN_CARD: + case COWL_OT_CE_DATA_MAX_CARD: + case COWL_OT_CE_DATA_EXACT_CARD: encode_card_restr(e, obj); break; + default: { + if (type >= COWL_OT_FIRST_A && type <= COWL_OT_LAST_A) { + encode_axiom_default(e, obj); + } else if (type >= COWL_OT_FIRST_CE && type <= COWL_OT_LAST_CE) { + encode_cls_exp_default(e, obj); + } else if (type >= COWL_OT_FIRST_DR && type <= COWL_OT_LAST_DR) { + encode_data_range_default(e, obj); + } + } + } +} + +static ulib_byte document_header(bool iri, bool version, bool annotations, bool imports) { + ulib_byte header = COWL_OBF_FORMAT_VERSION << COWL_OBF_HEADER_FORMAT_OFFSET; + header |= iri << COWL_OBF_HEADER_IRI_OFFSET; + header |= version << COWL_OBF_HEADER_VERSION_OFFSET; + header |= annotations << COWL_OBF_HEADER_ANNOTATION_OFFSET; + header |= imports << COWL_OBF_HEADER_IMPORT_OFFSET; + return header; +} + +static void encode_document_header(CowlOBFEncoder *e, CowlOntology *onto) { + bool has_iri = !!cowl_ontology_get_iri(onto); + bool has_version = !!cowl_ontology_get_version(onto); + bool has_annot = !!cowl_vector_count(cowl_ontology_get_annot(onto)); + bool has_imports = !!cowl_ontology_imports_count(onto, false); + ulib_byte header = document_header(has_iri, has_version, has_annot, has_imports); + write_bytes(e, &header, sizeof(header)); +} + +static void encode_prefix(CowlOBFEncoder *e, CowlString *prefix) { + if (!prefix) { + encode_ustring(e, &ustring_empty); + } else if (cowl_string_get_length(prefix)) { + encode_string(e, prefix); + } else { + encode_cstring(e, ":", 1); + } +} + +static void encode_prefix_decl(CowlOBFEncoder *e, CowlString *prefix, CowlString *ns) { + encode_prefix(e, prefix); + encode_string(e, ns); +} + +static void encode_prefix_decls(CowlOBFEncoder *e) { + UVec(CowlObjectPtr) prefix_view = uvec_view_from( + CowlObjectPtr, cowl_obf_map_get_prefixes(&e->map), COWL_OBF_STANDARD_PREFIX_COUNT); + UVec(CowlObjectPtr) ns_view = uvec_view_from( + CowlObjectPtr, cowl_obf_map_get_namespaces(&e->map), COWL_OBF_STANDARD_PREFIX_COUNT); + write_varint(e, uvec_count(CowlObjectPtr, &prefix_view)); + uvec_foreach (CowlObjectPtr, &prefix_view, prefix) { + encode_prefix_decl(e, *prefix.item, uvec_get(CowlObjectPtr, &ns_view, prefix.i)); + } +} + +static void encode_id_decl(CowlOBFEncoder *e, CowlIRIOrAnonInd *id) { + ulib_uint type; + CowlString *body; + + if (cowl_get_type(id) == COWL_OT_I_ANONYMOUS) { + type = 0; + body = cowl_anon_ind_get_id(id); + } else { + CowlIRI *iri = cowl_get_iri(id); + type = cowl_obf_map_get_ns_idx(&e->map, cowl_iri_get_ns(iri)); + if (type == COWL_OBF_NOT_FOUND) { + // Should never happen if the map is correctly populated. + e->state = COWL_ERR; + return; + } + type++; + body = cowl_iri_get_rem(iri); + } + + write_varint(e, type); + encode_string(e, body); +} + +static void encode_id_decls(CowlOBFEncoder *e) { + encode_uvec(e, cowl_obf_map_get_ids(&e->map), encode_id_decl); +} + +static void encode_ontology_iri_version(CowlOBFEncoder *e, CowlOntology *onto) { + CowlIRI *iri = cowl_ontology_get_iri(onto); + if (iri) encode_id(e, iri); + + iri = cowl_ontology_get_version(onto); + if (iri) encode_id(e, iri); +} + +static void encode_annotations(CowlOBFEncoder *e, CowlOntology *onto) { + CowlVector *annot = cowl_ontology_get_annot(onto); + if (cowl_vector_count(annot)) encode_vector(e, annot); +} + +static bool encode_import(void *ctx, CowlAny *iri) { + CowlOBFEncoder *e = ctx; + encode_id(e, iri); + return encoder_ok(e); +} + +static void encode_imports(CowlOBFEncoder *e, CowlOntology *onto) { + ulib_uint count = cowl_ontology_imports_count(onto, false); + if (!count) return; + write_varint(e, count); + CowlIterator iter = { e, encode_import }; + cowl_ontology_iterate_import_iris(onto, &iter, false); +} + +static bool encode_axiom(void *ctx, CowlAnyAxiom *axiom) { + CowlOBFEncoder *e = ctx; + encode_object(e, axiom); + return encoder_ok(e); +} + +static void encode_axioms(CowlOBFEncoder *e, CowlOntology *onto) { + CowlIterator iter = { e, encode_axiom }; + cowl_ontology_iterate_axioms(onto, &iter, false); +} + +static cowl_ret cowl_obf_write_ontology(UOStream *stream, CowlOntology *onto) { + CowlOBFEncoder e = {}; + if (encoder_init(&e, stream, onto)) return COWL_ERR_MEM; + encode_document_header(&e, onto); + encode_prefix_decls(&e); + encode_id_decls(&e); + encode_ontology_iri_version(&e, onto); + encode_annotations(&e, onto); + encode_imports(&e, onto); + encode_axioms(&e, onto); + encoder_deinit(&e); + return encoder_state(&e); +} + +CowlWriter cowl_writer_obf(void) { + return (CowlWriter){ + .name = "obf", + .write_ontology = cowl_obf_write_ontology, + }; +} diff --git a/test/tests/cowl_manager_tests.c b/test/tests/cowl_manager_tests.c index 2595fda..cc63437 100644 --- a/test/tests/cowl_manager_tests.c +++ b/test/tests/cowl_manager_tests.c @@ -183,6 +183,9 @@ bool cowl_test_manager_write_ontology(void) { } const formats[] = { #if COWL_READER_FUNCTIONAL && COWL_WRITER_FUNCTIONAL { cowl_reader_functional(), cowl_writer_functional() }, +#endif +#if COWL_READER_OBF && COWL_WRITER_OBF + { cowl_reader_obf(), cowl_writer_obf() }, #endif };