From 4d8db6c2a469183a88f03761c90bb8f866f2e691 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 13 May 2021 15:27:03 +0200 Subject: [PATCH] 0.14.0 --- CHANGELOG.md | 16 +++++++++-- CMakeLists.txt | 56 +++++++++++++++++++++--------------- README.md | 2 +- download.sh | 2 +- doxygen/Changelog.md | 18 ++++++++++-- doxygen/Doxyfile | 2 +- include/objectbox-dart.h | 34 ++++++++++++++++++++++ include/objectbox-sync.h | 11 ++++++-- include/objectbox-sync.hpp | 4 +-- include/objectbox.h | 29 +++++++++++++++++-- include/objectbox.hpp | 58 ++++++++++++++++++++++++++------------ 11 files changed, 176 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2631447..00077f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,24 @@ ObjectBox C and C++ API Changelog ================================= +0.14.0 (2021-05-13) +------------------- + +* change `obx_query_prop_count()` to respect case-sensitivity setting when counting distinct strings +* change `OBXSyncCredentialsType` values to start at 1 +* add `obx_query_find_first()` to get a first object matching the query +* add `obx_query_find_unique()` to get the only object matching the query +* add `obx_async_put_object4()` accepting a put_mode +* updated ARMv7hf toolchain, now requires GLIBC v2.28 (e.g. Raspbian 10, Ubuntu 20.04) +* semi-internal Dart APIs: add query streaming and finalizers + 0.13.0 (2021-03-16) ------------------- + * add Sync binary library variants for all supported platforms * add MacOS universal binary library, supporting Intel x64 and Apple Silicon arm64 -* split Sync symbols out of objectbox.h/pp into objectbox-sync.h/pp -* add Sync server-time getter, listener and local-to-server diff info - to access server time info on clients +* split Sync symbols out of objectbox.h/pp into objectbox-sync.h/pp +* add Sync server-time getter, listener and local-to-server diff info - to access server time info on clients * add Sync heartbeat interval configuration and an option to send one immediately * semi-internal: update Dart/Flutter SDK to v2.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 4eb0ea8..788934e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,14 +9,29 @@ if (${CMAKE_VERSION} VERSION_LESS "3.11.0") endif () link_directories("${CMAKE_CURRENT_SOURCE_DIR}/lib") else () - project(objectbox-download) + function(defineObjectBoxLibForURL VARIANT DL_URL) + include(FetchContent) + project(objectbox${VARIANT}-download) + FetchContent_Declare(${PROJECT_NAME} URL ${DL_URL}) - if (DEFINED ENV{OBJECTBOX_ARTIFACT_URL}) - set(DL_URL $ENV{OBJECTBOX_ARTIFACT_URL}) - message(STATUS "Using pre-compiled ObjectBox library from the OBJECTBOX_ARTIFACT_URL environment variable: ${DL_URL}") - else () + FetchContent_Populate(${PROJECT_NAME}) + set(DL_DIR "${${PROJECT_NAME}_SOURCE_DIR}") + message(STATUS "Pre-compiled ObjectBox library is saved in ${DL_DIR}") + + project(objectbox${VARIANT}) + add_library(${PROJECT_NAME} SHARED IMPORTED GLOBAL) + set(objectbox_include_dirs ${DL_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/external/) + set_target_properties( + ${PROJECT_NAME} PROPERTIES + IMPORTED_LOCATION ${DL_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}objectbox${CMAKE_SHARED_LIBRARY_SUFFIX} + IMPORTED_IMPLIB ${DL_DIR}/lib/${CMAKE_IMPORT_LIBRARY_PREFIX}objectbox${CMAKE_IMPORT_LIBRARY_SUFFIX} + INTERFACE_INCLUDE_DIRECTORIES "${objectbox_include_dirs}" + ) + endfunction() + + function(defineObjectBoxLib VARIANT) # Configuration updated for each release - set(DL_VERSION 0.13.0) + set(DL_VERSION 0.14.0) # Platform detection and other setup set(DL_URL https://github.com/objectbox/objectbox-c/releases/download) @@ -45,25 +60,20 @@ else () endif () string(TOLOWER ${DL_PLATFORM} DL_PLATFORM) - set(DL_URL ${DL_URL}/v${DL_VERSION}/objectbox-${DL_PLATFORM}.${DL_EXTENSION}) - message(STATUS "Using pre-compiled ObjectBox library v${DL_VERSION} for ${DL_PLATFORM}: ${DL_URL}") - endif () + set(DL_URL ${DL_URL}/v${DL_VERSION}/objectbox${VARIANT}-${DL_PLATFORM}.${DL_EXTENSION}) + message(STATUS "Using pre-compiled ObjectBox${VARIANT} library v${DL_VERSION} for ${DL_PLATFORM}: ${DL_URL}") - include(FetchContent) - FetchContent_Declare(${PROJECT_NAME} URL ${DL_URL}) + defineObjectBoxLibForURL("${VARIANT}" "${DL_URL}") + endfunction() - FetchContent_Populate(${PROJECT_NAME}) - message(STATUS "Pre-compiled ObjectBox library is saved in ${objectbox-download_SOURCE_DIR}") - - project(objectbox) - add_library(${PROJECT_NAME} SHARED IMPORTED GLOBAL) - set(objectbox_include_dirs ${objectbox-download_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/external/) - set_target_properties( - ${PROJECT_NAME} PROPERTIES - IMPORTED_LOCATION ${objectbox-download_SOURCE_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}objectbox${CMAKE_SHARED_LIBRARY_SUFFIX} - IMPORTED_IMPLIB ${objectbox-download_SOURCE_DIR}/lib/${CMAKE_IMPORT_LIBRARY_PREFIX}objectbox${CMAKE_IMPORT_LIBRARY_SUFFIX} - INTERFACE_INCLUDE_DIRECTORIES "${objectbox_include_dirs}" - ) + if (DEFINED ENV{OBJECTBOX_ARTIFACT_URL}) + set(DL_URL $ENV{OBJECTBOX_ARTIFACT_URL}) + message(STATUS "Using pre-compiled ObjectBox library from the OBJECTBOX_ARTIFACT_URL environment variable: ${DL_URL}") + defineObjectBoxLibForURL("" "${DL_URL}") + else () + defineObjectBoxLib("") + defineObjectBoxLib("-sync") + endif () endif () add_subdirectory(src-test) diff --git a/README.md b/README.md index 86dfc2f..b2c686b 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ box.put({.text = "Buy milk"}); See [ObjectBox C and C++ docs](https://cpp.objectbox.io/) for API details. -**Latest version: 0.13.0** (2021-03-16). +**Latest version: 0.14.0** (2021-05-13). See [changelog](CHANGELOG.md) for more details. Feature Highlights diff --git a/download.sh b/download.sh index 7f2ebb7..ec184a3 100755 --- a/download.sh +++ b/download.sh @@ -44,7 +44,7 @@ tty -s || quiet=true # Note: optional arguments like "--quiet" shifts argument positions in the case block above -version=${1:-0.13.0} +version=${1:-0.14.0} os=${2:-$(uname)} arch=${3:-$(uname -m)} echo "Base config: OS ${os} and architecture ${arch}" diff --git a/doxygen/Changelog.md b/doxygen/Changelog.md index 95bc16b..ecf21ef 100644 --- a/doxygen/Changelog.md +++ b/doxygen/Changelog.md @@ -3,10 +3,24 @@ ObjectBox C and C++ API Changelog ================================= +0.14.0 (2021-05-13) +------------------- + +* change `obx_query_prop_count()` to respect case-sensitivity setting when counting distinct strings +* change `OBXSyncCredentialsType` values to start at 1 +* add `obx_query_find_first()` to get a first object matching the query +* add `obx_query_find_unique()` to get the only object matching the query +* add `obx_async_put_object4()` accepting a put_mode +* updated ARMv7hf toolchain, now requires GLIBC v2.28 (e.g. Raspbian 10, Ubuntu 20.04) +* semi-internal Dart APIs: add query streaming and finalizers + 0.13.0 (2021-03-16) ------------------- -* split Sync symbols out of objectbox.h/pp into objectbox-sync.h/pp -* add Sync server-time getter, listener and local-to-server diff info - to access server time info on clients + +* add Sync binary library variants for all supported platforms +* add MacOS universal binary library, supporting Intel x64 and Apple Silicon arm64 +* split Sync symbols out of objectbox.h/pp into objectbox-sync.h/pp +* add Sync server-time getter, listener and local-to-server diff info - to access server time info on clients * add Sync heartbeat interval configuration and an option to send one immediately * semi-internal: update Dart/Flutter SDK to v2.0 diff --git a/doxygen/Doxyfile b/doxygen/Doxyfile index ca7348c..7ae2dfd 100644 --- a/doxygen/Doxyfile +++ b/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "ObjectBox C and C++ API" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "0.13.0" +PROJECT_NUMBER = "0.14.0" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/include/objectbox-dart.h b/include/objectbox-dart.h index c72c07a..291f57b 100644 --- a/include/objectbox-dart.h +++ b/include/objectbox-dart.h @@ -19,6 +19,7 @@ #include +#include "dart_api.h" #include "objectbox-sync.h" #ifdef __cplusplus @@ -71,6 +72,39 @@ OBX_dart_sync_listener* obx_dart_sync_listener_change(OBX_sync* sync, int64_t na /// @see obx_sync_listener_server_time() OBX_dart_sync_listener* obx_dart_sync_listener_server_time(OBX_sync* sync, int64_t native_port); +/// @see obx_async_put_object() +obx_id obx_dart_async_put_object(OBX_async* async, int64_t native_port, void* data, size_t size, OBXPutMode mode); + +struct OBX_dart_stream; +typedef struct OBX_dart_stream OBX_dart_stream; + +obx_err obx_dart_stream_close(OBX_dart_stream* stream); + +/// @see obx_dart_stream_query_find +OBX_dart_stream* obx_dart_query_find(OBX_query* query, int64_t native_port); + +OBX_dart_stream* obx_dart_query_find_ptr(OBX_query* query, int64_t native_port); + +struct OBX_dart_finalizer; +typedef struct OBX_dart_finalizer OBX_dart_finalizer; + +/// A function to clean up native resources. Must be a c-function (non-throwing). The returned error is ignored. +/// e.g. obx_query_close(), obx_store_close(), ... +typedef obx_err obx_dart_closer(void* native_object); + +/// Attaches a finalizer (destructor) to be called when the given object is garbage-collected. +/// @param dart_object marks the object owning the native pointer +/// @param native_object is the native pointer to be freed +/// @param closer is the function that frees native_object +/// @param native_object_size is an allocated size estimate - can be used by a the Dart garbage collector to prioritize +/// @return a finalizer freed automatically when the GC finalizer runs (or manually by obx_dart_detach_finalizer()) +/// @return NULL if the finalizer couldn't be attached, in which case the caller is responsible for running the closer +OBX_dart_finalizer* obx_dart_attach_finalizer(Dart_Handle dart_object, obx_dart_closer* closer, void* native_object, + size_t native_object_size); + +/// Detach the finalizer preliminarily, without executing its "closer" +obx_err obx_dart_detach_finalizer(OBX_dart_finalizer* finalizer, Dart_Handle dart_object); + #ifdef __cplusplus } #endif diff --git a/include/objectbox-sync.h b/include/objectbox-sync.h index 64ee557..5f492e7 100644 --- a/include/objectbox-sync.h +++ b/include/objectbox-sync.h @@ -33,6 +33,11 @@ #include "objectbox.h" +#if defined(static_assert) || defined(__cplusplus) +static_assert(OBX_VERSION_MAJOR == 0 && OBX_VERSION_MINOR == 14 && OBX_VERSION_PATCH == 0, + "Versions of objectbox.h and objectbox-sync.h files do not match, please update"); +#endif + #ifdef __cplusplus extern "C" { #endif @@ -51,9 +56,9 @@ struct OBX_sync; typedef struct OBX_sync OBX_sync; typedef enum { - OBXSyncCredentialsType_NONE = 0, - OBXSyncCredentialsType_SHARED_SECRET = 1, - OBXSyncCredentialsType_GOOGLE_AUTH = 2, + OBXSyncCredentialsType_NONE = 1, + OBXSyncCredentialsType_SHARED_SECRET = 2, + OBXSyncCredentialsType_GOOGLE_AUTH = 3, } OBXSyncCredentialsType; // TODO sync prefix diff --git a/include/objectbox-sync.hpp b/include/objectbox-sync.hpp index f9ce230..a7ff556 100644 --- a/include/objectbox-sync.hpp +++ b/include/objectbox-sync.hpp @@ -19,8 +19,8 @@ #include "objectbox-sync.h" #include "objectbox.hpp" -static_assert(OBX_VERSION_MAJOR == 0 && OBX_VERSION_MINOR == 13 && OBX_VERSION_PATCH == 0, - "Versions of objectbox.h and objectbox-sync.hpp files must be exactly the same"); +static_assert(OBX_VERSION_MAJOR == 0 && OBX_VERSION_MINOR == 14 && OBX_VERSION_PATCH == 0, + "Versions of objectbox.h and objectbox-sync.hpp files do not match, please update"); static_assert(sizeof(obx_id) == sizeof(OBX_id_array::ids[0]), "Can't directly link OBX_id_array.ids to std::vector::data()"); diff --git a/include/objectbox.h b/include/objectbox.h index 740fb34..af01e20 100644 --- a/include/objectbox.h +++ b/include/objectbox.h @@ -46,7 +46,7 @@ extern "C" { /// When using ObjectBox as a dynamic library, you should verify that a compatible version was linked using /// obx_version() or obx_version_is_at_least(). #define OBX_VERSION_MAJOR 0 -#define OBX_VERSION_MINOR 13 +#define OBX_VERSION_MINOR 14 #define OBX_VERSION_PATCH 0 // values >= 100 are reserved for dev releases leading to the next minor/major increase //---------------------------------------------- @@ -996,12 +996,12 @@ obx_err obx_box_put5(OBX_box* box, obx_id id, const void* data, size_t size, OBX /// FB ID slot must be present in the given data; new entities must have an ID value of zero or OBX_ID_NEW. /// @param data writable data buffer, which may be updated for the ID -/// @returns 0 on error +/// @returns id if the object could be put, or 0 in case of an error obx_id obx_box_put_object(OBX_box* box, void* data, size_t size); /// FB ID slot must be present in the given data; new entities must have an ID value of zero or OBX_ID_NEW /// @param data writable data buffer, which may be updated for the ID -/// @returns 0 on error, e.g. the entity was not put according to OBXPutMode +/// @returns id if the object, or 0 in case of an error, e.g. the entity was not put according to OBXPutMode obx_id obx_box_put_object4(OBX_box* box, void* data, size_t size, OBXPutMode mode); /// Put all given objects in the database in a single transaction. If any of the individual objects failed to put, @@ -1124,11 +1124,18 @@ obx_err obx_async_update(OBX_async* async, obx_id id, const void* data, size_t s /// Reserve an ID, which is returned immediately for future reference, and put asynchronously. /// Note: of course, it can NOT be guaranteed that the entity will actually be put successfully in the DB. /// @param data the given bytes are mutated to update the contained ID data. +/// @returns id of the new object, 0 on error obx_id obx_async_put_object(OBX_async* async, void* data, size_t size); +/// FB ID slot must be present in the given data; new entities must have an ID value of zero or OBX_ID_NEW +/// @param data writable data buffer, which may be updated for the ID +/// @returns id of the new object, 0 on error, e.g. the entity can't be put according to OBXPutMode +obx_id obx_async_put_object4(OBX_async* async, void* data, size_t size, OBXPutMode mode); + /// Reserve an ID, which is returned immediately for future reference, and insert asynchronously. /// Note: of course, it can NOT be guaranteed that the entity will actually be inserted successfully in the DB. /// @param data the given bytes are mutated to update the contained ID data. +/// @returns id of the new object, 0 on error obx_id obx_async_insert_object(OBX_async* async, void* data, size_t size); /// Remove asynchronously. @@ -1384,6 +1391,22 @@ obx_err obx_query_limit(OBX_query* query, uint64_t limit); /// Find entities matching the query. NOTE: the returned data is only valid as long the transaction is active! OBX_bytes_array* obx_query_find(OBX_query* query); +/// Find the first object matching the query. +/// @returns OBX_NOT_FOUND if no object matches. +/// The exposed data comes directly from the OS to allow zero-copy access, which limits the data lifetime: +/// @warning Currently ignores offset, taking the the first matching element. +/// @attention The exposed data is only valid as long as the (top) transaction is still active and no write +/// operation (e.g. put/remove) was executed. Accessing data after this is undefined behavior. +obx_err obx_query_find_first(OBX_query* query, const void** data, size_t* size); + +/// Find the only object matching the query. +/// @returns OBX_NOT_FOUND if no object matches, an error if there are multiple objects matching the query. +/// The exposed data comes directly from the OS to allow zero-copy access, which limits the data lifetime: +/// @warning Currently ignores offset and limit, considering all matching elements. +/// @attention The exposed data is only valid as long as the (top) transaction is still active and no write +/// operation (e.g. put/remove) was executed. Accessing data after this is undefined behavior. +obx_err obx_query_find_unique(OBX_query* query, const void** data, size_t* size); + /// Walk over matching objects using the given data visitor obx_err obx_query_visit(OBX_query* query, obx_data_visitor* visitor, void* user_data); diff --git a/include/objectbox.hpp b/include/objectbox.hpp index d87bfae..3bbaa94 100644 --- a/include/objectbox.hpp +++ b/include/objectbox.hpp @@ -25,8 +25,8 @@ #include #endif -static_assert(OBX_VERSION_MAJOR == 0 && OBX_VERSION_MINOR == 13 && OBX_VERSION_PATCH == 0, - "Versions of objectbox.h and objectbox.hpp files must be exactly the same"); +static_assert(OBX_VERSION_MAJOR == 0 && OBX_VERSION_MINOR == 14 && OBX_VERSION_PATCH == 0, + "Versions of objectbox.h and objectbox.hpp files do not match, please update"); static_assert(sizeof(obx_id) == sizeof(OBX_id_array::ids[0]), "Can't directly link OBX_id_array.ids to std::vector::data()"); @@ -1396,6 +1396,30 @@ class Query { return std::move(visitor.items); } + /// Find the first object matching the query or nullptr if none matches. + std::unique_ptr findFirst() { + return findSingle>(obx_query_find_first, EntityT::_OBX_MetaInfo::newFromFlatBuffer); + } + + /// Find the only object matching the query. + /// @throws if there are multiple objects matching the query + std::unique_ptr findUnique() { + return findSingle>(obx_query_find_unique, EntityT::_OBX_MetaInfo::newFromFlatBuffer); + } + +#ifdef __cpp_lib_optional + /// Find the first object matching the query or nullptr if none matches. + std::optional findFirstOptional() { + return findSingle>(obx_query_find_first, EntityT::_OBX_MetaInfo::fromFlatBuffer); + } + + /// Find the only object matching the query. + /// @throws if there are multiple objects matching the query + std::optional findUniqueOptional() { + return findSingle>(obx_query_find_unique, EntityT::_OBX_MetaInfo::fromFlatBuffer); + } +#endif + /// Returns IDs of all matching objects. std::vector findIds() { return idVectorOrThrow(obx_query_find_ids(cQuery_)); } @@ -1524,6 +1548,19 @@ class Query { const std::vector& value) { return setParameter(property, value.data(), value.size()); } + +private: + template + RET findSingle(obx_err nativeFn(OBX_query*, const void**, size_t*), T fromFlatBuffer(const void*, size_t)) { + OBJECTBOX_VERIFY_STATE(cQuery_ != nullptr); + Transaction tx = store_.txRead(); + const void* data; + size_t size; + obx_err err = nativeFn(cQuery_, &data, &size); + if (err == OBX_NOT_FOUND) return RET(); + checkErrOrThrow(err); + return fromFlatBuffer(data, size); + } }; template @@ -2023,22 +2060,7 @@ class AsyncBox { /// @return the reserved ID which will be used for the object if the asynchronous insert succeeds. obx_id put(const EntityT& object, OBXPutMode mode = OBXPutMode_PUT) { EntityBinding::toFlatBuffer(fbb, object); - obx_id id; - switch (mode) { - case OBXPutMode_PUT: - id = obx_async_put_object(cPtr(), fbb.GetBufferPointer(), fbb.GetSize()); - break; - case OBXPutMode_INSERT: - id = obx_async_insert_object(cPtr(), fbb.GetBufferPointer(), fbb.GetSize()); - break; - case OBXPutMode_UPDATE: - // TODO this looks like we should adjust the the C API... - throw std::invalid_argument( - "UPDATE mode is currently not supported in C++ API AsyncBox, because C-API requires providing an " - "ID. Use C-API directly."); - default: - throw std::invalid_argument("unknown mode"); - } + obx_id id = obx_async_put_object4(cPtr(), fbb.GetBufferPointer(), fbb.GetSize(), mode); fbbCleanAfterUse(); if (id == 0) throwLastError(); return id;