diff --git a/extensions/json/IJsonManager.h b/extensions/json/IJsonManager.h index e06e9d80d..ed4090e69 100755 --- a/extensions/json/IJsonManager.h +++ b/extensions/json/IJsonManager.h @@ -1,5 +1,5 @@ -#ifndef _INCLUDE_SM_JSON_IJSONMANAGER_H_ -#define _INCLUDE_SM_JSON_IJSONMANAGER_H_ +#ifndef _INCLUDE_IJSONMANAGER_H_ +#define _INCLUDE_IJSONMANAGER_H_ #include #include @@ -15,7 +15,7 @@ class JsonArrIter; class JsonObjIter; #define SMINTERFACE_JSONMANAGER_NAME "IJsonManager" -#define SMINTERFACE_JSONMANAGER_VERSION 1 +#define SMINTERFACE_JSONMANAGER_VERSION 2 #define JSON_PACK_ERROR_SIZE 256 #define JSON_ERROR_BUFFER_SIZE 256 #define JSON_INT64_BUFFER_SIZE 32 @@ -99,10 +99,69 @@ public: * * @note The out_size parameter returns the size including null terminator * @note Use GetSerializedSize() with the same write_flg to determine buffer size + * @warning This method performs multiple memory allocations and copies, resulting in poor performance. + * For better performance, use WriteToStringPtr() instead, which avoids intermediate buffers */ virtual bool WriteToString(JsonValue* handle, char* buffer, size_t buffer_size, uint32_t write_flg = 0, size_t* out_size = nullptr) = 0; + /** + * Write JSON to string and return allocated string (performance-optimized version) + * @param handle JSON value + * @param write_flg Write flags (YYJSON_WRITE_FLAG values, default: 0) + * @param out_size Pointer to receive actual size written (including null terminator) optional + * @return Allocated string pointer on success, nullptr on error. Caller must free() the returned pointer + * + * @note This is the recommended method for serialization as it avoids intermediate buffer allocations + */ + virtual char* WriteToStringPtr(JsonValue* handle, uint32_t write_flg = 0, size_t* out_size = nullptr) = 0; + + /** + * Apply JSON Patch (RFC 6902) and return a new JSON value + * @param target Target JSON value + * @param patch JSON Patch document + * @param result_mutable true to return mutable result, false for immutable + * @param error Error buffer (optional) + * @param error_size Error buffer size + * @return Patched JSON value on success, nullptr on failure + */ + virtual JsonValue* ApplyJsonPatch(JsonValue* target, JsonValue* patch, bool result_mutable, + char* error = nullptr, size_t error_size = 0) = 0; + + /** + * Apply JSON Patch in place (target must be mutable) + * @param target Target JSON value (mutable document) + * @param patch JSON Patch document + * @param error Error buffer (optional) + * @param error_size Error buffer size + * @return true on success, false on failure + */ + virtual bool JsonPatchInPlace(JsonValue* target, JsonValue* patch, + char* error = nullptr, size_t error_size = 0) = 0; + + /** + * Apply JSON Merge Patch (RFC 7396) and return a new JSON value + * @param target Target JSON value + * @param patch JSON Merge Patch document + * @param result_mutable true to return mutable result, false for immutable + * @param error Error buffer (optional) + * @param error_size Error buffer size + * @return Patched JSON value on success, nullptr on failure + */ + virtual JsonValue* ApplyMergePatch(JsonValue* target, JsonValue* patch, bool result_mutable, + char* error = nullptr, size_t error_size = 0) = 0; + + /** + * Apply JSON Merge Patch in place (target must be mutable) + * @param target Target JSON value (mutable document) + * @param patch JSON Merge Patch document + * @param error Error buffer (optional) + * @param error_size Error buffer size + * @return true on success, false on failure + */ + virtual bool MergePatchInPlace(JsonValue* target, JsonValue* patch, + char* error = nullptr, size_t error_size = 0) = 0; + /** * Write JSON to file * @param handle JSON value @@ -316,6 +375,14 @@ public: */ virtual size_t GetReadSize(JsonValue* handle) = 0; + /** + * Get the reference count of the document + * @param handle JSON value + * @return Reference count of the document (number of JsonValue objects sharing this document) + * @note Returns 0 if handle is null or has no document + */ + virtual size_t GetRefCount(JsonValue* handle) = 0; + /** * Create an empty mutable JSON object * @return New mutable JSON object or nullptr on failure @@ -964,10 +1031,10 @@ public: * Remove range of elements (mutable only) * @param handle JSON array * @param start_index Start index (inclusive) - * @param end_index End index (exclusive) + * @param count Number of elements to remove * @return true on success */ - virtual bool ArrayRemoveRange(JsonValue* handle, size_t start_index, size_t end_index) = 0; + virtual bool ArrayRemoveRange(JsonValue* handle, size_t start_index, size_t count) = 0; /** * Remove all elements (mutable only) @@ -1001,20 +1068,12 @@ public: virtual int ArrayIndexOfInt(JsonValue* handle, int search_value) = 0; /** - * Find index of 64-bit integer value + * Find index of 64-bit integer value (auto-detects signed/unsigned) * @param handle JSON array - * @param search_value 64-bit integer value to search for + * @param search_value 64-bit integer value to search for (std::variant) * @return Index of first match, or -1 if not found */ - virtual int ArrayIndexOfInt64(JsonValue* handle, int64_t search_value) = 0; - - /** - * Find index of 64-bit unsigned integer value - * @param handle JSON array - * @param search_value 64-bit unsigned integer value to search for - * @return Index of first match, or -1 if not found - */ - virtual int ArrayIndexOfUint64(JsonValue* handle, uint64_t search_value) = 0; + virtual int ArrayIndexOfInt64(JsonValue* handle, std::variant search_value) = 0; /** * Find index of float value @@ -1538,6 +1597,8 @@ public: * Initialize an array iterator (same as ArrIterWith but returns pointer) * @param handle JSON array value * @return New array iterator or nullptr on error + * @note Caller must release the iterator using ReleaseArrIter() once finished + * @note Iterators are single-pass; once ArrIterNext() returns nullptr, create a new iterator or call ArrIterReset() to iterate again */ virtual JsonArrIter* ArrIterInit(JsonValue* handle) = 0; @@ -1545,9 +1606,18 @@ public: * Create an array iterator with an array * @param handle JSON array value * @return New array iterator or nullptr on error + * @note Caller must release the iterator using ReleaseArrIter() once finished + * @note Iterators are single-pass; once ArrIterNext() returns nullptr, create a new iterator or call ArrIterReset() to iterate again */ virtual JsonArrIter* ArrIterWith(JsonValue* handle) = 0; + /** + * Reset an array iterator to the beginning + * @param iter Array iterator + * @return true on success, false if iterator is invalid or reset failed + */ + virtual bool ArrIterReset(JsonArrIter* iter) = 0; + /** * Get next element from array iterator * @param iter Array iterator @@ -1580,6 +1650,8 @@ public: * Initialize an object iterator (same as ObjIterWith but returns pointer) * @param handle JSON object value * @return New object iterator or nullptr on error + * @note Caller must release the iterator using ReleaseObjIter() once finished + * @note Iterators are single-pass; once ObjIterNext() returns nullptr, create a new iterator or call ObjIterReset() to iterate again */ virtual JsonObjIter* ObjIterInit(JsonValue* handle) = 0; @@ -1587,9 +1659,18 @@ public: * Create an object iterator with an object * @param handle JSON object value * @return New object iterator or nullptr on error + * @note Caller must release the iterator using ReleaseObjIter() once finished + * @note Iterators are single-pass; once ObjIterNext() returns nullptr, create a new iterator or call ObjIterReset() to iterate again */ virtual JsonObjIter* ObjIterWith(JsonValue* handle) = 0; + /** + * Reset an object iterator to the beginning + * @param iter Object iterator + * @return true on success, false if iterator is invalid or reset failed + */ + virtual bool ObjIterReset(JsonObjIter* iter) = 0; + /** * Get next key from object iterator * @param iter Object iterator @@ -1707,16 +1788,18 @@ public: * @param flt true to use single-precision (float), false to use double-precision (double) * @return true on success, false if handle is not a floating-point number * @note Only works on floating-point numbers (not integers) + * @note This affects how the number is serialized in all write operations */ virtual bool SetFpToFloat(JsonValue* handle, bool flt) = 0; /** * Set floating-point number's output format to fixed-point notation * @param handle JSON floating-point number value - * @param prec Precision (1-15), similar to ECMAScript Number.prototype.toFixed(prec) but with trailing zeros removed + * @param prec Precision (1-15), similar to ECMAScript `Number.prototype.toFixed(prec)` but with trailing zeros removed * @return true on success, false if handle is not a floating-point number or prec is out of range * @note Only works on floating-point numbers (not integers) * @note This will produce shorter output but may lose some precision + * @note This affects how the number is serialized in all write operations */ virtual bool SetFpToFixed(JsonValue* handle, int prec) = 0; @@ -1794,4 +1877,4 @@ public: char* error = nullptr, size_t error_size = 0) = 0; }; -#endif // _INCLUDE_SM_JSON_IJSONMANAGER_H_ \ No newline at end of file +#endif // _INCLUDE_IJSONMANAGER_H_ \ No newline at end of file diff --git a/extensions/json/JsonManager.cpp b/extensions/json/JsonManager.cpp index 3103c0bf5..28675497f 100755 --- a/extensions/json/JsonManager.cpp +++ b/extensions/json/JsonManager.cpp @@ -35,20 +35,70 @@ std::unique_ptr JsonManager::CreateWrapper() { return std::make_unique(); } -std::shared_ptr JsonManager::WrapDocument(yyjson_mut_doc* doc) { - return std::shared_ptr(doc, [](yyjson_mut_doc*){}); +RefPtr JsonManager::WrapDocument(yyjson_mut_doc* doc) { + if (!doc) { + return RefPtr(); + } + return make_ref(doc); } -std::shared_ptr JsonManager::CopyDocument(yyjson_doc* doc) { +RefPtr JsonManager::CopyDocument(yyjson_doc* doc) { return WrapDocument(yyjson_doc_mut_copy(doc, nullptr)); } -std::shared_ptr JsonManager::CreateDocument() { +RefPtr JsonManager::CreateDocument() { return WrapDocument(yyjson_mut_doc_new(nullptr)); } -std::shared_ptr JsonManager::WrapImmutableDocument(yyjson_doc* doc) { - return std::shared_ptr(doc, [](yyjson_doc*){}); +RefPtr JsonManager::WrapImmutableDocument(yyjson_doc* doc) { + if (!doc) { + return RefPtr(); + } + return make_ref(doc); +} + +RefPtr JsonManager::CloneValueToMutable(JsonValue* value) { + if (!value) { + return RefPtr(); + } + + if (value->IsMutable()) { + yyjson_mut_doc* dup = yyjson_mut_doc_mut_copy(value->m_pDocument_mut->get(), nullptr); + return WrapDocument(dup); + } + + if (!value->m_pDocument) { + return RefPtr(); + } + + return CopyDocument(value->m_pDocument->get()); +} + +static yyjson_mut_val* CopyValueIntoDoc(JsonValue* value, yyjson_mut_doc* doc, char* error, size_t error_size) { + if (!value || !doc) { + SetErrorSafe(error, error_size, "Invalid JSON value or document"); + return nullptr; + } + + yyjson_mut_val* copy = nullptr; + if (value->IsMutable()) { + if (!value->m_pVal_mut) { + SetErrorSafe(error, error_size, "Mutable JSON value has no root"); + return nullptr; + } + copy = yyjson_mut_val_mut_copy(doc, value->m_pVal_mut); + } else { + if (!value->m_pVal) { + SetErrorSafe(error, error_size, "Immutable JSON value has no root"); + return nullptr; + } + copy = yyjson_val_mut_copy(doc, value->m_pVal); + } + + if (!copy) { + SetErrorSafe(error, error_size, "Failed to copy JSON value"); + } + return copy; } JsonManager::JsonManager(): m_randomGenerator(m_randomDevice()) {} @@ -97,11 +147,29 @@ JsonValue* JsonManager::ParseJSON(const char* json_str, bool is_file, bool is_mu if (is_mutable) { pJSONValue->m_pDocument_mut = CopyDocument(idoc); - pJSONValue->m_pVal_mut = yyjson_mut_doc_get_root(pJSONValue->m_pDocument_mut.get()); yyjson_doc_free(idoc); + if (!pJSONValue->m_pDocument_mut) { + SetErrorSafe(error, error_size, "Failed to create mutable JSON document"); + return nullptr; + } + pJSONValue->m_pVal_mut = yyjson_mut_doc_get_root(pJSONValue->m_pDocument_mut->get()); + if (!pJSONValue->m_pVal_mut) { + SetErrorSafe(error, error_size, "Mutable JSON document has no root value"); + return nullptr; + } } else { pJSONValue->m_pDocument = WrapImmutableDocument(idoc); + if (!pJSONValue->m_pDocument) { + yyjson_doc_free(idoc); + SetErrorSafe(error, error_size, "Failed to create immutable JSON document"); + return nullptr; + } pJSONValue->m_pVal = yyjson_doc_get_root(idoc); + if (!pJSONValue->m_pVal) { + yyjson_doc_free(idoc); + SetErrorSafe(error, error_size, "Immutable JSON document has no root value"); + return nullptr; + } } return pJSONValue.release(); @@ -114,7 +182,37 @@ bool JsonManager::WriteToString(JsonValue* handle, char* buffer, size_t buffer_s return false; } - size_t json_size; + size_t written; + + if (handle->IsMutable()) { + written = yyjson_mut_val_write_buf(buffer, buffer_size, handle->m_pVal_mut, write_flg, nullptr); + } else { + written = yyjson_val_write_buf(buffer, buffer_size, handle->m_pVal, write_flg, nullptr); + } + + if (written == 0) { + return false; + } + + if (written + 1 > buffer_size) { + return false; + } + + buffer[written] = '\0'; + + if (out_size) { + *out_size = written + 1; + } + return true; +} + +char* JsonManager::WriteToStringPtr(JsonValue* handle, yyjson_write_flag write_flg, size_t* out_size) +{ + if (!handle) { + return nullptr; + } + + size_t json_size = 0; char* json_str; if (handle->IsMutable()) { @@ -123,24 +221,206 @@ bool JsonManager::WriteToString(JsonValue* handle, char* buffer, size_t buffer_s json_str = yyjson_val_write(handle->m_pVal, write_flg, &json_size); } - if (!json_str) { + if (json_str && out_size) { + *out_size = json_size + 1; + } + + return json_str; +} + +JsonValue* JsonManager::ApplyJsonPatch(JsonValue* target, JsonValue* patch, bool result_mutable, + char* error, size_t error_size) +{ + if (!target || !patch) { + SetErrorSafe(error, error_size, "Target or patch JSON value is null"); + return nullptr; + } + + auto docRef = CloneValueToMutable(target); + if (!docRef) { + SetErrorSafe(error, error_size, "Failed to clone target JSON value"); + return nullptr; + } + + yyjson_mut_doc* doc = docRef->get(); + yyjson_mut_val* root = yyjson_mut_doc_get_root(doc); + if (!root) { + SetErrorSafe(error, error_size, "Target JSON has no root value"); + return nullptr; + } + + yyjson_mut_val* patchCopy = CopyValueIntoDoc(patch, doc, error, error_size); + if (!patchCopy) { + return nullptr; + } + + yyjson_patch_err patch_err = {0}; + yyjson_mut_val* resultRoot = yyjson_mut_patch(doc, root, patchCopy, &patch_err); + if (!resultRoot) { + SetErrorSafe(error, error_size, "JSON patch failed (code %u, op index %zu, message: %s)", + patch_err.code, patch_err.idx, patch_err); + return nullptr; + } + + yyjson_mut_doc_set_root(doc, resultRoot); + + if (result_mutable) { + auto wrapper = CreateWrapper(); + wrapper->m_pDocument_mut = docRef; + wrapper->m_pVal_mut = yyjson_mut_doc_get_root(doc); + docRef.reset(); + return wrapper.release(); + } + + yyjson_doc* imutDoc = yyjson_mut_doc_imut_copy(doc, nullptr); + if (!imutDoc) { + SetErrorSafe(error, error_size, "Failed to convert patched JSON to immutable document"); + return nullptr; + } + + auto wrapper = CreateWrapper(); + wrapper->m_pDocument = WrapImmutableDocument(imutDoc); + if (!wrapper->m_pDocument) { + yyjson_doc_free(imutDoc); + SetErrorSafe(error, error_size, "Failed to wrap immutable JSON document"); + return nullptr; + } + wrapper->m_pVal = yyjson_doc_get_root(imutDoc); + return wrapper.release(); +} + +bool JsonManager::JsonPatchInPlace(JsonValue* target, JsonValue* patch, + char* error, size_t error_size) +{ + if (!target || !patch) { + SetErrorSafe(error, error_size, "Target or patch JSON value is null"); return false; } - size_t needed_size = json_size + 1; - if (needed_size > buffer_size) { - free(json_str); + if (!target->IsMutable()) { + SetErrorSafe(error, error_size, "Target JSON must be mutable for in-place JSON Patch"); return false; } - memcpy(buffer, json_str, json_size); - buffer[json_size] = '\0'; - free(json_str); + yyjson_mut_doc* doc = target->m_pDocument_mut->get(); + yyjson_mut_val* root = target->m_pVal_mut; - if (out_size) { - *out_size = needed_size; + if (!doc || !root) { + SetErrorSafe(error, error_size, "Target JSON has no root value"); + return false; } + yyjson_mut_val* patchCopy = CopyValueIntoDoc(patch, doc, error, error_size); + if (!patchCopy) { + return false; + } + + yyjson_patch_err patch_err = {0}; + yyjson_mut_val* resultRoot = yyjson_mut_patch(doc, root, patchCopy, &patch_err); + if (!resultRoot) { + SetErrorSafe(error, error_size, "JSON patch failed (code %u, op index %zu, message: %s)", + patch_err.code, patch_err.idx, patch_err); + return false; + } + + yyjson_mut_doc_set_root(doc, resultRoot); + target->m_pVal_mut = yyjson_mut_doc_get_root(doc); + return true; +} + +JsonValue* JsonManager::ApplyMergePatch(JsonValue* target, JsonValue* patch, bool result_mutable, + char* error, size_t error_size) +{ + if (!target || !patch) { + SetErrorSafe(error, error_size, "Target or patch JSON value is null"); + return nullptr; + } + + auto docRef = CloneValueToMutable(target); + if (!docRef) { + SetErrorSafe(error, error_size, "Failed to clone target JSON value"); + return nullptr; + } + + yyjson_mut_doc* doc = docRef->get(); + yyjson_mut_val* root = yyjson_mut_doc_get_root(doc); + if (!root) { + SetErrorSafe(error, error_size, "Target JSON has no root value"); + return nullptr; + } + + yyjson_mut_val* patchCopy = CopyValueIntoDoc(patch, doc, error, error_size); + if (!patchCopy) { + return nullptr; + } + + yyjson_mut_val* resultRoot = yyjson_mut_merge_patch(doc, root, patchCopy); + if (!resultRoot) { + SetErrorSafe(error, error_size, "Failed to apply JSON Merge Patch"); + return nullptr; + } + + yyjson_mut_doc_set_root(doc, resultRoot); + + if (result_mutable) { + auto wrapper = CreateWrapper(); + wrapper->m_pDocument_mut = docRef; + wrapper->m_pVal_mut = yyjson_mut_doc_get_root(doc); + docRef.reset(); + return wrapper.release(); + } + + yyjson_doc* imutDoc = yyjson_mut_doc_imut_copy(doc, nullptr); + if (!imutDoc) { + SetErrorSafe(error, error_size, "Failed to convert patched JSON to immutable document"); + return nullptr; + } + + auto wrapper = CreateWrapper(); + wrapper->m_pDocument = WrapImmutableDocument(imutDoc); + if (!wrapper->m_pDocument) { + yyjson_doc_free(imutDoc); + SetErrorSafe(error, error_size, "Failed to wrap immutable JSON document"); + return nullptr; + } + wrapper->m_pVal = yyjson_doc_get_root(imutDoc); + return wrapper.release(); +} + +bool JsonManager::MergePatchInPlace(JsonValue* target, JsonValue* patch, + char* error, size_t error_size) +{ + if (!target || !patch) { + SetErrorSafe(error, error_size, "Target or patch JSON value is null"); + return false; + } + + if (!target->IsMutable()) { + SetErrorSafe(error, error_size, "Target JSON must be mutable for in-place merge patch"); + return false; + } + + yyjson_mut_doc* doc = target->m_pDocument_mut->get(); + yyjson_mut_val* root = target->m_pVal_mut; + + if (!doc || !root) { + SetErrorSafe(error, error_size, "Target JSON has no root value"); + return false; + } + + yyjson_mut_val* patchCopy = CopyValueIntoDoc(patch, doc, error, error_size); + if (!patchCopy) { + return false; + } + + yyjson_mut_val* resultRoot = yyjson_mut_merge_patch(doc, root, patchCopy); + if (!resultRoot) { + SetErrorSafe(error, error_size, "Failed to apply JSON Merge Patch in place"); + return false; + } + + yyjson_mut_doc_set_root(doc, resultRoot); + target->m_pVal_mut = yyjson_mut_doc_get_root(doc); return true; } @@ -161,9 +441,9 @@ bool JsonManager::WriteToFile(JsonValue* handle, const char* path, yyjson_write_ bool is_success; if (handle->IsMutable()) { - is_success = yyjson_mut_write_file(realpath, handle->m_pDocument_mut.get(), write_flg, nullptr, &writeError); + is_success = yyjson_mut_write_file(realpath, handle->m_pDocument_mut->get(), write_flg, nullptr, &writeError); } else { - is_success = yyjson_write_file(realpath, handle->m_pDocument.get(), write_flg, nullptr, &writeError); + is_success = yyjson_write_file(realpath, handle->m_pDocument->get(), write_flg, nullptr, &writeError); } if (writeError.code && error && error_size > 0) { @@ -184,15 +464,15 @@ bool JsonManager::Equals(JsonValue* handle1, JsonValue* handle2) } if (!handle1->IsMutable() && !handle2->IsMutable()) { - auto doc1_mut = CopyDocument(handle1->m_pDocument.get()); - auto doc2_mut = CopyDocument(handle2->m_pDocument.get()); + auto doc1_mut = CopyDocument(handle1->m_pDocument->get()); + auto doc2_mut = CopyDocument(handle2->m_pDocument->get()); if (!doc1_mut || !doc2_mut) { return false; } - yyjson_mut_val* val1_mut = yyjson_mut_doc_get_root(doc1_mut.get()); - yyjson_mut_val* val2_mut = yyjson_mut_doc_get_root(doc2_mut.get()); + yyjson_mut_val* val1_mut = yyjson_mut_doc_get_root(doc1_mut->get()); + yyjson_mut_val* val2_mut = yyjson_mut_doc_get_root(doc2_mut->get()); if (!val1_mut || !val2_mut) { return false; @@ -204,12 +484,12 @@ bool JsonManager::Equals(JsonValue* handle1, JsonValue* handle2) JsonValue* immutable = handle1->IsMutable() ? handle2 : handle1; JsonValue* mutable_doc = handle1->IsMutable() ? handle1 : handle2; - auto doc_mut = CopyDocument(immutable->m_pDocument.get()); + auto doc_mut = CopyDocument(immutable->m_pDocument->get()); if (!doc_mut) { return false; } - yyjson_mut_val* val_mut = yyjson_mut_doc_get_root(doc_mut.get()); + yyjson_mut_val* val_mut = yyjson_mut_doc_get_root(doc_mut->get()); if (!val_mut) { return false; } @@ -240,19 +520,22 @@ JsonValue* JsonManager::DeepCopy(JsonValue* targetDoc, JsonValue* sourceValue) if (targetDoc->IsMutable()) { pJSONValue->m_pDocument_mut = CreateDocument(); + if (!pJSONValue->m_pDocument_mut) { + return nullptr; + } - yyjson_mut_val* val_copy = nullptr; + yyjson_mut_val* val_copy; if (sourceValue->IsMutable()) { - val_copy = yyjson_mut_val_mut_copy(pJSONValue->m_pDocument_mut.get(), sourceValue->m_pVal_mut); + val_copy = yyjson_mut_val_mut_copy(pJSONValue->m_pDocument_mut->get(), sourceValue->m_pVal_mut); } else { - val_copy = yyjson_val_mut_copy(pJSONValue->m_pDocument_mut.get(), sourceValue->m_pVal); + val_copy = yyjson_val_mut_copy(pJSONValue->m_pDocument_mut->get(), sourceValue->m_pVal); } if (!val_copy) { return nullptr; } - yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut.get(), val_copy); + yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut->get(), val_copy); pJSONValue->m_pVal_mut = val_copy; } else { yyjson_mut_doc* temp_doc = yyjson_mut_doc_new(nullptr); @@ -260,7 +543,7 @@ JsonValue* JsonManager::DeepCopy(JsonValue* targetDoc, JsonValue* sourceValue) return nullptr; } - yyjson_mut_val* temp_val = nullptr; + yyjson_mut_val* temp_val; if (sourceValue->IsMutable()) { temp_val = yyjson_mut_val_mut_copy(temp_doc, sourceValue->m_pVal_mut); } else { @@ -331,8 +614,14 @@ JsonValue* JsonManager::ToMutable(JsonValue* handle) } auto pJSONValue = CreateWrapper(); - pJSONValue->m_pDocument_mut = CopyDocument(handle->m_pDocument.get()); - pJSONValue->m_pVal_mut = yyjson_mut_doc_get_root(pJSONValue->m_pDocument_mut.get()); + pJSONValue->m_pDocument_mut = CopyDocument(handle->m_pDocument->get()); + if (!pJSONValue->m_pDocument_mut) { + return nullptr; + } + pJSONValue->m_pVal_mut = yyjson_mut_doc_get_root(pJSONValue->m_pDocument_mut->get()); + if (!pJSONValue->m_pVal_mut) { + return nullptr; + } return pJSONValue.release(); } @@ -344,9 +633,16 @@ JsonValue* JsonManager::ToImmutable(JsonValue* handle) } auto pJSONValue = CreateWrapper(); - yyjson_doc* mdoc = yyjson_mut_doc_imut_copy(handle->m_pDocument_mut.get(), nullptr); + yyjson_doc* mdoc = yyjson_mut_doc_imut_copy(handle->m_pDocument_mut->get(), nullptr); + if (!mdoc) { + return nullptr; + } pJSONValue->m_pDocument = WrapImmutableDocument(mdoc); - pJSONValue->m_pVal = yyjson_doc_get_root(pJSONValue->m_pDocument.get()); + if (!pJSONValue->m_pDocument) { + yyjson_doc_free(mdoc); + return nullptr; + } + pJSONValue->m_pVal = yyjson_doc_get_root(pJSONValue->m_pDocument->get()); return pJSONValue.release(); } @@ -577,12 +873,30 @@ size_t JsonManager::GetReadSize(JsonValue* handle) return handle->m_readSize + 1; } +size_t JsonManager::GetRefCount(JsonValue* handle) +{ + if (!handle) { + return 0; + } + return handle->GetDocumentRefCount(); +} + JsonValue* JsonManager::ObjectInit() { auto pJSONValue = CreateWrapper(); pJSONValue->m_pDocument_mut = CreateDocument(); - pJSONValue->m_pVal_mut = yyjson_mut_obj(pJSONValue->m_pDocument_mut.get()); - yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut.get(), pJSONValue->m_pVal_mut); + + if (!pJSONValue->m_pDocument_mut) { + return nullptr; + } + + pJSONValue->m_pVal_mut = yyjson_mut_obj(pJSONValue->m_pDocument_mut->get()); + + if (!pJSONValue->m_pVal_mut) { + return nullptr; + } + + yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut->get(), pJSONValue->m_pVal_mut); return pJSONValue.release(); } @@ -596,8 +910,12 @@ JsonValue* JsonManager::ObjectInitWithStrings(const char** pairs, size_t count) auto pJSONValue = CreateWrapper(); pJSONValue->m_pDocument_mut = CreateDocument(); + if (!pJSONValue->m_pDocument_mut) { + return nullptr; + } + pJSONValue->m_pVal_mut = yyjson_mut_obj_with_kv( - pJSONValue->m_pDocument_mut.get(), + pJSONValue->m_pDocument_mut->get(), pairs, count ); @@ -606,6 +924,8 @@ JsonValue* JsonManager::ObjectInitWithStrings(const char** pairs, size_t count) return nullptr; } + yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut->get(), pJSONValue->m_pVal_mut); + return pJSONValue.release(); } @@ -792,7 +1112,7 @@ JsonValue* JsonManager::ObjectGetValueAt(JsonValue* handle, size_t index) yyjson_obj_iter iter; yyjson_obj_iter_init(handle->m_pVal, &iter); - yyjson_val* key = nullptr; + yyjson_val* key; for (size_t i = 0; i <= index; i++) { key = yyjson_obj_iter_next(&iter); if (!key) { @@ -1000,14 +1320,14 @@ bool JsonManager::ObjectHasKey(JsonValue* handle, const char* key, bool use_poin if (handle->IsMutable()) { if (use_pointer) { - return yyjson_mut_doc_ptr_get(handle->m_pDocument_mut.get(), key) != nullptr; + return yyjson_mut_doc_ptr_get(handle->m_pDocument_mut->get(), key) != nullptr; } else { yyjson_mut_obj_iter iter = yyjson_mut_obj_iter_with(handle->m_pVal_mut); return yyjson_mut_obj_iter_get(&iter, key) != nullptr; } } else { if (use_pointer) { - return yyjson_doc_ptr_get(handle->m_pDocument.get(), key) != nullptr; + return yyjson_doc_ptr_get(handle->m_pDocument->get(), key) != nullptr; } else { yyjson_obj_iter iter = yyjson_obj_iter_with(handle->m_pVal); return yyjson_obj_iter_get(&iter, key) != nullptr; @@ -1029,7 +1349,7 @@ bool JsonManager::ObjectRenameKey(JsonValue* handle, const char* old_key, const return false; } - return yyjson_mut_obj_rename_key(handle->m_pDocument_mut.get(), handle->m_pVal_mut, old_key, new_key); + return yyjson_mut_obj_rename_key(handle->m_pDocument_mut->get(), handle->m_pVal_mut, old_key, new_key); } bool JsonManager::ObjectSet(JsonValue* handle, const char* key, JsonValue* value) @@ -1038,18 +1358,18 @@ bool JsonManager::ObjectSet(JsonValue* handle, const char* key, JsonValue* value return false; } - yyjson_mut_val* val_copy = nullptr; + yyjson_mut_val* val_copy; if (value->IsMutable()) { - val_copy = yyjson_mut_val_mut_copy(handle->m_pDocument_mut.get(), value->m_pVal_mut); + val_copy = yyjson_mut_val_mut_copy(handle->m_pDocument_mut->get(), value->m_pVal_mut); } else { - val_copy = yyjson_val_mut_copy(handle->m_pDocument_mut.get(), value->m_pVal); + val_copy = yyjson_val_mut_copy(handle->m_pDocument_mut->get(), value->m_pVal); } if (!val_copy) { return false; } - return yyjson_mut_obj_put(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut.get(), key), val_copy); + return yyjson_mut_obj_put(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut->get(), key), val_copy); } bool JsonManager::ObjectSetBool(JsonValue* handle, const char* key, bool value) @@ -1058,7 +1378,7 @@ bool JsonManager::ObjectSetBool(JsonValue* handle, const char* key, bool value) return false; } - return yyjson_mut_obj_put(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut.get(), key), yyjson_mut_bool(handle->m_pDocument_mut.get(), value)); + return yyjson_mut_obj_put(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut->get(), key), yyjson_mut_bool(handle->m_pDocument_mut->get(), value)); } bool JsonManager::ObjectSetFloat(JsonValue* handle, const char* key, double value) @@ -1067,7 +1387,7 @@ bool JsonManager::ObjectSetFloat(JsonValue* handle, const char* key, double valu return false; } - return yyjson_mut_obj_put(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut.get(), key), yyjson_mut_real(handle->m_pDocument_mut.get(), value)); + return yyjson_mut_obj_put(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut->get(), key), yyjson_mut_real(handle->m_pDocument_mut->get(), value)); } bool JsonManager::ObjectSetInt(JsonValue* handle, const char* key, int value) @@ -1076,7 +1396,7 @@ bool JsonManager::ObjectSetInt(JsonValue* handle, const char* key, int value) return false; } - return yyjson_mut_obj_put(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut.get(), key), yyjson_mut_int(handle->m_pDocument_mut.get(), value)); + return yyjson_mut_obj_put(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut->get(), key), yyjson_mut_int(handle->m_pDocument_mut->get(), value)); } bool JsonManager::ObjectSetInt64(JsonValue* handle, const char* key, std::variant value) @@ -1088,9 +1408,9 @@ bool JsonManager::ObjectSetInt64(JsonValue* handle, const char* key, std::varian return std::visit([&](auto&& val) -> bool { using T = std::decay_t; if constexpr (std::is_same_v) { - return yyjson_mut_obj_put(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut.get(), key), yyjson_mut_sint(handle->m_pDocument_mut.get(), val)); + return yyjson_mut_obj_put(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut->get(), key), yyjson_mut_sint(handle->m_pDocument_mut->get(), val)); } else if constexpr (std::is_same_v) { - return yyjson_mut_obj_put(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut.get(), key), yyjson_mut_uint(handle->m_pDocument_mut.get(), val)); + return yyjson_mut_obj_put(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut->get(), key), yyjson_mut_uint(handle->m_pDocument_mut->get(), val)); } return false; }, value); @@ -1102,7 +1422,7 @@ bool JsonManager::ObjectSetNull(JsonValue* handle, const char* key) return false; } - return yyjson_mut_obj_put(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut.get(), key), yyjson_mut_null(handle->m_pDocument_mut.get())); + return yyjson_mut_obj_put(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut->get(), key), yyjson_mut_null(handle->m_pDocument_mut->get())); } bool JsonManager::ObjectSetString(JsonValue* handle, const char* key, const char* value) @@ -1111,7 +1431,7 @@ bool JsonManager::ObjectSetString(JsonValue* handle, const char* key, const char return false; } - return yyjson_mut_obj_put(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut.get(), key), yyjson_mut_strcpy(handle->m_pDocument_mut.get(), value)); + return yyjson_mut_obj_put(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut->get(), key), yyjson_mut_strcpy(handle->m_pDocument_mut->get(), value)); } bool JsonManager::ObjectRemove(JsonValue* handle, const char* key) @@ -1195,8 +1515,18 @@ JsonValue* JsonManager::ArrayInit() { auto pJSONValue = CreateWrapper(); pJSONValue->m_pDocument_mut = CreateDocument(); - pJSONValue->m_pVal_mut = yyjson_mut_arr(pJSONValue->m_pDocument_mut.get()); - yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut.get(), pJSONValue->m_pVal_mut); + + if (!pJSONValue->m_pDocument_mut) { + return nullptr; + } + + pJSONValue->m_pVal_mut = yyjson_mut_arr(pJSONValue->m_pDocument_mut->get()); + + if (!pJSONValue->m_pVal_mut) { + return nullptr; + } + + yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut->get(), pJSONValue->m_pVal_mut); return pJSONValue.release(); } @@ -1210,8 +1540,12 @@ JsonValue* JsonManager::ArrayInitWithStrings(const char** strings, size_t count) auto pJSONValue = CreateWrapper(); pJSONValue->m_pDocument_mut = CreateDocument(); + if (!pJSONValue->m_pDocument_mut) { + return nullptr; + } + pJSONValue->m_pVal_mut = yyjson_mut_arr_with_strcpy( - pJSONValue->m_pDocument_mut.get(), + pJSONValue->m_pDocument_mut->get(), strings, count ); @@ -1220,6 +1554,8 @@ JsonValue* JsonManager::ArrayInitWithStrings(const char** strings, size_t count) return nullptr; } + yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut->get(), pJSONValue->m_pVal_mut); + return pJSONValue.release(); } @@ -1232,8 +1568,12 @@ JsonValue* JsonManager::ArrayInitWithInt32(const int32_t* values, size_t count) auto pJSONValue = CreateWrapper(); pJSONValue->m_pDocument_mut = CreateDocument(); + if (!pJSONValue->m_pDocument_mut) { + return nullptr; + } + pJSONValue->m_pVal_mut = yyjson_mut_arr_with_sint32( - pJSONValue->m_pDocument_mut.get(), + pJSONValue->m_pDocument_mut->get(), values, count ); @@ -1242,6 +1582,8 @@ JsonValue* JsonManager::ArrayInitWithInt32(const int32_t* values, size_t count) return nullptr; } + yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut->get(), pJSONValue->m_pVal_mut); + return pJSONValue.release(); } @@ -1257,15 +1599,35 @@ JsonValue* JsonManager::ArrayInitWithInt64(const char** values, size_t count, ch if (count == 0) { auto pJSONValue = CreateWrapper(); pJSONValue->m_pDocument_mut = CreateDocument(); - pJSONValue->m_pVal_mut = yyjson_mut_arr(pJSONValue->m_pDocument_mut.get()); + + if (!pJSONValue->m_pDocument_mut) { + return nullptr; + } + + pJSONValue->m_pVal_mut = yyjson_mut_arr(pJSONValue->m_pDocument_mut->get()); + + if (!pJSONValue->m_pVal_mut) { + return nullptr; + } + + yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut->get(), pJSONValue->m_pVal_mut); + return pJSONValue.release(); } auto pJSONValue = CreateWrapper(); pJSONValue->m_pDocument_mut = CreateDocument(); - auto doc = pJSONValue->m_pDocument_mut.get(); + if (!pJSONValue->m_pDocument_mut) { + if (error && error_size > 0) { + SetErrorSafe(error, error_size, "Failed to create document"); + } + return nullptr; + } + + auto doc = pJSONValue->m_pDocument_mut->get(); pJSONValue->m_pVal_mut = yyjson_mut_arr(doc); + if (!pJSONValue->m_pVal_mut) { if (error && error_size > 0) { SetErrorSafe(error, error_size, "Failed to create array"); @@ -1279,14 +1641,14 @@ JsonValue* JsonManager::ArrayInitWithInt64(const char** values, size_t count, ch return nullptr; } - yyjson_mut_val* val = nullptr; - std::visit([&](auto&& arg) { + yyjson_mut_val* val = std::visit([&](auto&& arg) -> yyjson_mut_val* { using T = std::decay_t; if constexpr (std::is_same_v) { - val = yyjson_mut_sint(doc, arg); + return yyjson_mut_sint(doc, arg); } else if constexpr (std::is_same_v) { - val = yyjson_mut_uint(doc, arg); + return yyjson_mut_uint(doc, arg); } + return nullptr; }, variant_value); if (!val || !yyjson_mut_arr_append(pJSONValue->m_pVal_mut, val)) { @@ -1297,6 +1659,8 @@ JsonValue* JsonManager::ArrayInitWithInt64(const char** values, size_t count, ch } } + yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut->get(), pJSONValue->m_pVal_mut); + return pJSONValue.release(); } @@ -1309,8 +1673,12 @@ JsonValue* JsonManager::ArrayInitWithBool(const bool* values, size_t count) auto pJSONValue = CreateWrapper(); pJSONValue->m_pDocument_mut = CreateDocument(); + if (!pJSONValue->m_pDocument_mut) { + return nullptr; + } + pJSONValue->m_pVal_mut = yyjson_mut_arr_with_bool( - pJSONValue->m_pDocument_mut.get(), + pJSONValue->m_pDocument_mut->get(), values, count ); @@ -1319,6 +1687,8 @@ JsonValue* JsonManager::ArrayInitWithBool(const bool* values, size_t count) return nullptr; } + yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut->get(), pJSONValue->m_pVal_mut); + return pJSONValue.release(); } @@ -1331,8 +1701,12 @@ JsonValue* JsonManager::ArrayInitWithFloat(const double* values, size_t count) auto pJSONValue = CreateWrapper(); pJSONValue->m_pDocument_mut = CreateDocument(); + if (!pJSONValue->m_pDocument_mut) { + return nullptr; + } + pJSONValue->m_pVal_mut = yyjson_mut_arr_with_real( - pJSONValue->m_pDocument_mut.get(), + pJSONValue->m_pDocument_mut->get(), values, count ); @@ -1341,6 +1715,8 @@ JsonValue* JsonManager::ArrayInitWithFloat(const double* values, size_t count) return nullptr; } + yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut->get(), pJSONValue->m_pVal_mut); + return pJSONValue.release(); } @@ -1779,11 +2155,11 @@ bool JsonManager::ArrayReplace(JsonValue* handle, size_t index, JsonValue* value return false; } - yyjson_mut_val* val_copy = nullptr; + yyjson_mut_val* val_copy; if (value->IsMutable()) { - val_copy = yyjson_mut_val_mut_copy(handle->m_pDocument_mut.get(), value->m_pVal_mut); + val_copy = yyjson_mut_val_mut_copy(handle->m_pDocument_mut->get(), value->m_pVal_mut); } else { - val_copy = yyjson_val_mut_copy(handle->m_pDocument_mut.get(), value->m_pVal); + val_copy = yyjson_val_mut_copy(handle->m_pDocument_mut->get(), value->m_pVal); } if (!val_copy) { @@ -1804,7 +2180,7 @@ bool JsonManager::ArrayReplaceBool(JsonValue* handle, size_t index, bool value) return false; } - return yyjson_mut_arr_replace(handle->m_pVal_mut, index, yyjson_mut_bool(handle->m_pDocument_mut.get(), value)) != nullptr; + return yyjson_mut_arr_replace(handle->m_pVal_mut, index, yyjson_mut_bool(handle->m_pDocument_mut->get(), value)) != nullptr; } bool JsonManager::ArrayReplaceFloat(JsonValue* handle, size_t index, double value) @@ -1818,7 +2194,7 @@ bool JsonManager::ArrayReplaceFloat(JsonValue* handle, size_t index, double valu return false; } - return yyjson_mut_arr_replace(handle->m_pVal_mut, index, yyjson_mut_real(handle->m_pDocument_mut.get(), value)) != nullptr; + return yyjson_mut_arr_replace(handle->m_pVal_mut, index, yyjson_mut_real(handle->m_pDocument_mut->get(), value)) != nullptr; } bool JsonManager::ArrayReplaceInt(JsonValue* handle, size_t index, int value) @@ -1832,7 +2208,7 @@ bool JsonManager::ArrayReplaceInt(JsonValue* handle, size_t index, int value) return false; } - return yyjson_mut_arr_replace(handle->m_pVal_mut, index, yyjson_mut_int(handle->m_pDocument_mut.get(), value)) != nullptr; + return yyjson_mut_arr_replace(handle->m_pVal_mut, index, yyjson_mut_int(handle->m_pDocument_mut->get(), value)) != nullptr; } bool JsonManager::ArrayReplaceInt64(JsonValue* handle, size_t index, std::variant value) @@ -1849,9 +2225,9 @@ bool JsonManager::ArrayReplaceInt64(JsonValue* handle, size_t index, std::varian return std::visit([&](auto&& val) -> bool { using T = std::decay_t; if constexpr (std::is_same_v) { - return yyjson_mut_arr_replace(handle->m_pVal_mut, index, yyjson_mut_sint(handle->m_pDocument_mut.get(), val)) != nullptr; + return yyjson_mut_arr_replace(handle->m_pVal_mut, index, yyjson_mut_sint(handle->m_pDocument_mut->get(), val)) != nullptr; } else if constexpr (std::is_same_v) { - return yyjson_mut_arr_replace(handle->m_pVal_mut, index, yyjson_mut_uint(handle->m_pDocument_mut.get(), val)) != nullptr; + return yyjson_mut_arr_replace(handle->m_pVal_mut, index, yyjson_mut_uint(handle->m_pDocument_mut->get(), val)) != nullptr; } return false; }, value); @@ -1868,7 +2244,7 @@ bool JsonManager::ArrayReplaceNull(JsonValue* handle, size_t index) return false; } - return yyjson_mut_arr_replace(handle->m_pVal_mut, index, yyjson_mut_null(handle->m_pDocument_mut.get())) != nullptr; + return yyjson_mut_arr_replace(handle->m_pVal_mut, index, yyjson_mut_null(handle->m_pDocument_mut->get())) != nullptr; } bool JsonManager::ArrayReplaceString(JsonValue* handle, size_t index, const char* value) @@ -1882,7 +2258,7 @@ bool JsonManager::ArrayReplaceString(JsonValue* handle, size_t index, const char return false; } - return yyjson_mut_arr_replace(handle->m_pVal_mut, index, yyjson_mut_strcpy(handle->m_pDocument_mut.get(), value)) != nullptr; + return yyjson_mut_arr_replace(handle->m_pVal_mut, index, yyjson_mut_strcpy(handle->m_pDocument_mut->get(), value)) != nullptr; } bool JsonManager::ArrayAppend(JsonValue* handle, JsonValue* value) @@ -1891,11 +2267,11 @@ bool JsonManager::ArrayAppend(JsonValue* handle, JsonValue* value) return false; } - yyjson_mut_val* val_copy = nullptr; + yyjson_mut_val* val_copy; if (value->IsMutable()) { - val_copy = yyjson_mut_val_mut_copy(handle->m_pDocument_mut.get(), value->m_pVal_mut); + val_copy = yyjson_mut_val_mut_copy(handle->m_pDocument_mut->get(), value->m_pVal_mut); } else { - val_copy = yyjson_val_mut_copy(handle->m_pDocument_mut.get(), value->m_pVal); + val_copy = yyjson_val_mut_copy(handle->m_pDocument_mut->get(), value->m_pVal); } if (!val_copy) { @@ -1911,7 +2287,7 @@ bool JsonManager::ArrayAppendBool(JsonValue* handle, bool value) return false; } - return yyjson_mut_arr_append(handle->m_pVal_mut, yyjson_mut_bool(handle->m_pDocument_mut.get(), value)); + return yyjson_mut_arr_append(handle->m_pVal_mut, yyjson_mut_bool(handle->m_pDocument_mut->get(), value)); } bool JsonManager::ArrayAppendFloat(JsonValue* handle, double value) @@ -1920,7 +2296,7 @@ bool JsonManager::ArrayAppendFloat(JsonValue* handle, double value) return false; } - return yyjson_mut_arr_append(handle->m_pVal_mut, yyjson_mut_real(handle->m_pDocument_mut.get(), value)); + return yyjson_mut_arr_append(handle->m_pVal_mut, yyjson_mut_real(handle->m_pDocument_mut->get(), value)); } bool JsonManager::ArrayAppendInt(JsonValue* handle, int value) @@ -1929,7 +2305,7 @@ bool JsonManager::ArrayAppendInt(JsonValue* handle, int value) return false; } - return yyjson_mut_arr_append(handle->m_pVal_mut, yyjson_mut_int(handle->m_pDocument_mut.get(), value)); + return yyjson_mut_arr_append(handle->m_pVal_mut, yyjson_mut_int(handle->m_pDocument_mut->get(), value)); } bool JsonManager::ArrayAppendInt64(JsonValue* handle, std::variant value) @@ -1941,9 +2317,9 @@ bool JsonManager::ArrayAppendInt64(JsonValue* handle, std::variant bool { using T = std::decay_t; if constexpr (std::is_same_v) { - return yyjson_mut_arr_append(handle->m_pVal_mut, yyjson_mut_sint(handle->m_pDocument_mut.get(), val)); + return yyjson_mut_arr_append(handle->m_pVal_mut, yyjson_mut_sint(handle->m_pDocument_mut->get(), val)); } else if constexpr (std::is_same_v) { - return yyjson_mut_arr_append(handle->m_pVal_mut, yyjson_mut_uint(handle->m_pDocument_mut.get(), val)); + return yyjson_mut_arr_append(handle->m_pVal_mut, yyjson_mut_uint(handle->m_pDocument_mut->get(), val)); } return false; }, value); @@ -1955,7 +2331,7 @@ bool JsonManager::ArrayAppendNull(JsonValue* handle) return false; } - return yyjson_mut_arr_append(handle->m_pVal_mut, yyjson_mut_null(handle->m_pDocument_mut.get())); + return yyjson_mut_arr_append(handle->m_pVal_mut, yyjson_mut_null(handle->m_pDocument_mut->get())); } bool JsonManager::ArrayAppendString(JsonValue* handle, const char* value) @@ -1964,7 +2340,7 @@ bool JsonManager::ArrayAppendString(JsonValue* handle, const char* value) return false; } - return yyjson_mut_arr_append(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut.get(), value)); + return yyjson_mut_arr_append(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut->get(), value)); } bool JsonManager::ArrayInsert(JsonValue* handle, size_t index, JsonValue* value) @@ -1973,7 +2349,23 @@ bool JsonManager::ArrayInsert(JsonValue* handle, size_t index, JsonValue* value) return false; } - return yyjson_mut_arr_insert(handle->m_pVal_mut, value->m_pVal_mut, index); + size_t arr_size = yyjson_mut_arr_size(handle->m_pVal_mut); + if (index > arr_size) { + return false; + } + + yyjson_mut_val* val_copy; + if (value->IsMutable()) { + val_copy = yyjson_mut_val_mut_copy(handle->m_pDocument_mut->get(), value->m_pVal_mut); + } else { + val_copy = yyjson_val_mut_copy(handle->m_pDocument_mut->get(), value->m_pVal); + } + + if (!val_copy) { + return false; + } + + return yyjson_mut_arr_insert(handle->m_pVal_mut, val_copy, index); } bool JsonManager::ArrayInsertBool(JsonValue* handle, size_t index, bool value) @@ -1982,7 +2374,7 @@ bool JsonManager::ArrayInsertBool(JsonValue* handle, size_t index, bool value) return false; } - return yyjson_mut_arr_insert(handle->m_pVal_mut, yyjson_mut_bool(handle->m_pDocument_mut.get(), value), index); + return yyjson_mut_arr_insert(handle->m_pVal_mut, yyjson_mut_bool(handle->m_pDocument_mut->get(), value), index); } bool JsonManager::ArrayInsertInt(JsonValue* handle, size_t index, int value) @@ -1991,7 +2383,7 @@ bool JsonManager::ArrayInsertInt(JsonValue* handle, size_t index, int value) return false; } - return yyjson_mut_arr_insert(handle->m_pVal_mut, yyjson_mut_sint(handle->m_pDocument_mut.get(), value), index); + return yyjson_mut_arr_insert(handle->m_pVal_mut, yyjson_mut_sint(handle->m_pDocument_mut->get(), value), index); } bool JsonManager::ArrayInsertInt64(JsonValue* handle, size_t index, std::variant value) @@ -2000,14 +2392,14 @@ bool JsonManager::ArrayInsertInt64(JsonValue* handle, size_t index, std::variant return false; } - yyjson_mut_val* val = nullptr; - std::visit([&](auto&& arg) { + yyjson_mut_val* val = std::visit([&](auto&& arg) -> yyjson_mut_val* { using T = std::decay_t; if constexpr (std::is_same_v) { - val = yyjson_mut_sint(handle->m_pDocument_mut.get(), arg); + return yyjson_mut_sint(handle->m_pDocument_mut->get(), arg); } else if constexpr (std::is_same_v) { - val = yyjson_mut_uint(handle->m_pDocument_mut.get(), arg); + return yyjson_mut_uint(handle->m_pDocument_mut->get(), arg); } + return nullptr; }, value); if (!val) { @@ -2023,7 +2415,7 @@ bool JsonManager::ArrayInsertFloat(JsonValue* handle, size_t index, double value return false; } - return yyjson_mut_arr_insert(handle->m_pVal_mut, yyjson_mut_real(handle->m_pDocument_mut.get(), value), index); + return yyjson_mut_arr_insert(handle->m_pVal_mut, yyjson_mut_real(handle->m_pDocument_mut->get(), value), index); } bool JsonManager::ArrayInsertString(JsonValue* handle, size_t index, const char* value) @@ -2032,7 +2424,7 @@ bool JsonManager::ArrayInsertString(JsonValue* handle, size_t index, const char* return false; } - return yyjson_mut_arr_insert(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut.get(), value), index); + return yyjson_mut_arr_insert(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut->get(), value), index); } bool JsonManager::ArrayInsertNull(JsonValue* handle, size_t index) @@ -2041,7 +2433,7 @@ bool JsonManager::ArrayInsertNull(JsonValue* handle, size_t index) return false; } - return yyjson_mut_arr_insert(handle->m_pVal_mut, yyjson_mut_null(handle->m_pDocument_mut.get()), index); + return yyjson_mut_arr_insert(handle->m_pVal_mut, yyjson_mut_null(handle->m_pDocument_mut->get()), index); } bool JsonManager::ArrayPrepend(JsonValue* handle, JsonValue* value) @@ -2050,7 +2442,18 @@ bool JsonManager::ArrayPrepend(JsonValue* handle, JsonValue* value) return false; } - return yyjson_mut_arr_prepend(handle->m_pVal_mut, value->m_pVal_mut); + yyjson_mut_val* val_copy; + if (value->IsMutable()) { + val_copy = yyjson_mut_val_mut_copy(handle->m_pDocument_mut->get(), value->m_pVal_mut); + } else { + val_copy = yyjson_val_mut_copy(handle->m_pDocument_mut->get(), value->m_pVal); + } + + if (!val_copy) { + return false; + } + + return yyjson_mut_arr_prepend(handle->m_pVal_mut, val_copy); } bool JsonManager::ArrayPrependBool(JsonValue* handle, bool value) @@ -2059,7 +2462,7 @@ bool JsonManager::ArrayPrependBool(JsonValue* handle, bool value) return false; } - return yyjson_mut_arr_prepend(handle->m_pVal_mut, yyjson_mut_bool(handle->m_pDocument_mut.get(), value)); + return yyjson_mut_arr_prepend(handle->m_pVal_mut, yyjson_mut_bool(handle->m_pDocument_mut->get(), value)); } bool JsonManager::ArrayPrependInt(JsonValue* handle, int value) @@ -2068,7 +2471,7 @@ bool JsonManager::ArrayPrependInt(JsonValue* handle, int value) return false; } - return yyjson_mut_arr_prepend(handle->m_pVal_mut, yyjson_mut_sint(handle->m_pDocument_mut.get(), value)); + return yyjson_mut_arr_prepend(handle->m_pVal_mut, yyjson_mut_sint(handle->m_pDocument_mut->get(), value)); } bool JsonManager::ArrayPrependInt64(JsonValue* handle, std::variant value) @@ -2077,14 +2480,14 @@ bool JsonManager::ArrayPrependInt64(JsonValue* handle, std::variant yyjson_mut_val* { using T = std::decay_t; if constexpr (std::is_same_v) { - val = yyjson_mut_sint(handle->m_pDocument_mut.get(), arg); + return yyjson_mut_sint(handle->m_pDocument_mut->get(), arg); } else if constexpr (std::is_same_v) { - val = yyjson_mut_uint(handle->m_pDocument_mut.get(), arg); + return yyjson_mut_uint(handle->m_pDocument_mut->get(), arg); } + return nullptr; }, value); if (!val) { @@ -2100,7 +2503,7 @@ bool JsonManager::ArrayPrependFloat(JsonValue* handle, double value) return false; } - return yyjson_mut_arr_prepend(handle->m_pVal_mut, yyjson_mut_real(handle->m_pDocument_mut.get(), value)); + return yyjson_mut_arr_prepend(handle->m_pVal_mut, yyjson_mut_real(handle->m_pDocument_mut->get(), value)); } bool JsonManager::ArrayPrependString(JsonValue* handle, const char* value) @@ -2109,7 +2512,7 @@ bool JsonManager::ArrayPrependString(JsonValue* handle, const char* value) return false; } - return yyjson_mut_arr_prepend(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut.get(), value)); + return yyjson_mut_arr_prepend(handle->m_pVal_mut, yyjson_mut_strcpy(handle->m_pDocument_mut->get(), value)); } bool JsonManager::ArrayPrependNull(JsonValue* handle) @@ -2118,7 +2521,7 @@ bool JsonManager::ArrayPrependNull(JsonValue* handle) return false; } - return yyjson_mut_arr_prepend(handle->m_pVal_mut, yyjson_mut_null(handle->m_pDocument_mut.get())); + return yyjson_mut_arr_prepend(handle->m_pVal_mut, yyjson_mut_null(handle->m_pDocument_mut->get())); } bool JsonManager::ArrayRemove(JsonValue* handle, size_t index) @@ -2161,7 +2564,7 @@ bool JsonManager::ArrayRemoveLast(JsonValue* handle) return yyjson_mut_arr_remove_last(handle->m_pVal_mut) != nullptr; } -bool JsonManager::ArrayRemoveRange(JsonValue* handle, size_t start_index, size_t end_index) +bool JsonManager::ArrayRemoveRange(JsonValue* handle, size_t start_index, size_t count) { if (!handle || !handle->IsMutable()) { return false; @@ -2169,11 +2572,19 @@ bool JsonManager::ArrayRemoveRange(JsonValue* handle, size_t start_index, size_t size_t arr_size = yyjson_mut_arr_size(handle->m_pVal_mut); - if (start_index >= arr_size || end_index > arr_size || start_index > end_index) { + if (start_index >= arr_size) { return false; } - return yyjson_mut_arr_remove_range(handle->m_pVal_mut, start_index, end_index); + if (count == 0) { + return true; + } + + if (count > (arr_size - start_index)) { + return false; + } + + return yyjson_mut_arr_remove_range(handle->m_pVal_mut, start_index, count); } bool JsonManager::ArrayClear(JsonValue* handle) @@ -2266,53 +2677,44 @@ int JsonManager::ArrayIndexOfInt(JsonValue* handle, int search_value) return -1; } -int JsonManager::ArrayIndexOfInt64(JsonValue* handle, int64_t search_value) +int JsonManager::ArrayIndexOfInt64(JsonValue* handle, std::variant search_value) { if (!handle) { return -1; } + bool is_unsigned = std::holds_alternative(search_value); + if (handle->IsMutable()) { size_t idx, max; yyjson_mut_val *val; yyjson_mut_arr_foreach(handle->m_pVal_mut, idx, max, val) { - if (yyjson_mut_is_int(val) && yyjson_mut_get_sint(val) == search_value) { - return static_cast(idx); + if (yyjson_mut_is_int(val)) { + if (is_unsigned) { + if (yyjson_mut_get_uint(val) == std::get(search_value)) { + return static_cast(idx); + } + } else { + if (yyjson_mut_get_sint(val) == std::get(search_value)) { + return static_cast(idx); + } + } } } } else { size_t idx, max; yyjson_val *val; yyjson_arr_foreach(handle->m_pVal, idx, max, val) { - if (yyjson_is_int(val) && yyjson_get_sint(val) == search_value) { - return static_cast(idx); - } - } - } - - return -1; -} - -int JsonManager::ArrayIndexOfUint64(JsonValue* handle, uint64_t search_value) -{ - if (!handle) { - return -1; - } - - if (handle->IsMutable()) { - size_t idx, max; - yyjson_mut_val *val; - yyjson_mut_arr_foreach(handle->m_pVal_mut, idx, max, val) { - if (yyjson_mut_is_int(val) && yyjson_mut_get_uint(val) == search_value) { - return static_cast(idx); - } - } - } else { - size_t idx, max; - yyjson_val *val; - yyjson_arr_foreach(handle->m_pVal, idx, max, val) { - if (yyjson_is_int(val) && yyjson_get_uint(val) == search_value) { - return static_cast(idx); + if (yyjson_is_int(val)) { + if (is_unsigned) { + if (yyjson_get_uint(val) == std::get(search_value)) { + return static_cast(idx); + } + } else { + if (yyjson_get_sint(val) == std::get(search_value)) { + return static_cast(idx); + } + } } } } @@ -2497,7 +2899,7 @@ yyjson_mut_val* JsonManager::PackImpl(yyjson_mut_doc* doc, const char* format, return nullptr; } - yyjson_mut_val* root = nullptr; + yyjson_mut_val* root; const char* ptr = format; bool is_obj = false; @@ -2518,8 +2920,8 @@ yyjson_mut_val* JsonManager::PackImpl(yyjson_mut_doc* doc, const char* format, return nullptr; } - yyjson_mut_val* key_val = nullptr; - yyjson_mut_val* val = nullptr; + yyjson_mut_val* key_val; + yyjson_mut_val* val; while (*ptr && *ptr != '}' && *ptr != ']') { if (is_obj) { @@ -2721,15 +3123,15 @@ JsonValue* JsonManager::Pack(const char* format, IPackParamProvider* param_provi return nullptr; } - const char* end_ptr = nullptr; - pJSONValue->m_pVal_mut = PackImpl(pJSONValue->m_pDocument_mut.get(), format, + const char* end_ptr; + pJSONValue->m_pVal_mut = PackImpl(pJSONValue->m_pDocument_mut->get(), format, param_provider, error, error_size, &end_ptr); if (!pJSONValue->m_pVal_mut) { return nullptr; } - yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut.get(), pJSONValue->m_pVal_mut); + yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut->get(), pJSONValue->m_pVal_mut); return pJSONValue.release(); } @@ -2738,12 +3140,19 @@ JsonValue* JsonManager::CreateBool(bool value) { auto pJSONValue = CreateWrapper(); pJSONValue->m_pDocument_mut = CreateDocument(); - pJSONValue->m_pVal_mut = yyjson_mut_bool(pJSONValue->m_pDocument_mut.get(), value); + + if (!pJSONValue->m_pDocument_mut) { + return nullptr; + } + + pJSONValue->m_pVal_mut = yyjson_mut_bool(pJSONValue->m_pDocument_mut->get(), value); if (!pJSONValue->m_pVal_mut) { return nullptr; } + yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut->get(), pJSONValue->m_pVal_mut); + return pJSONValue.release(); } @@ -2751,12 +3160,19 @@ JsonValue* JsonManager::CreateFloat(double value) { auto pJSONValue = CreateWrapper(); pJSONValue->m_pDocument_mut = CreateDocument(); - pJSONValue->m_pVal_mut = yyjson_mut_real(pJSONValue->m_pDocument_mut.get(), value); + + if (!pJSONValue->m_pDocument_mut) { + return nullptr; + } + + pJSONValue->m_pVal_mut = yyjson_mut_real(pJSONValue->m_pDocument_mut->get(), value); if (!pJSONValue->m_pVal_mut) { return nullptr; } + yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut->get(), pJSONValue->m_pVal_mut); + return pJSONValue.release(); } @@ -2764,12 +3180,19 @@ JsonValue* JsonManager::CreateInt(int value) { auto pJSONValue = CreateWrapper(); pJSONValue->m_pDocument_mut = CreateDocument(); - pJSONValue->m_pVal_mut = yyjson_mut_int(pJSONValue->m_pDocument_mut.get(), value); + + if (!pJSONValue->m_pDocument_mut) { + return nullptr; + } + + pJSONValue->m_pVal_mut = yyjson_mut_int(pJSONValue->m_pDocument_mut->get(), value); if (!pJSONValue->m_pVal_mut) { return nullptr; } + yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut->get(), pJSONValue->m_pVal_mut); + return pJSONValue.release(); } @@ -2778,12 +3201,18 @@ JsonValue* JsonManager::CreateInt64(std::variant value) auto pJSONValue = CreateWrapper(); pJSONValue->m_pDocument_mut = CreateDocument(); + if (!pJSONValue->m_pDocument_mut) { + return nullptr; + } + + auto* doc = pJSONValue->m_pDocument_mut->get(); + std::visit([&](auto&& val) { using T = std::decay_t; if constexpr (std::is_same_v) { - pJSONValue->m_pVal_mut = yyjson_mut_sint(pJSONValue->m_pDocument_mut.get(), val); + pJSONValue->m_pVal_mut = yyjson_mut_sint(doc, val); } else if constexpr (std::is_same_v) { - pJSONValue->m_pVal_mut = yyjson_mut_uint(pJSONValue->m_pDocument_mut.get(), val); + pJSONValue->m_pVal_mut = yyjson_mut_uint(doc, val); } }, value); @@ -2791,6 +3220,8 @@ JsonValue* JsonManager::CreateInt64(std::variant value) return nullptr; } + yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut->get(), pJSONValue->m_pVal_mut); + return pJSONValue.release(); } @@ -2798,12 +3229,19 @@ JsonValue* JsonManager::CreateNull() { auto pJSONValue = CreateWrapper(); pJSONValue->m_pDocument_mut = CreateDocument(); - pJSONValue->m_pVal_mut = yyjson_mut_null(pJSONValue->m_pDocument_mut.get()); + + if (!pJSONValue->m_pDocument_mut) { + return nullptr; + } + + pJSONValue->m_pVal_mut = yyjson_mut_null(pJSONValue->m_pDocument_mut->get()); if (!pJSONValue->m_pVal_mut) { return nullptr; } + yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut->get(), pJSONValue->m_pVal_mut); + return pJSONValue.release(); } @@ -2815,12 +3253,19 @@ JsonValue* JsonManager::CreateString(const char* value) auto pJSONValue = CreateWrapper(); pJSONValue->m_pDocument_mut = CreateDocument(); - pJSONValue->m_pVal_mut = yyjson_mut_strcpy(pJSONValue->m_pDocument_mut.get(), value); + + if (!pJSONValue->m_pDocument_mut) { + return nullptr; + } + + pJSONValue->m_pVal_mut = yyjson_mut_strcpy(pJSONValue->m_pDocument_mut->get(), value); if (!pJSONValue->m_pVal_mut) { return nullptr; } + yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut->get(), pJSONValue->m_pVal_mut); + return pJSONValue.release(); } @@ -2948,7 +3393,7 @@ JsonValue* JsonManager::PtrGet(JsonValue* handle, const char* path, char* error, yyjson_ptr_err ptrGetError; if (handle->IsMutable()) { - yyjson_mut_val* val = yyjson_mut_doc_ptr_getx(handle->m_pDocument_mut.get(), path, strlen(path), nullptr, &ptrGetError); + yyjson_mut_val* val = yyjson_mut_doc_ptr_getx(handle->m_pDocument_mut->get(), path, strlen(path), nullptr, &ptrGetError); if (!val || ptrGetError.code) { if (error && error_size > 0) { @@ -2961,7 +3406,7 @@ JsonValue* JsonManager::PtrGet(JsonValue* handle, const char* path, char* error, pJSONValue->m_pDocument_mut = handle->m_pDocument_mut; pJSONValue->m_pVal_mut = val; } else { - yyjson_val* val = yyjson_doc_ptr_getx(handle->m_pDocument.get(), path, strlen(path), &ptrGetError); + yyjson_val* val = yyjson_doc_ptr_getx(handle->m_pDocument->get(), path, strlen(path), &ptrGetError); if (!val || ptrGetError.code) { if (error && error_size > 0) { @@ -2990,7 +3435,7 @@ bool JsonManager::PtrGetBool(JsonValue* handle, const char* path, bool* out_valu yyjson_ptr_err ptrGetError; if (handle->IsMutable()) { - yyjson_mut_val* val = yyjson_mut_doc_ptr_getx(handle->m_pDocument_mut.get(), path, strlen(path), nullptr, &ptrGetError); + yyjson_mut_val* val = yyjson_mut_doc_ptr_getx(handle->m_pDocument_mut->get(), path, strlen(path), nullptr, &ptrGetError); if (ptrGetError.code) { if (error && error_size > 0) { @@ -3010,7 +3455,7 @@ bool JsonManager::PtrGetBool(JsonValue* handle, const char* path, bool* out_valu *out_value = yyjson_mut_get_bool(val); return true; } else { - yyjson_val* val = yyjson_doc_ptr_getx(handle->m_pDocument.get(), path, strlen(path), &ptrGetError); + yyjson_val* val = yyjson_doc_ptr_getx(handle->m_pDocument->get(), path, strlen(path), &ptrGetError); if (ptrGetError.code) { if (error && error_size > 0) { @@ -3044,7 +3489,7 @@ bool JsonManager::PtrGetFloat(JsonValue* handle, const char* path, double* out_v yyjson_ptr_err ptrGetError; if (handle->IsMutable()) { - yyjson_mut_val* val = yyjson_mut_doc_ptr_getx(handle->m_pDocument_mut.get(), path, strlen(path), nullptr, &ptrGetError); + yyjson_mut_val* val = yyjson_mut_doc_ptr_getx(handle->m_pDocument_mut->get(), path, strlen(path), nullptr, &ptrGetError); if (ptrGetError.code) { if (error && error_size > 0) { @@ -3064,7 +3509,7 @@ bool JsonManager::PtrGetFloat(JsonValue* handle, const char* path, double* out_v *out_value = yyjson_mut_get_real(val); return true; } else { - yyjson_val* val = yyjson_doc_ptr_getx(handle->m_pDocument.get(), path, strlen(path), &ptrGetError); + yyjson_val* val = yyjson_doc_ptr_getx(handle->m_pDocument->get(), path, strlen(path), &ptrGetError); if (ptrGetError.code) { if (error && error_size > 0) { @@ -3098,7 +3543,7 @@ bool JsonManager::PtrGetInt(JsonValue* handle, const char* path, int* out_value, yyjson_ptr_err ptrGetError; if (handle->IsMutable()) { - yyjson_mut_val* val = yyjson_mut_doc_ptr_getx(handle->m_pDocument_mut.get(), path, strlen(path), nullptr, &ptrGetError); + yyjson_mut_val* val = yyjson_mut_doc_ptr_getx(handle->m_pDocument_mut->get(), path, strlen(path), nullptr, &ptrGetError); if (ptrGetError.code) { if (error && error_size > 0) { @@ -3118,7 +3563,7 @@ bool JsonManager::PtrGetInt(JsonValue* handle, const char* path, int* out_value, *out_value = yyjson_mut_get_int(val); return true; } else { - yyjson_val* val = yyjson_doc_ptr_getx(handle->m_pDocument.get(), path, strlen(path), &ptrGetError); + yyjson_val* val = yyjson_doc_ptr_getx(handle->m_pDocument->get(), path, strlen(path), &ptrGetError); if (ptrGetError.code) { if (error && error_size > 0) { @@ -3152,7 +3597,7 @@ bool JsonManager::PtrGetInt64(JsonValue* handle, const char* path, std::variant< yyjson_ptr_err ptrGetError; if (handle->IsMutable()) { - yyjson_mut_val* val = yyjson_mut_doc_ptr_getx(handle->m_pDocument_mut.get(), path, strlen(path), nullptr, &ptrGetError); + yyjson_mut_val* val = yyjson_mut_doc_ptr_getx(handle->m_pDocument_mut->get(), path, strlen(path), nullptr, &ptrGetError); if (!val || ptrGetError.code) { if (error && error_size > 0) { @@ -3172,7 +3617,7 @@ bool JsonManager::PtrGetInt64(JsonValue* handle, const char* path, std::variant< ReadInt64FromMutVal(val, out_value); return true; } else { - yyjson_val* val = yyjson_doc_ptr_getx(handle->m_pDocument.get(), path, strlen(path), &ptrGetError); + yyjson_val* val = yyjson_doc_ptr_getx(handle->m_pDocument->get(), path, strlen(path), &ptrGetError); if (!val || ptrGetError.code) { if (error && error_size > 0) { @@ -3206,7 +3651,7 @@ bool JsonManager::PtrGetString(JsonValue* handle, const char* path, const char** yyjson_ptr_err ptrGetError; if (handle->IsMutable()) { - yyjson_mut_val* val = yyjson_mut_doc_ptr_getx(handle->m_pDocument_mut.get(), path, strlen(path), nullptr, &ptrGetError); + yyjson_mut_val* val = yyjson_mut_doc_ptr_getx(handle->m_pDocument_mut->get(), path, strlen(path), nullptr, &ptrGetError); if (ptrGetError.code) { if (error && error_size > 0) { @@ -3229,7 +3674,7 @@ bool JsonManager::PtrGetString(JsonValue* handle, const char* path, const char** } return true; } else { - yyjson_val* val = yyjson_doc_ptr_getx(handle->m_pDocument.get(), path, strlen(path), &ptrGetError); + yyjson_val* val = yyjson_doc_ptr_getx(handle->m_pDocument->get(), path, strlen(path), &ptrGetError); if (ptrGetError.code) { if (error && error_size > 0) { @@ -3266,7 +3711,7 @@ bool JsonManager::PtrGetIsNull(JsonValue* handle, const char* path, bool* out_is yyjson_ptr_err ptrGetError; if (handle->IsMutable()) { - yyjson_mut_val* val = yyjson_mut_doc_ptr_getx(handle->m_pDocument_mut.get(), path, strlen(path), nullptr, &ptrGetError); + yyjson_mut_val* val = yyjson_mut_doc_ptr_getx(handle->m_pDocument_mut->get(), path, strlen(path), nullptr, &ptrGetError); if (ptrGetError.code) { if (error && error_size > 0) { @@ -3279,7 +3724,7 @@ bool JsonManager::PtrGetIsNull(JsonValue* handle, const char* path, bool* out_is *out_is_null = yyjson_mut_is_null(val); return true; } else { - yyjson_val* val = yyjson_doc_ptr_getx(handle->m_pDocument.get(), path, strlen(path), &ptrGetError); + yyjson_val* val = yyjson_doc_ptr_getx(handle->m_pDocument->get(), path, strlen(path), &ptrGetError); if (ptrGetError.code) { if (error && error_size > 0) { @@ -3306,7 +3751,7 @@ bool JsonManager::PtrGetLength(JsonValue* handle, const char* path, size_t* out_ yyjson_ptr_err ptrGetError; if (handle->IsMutable()) { - yyjson_mut_val* val = yyjson_mut_doc_ptr_getx(handle->m_pDocument_mut.get(), path, strlen(path), nullptr, &ptrGetError); + yyjson_mut_val* val = yyjson_mut_doc_ptr_getx(handle->m_pDocument_mut->get(), path, strlen(path), nullptr, &ptrGetError); if (ptrGetError.code) { if (error && error_size > 0) { @@ -3323,7 +3768,7 @@ bool JsonManager::PtrGetLength(JsonValue* handle, const char* path, size_t* out_ } return true; } else { - yyjson_val* val = yyjson_doc_ptr_getx(handle->m_pDocument.get(), path, strlen(path), &ptrGetError); + yyjson_val* val = yyjson_doc_ptr_getx(handle->m_pDocument->get(), path, strlen(path), &ptrGetError); if (ptrGetError.code) { if (error && error_size > 0) { @@ -3351,11 +3796,11 @@ bool JsonManager::PtrSet(JsonValue* handle, const char* path, JsonValue* value, return false; } - yyjson_mut_val* val_copy = nullptr; + yyjson_mut_val* val_copy; if (value->IsMutable()) { - val_copy = yyjson_mut_val_mut_copy(handle->m_pDocument_mut.get(), value->m_pVal_mut); + val_copy = yyjson_mut_val_mut_copy(handle->m_pDocument_mut->get(), value->m_pVal_mut); } else { - val_copy = yyjson_val_mut_copy(handle->m_pDocument_mut.get(), value->m_pVal); + val_copy = yyjson_val_mut_copy(handle->m_pDocument_mut->get(), value->m_pVal); } if (!val_copy) { @@ -3366,9 +3811,9 @@ bool JsonManager::PtrSet(JsonValue* handle, const char* path, JsonValue* value, } yyjson_ptr_err ptrSetError; - bool success = yyjson_mut_doc_ptr_setx(handle->m_pDocument_mut.get(), path, strlen(path), val_copy, true, nullptr, &ptrSetError); + bool success = yyjson_mut_doc_ptr_setx(handle->m_pDocument_mut->get(), path, strlen(path), val_copy, true, nullptr, &ptrSetError); - if (ptrSetError.code && error && error_size > 0) { + if (!success && ptrSetError.code && error && error_size > 0) { SetErrorSafe(error, error_size, "Failed to set JSON pointer: %s (error code: %u, position: %zu, path: %s)", ptrSetError.msg, ptrSetError.code, ptrSetError.pos, path); } @@ -3385,10 +3830,18 @@ bool JsonManager::PtrSetBool(JsonValue* handle, const char* path, bool value, ch return false; } - yyjson_ptr_err ptrSetError; - bool success = yyjson_mut_doc_ptr_setx(handle->m_pDocument_mut.get(), path, strlen(path), yyjson_mut_bool(handle->m_pDocument_mut.get(), value), true, nullptr, &ptrSetError); + yyjson_mut_val* val = yyjson_mut_bool(handle->m_pDocument_mut->get(), value); + if (!val) { + if (error && error_size > 0) { + SetErrorSafe(error, error_size, "Failed to create JSON value"); + } + return false; + } - if (ptrSetError.code && error && error_size > 0) { + yyjson_ptr_err ptrSetError; + bool success = yyjson_mut_doc_ptr_setx(handle->m_pDocument_mut->get(), path, strlen(path), val, true, nullptr, &ptrSetError); + + if (!success && ptrSetError.code && error && error_size > 0) { SetErrorSafe(error, error_size, "Failed to set JSON pointer: %s (error code: %u, position: %zu, path: %s)", ptrSetError.msg, ptrSetError.code, ptrSetError.pos, path); } @@ -3405,10 +3858,18 @@ bool JsonManager::PtrSetFloat(JsonValue* handle, const char* path, double value, return false; } - yyjson_ptr_err ptrSetError; - bool success = yyjson_mut_doc_ptr_setx(handle->m_pDocument_mut.get(), path, strlen(path), yyjson_mut_real(handle->m_pDocument_mut.get(), value), true, nullptr, &ptrSetError); + yyjson_mut_val* val = yyjson_mut_real(handle->m_pDocument_mut->get(), value); + if (!val) { + if (error && error_size > 0) { + SetErrorSafe(error, error_size, "Failed to create JSON value"); + } + return false; + } - if (ptrSetError.code && error && error_size > 0) { + yyjson_ptr_err ptrSetError; + bool success = yyjson_mut_doc_ptr_setx(handle->m_pDocument_mut->get(), path, strlen(path), val, true, nullptr, &ptrSetError); + + if (!success && ptrSetError.code && error && error_size > 0) { SetErrorSafe(error, error_size, "Failed to set JSON pointer: %s (error code: %u, position: %zu, path: %s)", ptrSetError.msg, ptrSetError.code, ptrSetError.pos, path); } @@ -3425,10 +3886,18 @@ bool JsonManager::PtrSetInt(JsonValue* handle, const char* path, int value, char return false; } - yyjson_ptr_err ptrSetError; - bool success = yyjson_mut_doc_ptr_setx(handle->m_pDocument_mut.get(), path, strlen(path), yyjson_mut_int(handle->m_pDocument_mut.get(), value), true, nullptr, &ptrSetError); + yyjson_mut_val* val = yyjson_mut_int(handle->m_pDocument_mut->get(), value); + if (!val) { + if (error && error_size > 0) { + SetErrorSafe(error, error_size, "Failed to create JSON value"); + } + return false; + } - if (ptrSetError.code && error && error_size > 0) { + yyjson_ptr_err ptrSetError; + bool success = yyjson_mut_doc_ptr_setx(handle->m_pDocument_mut->get(), path, strlen(path), val, true, nullptr, &ptrSetError); + + if (!success && ptrSetError.code && error && error_size > 0) { SetErrorSafe(error, error_size, "Failed to set JSON pointer: %s (error code: %u, position: %zu, path: %s)", ptrSetError.msg, ptrSetError.code, ptrSetError.pos, path); } @@ -3445,16 +3914,23 @@ bool JsonManager::PtrSetInt64(JsonValue* handle, const char* path, std::variant< return false; } - yyjson_ptr_err ptrSetError; - bool success = std::visit([&](auto&& val) -> bool { - using T = std::decay_t; + yyjson_mut_val* val = std::visit([&](auto&& val_input) -> yyjson_mut_val* { + using T = std::decay_t; if constexpr (std::is_same_v) { - return yyjson_mut_doc_ptr_setx(handle->m_pDocument_mut.get(), path, strlen(path), yyjson_mut_sint(handle->m_pDocument_mut.get(), val), true, nullptr, &ptrSetError); - } else if constexpr (std::is_same_v) { - return yyjson_mut_doc_ptr_setx(handle->m_pDocument_mut.get(), path, strlen(path), yyjson_mut_uint(handle->m_pDocument_mut.get(), val), true, nullptr, &ptrSetError); + return yyjson_mut_sint(handle->m_pDocument_mut->get(), val_input); + } else { + return yyjson_mut_uint(handle->m_pDocument_mut->get(), val_input); + } + }, value); + if (!val) { + if (error && error_size > 0) { + SetErrorSafe(error, error_size, "Failed to create JSON value"); } return false; - }, value); + } + + yyjson_ptr_err ptrSetError; + bool success = yyjson_mut_doc_ptr_setx(handle->m_pDocument_mut->get(), path, strlen(path), val, true, nullptr, &ptrSetError); if (!success && ptrSetError.code && error && error_size > 0) { SetErrorSafe(error, error_size, "Failed to set JSON pointer: %s (error code: %u, position: %zu, path: %s)", @@ -3473,10 +3949,18 @@ bool JsonManager::PtrSetString(JsonValue* handle, const char* path, const char* return false; } - yyjson_ptr_err ptrSetError; - bool success = yyjson_mut_doc_ptr_setx(handle->m_pDocument_mut.get(), path, strlen(path), yyjson_mut_strcpy(handle->m_pDocument_mut.get(), value), true, nullptr, &ptrSetError); + yyjson_mut_val* val = yyjson_mut_strcpy(handle->m_pDocument_mut->get(), value); + if (!val) { + if (error && error_size > 0) { + SetErrorSafe(error, error_size, "Failed to create JSON value"); + } + return false; + } - if (ptrSetError.code && error && error_size > 0) { + yyjson_ptr_err ptrSetError; + bool success = yyjson_mut_doc_ptr_setx(handle->m_pDocument_mut->get(), path, strlen(path), val, true, nullptr, &ptrSetError); + + if (!success && ptrSetError.code && error && error_size > 0) { SetErrorSafe(error, error_size, "Failed to set JSON pointer: %s (error code: %u, position: %zu, path: %s)", ptrSetError.msg, ptrSetError.code, ptrSetError.pos, path); } @@ -3493,10 +3977,18 @@ bool JsonManager::PtrSetNull(JsonValue* handle, const char* path, char* error, s return false; } - yyjson_ptr_err ptrSetError; - bool success = yyjson_mut_doc_ptr_setx(handle->m_pDocument_mut.get(), path, strlen(path), yyjson_mut_null(handle->m_pDocument_mut.get()), true, nullptr, &ptrSetError); + yyjson_mut_val* val = yyjson_mut_null(handle->m_pDocument_mut->get()); + if (!val) { + if (error && error_size > 0) { + SetErrorSafe(error, error_size, "Failed to create JSON value"); + } + return false; + } - if (ptrSetError.code && error && error_size > 0) { + yyjson_ptr_err ptrSetError; + bool success = yyjson_mut_doc_ptr_setx(handle->m_pDocument_mut->get(), path, strlen(path), val, true, nullptr, &ptrSetError); + + if (!success && ptrSetError.code && error && error_size > 0) { SetErrorSafe(error, error_size, "Failed to set JSON pointer: %s (error code: %u, position: %zu, path: %s)", ptrSetError.msg, ptrSetError.code, ptrSetError.pos, path); } @@ -3513,11 +4005,11 @@ bool JsonManager::PtrAdd(JsonValue* handle, const char* path, JsonValue* value, return false; } - yyjson_mut_val* val_copy = nullptr; + yyjson_mut_val* val_copy; if (value->IsMutable()) { - val_copy = yyjson_mut_val_mut_copy(handle->m_pDocument_mut.get(), value->m_pVal_mut); + val_copy = yyjson_mut_val_mut_copy(handle->m_pDocument_mut->get(), value->m_pVal_mut); } else { - val_copy = yyjson_val_mut_copy(handle->m_pDocument_mut.get(), value->m_pVal); + val_copy = yyjson_val_mut_copy(handle->m_pDocument_mut->get(), value->m_pVal); } if (!val_copy) { @@ -3528,9 +4020,9 @@ bool JsonManager::PtrAdd(JsonValue* handle, const char* path, JsonValue* value, } yyjson_ptr_err ptrAddError; - bool success = yyjson_mut_doc_ptr_addx(handle->m_pDocument_mut.get(), path, strlen(path), val_copy, true, nullptr, &ptrAddError); + bool success = yyjson_mut_doc_ptr_addx(handle->m_pDocument_mut->get(), path, strlen(path), val_copy, true, nullptr, &ptrAddError); - if (ptrAddError.code && error && error_size > 0) { + if (!success && ptrAddError.code && error && error_size > 0) { SetErrorSafe(error, error_size, "Failed to add JSON pointer: %s (error code: %u, position: %zu, path: %s)", ptrAddError.msg, ptrAddError.code, ptrAddError.pos, path); } @@ -3547,10 +4039,18 @@ bool JsonManager::PtrAddBool(JsonValue* handle, const char* path, bool value, ch return false; } - yyjson_ptr_err ptrAddError; - bool success = yyjson_mut_doc_ptr_addx(handle->m_pDocument_mut.get(), path, strlen(path), yyjson_mut_bool(handle->m_pDocument_mut.get(), value), true, nullptr, &ptrAddError); + yyjson_mut_val* val = yyjson_mut_bool(handle->m_pDocument_mut->get(), value); + if (!val) { + if (error && error_size > 0) { + SetErrorSafe(error, error_size, "Failed to create JSON value"); + } + return false; + } - if (ptrAddError.code && error && error_size > 0) { + yyjson_ptr_err ptrAddError; + bool success = yyjson_mut_doc_ptr_addx(handle->m_pDocument_mut->get(), path, strlen(path), val, true, nullptr, &ptrAddError); + + if (!success && ptrAddError.code && error && error_size > 0) { SetErrorSafe(error, error_size, "Failed to add JSON pointer: %s (error code: %u, position: %zu, path: %s)", ptrAddError.msg, ptrAddError.code, ptrAddError.pos, path); } @@ -3567,10 +4067,18 @@ bool JsonManager::PtrAddFloat(JsonValue* handle, const char* path, double value, return false; } - yyjson_ptr_err ptrAddError; - bool success = yyjson_mut_doc_ptr_addx(handle->m_pDocument_mut.get(), path, strlen(path), yyjson_mut_real(handle->m_pDocument_mut.get(), value), true, nullptr, &ptrAddError); + yyjson_mut_val* val = yyjson_mut_real(handle->m_pDocument_mut->get(), value); + if (!val) { + if (error && error_size > 0) { + SetErrorSafe(error, error_size, "Failed to create JSON value"); + } + return false; + } - if (ptrAddError.code && error && error_size > 0) { + yyjson_ptr_err ptrAddError; + bool success = yyjson_mut_doc_ptr_addx(handle->m_pDocument_mut->get(), path, strlen(path), val, true, nullptr, &ptrAddError); + + if (!success && ptrAddError.code && error && error_size > 0) { SetErrorSafe(error, error_size, "Failed to add JSON pointer: %s (error code: %u, position: %zu, path: %s)", ptrAddError.msg, ptrAddError.code, ptrAddError.pos, path); } @@ -3587,10 +4095,18 @@ bool JsonManager::PtrAddInt(JsonValue* handle, const char* path, int value, char return false; } - yyjson_ptr_err ptrAddError; - bool success = yyjson_mut_doc_ptr_addx(handle->m_pDocument_mut.get(), path, strlen(path), yyjson_mut_int(handle->m_pDocument_mut.get(), value), true, nullptr, &ptrAddError); + yyjson_mut_val* val = yyjson_mut_int(handle->m_pDocument_mut->get(), value); + if (!val) { + if (error && error_size > 0) { + SetErrorSafe(error, error_size, "Failed to create JSON value"); + } + return false; + } - if (ptrAddError.code && error && error_size > 0) { + yyjson_ptr_err ptrAddError; + bool success = yyjson_mut_doc_ptr_addx(handle->m_pDocument_mut->get(), path, strlen(path), val, true, nullptr, &ptrAddError); + + if (!success && ptrAddError.code && error && error_size > 0) { SetErrorSafe(error, error_size, "Failed to add JSON pointer: %s (error code: %u, position: %zu, path: %s)", ptrAddError.msg, ptrAddError.code, ptrAddError.pos, path); } @@ -3607,18 +4123,25 @@ bool JsonManager::PtrAddInt64(JsonValue* handle, const char* path, std::variant< return false; } - yyjson_ptr_err ptrAddError; - bool success = std::visit([&](auto&& val) -> bool { - using T = std::decay_t; + yyjson_mut_val* val = std::visit([&](auto&& val_input) -> yyjson_mut_val* { + using T = std::decay_t; if constexpr (std::is_same_v) { - return yyjson_mut_doc_ptr_addx(handle->m_pDocument_mut.get(), path, strlen(path), yyjson_mut_sint(handle->m_pDocument_mut.get(), val), true, nullptr, &ptrAddError); - } else if constexpr (std::is_same_v) { - return yyjson_mut_doc_ptr_addx(handle->m_pDocument_mut.get(), path, strlen(path), yyjson_mut_uint(handle->m_pDocument_mut.get(), val), true, nullptr, &ptrAddError); + return yyjson_mut_sint(handle->m_pDocument_mut->get(), val_input); + } else { + return yyjson_mut_uint(handle->m_pDocument_mut->get(), val_input); + } + }, value); + if (!val) { + if (error && error_size > 0) { + SetErrorSafe(error, error_size, "Failed to create JSON value"); } return false; - }, value); + } - if (ptrAddError.code && error && error_size > 0) { + yyjson_ptr_err ptrAddError; + bool success = yyjson_mut_doc_ptr_addx(handle->m_pDocument_mut->get(), path, strlen(path), val, true, nullptr, &ptrAddError); + + if (!success && ptrAddError.code && error && error_size > 0) { SetErrorSafe(error, error_size, "Failed to add JSON pointer: %s (error code: %u, position: %zu, path: %s)", ptrAddError.msg, ptrAddError.code, ptrAddError.pos, path); } @@ -3635,10 +4158,18 @@ bool JsonManager::PtrAddString(JsonValue* handle, const char* path, const char* return false; } - yyjson_ptr_err ptrAddError; - bool success = yyjson_mut_doc_ptr_addx(handle->m_pDocument_mut.get(), path, strlen(path), yyjson_mut_strcpy(handle->m_pDocument_mut.get(), value), true, nullptr, &ptrAddError); + yyjson_mut_val* val = yyjson_mut_strcpy(handle->m_pDocument_mut->get(), value); + if (!val) { + if (error && error_size > 0) { + SetErrorSafe(error, error_size, "Failed to create JSON value"); + } + return false; + } - if (ptrAddError.code && error && error_size > 0) { + yyjson_ptr_err ptrAddError; + bool success = yyjson_mut_doc_ptr_addx(handle->m_pDocument_mut->get(), path, strlen(path), val, true, nullptr, &ptrAddError); + + if (!success && ptrAddError.code && error && error_size > 0) { SetErrorSafe(error, error_size, "Failed to add JSON pointer: %s (error code: %u, position: %zu, path: %s)", ptrAddError.msg, ptrAddError.code, ptrAddError.pos, path); } @@ -3655,10 +4186,18 @@ bool JsonManager::PtrAddNull(JsonValue* handle, const char* path, char* error, s return false; } - yyjson_ptr_err ptrAddError; - bool success = yyjson_mut_doc_ptr_addx(handle->m_pDocument_mut.get(), path, strlen(path), yyjson_mut_null(handle->m_pDocument_mut.get()), true, nullptr, &ptrAddError); + yyjson_mut_val* val = yyjson_mut_null(handle->m_pDocument_mut->get()); + if (!val) { + if (error && error_size > 0) { + SetErrorSafe(error, error_size, "Failed to create JSON value"); + } + return false; + } - if (ptrAddError.code && error && error_size > 0) { + yyjson_ptr_err ptrAddError; + bool success = yyjson_mut_doc_ptr_addx(handle->m_pDocument_mut->get(), path, strlen(path), val, true, nullptr, &ptrAddError); + + if (!success && ptrAddError.code && error && error_size > 0) { SetErrorSafe(error, error_size, "Failed to add JSON pointer: %s (error code: %u, position: %zu, path: %s)", ptrAddError.msg, ptrAddError.code, ptrAddError.pos, path); } @@ -3676,9 +4215,9 @@ bool JsonManager::PtrRemove(JsonValue* handle, const char* path, char* error, si } yyjson_ptr_err ptrRemoveError; - bool success = yyjson_mut_doc_ptr_removex(handle->m_pDocument_mut.get(), path, strlen(path), nullptr, &ptrRemoveError) != nullptr; + bool success = yyjson_mut_doc_ptr_removex(handle->m_pDocument_mut->get(), path, strlen(path), nullptr, &ptrRemoveError) != nullptr; - if (ptrRemoveError.code && error && error_size > 0) { + if (!success && ptrRemoveError.code && error && error_size > 0) { SetErrorSafe(error, error_size, "Failed to remove JSON pointer: %s (error code: %u, position: %zu, path: %s)", ptrRemoveError.msg, ptrRemoveError.code, ptrRemoveError.pos, path); } @@ -3698,12 +4237,12 @@ JsonManager::PtrGetValueResult JsonManager::PtrGetValueInternal(JsonValue* handl yyjson_ptr_err ptrGetError; if (handle->IsMutable()) { - result.mut_val = yyjson_mut_doc_ptr_getx(handle->m_pDocument_mut.get(), path, strlen(path), nullptr, &ptrGetError); + result.mut_val = yyjson_mut_doc_ptr_getx(handle->m_pDocument_mut->get(), path, strlen(path), nullptr, &ptrGetError); if (result.mut_val && !ptrGetError.code) { result.success = true; } } else { - result.imm_val = yyjson_doc_ptr_getx(handle->m_pDocument.get(), path, strlen(path), &ptrGetError); + result.imm_val = yyjson_doc_ptr_getx(handle->m_pDocument->get(), path, strlen(path), &ptrGetError); if (result.imm_val && !ptrGetError.code) { result.success = true; } @@ -4112,14 +4651,18 @@ JsonArrIter* JsonManager::ArrIterWith(JsonValue* handle) iter->m_isMutable = handle->IsMutable(); iter->m_pDocument_mut = handle->m_pDocument_mut; iter->m_pDocument = handle->m_pDocument; + iter->m_rootMut = nullptr; + iter->m_rootImm = nullptr; if (handle->IsMutable()) { - if (!yyjson_mut_arr_iter_init(handle->m_pVal_mut, &iter->m_iterMut)) { + iter->m_rootMut = handle->m_pVal_mut; + if (!iter->m_rootMut || !yyjson_mut_arr_iter_init(iter->m_rootMut, &iter->m_iterMut)) { delete iter; return nullptr; } } else { - if (!yyjson_arr_iter_init(handle->m_pVal, &iter->m_iterImm)) { + iter->m_rootImm = handle->m_pVal; + if (!iter->m_rootImm || !yyjson_arr_iter_init(iter->m_rootImm, &iter->m_iterImm)) { delete iter; return nullptr; } @@ -4129,13 +4672,34 @@ JsonArrIter* JsonManager::ArrIterWith(JsonValue* handle) return iter; } +bool JsonManager::ArrIterReset(JsonArrIter* iter) +{ + if (!iter || !iter->m_initialized) { + return false; + } + + bool success; + if (iter->m_isMutable) { + success = iter->m_rootMut && yyjson_mut_arr_iter_init(iter->m_rootMut, &iter->m_iterMut); + } else { + success = iter->m_rootImm && yyjson_arr_iter_init(iter->m_rootImm, &iter->m_iterImm); + } + + if (!success) { + iter->m_initialized = false; + return false; + } + + return true; +} + JsonValue* JsonManager::ArrIterNext(JsonArrIter* iter) { if (!iter || !iter->m_initialized) { return nullptr; } - JsonValue* val = nullptr; + JsonValue* val; if (iter->m_isMutable) { yyjson_mut_val* raw_val = yyjson_mut_arr_iter_next(&iter->m_iterMut); @@ -4218,14 +4782,18 @@ JsonObjIter* JsonManager::ObjIterWith(JsonValue* handle) iter->m_isMutable = handle->IsMutable(); iter->m_pDocument_mut = handle->m_pDocument_mut; iter->m_pDocument = handle->m_pDocument; + iter->m_rootMut = nullptr; + iter->m_rootImm = nullptr; if (handle->IsMutable()) { - if (!yyjson_mut_obj_iter_init(handle->m_pVal_mut, &iter->m_iterMut)) { + iter->m_rootMut = handle->m_pVal_mut; + if (!iter->m_rootMut || !yyjson_mut_obj_iter_init(iter->m_rootMut, &iter->m_iterMut)) { delete iter; return nullptr; } } else { - if (!yyjson_obj_iter_init(handle->m_pVal, &iter->m_iterImm)) { + iter->m_rootImm = handle->m_pVal; + if (!iter->m_rootImm || !yyjson_obj_iter_init(iter->m_rootImm, &iter->m_iterImm)) { delete iter; return nullptr; } @@ -4235,6 +4803,28 @@ JsonObjIter* JsonManager::ObjIterWith(JsonValue* handle) return iter; } +bool JsonManager::ObjIterReset(JsonObjIter* iter) +{ + if (!iter || !iter->m_initialized) { + return false; + } + + bool success; + if (iter->m_isMutable) { + success = iter->m_rootMut && yyjson_mut_obj_iter_init(iter->m_rootMut, &iter->m_iterMut); + } else { + success = iter->m_rootImm && yyjson_obj_iter_init(iter->m_rootImm, &iter->m_iterImm); + } + + if (!success) { + iter->m_initialized = false; + return false; + } + + iter->m_currentKey = nullptr; + return true; +} + void* JsonManager::ObjIterNext(JsonObjIter* iter) { if (!iter || !iter->m_initialized) { @@ -4421,7 +5011,14 @@ JsonValue* JsonManager::ReadNumber(const char* dat, uint32_t read_flg, char* err auto pJSONValue = CreateWrapper(); pJSONValue->m_pDocument_mut = CreateDocument(); - yyjson_mut_val* val = yyjson_mut_int(pJSONValue->m_pDocument_mut.get(), 0); + if (!pJSONValue->m_pDocument_mut) { + if (error && error_size > 0) { + SetErrorSafe(error, error_size, "Failed to create number document"); + } + return nullptr; + } + + yyjson_mut_val* val = yyjson_mut_int(pJSONValue->m_pDocument_mut->get(), 0); if (!val) { if (error && error_size > 0) { SetErrorSafe(error, error_size, "Failed to create number value"); @@ -4446,7 +5043,7 @@ JsonValue* JsonManager::ReadNumber(const char* dat, uint32_t read_flg, char* err } pJSONValue->m_pVal_mut = val; - yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut.get(), val); + yyjson_mut_doc_set_root(pJSONValue->m_pDocument_mut->get(), val); return pJSONValue.release(); } @@ -4461,7 +5058,7 @@ bool JsonManager::WriteNumber(JsonValue* handle, char* buffer, size_t buffer_siz return false; } - char* result = nullptr; + char* result; if (handle->IsMutable()) { result = yyjson_mut_write_number(handle->m_pVal_mut, buffer); } else { diff --git a/extensions/json/JsonManager.h b/extensions/json/JsonManager.h index 0de2e7915..2fc6a1480 100755 --- a/extensions/json/JsonManager.h +++ b/extensions/json/JsonManager.h @@ -1,5 +1,5 @@ -#ifndef _INCLUDE_SM_JSON_JSONMANAGER_H_ -#define _INCLUDE_SM_JSON_JSONMANAGER_H_ +#ifndef _INCLUDE_JSONMANAGER_H_ +#define _INCLUDE_JSONMANAGER_H_ #include #include @@ -7,6 +7,172 @@ #include #include +/** + * @brief Base class for intrusive reference counting + * + * Objects inheriting from this class can be managed by RefPtr. + * Reference count starts at 0 and is incremented when RefPtr takes ownership. + */ +class RefCounted { +private: + mutable size_t ref_count_ = 0; + +protected: + virtual ~RefCounted() = default; + RefCounted() = default; + + RefCounted(const RefCounted &) = delete; + RefCounted &operator=(const RefCounted &) = delete; + + RefCounted(RefCounted &&) noexcept : ref_count_(0) {} + RefCounted &operator=(RefCounted &&) noexcept { return *this; } + +public: + void add_ref() const noexcept { ++ref_count_; } + + void release() const noexcept { + assert(ref_count_ > 0 && "Reference count underflow"); + if (--ref_count_ == 0) { + delete this; + } + } + + size_t use_count() const noexcept { return ref_count_; } +}; + +/** + * @brief Smart pointer for intrusive reference counting + * + * Similar to std::shared_ptr but uses intrusive reference counting. + * Automatically manages object lifetime through add_ref() and release(). + * @warning This implementation is not thread-safe. It must only be used + * in single-threaded contexts or with external synchronization. + */ +template class RefPtr { +private: + T *ptr_; + +public: + RefPtr() noexcept : ptr_(nullptr) {} + + explicit RefPtr(T *p) noexcept : ptr_(p) { + if (ptr_) + ptr_->add_ref(); + } + + RefPtr(const RefPtr &other) noexcept : ptr_(other.ptr_) { + if (ptr_) + ptr_->add_ref(); + } + + RefPtr(RefPtr &&other) noexcept : ptr_(other.ptr_) { other.ptr_ = nullptr; } + + ~RefPtr() { + if (ptr_) + ptr_->release(); + } + + RefPtr &operator=(const RefPtr &other) noexcept { + if (ptr_ != other.ptr_) { + if (other.ptr_) + other.ptr_->add_ref(); + if (ptr_) + ptr_->release(); + ptr_ = other.ptr_; + } + return *this; + } + + RefPtr &operator=(RefPtr &&other) noexcept { + if (this != &other) { + if (ptr_) + ptr_->release(); + ptr_ = other.ptr_; + other.ptr_ = nullptr; + } + return *this; + } + + RefPtr &operator=(T *p) noexcept { + reset(p); + return *this; + } + + T *operator->() const noexcept { return ptr_; } + T &operator*() const noexcept { return *ptr_; } + T *get() const noexcept { return ptr_; } + + explicit operator bool() const noexcept { return ptr_ != nullptr; } + + size_t use_count() const noexcept { return ptr_ ? ptr_->use_count() : 0; } + + void reset(T *p = nullptr) noexcept { + if (ptr_ != p) { + if (p) + p->add_ref(); + if (ptr_) + ptr_->release(); + ptr_ = p; + } + } + + bool operator==(const RefPtr &other) const noexcept { return ptr_ == other.ptr_; } + bool operator!=(const RefPtr &other) const noexcept { return ptr_ != other.ptr_; } + bool operator==(std::nullptr_t) const noexcept { return ptr_ == nullptr; } + bool operator!=(std::nullptr_t) const noexcept { return ptr_ != nullptr; } +}; + +/** + * @brief Factory function to create RefPtr from new object + */ +template RefPtr make_ref(Args &&...args) { + return RefPtr(new T(std::forward(args)...)); +} + +/** + * @brief Wrapper for yyjson_mut_doc with intrusive reference counting + */ +class RefCountedMutDoc : public RefCounted { +private: + yyjson_mut_doc *doc_; + +public: + explicit RefCountedMutDoc(yyjson_mut_doc *doc) noexcept : doc_(doc) {} + + RefCountedMutDoc(const RefCountedMutDoc &) = delete; + RefCountedMutDoc &operator=(const RefCountedMutDoc &) = delete; + + ~RefCountedMutDoc() noexcept override { + if (doc_) { + yyjson_mut_doc_free(doc_); + } + } + + yyjson_mut_doc *get() const noexcept { return doc_; } +}; + +/** + * @brief Wrapper for yyjson_doc with intrusive reference counting + */ +class RefCountedImmutableDoc : public RefCounted { +private: + yyjson_doc *doc_; + +public: + explicit RefCountedImmutableDoc(yyjson_doc *doc) noexcept : doc_(doc) {} + + RefCountedImmutableDoc(const RefCountedImmutableDoc &) = delete; + RefCountedImmutableDoc &operator=(const RefCountedImmutableDoc &) = delete; + + ~RefCountedImmutableDoc() noexcept override { + if (doc_) { + yyjson_doc_free(doc_); + } + } + + yyjson_doc *get() const noexcept { return doc_; } +}; + /** * @brief JSON value wrapper * @@ -16,14 +182,7 @@ class JsonValue { public: JsonValue() = default; - ~JsonValue() { - if (m_pDocument_mut.use_count() == 1) { - yyjson_mut_doc_free(m_pDocument_mut.get()); - } - if (m_pDocument.use_count() == 1) { - yyjson_doc_free(m_pDocument.get()); - } - } + ~JsonValue() = default; JsonValue(const JsonValue&) = delete; JsonValue& operator=(const JsonValue&) = delete; @@ -45,12 +204,21 @@ public: return m_pDocument != nullptr; } + size_t GetDocumentRefCount() const { + if (m_pDocument_mut) { + return m_pDocument_mut.use_count(); + } else if (m_pDocument) { + return m_pDocument.use_count(); + } + return 0; + } + // Mutable document - std::shared_ptr m_pDocument_mut; + RefPtr m_pDocument_mut; yyjson_mut_val* m_pVal_mut{ nullptr }; // Immutable document - std::shared_ptr m_pDocument; + RefPtr m_pDocument; yyjson_val* m_pVal{ nullptr }; // Mutable document iterators @@ -84,12 +252,13 @@ public: return m_isMutable; } - std::shared_ptr m_pDocument_mut; - std::shared_ptr m_pDocument; + RefPtr m_pDocument_mut; + RefPtr m_pDocument; yyjson_mut_arr_iter m_iterMut; - yyjson_arr_iter m_iterImm; + yyjson_mut_val* m_rootMut{ nullptr }; + yyjson_val* m_rootImm{ nullptr }; Handle_t m_handle{ BAD_HANDLE }; bool m_isMutable{ false }; @@ -113,12 +282,13 @@ public: return m_isMutable; } - std::shared_ptr m_pDocument_mut; - std::shared_ptr m_pDocument; + RefPtr m_pDocument_mut; + RefPtr m_pDocument; yyjson_mut_obj_iter m_iterMut; - yyjson_obj_iter m_iterImm; + yyjson_mut_val* m_rootMut{ nullptr }; + yyjson_val* m_rootImm{ nullptr }; void* m_currentKey{ nullptr }; @@ -139,6 +309,15 @@ public: yyjson_read_flag read_flg, char* error, size_t error_size) override; virtual bool WriteToString(JsonValue* handle, char* buffer, size_t buffer_size, yyjson_write_flag write_flg, size_t* out_size) override; + virtual char* WriteToStringPtr(JsonValue* handle, yyjson_write_flag write_flg, size_t* out_size) override; + virtual JsonValue* ApplyJsonPatch(JsonValue* target, JsonValue* patch, bool result_mutable, + char* error, size_t error_size) override; + virtual bool JsonPatchInPlace(JsonValue* target, JsonValue* patch, + char* error, size_t error_size) override; + virtual JsonValue* ApplyMergePatch(JsonValue* target, JsonValue* patch, bool result_mutable, + char* error, size_t error_size) override; + virtual bool MergePatchInPlace(JsonValue* target, JsonValue* patch, + char* error, size_t error_size) override; virtual bool WriteToFile(JsonValue* handle, const char* path, yyjson_write_flag write_flg, char* error, size_t error_size) override; virtual bool Equals(JsonValue* handle1, JsonValue* handle2) override; @@ -166,6 +345,7 @@ public: virtual bool IsMutable(JsonValue* handle) override; virtual bool IsImmutable(JsonValue* handle) override; virtual size_t GetReadSize(JsonValue* handle) override; + virtual size_t GetRefCount(JsonValue* handle) override; // ========== Object Operations ========== virtual JsonValue* ObjectInit() override; @@ -250,13 +430,12 @@ public: virtual bool ArrayRemove(JsonValue* handle, size_t index) override; virtual bool ArrayRemoveFirst(JsonValue* handle) override; virtual bool ArrayRemoveLast(JsonValue* handle) override; - virtual bool ArrayRemoveRange(JsonValue* handle, size_t start_index, size_t end_index) override; + virtual bool ArrayRemoveRange(JsonValue* handle, size_t start_index, size_t count) override; virtual bool ArrayClear(JsonValue* handle) override; virtual int ArrayIndexOfBool(JsonValue* handle, bool search_value) override; virtual int ArrayIndexOfString(JsonValue* handle, const char* search_value) override; virtual int ArrayIndexOfInt(JsonValue* handle, int search_value) override; - virtual int ArrayIndexOfInt64(JsonValue* handle, int64_t search_value) override; - virtual int ArrayIndexOfUint64(JsonValue* handle, uint64_t search_value) override; + virtual int ArrayIndexOfInt64(JsonValue* handle, std::variant search_value) override; virtual int ArrayIndexOfFloat(JsonValue* handle, double search_value) override; virtual bool ArraySort(JsonValue* handle, JSON_SORT_ORDER sort_mode) override; @@ -317,6 +496,7 @@ public: // ========== Array Iterator Operations ========== virtual JsonArrIter* ArrIterInit(JsonValue* handle) override; virtual JsonArrIter* ArrIterWith(JsonValue* handle) override; + virtual bool ArrIterReset(JsonArrIter* iter) override; virtual JsonValue* ArrIterNext(JsonArrIter* iter) override; virtual bool ArrIterHasNext(JsonArrIter* iter) override; virtual size_t ArrIterGetIndex(JsonArrIter* iter) override; @@ -325,6 +505,7 @@ public: // ========== Object Iterator Operations ========== virtual JsonObjIter* ObjIterInit(JsonValue* handle) override; virtual JsonObjIter* ObjIterWith(JsonValue* handle) override; + virtual bool ObjIterReset(JsonObjIter* iter) override; virtual void* ObjIterNext(JsonObjIter* iter) override; virtual bool ObjIterHasNext(JsonObjIter* iter) override; virtual JsonValue* ObjIterGetVal(JsonObjIter* iter, void* key) override; @@ -378,10 +559,11 @@ private: // Helper methods static std::unique_ptr CreateWrapper(); - static std::shared_ptr WrapDocument(yyjson_mut_doc* doc); - static std::shared_ptr CopyDocument(yyjson_doc* doc); - static std::shared_ptr CreateDocument(); - static std::shared_ptr WrapImmutableDocument(yyjson_doc* doc); + static RefPtr WrapDocument(yyjson_mut_doc* doc); + static RefPtr CopyDocument(yyjson_doc* doc); + static RefPtr CreateDocument(); + static RefPtr WrapImmutableDocument(yyjson_doc* doc); + static RefPtr CloneValueToMutable(JsonValue* value); // Pack helper methods static const char* SkipSeparators(const char* ptr); @@ -398,4 +580,4 @@ private: static PtrGetValueResult PtrGetValueInternal(JsonValue* handle, const char* path); }; -#endif // _INCLUDE_SM_JSON_JSONMANAGER_H_ \ No newline at end of file +#endif // _INCLUDE_JSONMANAGER_H_ \ No newline at end of file diff --git a/extensions/json/JsonNatives.cpp b/extensions/json/JsonNatives.cpp index c90eba564..c964a434e 100755 --- a/extensions/json/JsonNatives.cpp +++ b/extensions/json/JsonNatives.cpp @@ -147,7 +147,14 @@ static cell_t CreateAndReturnObjIterHandle(IPluginContext* pContext, JsonObjIter return handle; } -static cell_t json_pack(IPluginContext* pContext, const cell_t* params) { +static cell_t json_pack(IPluginContext* pContext, const cell_t* params) +{ + // SourcePawn has a limit of 32 parameters (defined SP_MAX_EXEC_PARAMS) + // including the format string, so we need to check if the number of parameters is less than the limit + if (params[0] > SP_MAX_EXEC_PARAMS - 1) { + return pContext->ThrowNativeError("Too many parameters (max %d)", SP_MAX_EXEC_PARAMS - 1); + } + char* fmt; pContext->LocalToString(params[1], &fmt); @@ -170,7 +177,7 @@ static cell_t json_doc_parse(IPluginContext* pContext, const cell_t* params) bool is_file = params[2]; bool is_mutable_doc = params[3]; - yyjson_read_flag read_flg = static_cast(params[4]); + uint32_t read_flg = static_cast(params[4]); char error[JSON_ERROR_BUFFER_SIZE]; JsonValue* pJSONValue = g_pJsonManager->ParseJSON(str, is_file, is_mutable_doc, read_flg, error, sizeof(error)); @@ -224,7 +231,7 @@ static cell_t json_obj_parse_str(IPluginContext* pContext, const cell_t* params) { char* str; pContext->LocalToString(params[1], &str); - yyjson_read_flag read_flg = static_cast(params[2]); + uint32_t read_flg = static_cast(params[2]); char error[JSON_PACK_ERROR_SIZE]; JsonValue* pJSONValue = g_pJsonManager->ObjectParseString(str, read_flg, error, sizeof(error)); @@ -240,7 +247,7 @@ static cell_t json_obj_parse_file(IPluginContext* pContext, const cell_t* params { char* path; pContext->LocalToString(params[1], &path); - yyjson_read_flag read_flg = static_cast(params[2]); + uint32_t read_flg = static_cast(params[2]); char error[JSON_PACK_ERROR_SIZE]; JsonValue* pJSONValue = g_pJsonManager->ObjectParseFile(path, read_flg, error, sizeof(error)); @@ -256,7 +263,7 @@ static cell_t json_arr_parse_str(IPluginContext* pContext, const cell_t* params) { char* str; pContext->LocalToString(params[1], &str); - yyjson_read_flag read_flg = static_cast(params[2]); + uint32_t read_flg = static_cast(params[2]); char error[JSON_PACK_ERROR_SIZE]; JsonValue* pJSONValue = g_pJsonManager->ArrayParseString(str, read_flg, error, sizeof(error)); @@ -272,7 +279,7 @@ static cell_t json_arr_parse_file(IPluginContext* pContext, const cell_t* params { char* path; pContext->LocalToString(params[1], &path); - yyjson_read_flag read_flg = static_cast(params[2]); + uint32_t read_flg = static_cast(params[2]); char error[JSON_PACK_ERROR_SIZE]; JsonValue* pJSONValue = g_pJsonManager->ArrayParseFile(path, read_flg, error, sizeof(error)); @@ -325,35 +332,14 @@ static cell_t json_arr_index_of_integer64(IPluginContext* pContext, const cell_t char* searchStr; pContext->LocalToString(params[2], &searchStr); - char* endptr; - errno = 0; - long long searchValue = strtoll(searchStr, &endptr, 10); + std::variant variant_value; + char error[JSON_ERROR_BUFFER_SIZE]; - if (errno == ERANGE || *endptr != '\0') { - return pContext->ThrowNativeError("Invalid integer64 value: %s", searchStr); + if (!g_pJsonManager->ParseInt64Variant(searchStr, &variant_value, error, sizeof(error))) { + return pContext->ThrowNativeError("%s", error); } - return g_pJsonManager->ArrayIndexOfInt64(handle, searchValue); -} - -static cell_t json_arr_index_of_uint64(IPluginContext* pContext, const cell_t* params) -{ - JsonValue* handle = g_pJsonManager->GetFromHandle(pContext, params[1]); - - if (!handle) return 0; - - char* searchStr; - pContext->LocalToString(params[2], &searchStr); - - char* endptr; - errno = 0; - unsigned long long searchValue = strtoull(searchStr, &endptr, 10); - - if (errno == ERANGE || *endptr != '\0') { - return pContext->ThrowNativeError("Invalid unsigned integer64 value: %s", searchStr); - } - - return g_pJsonManager->ArrayIndexOfUint64(handle, searchValue); + return g_pJsonManager->ArrayIndexOfInt64(handle, variant_value); } static cell_t json_arr_index_of_float(IPluginContext* pContext, const cell_t* params) @@ -362,7 +348,7 @@ static cell_t json_arr_index_of_float(IPluginContext* pContext, const cell_t* pa if (!handle) return 0; - double searchValue = static_cast(sp_ctof(params[2])); + float searchValue = sp_ctof(params[2]); return g_pJsonManager->ArrayIndexOfFloat(handle, searchValue); } @@ -597,6 +583,7 @@ static cell_t json_create_integer64(IPluginContext* pContext, const cell_t* para std::variant variant_value; char error[JSON_ERROR_BUFFER_SIZE]; + if (!g_pJsonManager->ParseInt64Variant(value, &variant_value, error, sizeof(error))) { return pContext->ThrowNativeError("%s", error); } @@ -694,8 +681,8 @@ static cell_t json_get_str(IPluginContext* pContext, const cell_t* params) if (!handle) return 0; - const char* str = nullptr; - size_t len = 0; + const char* str; + size_t len; if (!g_pJsonManager->GetString(handle, &str, &len)) { return pContext->ThrowNativeError("Type mismatch: expected string value"); @@ -729,7 +716,7 @@ static cell_t json_get_serialized_size(IPluginContext* pContext, const cell_t* p if (!handle) return 0; - yyjson_write_flag write_flg = static_cast(params[2]); + uint32_t write_flg = static_cast(params[2]); size_t size = g_pJsonManager->GetSerializedSize(handle, write_flg); return static_cast(size); @@ -747,6 +734,15 @@ static cell_t json_get_read_size(IPluginContext* pContext, const cell_t* params) return static_cast(size); } +static cell_t json_get_ref_count(IPluginContext* pContext, const cell_t* params) +{ + JsonValue* handle = g_pJsonManager->GetFromHandle(pContext, params[1]); + + if (!handle) return 0; + + return g_pJsonManager->GetRefCount(handle); +} + static cell_t json_create_null(IPluginContext* pContext, const cell_t* params) { JsonValue* pJSONValue = g_pJsonManager->CreateNull(); @@ -890,7 +886,11 @@ static cell_t json_arr_get_val(IPluginContext* pContext, const cell_t* params) if (!handle) return 0; - size_t index = static_cast(params[2]); + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); JsonValue* pJSONValue = g_pJsonManager->ArrayGet(handle, index); @@ -937,7 +937,11 @@ static cell_t json_arr_get_bool(IPluginContext* pContext, const cell_t* params) if (!handle) return 0; - size_t index = static_cast(params[2]); + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); bool value; if (!g_pJsonManager->ArrayGetBool(handle, index, &value)) { @@ -953,7 +957,11 @@ static cell_t json_arr_get_float(IPluginContext* pContext, const cell_t* params) if (!handle) return 0; - size_t index = static_cast(params[2]); + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); double value; if (!g_pJsonManager->ArrayGetFloat(handle, index, &value)) { @@ -969,7 +977,11 @@ static cell_t json_arr_get_integer(IPluginContext* pContext, const cell_t* param if (!handle) return 0; - size_t index = static_cast(params[2]); + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); int value; if (!g_pJsonManager->ArrayGetInt(handle, index, &value)) { @@ -985,9 +997,13 @@ static cell_t json_arr_get_integer64(IPluginContext* pContext, const cell_t* par if (!handle) return 0; - size_t index = static_cast(params[2]); - std::variant value; + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); + std::variant value; if (!g_pJsonManager->ArrayGetInt64(handle, index, &value)) { return pContext->ThrowNativeError("Failed to get integer64 at index %d", index); } @@ -1009,10 +1025,14 @@ static cell_t json_arr_get_str(IPluginContext* pContext, const cell_t* params) if (!handle) return 0; - size_t index = static_cast(params[2]); - const char* str = nullptr; - size_t len = 0; + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); + const char* str; + size_t len; if (!g_pJsonManager->ArrayGetString(handle, index, &str, &len)) { return pContext->ThrowNativeError("Failed to get string at index %d", index); } @@ -1033,7 +1053,11 @@ static cell_t json_arr_is_null(IPluginContext* pContext, const cell_t* params) if (!handle) return 0; - size_t index = static_cast(params[2]); + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); return g_pJsonManager->ArrayIsNull(handle, index); } @@ -1049,7 +1073,12 @@ static cell_t json_arr_replace_val(IPluginContext* pContext, const cell_t* param return pContext->ThrowNativeError("Cannot replace value in an immutable JSON array"); } - size_t index = static_cast(params[2]); + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); + return g_pJsonManager->ArrayReplace(handle1, index, handle2); } @@ -1063,7 +1092,12 @@ static cell_t json_arr_replace_bool(IPluginContext* pContext, const cell_t* para return pContext->ThrowNativeError("Cannot replace value in an immutable JSON array"); } - size_t index = static_cast(params[2]); + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); + return g_pJsonManager->ArrayReplaceBool(handle, index, params[3]); } @@ -1077,7 +1111,12 @@ static cell_t json_arr_replace_float(IPluginContext* pContext, const cell_t* par return pContext->ThrowNativeError("Cannot replace value in an immutable JSON array"); } - size_t index = static_cast(params[2]); + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); + return g_pJsonManager->ArrayReplaceFloat(handle, index, sp_ctof(params[3])); } @@ -1091,7 +1130,12 @@ static cell_t json_arr_replace_integer(IPluginContext* pContext, const cell_t* p return pContext->ThrowNativeError("Cannot replace value in an immutable JSON array"); } - size_t index = static_cast(params[2]); + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); + return g_pJsonManager->ArrayReplaceInt(handle, index, params[3]); } @@ -1110,11 +1154,17 @@ static cell_t json_arr_replace_integer64(IPluginContext* pContext, const cell_t* std::variant variant_value; char error[JSON_ERROR_BUFFER_SIZE]; + if (!g_pJsonManager->ParseInt64Variant(value, &variant_value, error, sizeof(error))) { return pContext->ThrowNativeError("%s", error); } - size_t index = static_cast(params[2]); + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); + return g_pJsonManager->ArrayReplaceInt64(handle, index, variant_value); } @@ -1128,7 +1178,12 @@ static cell_t json_arr_replace_null(IPluginContext* pContext, const cell_t* para return pContext->ThrowNativeError("Cannot replace value in an immutable JSON array"); } - size_t index = static_cast(params[2]); + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); + return g_pJsonManager->ArrayReplaceNull(handle, index); } @@ -1145,7 +1200,12 @@ static cell_t json_arr_replace_str(IPluginContext* pContext, const cell_t* param char* val; pContext->LocalToString(params[3], &val); - size_t index = static_cast(params[2]); + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); + return g_pJsonManager->ArrayReplaceString(handle, index, val); } @@ -1217,6 +1277,7 @@ static cell_t json_arr_append_integer64(IPluginContext* pContext, const cell_t* std::variant variant_value; char error[JSON_ERROR_BUFFER_SIZE]; + if (!g_pJsonManager->ParseInt64Variant(value, &variant_value, error, sizeof(error))) { return pContext->ThrowNativeError("%s", error); } @@ -1262,7 +1323,17 @@ static cell_t json_arr_insert(IPluginContext* pContext, const cell_t* params) return pContext->ThrowNativeError("Cannot insert value into an immutable JSON array"); } - size_t index = params[2]; + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); + + size_t arr_size = g_pJsonManager->ArrayGetSize(handle); + if (index > arr_size) { + return pContext->ThrowNativeError("Index is out of bounds (got %d, max %zu)", index, arr_size); + } + JsonValue* value = g_pJsonManager->GetFromHandle(pContext, params[3]); if (!value) return 0; @@ -1278,7 +1349,17 @@ static cell_t json_arr_insert_bool(IPluginContext* pContext, const cell_t* param return pContext->ThrowNativeError("Cannot insert value into an immutable JSON array"); } - size_t index = params[2]; + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); + + size_t arr_size = g_pJsonManager->ArrayGetSize(handle); + if (index > arr_size) { + return pContext->ThrowNativeError("Index is out of bounds (got %d, max %zu)", index, arr_size); + } + bool value = params[3] != 0; return g_pJsonManager->ArrayInsertBool(handle, index, value); @@ -1293,7 +1374,17 @@ static cell_t json_arr_insert_int(IPluginContext* pContext, const cell_t* params return pContext->ThrowNativeError("Cannot insert value into an immutable JSON array"); } - size_t index = params[2]; + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); + + size_t arr_size = g_pJsonManager->ArrayGetSize(handle); + if (index > arr_size) { + return pContext->ThrowNativeError("Index is out of bounds (got %d, max %zu)", index, arr_size); + } + int value = params[3]; return g_pJsonManager->ArrayInsertInt(handle, index, value); @@ -1308,12 +1399,23 @@ static cell_t json_arr_insert_int64(IPluginContext* pContext, const cell_t* para return pContext->ThrowNativeError("Cannot insert value into an immutable JSON array"); } - size_t index = params[2]; + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); + + size_t arr_size = g_pJsonManager->ArrayGetSize(handle); + if (index > arr_size) { + return pContext->ThrowNativeError("Index is out of bounds (got %d, max %zu)", index, arr_size); + } + char* value; pContext->LocalToString(params[3], &value); std::variant variant_value; char error[JSON_ERROR_BUFFER_SIZE]; + if (!g_pJsonManager->ParseInt64Variant(value, &variant_value, error, sizeof(error))) { return pContext->ThrowNativeError("%s", error); } @@ -1330,8 +1432,18 @@ static cell_t json_arr_insert_float(IPluginContext* pContext, const cell_t* para return pContext->ThrowNativeError("Cannot insert value into an immutable JSON array"); } - size_t index = params[2]; - double value = sp_ctof(params[3]); + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); + + size_t arr_size = g_pJsonManager->ArrayGetSize(handle); + if (index > arr_size) { + return pContext->ThrowNativeError("Index is out of bounds (got %d, max %zu)", index, arr_size); + } + + float value = sp_ctof(params[3]); return g_pJsonManager->ArrayInsertFloat(handle, index, value); } @@ -1345,7 +1457,17 @@ static cell_t json_arr_insert_str(IPluginContext* pContext, const cell_t* params return pContext->ThrowNativeError("Cannot insert value into an immutable JSON array"); } - size_t index = params[2]; + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); + + size_t arr_size = g_pJsonManager->ArrayGetSize(handle); + if (index > arr_size) { + return pContext->ThrowNativeError("Index is out of bounds (got %d, max %zu)", index, arr_size); + } + char* str; pContext->LocalToString(params[3], &str); @@ -1361,7 +1483,17 @@ static cell_t json_arr_insert_null(IPluginContext* pContext, const cell_t* param return pContext->ThrowNativeError("Cannot insert value into an immutable JSON array"); } - size_t index = params[2]; + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); + + size_t arr_size = g_pJsonManager->ArrayGetSize(handle); + if (index > arr_size) { + return pContext->ThrowNativeError("Index is out of bounds (got %d, max %zu)", index, arr_size); + } + return g_pJsonManager->ArrayInsertNull(handle, index); } @@ -1423,6 +1555,7 @@ static cell_t json_arr_prepend_int64(IPluginContext* pContext, const cell_t* par std::variant variant_value; char error[JSON_ERROR_BUFFER_SIZE]; + if (!g_pJsonManager->ParseInt64Variant(value, &variant_value, error, sizeof(error))) { return pContext->ThrowNativeError("%s", error); } @@ -1439,7 +1572,7 @@ static cell_t json_arr_prepend_float(IPluginContext* pContext, const cell_t* par return pContext->ThrowNativeError("Cannot prepend value to an immutable JSON array"); } - double value = sp_ctof(params[2]); + float value = sp_ctof(params[2]); return g_pJsonManager->ArrayPrependFloat(handle, value); } @@ -1481,7 +1614,12 @@ static cell_t json_arr_remove(IPluginContext* pContext, const cell_t* params) return pContext->ThrowNativeError("Cannot remove value from an immutable JSON array"); } - size_t index = static_cast(params[2]); + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); + return g_pJsonManager->ArrayRemove(handle, index); } @@ -1521,10 +1659,18 @@ static cell_t json_arr_remove_range(IPluginContext* pContext, const cell_t* para return pContext->ThrowNativeError("Cannot remove value from an immutable JSON array"); } - size_t start_index = static_cast(params[2]); - size_t end_index = static_cast(params[3]); + cell_t start_index_param = params[2]; + cell_t count_param = params[3]; + if (start_index_param < 0) { + return pContext->ThrowNativeError("Start index must be >= 0 (got %d)", start_index_param); + } + if (count_param < 0) { + return pContext->ThrowNativeError("Count must be >= 0 (got %d)", count_param); + } - return g_pJsonManager->ArrayRemoveRange(handle, start_index, end_index); + size_t start_index = static_cast(start_index_param); + size_t count = static_cast(count_param); + return g_pJsonManager->ArrayRemoveRange(handle, start_index, count); } static cell_t json_arr_clear(IPluginContext* pContext, const cell_t* params) @@ -1547,22 +1693,24 @@ static cell_t json_doc_write_to_str(IPluginContext* pContext, const cell_t* para if (!handle) return 0; size_t buffer_size = static_cast(params[3]); - yyjson_write_flag write_flg = static_cast(params[4]); + uint32_t write_flg = static_cast(params[4]); - char* temp_buffer = (char*)malloc(buffer_size); - if (!temp_buffer) { - return pContext->ThrowNativeError("Failed to allocate buffer"); + size_t json_size; + char* json_str = g_pJsonManager->WriteToStringPtr(handle, write_flg, &json_size); + + if (!json_str) { + return pContext->ThrowNativeError("Failed to serialize JSON"); } - size_t output_len = 0; - if (!g_pJsonManager->WriteToString(handle, temp_buffer, buffer_size, write_flg, &output_len)) { - free(temp_buffer); - return pContext->ThrowNativeError("Buffer too small or write failed"); + if (json_size > buffer_size) { + free(json_str); + return pContext->ThrowNativeError("Buffer too small (need %d, have %d)", json_size, buffer_size); } - pContext->StringToLocalUTF8(params[2], buffer_size, temp_buffer, nullptr); - free(temp_buffer); - return static_cast(output_len); + pContext->StringToLocalUTF8(params[2], buffer_size, json_str, nullptr); + free(json_str); + + return static_cast(json_size); } static cell_t json_doc_write_to_file(IPluginContext* pContext, const cell_t* params) @@ -1573,7 +1721,7 @@ static cell_t json_doc_write_to_file(IPluginContext* pContext, const cell_t* par char* path; pContext->LocalToString(params[2], &path); - yyjson_write_flag write_flg = static_cast(params[3]); + uint32_t write_flg = static_cast(params[3]); char error[JSON_PACK_ERROR_SIZE]; if (!g_pJsonManager->WriteToFile(handle, path, write_flg, error, sizeof(error))) { @@ -1599,8 +1747,12 @@ static cell_t json_obj_get_key(IPluginContext* pContext, const cell_t* params) if (!handle) return 0; - size_t index = static_cast(params[2]); - const char* key = nullptr; + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); + const char* key; if (!g_pJsonManager->ObjectGetKey(handle, index, &key)) { return pContext->ThrowNativeError("Index %d is out of bounds", index); @@ -1616,7 +1768,11 @@ static cell_t json_obj_get_val_at(IPluginContext* pContext, const cell_t* params if (!handle) return 0; - size_t index = static_cast(params[2]); + cell_t index_param = params[2]; + if (index_param < 0) { + return pContext->ThrowNativeError("Index must be >= 0 (got %d)", index_param); + } + size_t index = static_cast(index_param); JsonValue* pJSONValue = g_pJsonManager->ObjectGetValueAt(handle, index); @@ -1730,8 +1886,8 @@ static cell_t json_obj_get_str(IPluginContext* pContext, const cell_t* params) char* key; pContext->LocalToString(params[2], &key); - const char* str = nullptr; - size_t len = 0; + const char* str; + size_t len; if (!g_pJsonManager->ObjectGetString(handle, key, &str, &len)) { return pContext->ThrowNativeError("Failed to get string for key '%s'", key); } @@ -1768,7 +1924,7 @@ static cell_t json_obj_is_null(IPluginContext* pContext, const cell_t* params) char* key; pContext->LocalToString(params[2], &key); - bool is_null = false; + bool is_null; if (!g_pJsonManager->ObjectIsNull(handle, key, &is_null)) { return pContext->ThrowNativeError("Key not found: %s", key); } @@ -1896,6 +2052,7 @@ static cell_t json_obj_set_integer64(IPluginContext* pContext, const cell_t* par std::variant variant_value; char error[JSON_ERROR_BUFFER_SIZE]; + if (!g_pJsonManager->ParseInt64Variant(value, &variant_value, error, sizeof(error))) { return pContext->ThrowNativeError("%s", error); } @@ -2060,8 +2217,8 @@ static cell_t json_ptr_get_str(IPluginContext* pContext, const cell_t* params) char* path; pContext->LocalToString(params[2], &path); - const char* str = nullptr; - size_t len = 0; + const char* str; + size_t len; char error[JSON_PACK_ERROR_SIZE]; if (!g_pJsonManager->PtrGetString(handle, path, &str, &len, error, sizeof(error))) { return pContext->ThrowNativeError("%s", error); @@ -2214,6 +2371,7 @@ static cell_t json_ptr_set_integer64(IPluginContext* pContext, const cell_t* par std::variant variant_value; char error[JSON_PACK_ERROR_SIZE]; + if (!g_pJsonManager->ParseInt64Variant(value, &variant_value, error, sizeof(error))) { return pContext->ThrowNativeError("%s", error); } @@ -2369,6 +2527,7 @@ static cell_t json_ptr_add_integer64(IPluginContext* pContext, const cell_t* par std::variant variant_value; char error[JSON_PACK_ERROR_SIZE]; + if (!g_pJsonManager->ParseInt64Variant(value, &variant_value, error, sizeof(error))) { return pContext->ThrowNativeError("%s", error); } @@ -2554,8 +2713,8 @@ static cell_t json_ptr_try_get_str(IPluginContext* pContext, const cell_t* param char* path; pContext->LocalToString(params[2], &path); - const char* str = nullptr; - size_t len = 0; + const char* str; + size_t len; if (!g_pJsonManager->PtrTryGetString(handle, path, &str, &len)) { return 0; @@ -2576,8 +2735,8 @@ static cell_t json_obj_foreach(IPluginContext* pContext, const cell_t* params) JsonValue* handle = g_pJsonManager->GetFromHandle(pContext, params[1]); if (!handle) return 0; - const char* key = nullptr; - JsonValue* pJSONValue = nullptr; + const char* key; + JsonValue* pJSONValue; if (!g_pJsonManager->ObjectForeachNext(handle, &key, nullptr, &pJSONValue)) { return false; @@ -2593,8 +2752,8 @@ static cell_t json_arr_foreach(IPluginContext* pContext, const cell_t* params) JsonValue* handle = g_pJsonManager->GetFromHandle(pContext, params[1]); if (!handle) return 0; - size_t index = 0; - JsonValue* pJSONValue = nullptr; + size_t index; + JsonValue* pJSONValue; if (!g_pJsonManager->ArrayForeachNext(handle, &index, &pJSONValue)) { return false; @@ -2612,7 +2771,7 @@ static cell_t json_obj_foreach_key(IPluginContext* pContext, const cell_t* param JsonValue* handle = g_pJsonManager->GetFromHandle(pContext, params[1]); if (!handle) return 0; - const char* key = nullptr; + const char* key; if (!g_pJsonManager->ObjectForeachKeyNext(handle, &key, nullptr)) { return false; @@ -2628,7 +2787,7 @@ static cell_t json_arr_foreach_index(IPluginContext* pContext, const cell_t* par JsonValue* handle = g_pJsonManager->GetFromHandle(pContext, params[1]); if (!handle) return 0; - size_t index = 0; + size_t index; if (!g_pJsonManager->ArrayForeachIndexNext(handle, &index)) { return false; @@ -2715,6 +2874,84 @@ static cell_t json_doc_to_immutable(IPluginContext* pContext, const cell_t* para return CreateAndReturnHandle(pContext, pJSONValue, "immutable JSON document"); } +static cell_t json_apply_json_patch(IPluginContext* pContext, const cell_t* params) +{ + JsonValue* target = g_pJsonManager->GetFromHandle(pContext, params[1]); + JsonValue* patch = g_pJsonManager->GetFromHandle(pContext, params[2]); + + if (!target || !patch) return 0; + + bool resultMutable = params[3] != 0; + char error[JSON_ERROR_BUFFER_SIZE] = {0}; + + JsonValue* result = g_pJsonManager->ApplyJsonPatch(target, patch, resultMutable, error, sizeof(error)); + if (!result) { + if (error[0] != '\0') { + return pContext->ThrowNativeError("%s", error); + } + return pContext->ThrowNativeError("Failed to apply JSON Patch"); + } + + return CreateAndReturnHandle(pContext, result, "JSON patch result"); +} + +static cell_t json_json_patch_in_place(IPluginContext* pContext, const cell_t* params) +{ + JsonValue* target = g_pJsonManager->GetFromHandle(pContext, params[1]); + JsonValue* patch = g_pJsonManager->GetFromHandle(pContext, params[2]); + + if (!target || !patch) return 0; + + char error[JSON_ERROR_BUFFER_SIZE] = {0}; + if (!g_pJsonManager->JsonPatchInPlace(target, patch, error, sizeof(error))) { + if (error[0] != '\0') { + return pContext->ThrowNativeError("%s", error); + } + return pContext->ThrowNativeError("Failed to apply JSON Patch in place"); + } + + return 1; +} + +static cell_t json_apply_merge_patch(IPluginContext* pContext, const cell_t* params) +{ + JsonValue* target = g_pJsonManager->GetFromHandle(pContext, params[1]); + JsonValue* patch = g_pJsonManager->GetFromHandle(pContext, params[2]); + + if (!target || !patch) return 0; + + bool resultMutable = params[3] != 0; + char error[JSON_ERROR_BUFFER_SIZE] = {0}; + + JsonValue* result = g_pJsonManager->ApplyMergePatch(target, patch, resultMutable, error, sizeof(error)); + if (!result) { + if (error[0] != '\0') { + return pContext->ThrowNativeError("%s", error); + } + return pContext->ThrowNativeError("Failed to apply JSON Merge Patch"); + } + + return CreateAndReturnHandle(pContext, result, "JSON merge patch result"); +} + +static cell_t json_merge_patch_in_place(IPluginContext* pContext, const cell_t* params) +{ + JsonValue* target = g_pJsonManager->GetFromHandle(pContext, params[1]); + JsonValue* patch = g_pJsonManager->GetFromHandle(pContext, params[2]); + + if (!target || !patch) return 0; + + char error[JSON_ERROR_BUFFER_SIZE] = {0}; + if (!g_pJsonManager->MergePatchInPlace(target, patch, error, sizeof(error))) { + if (error[0] != '\0') { + return pContext->ThrowNativeError("%s", error); + } + return pContext->ThrowNativeError("Failed to apply JSON Merge Patch in place"); + } + + return 1; +} + static cell_t json_arr_iter_init(IPluginContext* pContext, const cell_t* params) { JsonValue* handle = g_pJsonManager->GetFromHandle(pContext, params[1]); @@ -2769,6 +3006,14 @@ static cell_t json_arr_iter_remove(IPluginContext* pContext, const cell_t* param return removed != nullptr; } +static cell_t json_arr_iter_reset(IPluginContext* pContext, const cell_t* params) +{ + JsonArrIter* iter = g_pJsonManager->GetArrIterFromHandle(pContext, params[1]); + if (!iter) return 0; + + return g_pJsonManager->ArrIterReset(iter); +} + static cell_t json_obj_iter_init(IPluginContext* pContext, const cell_t* params) { JsonValue* handle = g_pJsonManager->GetFromHandle(pContext, params[1]); @@ -2787,12 +3032,8 @@ static cell_t json_obj_iter_next(IPluginContext* pContext, const cell_t* params) if (!key) return 0; const char* key_str; - if (iter->IsMutable()) { - key_str = yyjson_mut_get_str(reinterpret_cast(key)); - } else { - key_str = yyjson_get_str(reinterpret_cast(key)); - } - + size_t key_len; + g_pJsonManager->GetString(reinterpret_cast(key), &key_str, &key_len); pContext->StringToLocalUTF8(params[2], params[3], key_str, nullptr); return 1; } @@ -2810,13 +3051,7 @@ static cell_t json_obj_iter_get_val(IPluginContext* pContext, const cell_t* para JsonObjIter* iter = g_pJsonManager->GetObjIterFromHandle(pContext, params[1]); if (!iter) return 0; - void* key = nullptr; - if (iter->IsMutable()) { - key = iter->m_currentKey; - } else { - key = iter->m_currentKey; - } - + void* key = iter->m_currentKey; if (!key) { return pContext->ThrowNativeError("Iterator not positioned at a valid key (call Next() first)"); } @@ -2871,14 +3106,22 @@ static cell_t json_obj_iter_remove(IPluginContext* pContext, const cell_t* param return removed != nullptr; } +static cell_t json_obj_iter_reset(IPluginContext* pContext, const cell_t* params) +{ + JsonObjIter* iter = g_pJsonManager->GetObjIterFromHandle(pContext, params[1]); + if (!iter) return 0; + + return g_pJsonManager->ObjIterReset(iter); +} + static cell_t json_read_number(IPluginContext* pContext, const cell_t* params) { char* dat; pContext->LocalToString(params[1], &dat); - yyjson_read_flag read_flg = static_cast(params[2]); + uint32_t read_flg = static_cast(params[2]); char error[JSON_ERROR_BUFFER_SIZE]; - size_t consumed = 0; + size_t consumed; JsonValue* pJSONValue = g_pJsonManager->ReadNumber(dat, read_flg, error, sizeof(error), &consumed); if (!pJSONValue) { @@ -2901,7 +3144,12 @@ static cell_t json_write_number(IPluginContext* pContext, const cell_t* params) JsonValue* handle = g_pJsonManager->GetFromHandle(pContext, params[1]); if (!handle) return 0; - size_t buffer_size = static_cast(params[3]); + cell_t buffer_size_param = params[3]; + if (buffer_size_param <= 0) { + return pContext->ThrowNativeError("Buffer size must be > 0 (got %d)", buffer_size_param); + } + + size_t buffer_size = static_cast(buffer_size_param); char* temp_buffer = (char*)malloc(buffer_size); if (!temp_buffer) { return pContext->ThrowNativeError("Failed to allocate buffer"); @@ -2946,8 +3194,12 @@ static cell_t json_set_fp_to_fixed(IPluginContext* pContext, const cell_t* param if (!handle) return 0; int prec = params[2]; + if (prec < 1 || prec > 7) { + return pContext->ThrowNativeError("Precision out of range (1-7)"); + } + if (!g_pJsonManager->SetFpToFixed(handle, prec)) { - return pContext->ThrowNativeError("Failed to set floating-point format to fixed (value is not a floating-point number or precision out of range 1-15)"); + return pContext->ThrowNativeError("Failed to set floating-point format to fixed (value is not a floating-point number or precision out of range 1-7)"); } return 1; @@ -2989,6 +3241,7 @@ static cell_t json_set_int64(IPluginContext* pContext, const cell_t* params) std::variant variant_value; char error[JSON_ERROR_BUFFER_SIZE]; + if (!g_pJsonManager->ParseInt64Variant(str, &variant_value, error, sizeof(error))) { return pContext->ThrowNativeError("%s", error); } @@ -3126,7 +3379,6 @@ const sp_nativeinfo_t g_JsonNatives[] = {"JSONArray.IndexOfString", json_arr_index_of_str}, {"JSONArray.IndexOfInt", json_arr_index_of_int}, {"JSONArray.IndexOfInt64", json_arr_index_of_integer64}, - {"JSONArray.IndexOfUint64", json_arr_index_of_uint64}, {"JSONArray.IndexOfFloat", json_arr_index_of_float}, {"JSONArray.Sort", json_arr_sort}, @@ -3140,6 +3392,7 @@ const sp_nativeinfo_t g_JsonNatives[] = {"JSON.GetTypeDesc", json_get_type_desc}, {"JSON.GetSerializedSize", json_get_serialized_size}, {"JSON.ReadSize.get", json_get_read_size}, + {"JSON.RefCount.get", json_get_ref_count}, {"JSON.Type.get", json_get_type}, {"JSON.SubType.get", json_get_subtype}, {"JSON.IsArray.get", json_is_array}, @@ -3163,6 +3416,10 @@ const sp_nativeinfo_t g_JsonNatives[] = {"JSON.ForeachIndex", json_arr_foreach_index}, {"JSON.ToMutable", json_doc_to_mutable}, {"JSON.ToImmutable", json_doc_to_immutable}, + {"JSON.ApplyJsonPatch", json_apply_json_patch}, + {"JSON.JsonPatchInPlace", json_json_patch_in_place}, + {"JSON.ApplyMergePatch", json_apply_merge_patch}, + {"JSON.MergePatchInPlace", json_merge_patch_in_place}, {"JSON.ReadNumber", json_read_number}, {"JSON.WriteNumber", json_write_number}, {"JSON.SetFpToFloat", json_set_fp_to_float}, @@ -3225,6 +3482,7 @@ const sp_nativeinfo_t g_JsonNatives[] = {"JSONArrIter.HasNext.get", json_arr_iter_has_next}, {"JSONArrIter.Index.get", json_arr_iter_get_index}, {"JSONArrIter.Remove", json_arr_iter_remove}, + {"JSONArrIter.Reset", json_arr_iter_reset}, // JSONObjIter {"JSONObjIter.JSONObjIter", json_obj_iter_init}, @@ -3234,6 +3492,7 @@ const sp_nativeinfo_t g_JsonNatives[] = {"JSONObjIter.Get", json_obj_iter_get}, {"JSONObjIter.Index.get", json_obj_iter_get_index}, {"JSONObjIter.Remove", json_obj_iter_remove}, + {"JSONObjIter.Reset", json_obj_iter_reset}, {nullptr, nullptr} }; \ No newline at end of file diff --git a/extensions/json/extension.cpp b/extensions/json/extension.cpp index 3a3ed0ccb..2c624acf0 100755 --- a/extensions/json/extension.cpp +++ b/extensions/json/extension.cpp @@ -42,7 +42,6 @@ bool JsonExtension::SDK_OnLoad(char* error, size_t maxlen, bool late) return false; } - // Delete the existing instance if it exists if (g_pJsonManager) { delete g_pJsonManager; g_pJsonManager = nullptr; diff --git a/extensions/json/smsdk_config.h b/extensions/json/smsdk_config.h index 71f619730..7abb641b7 100755 --- a/extensions/json/smsdk_config.h +++ b/extensions/json/smsdk_config.h @@ -3,7 +3,7 @@ #define SMEXT_CONF_NAME "SourceMod JSON Extension" #define SMEXT_CONF_DESCRIPTION "Provide JSON Native" -#define SMEXT_CONF_VERSION "1.1.5b" +#define SMEXT_CONF_VERSION "1.1.5e" #define SMEXT_CONF_AUTHOR "ProjectSky" #define SMEXT_CONF_URL "https://github.com/ProjectSky/sm-ext-yyjson" #define SMEXT_CONF_LOGTAG "json" diff --git a/plugins/include/json.inc b/plugins/include/json.inc index 6d8109618..10a298ae9 100755 --- a/plugins/include/json.inc +++ b/plugins/include/json.inc @@ -71,12 +71,17 @@ enum JSON_WRITE_FLAG JSON_WRITE_FP_TO_FLOAT = 1 << 27 // Write floating-point numbers using single-precision (float) } -/** Write floating-point number using fixed-point notation - - This is similar to ECMAScript Number.prototype.toFixed(prec) but with trailing zeros removed. The prec ranges from 1 to 15 - - This will produce shorter output but may lose some precision -*/ +/** + * Write floating-point number using fixed-point notation + * This is similar to ECMAScript Number.prototype.toFixed(prec) but with trailing zeros removed + * + * @param n Precision digits (1-7) + * @return JSON write flag with precision setting + * @note This will produce shorter output but may lose some precision + */ stock JSON_WRITE_FLAG JSON_WRITE_FP_TO_FIXED(int n) { + n = (n < 1) ? 1 : (n > 7 ? 7 : n); return view_as(n << 28); } @@ -103,6 +108,10 @@ methodmap JSON < Handle * - [: start array * - ]: end array * + * @note Needs to be freed using delete or CloseHandle() + * @note There is a limit of 32 parameters (defined in SourcePawn) + * including the format string, so the number of arguments must be less than or equal to 31 + * * @param format Format string * @param ... Arguments based on format string * @@ -178,6 +187,8 @@ methodmap JSON < Handle /** * Converts an immutable JSON document to a mutable one * + * @note Needs to be freed using delete or CloseHandle() + * * @return Handle to the new mutable JSON document, INVALID_HANDLE on failure * @error If the document is already mutable */ @@ -186,11 +197,59 @@ methodmap JSON < Handle /** * Converts a mutable JSON document to an immutable one * + * @note Needs to be freed using delete or CloseHandle() + * * @return Handle to the new immutable JSON document, INVALID_HANDLE on failure * @error If the document is already immutable */ public native any ToImmutable(); + /** + * Apply a JSON Patch (RFC 6902) to this value and return a new JSON handle + * + * @note Needs to be freed using delete or CloseHandle() + * + * @param patch JSON Patch document + * @param resultMutable True to return a mutable result, false for immutable + * + * @return New JSON handle on success + * @error Throws if the patch cannot be applied + */ + public native any ApplyJsonPatch(const JSON patch, bool resultMutable = false); + + /** + * Apply a JSON Patch (RFC 6902) to this value in place + * + * @param patch JSON Patch document + * + * @return True on success, false otherwise + * @error Throws if this value is immutable or patch failed + */ + public native bool JsonPatchInPlace(const JSON patch); + + /** + * Apply a JSON Merge Patch (RFC 7396) to this value and return a new JSON handle + * + * @note Needs to be freed using delete or CloseHandle() + * + * @param patch JSON Merge Patch document + * @param resultMutable True to return a mutable result, false for immutable + * + * @return New JSON handle on success + * @error Throws if the merge patch cannot be applied + */ + public native any ApplyMergePatch(const JSON patch, bool resultMutable = false); + + /** + * Apply a JSON Merge Patch (RFC 7396) to this value in place + * + * @param patch JSON Merge Patch document + * + * @return True on success, false otherwise + * @error Throws if this value is immutable or merge patch failed + */ + public native bool MergePatchInPlace(const JSON patch); + /** * Write a document to JSON file with options * @@ -233,7 +292,7 @@ methodmap JSON < Handle * Set floating-point number's output format to single-precision * * @note Only works on floating-point numbers (not integers) - * @note This affects how the number is serialized when using ToString() + * @note This affects how the number is serialized in all write operations * * @param flt True to use single-precision (float), false to use double-precision (double) * @@ -248,12 +307,12 @@ methodmap JSON < Handle * @note Only works on floating-point numbers (not integers) * @note This is similar to ECMAScript Number.prototype.toFixed(prec) but with trailing zeros removed * @note This will produce shorter output but may lose some precision - * @note This affects how the number is serialized when using ToString() + * @note This affects how the number is serialized in all write operations * - * @param prec Precision (1-15) + * @param prec Precision (1-7) * * @return True on success, false if handle is not a floating-point number or prec is out of range - * @error Invalid handle, handle is not a floating-point number, or precision out of range (1-15) + * @error Invalid handle, handle is not a floating-point number, or precision out of range (1-7) */ public native bool SetFpToFixed(int prec); @@ -381,14 +440,13 @@ methodmap JSON < Handle /** * Returns the JSON value's type description * - * @param value JSON handle * @param buffer String buffer to write to * @param maxlength Maximum length of the string buffer * * @return The return value should be one of these strings: "raw", "null", "string", * "array", "object", "true", "false", "uint", "sint", "real", "unknown" */ - public static native void GetTypeDesc(const JSON value, char[] buffer, int maxlength); + public native void GetTypeDesc(char[] buffer, int maxlength); /** * Returns whether two JSON values are equal (deep compare) @@ -396,8 +454,8 @@ methodmap JSON < Handle * @note This function is recursive and may cause a stack overflow if the object level is too deep * @note the result may be inaccurate if object has duplicate keys * - * @param value1 JSON handle - * @param value2 JSON handle + * @param value1 First JSON value to compare + * @param value2 Second JSON value to compare * * @return True if they are the same, false otherwise */ @@ -537,6 +595,7 @@ methodmap JSON < Handle * Get value by a JSON Pointer * * @note Needs to be freed using delete or CloseHandle() + * @note JSON Pointer paths are always resolved from the document root, not from the current value * * @param path The JSON pointer string * @@ -547,6 +606,8 @@ methodmap JSON < Handle /** * Get boolean value by a JSON Pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * * @return boolean value referenced by the JSON pointer @@ -556,6 +617,8 @@ methodmap JSON < Handle /** * Get float value by a JSON Pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * * @return float value referenced by the JSON pointer @@ -565,6 +628,8 @@ methodmap JSON < Handle /** * Get integer value by a JSON Pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * * @return integer value referenced by the JSON pointer @@ -574,17 +639,21 @@ methodmap JSON < Handle /** * Get integer64 value by a JSON Pointer (auto-detects signed/unsigned) * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * @param buffer Buffer to copy to * @param maxlength Maximum size of the buffer * - * @return integer64 value referenced by the JSON pointer + * @return True on success, false on failure */ public native bool PtrGetInt64(const char[] path, char[] buffer, int maxlength); /** * Get string value by a JSON Pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * @param buffer Buffer to copy to * @param maxlength Maximum size of the buffer @@ -596,6 +665,8 @@ methodmap JSON < Handle /** * Get value is null by a JSON Pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * * @return True if the value is null, false otherwise @@ -605,6 +676,7 @@ methodmap JSON < Handle /** * Get JSON content length (string length, array size, object size) * + * @note JSON Pointer paths are always resolved from the document root, not from the current value * @note For strings: returns string length including null-terminator * @note For arrays/objects: returns number of elements * @note Returns 0 if value is null or type is not string/array/object @@ -618,6 +690,7 @@ methodmap JSON < Handle /** * Set value by a JSON Pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value * @note The parent nodes will be created if they do not exist. * If the target value already exists, it will be replaced by the new value * @@ -631,6 +704,7 @@ methodmap JSON < Handle /** * Set boolean value by a JSON Pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value * @note The parent nodes will be created if they do not exist. * If the target value already exists, it will be replaced by the new value * @@ -644,6 +718,7 @@ methodmap JSON < Handle /** * Set float value by a JSON Pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value * @note The parent nodes will be created if they do not exist. * If the target value already exists, it will be replaced by the new value * @@ -657,6 +732,7 @@ methodmap JSON < Handle /** * Set integer value by a JSON Pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value * @note The parent nodes will be created if they do not exist. * If the target value already exists, it will be replaced by the new value * @@ -670,6 +746,7 @@ methodmap JSON < Handle /** * Set integer64 value by a JSON Pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value * @note The parent nodes will be created if they do not exist. * If the target value already exists, it will be replaced by the new value * @note This function auto-detects whether the value is signed or unsigned @@ -684,6 +761,7 @@ methodmap JSON < Handle /** * Set string value by a JSON Pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value * @note The parent nodes will be created if they do not exist. * If the target value already exists, it will be replaced by the new value * @@ -697,6 +775,7 @@ methodmap JSON < Handle /** * Set null value by a JSON Pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value * @note The parent nodes will be created if they do not exist. * If the target value already exists, it will be replaced by the new value * @@ -709,6 +788,8 @@ methodmap JSON < Handle /** * Add (insert) value by a JSON pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * @param value The value to be added * @@ -719,6 +800,8 @@ methodmap JSON < Handle /** * Add (insert) boolean value by a JSON pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * @param value The boolean value to be added * @@ -729,6 +812,8 @@ methodmap JSON < Handle /** * Add (insert) float value by a JSON pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * @param value The float value to be added * @@ -739,6 +824,8 @@ methodmap JSON < Handle /** * Add (insert) integer value by a JSON pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * @param value The int value to be added * @@ -749,6 +836,8 @@ methodmap JSON < Handle /** * Add (insert) integer64 value by a JSON pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * @param value The integer64 value to be added * @@ -759,6 +848,8 @@ methodmap JSON < Handle /** * Add (insert) string value by a JSON pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * @param value The str value to be added * @@ -769,6 +860,8 @@ methodmap JSON < Handle /** * Add (insert) null value by a JSON pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * * @return true if JSON pointer is valid and new value is set, false otherwise @@ -778,6 +871,8 @@ methodmap JSON < Handle /** * Remove value by a JSON pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * * @return true if removed value, false otherwise @@ -787,6 +882,8 @@ methodmap JSON < Handle /** * Try to get value by a JSON Pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * @param value Handle to store value * @@ -797,6 +894,8 @@ methodmap JSON < Handle /** * Try to get boolean value by a JSON Pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * @param value Store the boolean value * @@ -807,6 +906,8 @@ methodmap JSON < Handle /** * Try to get float value by a JSON Pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * @param value Store the float value * @@ -817,6 +918,8 @@ methodmap JSON < Handle /** * Try to get integer value by a JSON Pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * @param value Store the integer value * @@ -827,6 +930,8 @@ methodmap JSON < Handle /** * Try to get integer64 value by a JSON Pointer (automatically detects signed/unsigned) * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * @param buffer Buffer to store the integer64 value * @param maxlength Maximum length of the buffer @@ -838,6 +943,8 @@ methodmap JSON < Handle /** * Try to get string value by a JSON Pointer * + * @note JSON Pointer paths are always resolved from the document root, not from the current value + * * @param path The JSON pointer string * @param buffer Buffer to store the string value * @param maxlength Maximum length of the buffer @@ -978,6 +1085,17 @@ methodmap JSON < Handle property int ReadSize { public native get(); } + + /** + * Get the reference count of the document (debugging purpose) + * + * @return Reference count (number of JSON objects sharing this document) + * + * @note Returns 0 if the handle is invalid + */ + property int RefCount { + public native get(); + } }; methodmap JSONObject < JSON @@ -995,6 +1113,8 @@ methodmap JSONObject < JSON * The array must contain an even number of strings, where even indices are keys * and odd indices are values. Keys cannot be empty strings * + * @note Needs to be freed using delete or CloseHandle() + * * @param pairs Array of strings containing alternating keys and values * @param size Total size of the array (must be even) * @@ -1007,6 +1127,8 @@ methodmap JSONObject < JSON /** * Loads a JSON object from a file * + * @note Needs to be freed using delete or CloseHandle() + * * @param file File to read from * @param flag The JSON read options * @@ -1017,6 +1139,8 @@ methodmap JSONObject < JSON /** * Loads a JSON object from a string * + * @note Needs to be freed using delete or CloseHandle() + * * @param buffer String buffer to load into the JSON object * @param flag The JSON read options * @@ -1027,6 +1151,7 @@ methodmap JSONObject < JSON /** * Gets a value from the object * + * @note Needs to be freed using delete or CloseHandle() * @note This function takes a linear search time * * @param key Key name @@ -1262,6 +1387,8 @@ methodmap JSONArray < JSON /** * Creates a new JSON array from an array of strings * + * @note Needs to be freed using delete or CloseHandle() + * * @param strings Array of strings to create array from * @param size Size of the array * @return New JSON array handle, or null if creation failed @@ -1271,6 +1398,8 @@ methodmap JSONArray < JSON /** * Creates a new JSON array from an array of integer values * + * @note Needs to be freed using delete or CloseHandle() + * * @param values Array of int values to create array from * @param size Size of the array * @return New JSON array handle, or null if creation failed @@ -1280,6 +1409,8 @@ methodmap JSONArray < JSON /** * Creates a new JSON array from an array of 64-bit integer strings (auto-detects signed/unsigned) * + * @note Needs to be freed using delete or CloseHandle() + * * @param values Array of int64 string values (can be signed or unsigned) * @param size Size of the array * @return New JSON array handle, or null if creation failed @@ -1290,6 +1421,8 @@ methodmap JSONArray < JSON /** * Creates a new JSON array from an array of boolean values * + * @note Needs to be freed using delete or CloseHandle() + * * @param values Array of bool values to create array from * @param size Size of the array * @return New JSON array handle, or null if creation failed @@ -1299,6 +1432,8 @@ methodmap JSONArray < JSON /** * Creates a new JSON array from an array of float values * + * @note Needs to be freed using delete or CloseHandle() + * * @param values Array of float values to create array from * @param size Size of the array * @return New JSON array handle, or null if creation failed @@ -1308,6 +1443,8 @@ methodmap JSONArray < JSON /** * Loads a JSON array from a file * + * @note Needs to be freed using delete or CloseHandle() + * * @param file File to read from * @param flag Read flag * @return Array handle, or null on failure @@ -1317,6 +1454,8 @@ methodmap JSONArray < JSON /** * Loads a JSON array from a string * + * @note Needs to be freed using delete or CloseHandle() + * * @param buffer String buffer to load into the JSON array * @param flag Read flag * @return Array handle, or null on failure @@ -1326,6 +1465,7 @@ methodmap JSONArray < JSON /** * Gets a value from the array * + * @note Needs to be freed using delete or CloseHandle() * @note This function takes a linear search time * * @param index Position in the array (starting from 0) @@ -1368,9 +1508,9 @@ methodmap JSONArray < JSON * @param buffer Buffer to copy to * @param maxlength Maximum size of the buffer * - * @return 64-bit integer + * @return True on success, false on failure */ - public native void GetInt64(int index, char[] buffer, int maxlength); + public native bool GetInt64(int index, char[] buffer, int maxlength); /** * Gets string data from the array @@ -1470,7 +1610,7 @@ methodmap JSONArray < JSON * * @param value JSON handle to set * - * @return The value to be inserted. Returns false if it is null + * @return True if succeed, false otherwise */ public native bool Push(const JSON value); @@ -1479,7 +1619,7 @@ methodmap JSONArray < JSON * * @param value Boolean value to set * - * @return The value to be inserted. Returns false if it is null + * @return True if succeed, false otherwise */ public native bool PushBool(bool value); @@ -1488,7 +1628,7 @@ methodmap JSONArray < JSON * * @param value float to set * - * @return The value to be inserted. Returns false if it is null + * @return True if succeed, false otherwise */ public native bool PushFloat(float value); @@ -1497,7 +1637,7 @@ methodmap JSONArray < JSON * * @param value integer to set * - * @return The value to be inserted. Returns false if it is null + * @return True if succeed, false otherwise */ public native bool PushInt(int value); @@ -1506,7 +1646,7 @@ methodmap JSONArray < JSON * * @param value integer64 value * - * @return The value to be inserted. Returns false if it is null + * @return True if succeed, false otherwise */ public native bool PushInt64(const char[] value); @@ -1515,20 +1655,22 @@ methodmap JSONArray < JSON * * @param value String to copy * - * @return The value to be inserted. Returns false if it is null + * @return True if succeed, false otherwise */ public native bool PushString(const char[] value); /** * Inserts a null value at the end of the array * - * @return The value to be inserted. Returns false if it is null + * @return True if succeed, false otherwise */ public native bool PushNull(); /** * Inserts a JSON value at specific index * + * @note This function takes a linear search time + * * @param index Position to insert (0 to size, size means append) * @param value JSON handle to insert * @@ -1539,6 +1681,8 @@ methodmap JSONArray < JSON /** * Inserts a boolean value at specific index * + * @note This function takes a linear search time + * * @param index Position to insert * @param value Boolean value * @@ -1549,6 +1693,8 @@ methodmap JSONArray < JSON /** * Inserts an integer value at specific index * + * @note This function takes a linear search time + * * @param index Position to insert * @param value Integer value * @@ -1559,6 +1705,8 @@ methodmap JSONArray < JSON /** * Inserts an integer64 value at specific index * + * @note This function takes a linear search time + * * @param index Position to insert * @param value Integer64 value (as string) * @@ -1569,6 +1717,8 @@ methodmap JSONArray < JSON /** * Inserts a float value at specific index * + * @note This function takes a linear search time + * * @param index Position to insert * @param value Float value * @@ -1579,6 +1729,8 @@ methodmap JSONArray < JSON /** * Inserts a string value at specific index * + * @note This function takes a linear search time + * * @param index Position to insert * @param value String value * @@ -1589,6 +1741,8 @@ methodmap JSONArray < JSON /** * Inserts a null value at specific index * + * @note This function takes a linear search time + * * @param index Position to insert * * @return True if succeed, false otherwise @@ -1675,7 +1829,7 @@ methodmap JSONArray < JSON public native bool RemoveFirst(); /** - * Removes and returns the last value in this array + * Removes the last value in this array * * @return True if succeed, false otherwise */ @@ -1687,12 +1841,11 @@ methodmap JSONArray < JSON * @note This function takes a linear search time * * @param start_index The start index of the range (0 is the first) - * @param end_index The number of items in the range (can be 0, but do nothing) - * + * @param count Number of items to remove (can be 0, in which case nothing happens) * * @return True if succeed, false otherwise */ - public native bool RemoveRange(int start_index, int end_index); + public native bool RemoveRange(int start_index, int count); /** * Searches for a boolean value in the array and returns its index @@ -1802,6 +1955,8 @@ methodmap JSONArrIter < Handle * Creates an array iterator from a JSON array value * * @note Needs to be freed using delete or CloseHandle() + * @note Iterators are single-pass. Once Next returns null, call Reset() or create + * a new iterator to traverse the array again * * @param array JSON array value to iterate * @@ -1810,9 +1965,21 @@ methodmap JSONArrIter < Handle */ public native JSONArrIter(JSON array); + /** + * Resets the iterator to the beginning of the array + * + * @note Only resets the iterator, does not free the iterator handle + * + * @return True on success, false on failure + * @error Invalid iterator handle + */ + public native bool Reset(); + /** * Moves the iterator to the next element * + * @note Needs to be freed using delete or CloseHandle() + * * @return JSON value handle for the next element, or null if iteration is complete * @error Invalid iterator handle */ @@ -1858,6 +2025,8 @@ methodmap JSONObjIter < Handle * Creates an object iterator from a JSON object value * * @note Needs to be freed using delete or CloseHandle() + * @note Iterators are single-pass. Once Next returns null, call Reset() or create + * a new iterator to traverse the object again * * @param obj JSON object value to iterate * @@ -1866,6 +2035,16 @@ methodmap JSONObjIter < Handle */ public native JSONObjIter(JSON obj); + /** + * Resets the iterator to the beginning of the object + * + * @note Only resets the iterator, does not free the iterator handle + * + * @return True on success, false on failure + * @error Invalid iterator handle + */ + public native bool Reset(); + /** * Moves the iterator to the next key * @@ -2055,6 +2234,7 @@ public void __pl_json_SetNTVOptional() MarkNativeAsOptional("JSON.GetTypeDesc"); MarkNativeAsOptional("JSON.GetSerializedSize"); MarkNativeAsOptional("JSON.ReadSize.get"); + MarkNativeAsOptional("JSON.RefCount.get"); MarkNativeAsOptional("JSON.Type.get"); MarkNativeAsOptional("JSON.SubType.get"); MarkNativeAsOptional("JSON.IsArray.get"); @@ -2078,6 +2258,10 @@ public void __pl_json_SetNTVOptional() MarkNativeAsOptional("JSON.ForeachIndex"); MarkNativeAsOptional("JSON.ToMutable"); MarkNativeAsOptional("JSON.ToImmutable"); + MarkNativeAsOptional("JSON.ApplyJsonPatch"); + MarkNativeAsOptional("JSON.JsonPatchInPlace"); + MarkNativeAsOptional("JSON.ApplyMergePatch"); + MarkNativeAsOptional("JSON.MergePatchInPlace"); // JSON CREATE & GET MarkNativeAsOptional("JSON.Pack"); @@ -2140,14 +2324,16 @@ public void __pl_json_SetNTVOptional() MarkNativeAsOptional("JSONArrIter.HasNext.get"); MarkNativeAsOptional("JSONArrIter.Index.get"); MarkNativeAsOptional("JSONArrIter.Remove"); + MarkNativeAsOptional("JSONArrIter.Reset"); // JSONObjIter MarkNativeAsOptional("JSONObjIter.JSONObjIter"); MarkNativeAsOptional("JSONObjIter.Next"); - MarkNativeAsOptional("JSONObjIter.HasNext"); + MarkNativeAsOptional("JSONObjIter.HasNext.get"); MarkNativeAsOptional("JSONObjIter.Value.get"); MarkNativeAsOptional("JSONObjIter.Get"); MarkNativeAsOptional("JSONObjIter.Index.get"); MarkNativeAsOptional("JSONObjIter.Remove"); + MarkNativeAsOptional("JSONObjIter.Reset"); } #endif \ No newline at end of file diff --git a/plugins/testsuite/test_json.sp b/plugins/testsuite/test_json.sp index d732b26eb..2c0b14920 100755 --- a/plugins/testsuite/test_json.sp +++ b/plugins/testsuite/test_json.sp @@ -405,19 +405,19 @@ void Test_BasicValues() char buffer[32]; - JSON.GetTypeDesc(boolVal, buffer, sizeof(buffer)); + boolVal.GetTypeDesc(buffer, sizeof(buffer)); AssertStrEq(buffer, "true"); - JSON.GetTypeDesc(intVal, buffer, sizeof(buffer)); + intVal.GetTypeDesc(buffer, sizeof(buffer)); AssertTrue(strcmp(buffer, "uint") == 0 || strcmp(buffer, "sint") == 0); - JSON.GetTypeDesc(floatVal, buffer, sizeof(buffer)); + floatVal.GetTypeDesc(buffer, sizeof(buffer)); AssertStrEq(buffer, "real"); - JSON.GetTypeDesc(strVal, buffer, sizeof(buffer)); + strVal.GetTypeDesc(buffer, sizeof(buffer)); AssertStrEq(buffer, "string"); - JSON.GetTypeDesc(nullVal, buffer, sizeof(buffer)); + nullVal.GetTypeDesc(buffer, sizeof(buffer)); AssertStrEq(buffer, "null"); delete boolVal; @@ -1547,7 +1547,7 @@ void Test_ParseAndSerialize() TestStart("Parse_MutableDocument"); { - JSON json = JSON.Parse("{\"key\":\"value\"}", false, true); + JSON json = JSON.Parse("{\"key\":\"value\"}", .is_mutable_doc = true); AssertValidHandle(json); AssertTrue(json.IsMutable); AssertFalse(json.IsImmutable); @@ -1607,7 +1607,7 @@ void Test_ParseAndSerialize() // Test read flags TestStart("Parse_WithTrailingCommas"); { - JSON json = JSON.Parse("[1,2,3,]", false, false, JSON_READ_ALLOW_TRAILING_COMMAS); + JSON json = JSON.Parse("[1,2,3,]", .flag = JSON_READ_ALLOW_TRAILING_COMMAS); AssertValidHandle(json); delete json; } @@ -1615,7 +1615,7 @@ void Test_ParseAndSerialize() TestStart("Parse_WithComments"); { - JSON json = JSON.Parse("/* comment */ {\"key\":\"value\"}", false, false, JSON_READ_ALLOW_COMMENTS); + JSON json = JSON.Parse("/* comment */ {\"key\":\"value\"}", .flag = JSON_READ_ALLOW_COMMENTS); AssertValidHandle(json); delete json; } @@ -1687,16 +1687,30 @@ void Test_Iterators() int count = 0; char key[32]; - JSON value; + JSONObjIter iter = new JSONObjIter(obj); - while (obj.ForeachObject(key, sizeof(key), value)) + while (iter.Next(key, sizeof(key))) { count++; + JSON value = iter.Value; AssertValidHandle(value); delete value; } - + AssertFalse(iter.HasNext); AssertEq(count, 3); + + AssertTrue(iter.Reset()); + + count = 0; + while (iter.Next(key, sizeof(key))) + { + count++; + JSON value = iter.Value; + AssertValidHandle(value); + delete value; + } + AssertEq(count, 3); + delete iter; delete obj; } TestEnd(); @@ -1710,18 +1724,32 @@ void Test_Iterators() arr.PushInt(30); int count = 0; - int index; - JSON value; + JSONArrIter iter = new JSONArrIter(arr); - while (arr.ForeachArray(index, value)) + while (iter.HasNext) { - AssertEq(index, count); + JSON value = iter.Next; AssertValidHandle(value); + AssertEq(iter.Index, count); delete value; count++; } - + AssertFalse(iter.HasNext); AssertEq(count, 3); + + AssertTrue(iter.Reset()); + + count = 0; + while (iter.HasNext) + { + JSON value = iter.Next; + AssertValidHandle(value); + AssertEq(iter.Index, count); + delete value; + count++; + } + AssertEq(count, 3); + delete iter; delete arr; } TestEnd(); @@ -1736,14 +1764,26 @@ void Test_Iterators() int count = 0; char key[32]; + JSONObjIter iter = new JSONObjIter(obj); - while (obj.ForeachKey(key, sizeof(key))) + while (iter.Next(key, sizeof(key))) { AssertTrue(strlen(key) > 0); count++; } - + AssertFalse(iter.HasNext); AssertEq(count, 3); + + AssertTrue(iter.Reset()); + + count = 0; + while (iter.Next(key, sizeof(key))) + { + AssertTrue(strlen(key) > 0); + count++; + } + AssertEq(count, 3); + delete iter; delete obj; } TestEnd(); @@ -1757,15 +1797,30 @@ void Test_Iterators() arr.PushInt(3); int count = 0; - int index; + JSONArrIter iter = new JSONArrIter(arr); - while (arr.ForeachIndex(index)) + while (iter.HasNext) { - AssertEq(index, count); + JSON value = iter.Next; + AssertEq(iter.Index, count); + delete value; count++; } - + AssertFalse(iter.HasNext); AssertEq(count, 3); + + AssertTrue(iter.Reset()); + + count = 0; + while (iter.HasNext) + { + JSON value = iter.Next; + AssertEq(iter.Index, count); + delete value; + count++; + } + AssertEq(count, 3); + delete iter; delete arr; } TestEnd(); @@ -1774,10 +1829,12 @@ void Test_Iterators() TestStart("Iterator_EmptyObject"); { JSONObject obj = new JSONObject(); - char key[32]; - JSON value; - AssertFalse(obj.ForeachObject(key, sizeof(key), value)); + JSONObjIter iter = new JSONObjIter(obj); + AssertFalse(iter.Next(key, sizeof(key))); + AssertTrue(iter.Reset()); + AssertFalse(iter.Next(key, sizeof(key))); + delete iter; delete obj; } @@ -1786,10 +1843,11 @@ void Test_Iterators() TestStart("Iterator_EmptyArray"); { JSONArray arr = new JSONArray(); - - int index; - JSON value; - AssertFalse(arr.ForeachArray(index, value)); + JSONArrIter iter = new JSONArrIter(arr); + AssertFalse(iter.HasNext); + AssertTrue(iter.Reset()); + AssertFalse(iter.HasNext); + delete iter; delete arr; } @@ -2146,6 +2204,144 @@ void Test_AdvancedFeatures() } TestEnd(); + // Test ApplyJsonPatch (new value, immutable result) + TestStart("Advanced_ApplyJsonPatch"); + { + JSONObject original = new JSONObject(); + original.SetInt("score", 10); + original.SetString("name", "bot"); + + JSON patch = JSON.Parse("[{\"op\":\"replace\",\"path\":\"/score\",\"value\":42}]"); + AssertValidHandle(patch); + + JSON result = original.ApplyJsonPatch(patch); + AssertValidHandle(result); + AssertTrue(result.IsImmutable); + + AssertEq(result.PtrGetInt("/score"), 42); + char buffer[32]; + result.PtrGetString("/name", buffer, sizeof(buffer)); + AssertStrEq(buffer, "bot"); + + // ensure original unchanged + AssertEq(original.GetInt("score"), 10); + + delete result; + delete patch; + delete original; + } + TestEnd(); + + // Test ApplyJsonPatch resultMutable = true + TestStart("Advanced_ApplyJsonPatch_MutableResult"); + { + JSONObject original = new JSONObject(); + original.SetInt("count", 1); + + JSON patch = JSON.Parse("[{\"op\":\"add\",\"path\":\"/newField\",\"value\":\"hello\"}]"); + AssertValidHandle(patch); + + JSON result = original.ApplyJsonPatch(patch, true); + AssertValidHandle(result); + AssertTrue(result.IsMutable); + + char buffer[16]; + result.PtrGetString("/newField", buffer, sizeof(buffer)); + AssertStrEq(buffer, "hello"); + + delete result; + delete patch; + delete original; + } + TestEnd(); + + // Test JsonPatchInPlace + TestStart("Advanced_JsonPatchInPlace"); + { + JSONObject target = new JSONObject(); + target.SetInt("score", 5); + target.SetInt("lives", 3); + + JSON patch = JSON.Parse("[{\"op\":\"remove\",\"path\":\"/lives\"},{\"op\":\"replace\",\"path\":\"/score\",\"value\":9}]"); + AssertValidHandle(patch); + + AssertTrue(target.JsonPatchInPlace(patch)); + AssertEq(target.GetInt("score"), 9); + AssertFalse(target.HasKey("lives")); + + delete patch; + delete target; + } + TestEnd(); + + // Test ApplyMergePatch (immutable result) + TestStart("Advanced_ApplyMergePatch"); + { + JSONObject original = new JSONObject(); + original.PtrSetString("/settings/mode", "coop"); + original.PtrSetInt("/settings/difficulty", 1); + + JSON mergePatch = JSON.Parse("{\"settings\":{\"difficulty\":3,\"friendlyFire\":true}}"); + AssertValidHandle(mergePatch); + + JSON result = original.ApplyMergePatch(mergePatch); + AssertValidHandle(result); + AssertTrue(result.IsImmutable); + + AssertEq(result.PtrGetInt("/settings/difficulty"), 3); + AssertTrue(result.PtrGetBool("/settings/friendlyFire")); + + delete result; + delete mergePatch; + delete original; + } + TestEnd(); + + // Test ApplyMergePatch resultMutable = true + TestStart("Advanced_ApplyMergePatch_MutableResult"); + { + JSONObject original = new JSONObject(); + original.PtrSetString("/profile/name", "player"); + + JSON mergePatch = JSON.Parse("{\"profile\":{\"rank\":10}}"); + AssertValidHandle(mergePatch); + + JSON result = original.ApplyMergePatch(mergePatch, true); + AssertValidHandle(result); + AssertTrue(result.IsMutable); + + AssertEq(result.PtrGetInt("/profile/rank"), 10); + char buffer[16]; + result.PtrGetString("/profile/name", buffer, sizeof(buffer)); + AssertStrEq(buffer, "player"); + + delete result; + delete mergePatch; + delete original; + } + TestEnd(); + + // Test MergePatchInPlace + TestStart("Advanced_MergePatchInPlace"); + { + JSONObject target = new JSONObject(); + target.PtrSetString("/config/mode", "coop"); + target.PtrSetInt("/config/players", 4); + + JSON mergePatch = JSON.Parse("{\"config\":{\"players\":6,\"region\":\"EU\"}}"); + AssertValidHandle(mergePatch); + + AssertTrue(target.MergePatchInPlace(mergePatch)); + AssertEq(target.PtrGetInt("/config/players"), 6); + char buffer[16]; + target.PtrGetString("/config/region", buffer, sizeof(buffer)); + AssertStrEq(buffer, "EU"); + + delete mergePatch; + delete target; + } + TestEnd(); + // Test Pack TestStart("Advanced_Pack_SimpleObject"); { @@ -2210,7 +2406,7 @@ void Test_AdvancedFeatures() // Test mixed type array sorting TestStart("Advanced_MixedTypeSort"); { - JSONArray json = JSON.Parse("[true, 42, \"hello\", 1.5, false]", false, true); + JSONArray json = JSON.Parse("[true, 42, \"hello\", 1.5, false]", .is_mutable_doc = true); JSONArray arr = json; AssertTrue(arr.Sort(JSON_SORT_ASC));