Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
230 changes: 109 additions & 121 deletions benchmark/jsonld.cc

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions src/core/json/include/sourcemeta/core/json_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,54 @@ template <typename Key, typename Value, typename Hash> class JSONObject {
std::unreachable();
}

/// Try to access an object entry by its key name
[[nodiscard]] inline auto try_at(const Key &key, const hash_type key_hash)
-> mapped_type * {
assert(this->hash(key) == key_hash);

// Move the perfect hash condition out of the loop for extra performance
if (this->hasher.is_perfect(key_hash)) {
for (auto &entry : this->data) {
if (entry.hash == key_hash && entry.first.size() == key.size()) {
return &entry.second;
}
}
} else {
for (auto &entry : this->data) {
if (entry.hash == key_hash && entry.first == key) {
return &entry.second;
}
}
}

return nullptr;
}

/// Try to access an object entry by its key name
template <typename T>
requires std::same_as<std::remove_cvref_t<T>, KeyView>
[[nodiscard]] inline auto try_at(T key, const hash_type key_hash)
-> mapped_type * {
assert(this->hash(key) == key_hash);

// Move the perfect hash condition out of the loop for extra performance
if (this->hasher.is_perfect(key_hash)) {
for (auto &entry : this->data) {
if (entry.hash == key_hash && entry.first.size() == key.size()) {
return &entry.second;
}
}
} else {
for (auto &entry : this->data) {
if (entry.hash == key_hash && entry.first == key) {
return &entry.second;
}
}
}

return nullptr;
}

/// Try to access an object entry by its underlying positional index
[[nodiscard]] inline auto try_at(const Key &key,
const hash_type key_hash) const
Expand Down
61 changes: 61 additions & 0 deletions src/core/json/include/sourcemeta/core/json_value.h
Original file line number Diff line number Diff line change
Expand Up @@ -1419,6 +1419,67 @@ class SOURCEMETA_CORE_JSON_EXPORT JSON {
return object.try_at(key, hash);
}

/// This method tries to retrieve a mutable object element by key. For
/// example:
///
/// ```cpp
/// #include <sourcemeta/core/json.h>
/// #include <cassert>
///
/// sourcemeta::core::JSON document =
/// sourcemeta::core::parse_json("{ \"foo\": 1 }");
/// auto result{document.try_at("foo")};
/// assert(result);
/// result->into(sourcemeta::core::JSON{2});
/// assert(document.at("foo").to_integer() == 2);
/// ```
[[nodiscard]] SOURCEMETA_FORCEINLINE inline auto try_at(const String &key)
-> JSON * {
assert(this->is_object());
auto &object{this->data_object};
return object.try_at(key, object.hash(key));
}

/// This method tries to retrieve a mutable object element by string view key
template <typename T>
requires std::same_as<std::remove_cvref_t<T>, StringView>
[[nodiscard]] SOURCEMETA_FORCEINLINE inline auto try_at(T key) -> JSON * {
assert(this->is_object());
auto &object{this->data_object};
return object.try_at(key, object.hash(key));
}

/// This method tries to retrieve a mutable object element given a
/// pre-calculated property hash. For example:
///
/// ```cpp
/// #include <sourcemeta/core/json.h>
/// #include <cassert>
///
/// sourcemeta::core::JSON document =
/// sourcemeta::core::parse_json("{ \"foo\": 1 }");
/// auto result{document.try_at("foo",
/// document.as_object().hash("foo"))};
/// assert(result);
/// result->into(sourcemeta::core::JSON{2});
/// assert(document.at("foo").to_integer() == 2);
/// ```
[[nodiscard]] SOURCEMETA_FORCEINLINE inline auto
try_at(const String &key, const typename Object::hash_type hash) -> JSON * {
assert(this->is_object());
return this->data_object.try_at(key, hash);
}

/// This method tries to retrieve a mutable object element by string view key
/// given a pre-calculated property hash
template <typename T>
requires std::same_as<std::remove_cvref_t<T>, StringView>
[[nodiscard]] SOURCEMETA_FORCEINLINE inline auto
try_at(T key, const typename Object::hash_type hash) -> JSON * {
assert(this->is_object());
return this->data_object.try_at(key, hash);
}

/// Try to get a property, scanning from a caller-provided start offset.
/// On hit, advances `start` past the found index. When looking up multiple
/// keys in insertion order, each lookup hits on the first probe, making the
Expand Down
105 changes: 60 additions & 45 deletions src/core/jsonld/include/sourcemeta/core/jsonld_materialize.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@
#include <sourcemeta/core/json.h>
#include <sourcemeta/core/jsonpointer.h>

#include <cstdint> // std::uint8_t
#include <optional> // std::optional
#include <unordered_map> // std::unordered_map
#include <variant> // std::variant
#include <vector> // std::vector
#include <cstdint> // std::uint8_t
#include <optional> // std::optional
#include <variant> // std::variant
#include <vector> // std::vector

namespace sourcemeta::core {

Expand Down Expand Up @@ -108,27 +107,36 @@ struct JSONLDDescriptor {
};

/// @ingroup jsonld
/// A resolved mapping of instance positions to their JSON-LD semantics, keyed
/// by a JSON Pointer of the given kind
/// An instance position paired with its JSON-LD semantics
template <typename PointerT> struct JSONLDBasicAnnotation {
/// The instance position the annotation describes.
PointerT pointer{};
/// The JSON-LD semantics of the position.
JSONLDDescriptor descriptor{};
};

/// @ingroup jsonld
/// A flat collection of annotated instance positions, in any order. When more
/// than one entry describes the same position, the first one wins.
template <typename PointerT>
using JSONLDBasicAnnotationMap =
std::unordered_map<PointerT, JSONLDDescriptor, typename PointerT::Hasher>;
using JSONLDBasicAnnotationList = std::vector<JSONLDBasicAnnotation<PointerT>>;

/// @ingroup jsonld
/// A resolved annotation map keyed by an owning JSON Pointer
using JSONLDAnnotationMap = JSONLDBasicAnnotationMap<Pointer>;
/// An annotation list whose positions are owning JSON Pointers
using JSONLDAnnotationList = JSONLDBasicAnnotationList<Pointer>;

/// @ingroup jsonld
/// A resolved annotation map keyed by a non-owning weak JSON Pointer. The keys
/// reference strings owned elsewhere that must outlive any materialization
/// call.
using JSONLDWeakAnnotationMap = JSONLDBasicAnnotationMap<WeakPointer>;
/// An annotation list whose positions are non-owning weak JSON Pointers. The
/// positions reference strings owned elsewhere that must outlive any
/// materialization call.
using JSONLDWeakAnnotationList = JSONLDBasicAnnotationList<WeakPointer>;

/// @ingroup jsonld
///
/// Materialize an instance into expanded JSON-LD using an annotation map that
/// assigns JSON-LD semantics to instance positions. The result is always a JSON
/// array. For example:
/// Materialize an instance into expanded JSON-LD using an annotation list that
/// assigns JSON-LD semantics to instance positions. An undescribed member of a
/// collection defaults to a plain literal, or to an unordered collection for a
/// nested array. The result is always a JSON array. For example:
///
/// ```cpp
/// #include <sourcemeta/core/json.h>
Expand All @@ -138,30 +146,34 @@ using JSONLDWeakAnnotationMap = JSONLDBasicAnnotationMap<WeakPointer>;
/// const auto instance{sourcemeta::core::parse_json(
/// R"({ "name": "Sourcemeta" })")};
///
/// sourcemeta::core::JSONLDAnnotationMap map;
/// map.emplace(sourcemeta::core::Pointer{},
/// sourcemeta::core::JSONLDDescriptor{
/// {}, sourcemeta::core::JSONLDNode{
/// "https://example.com/org", {}, false }});
/// map.emplace(sourcemeta::core::Pointer{"name"},
/// sourcemeta::core::JSONLDDescriptor{
/// { { "https://schema.org/name", false } },
/// sourcemeta::core::JSONLDLiteral{}});
/// sourcemeta::core::JSONLDAnnotationList annotations;
/// annotations.push_back(
/// {sourcemeta::core::Pointer{},
/// sourcemeta::core::JSONLDDescriptor{
/// {}, sourcemeta::core::JSONLDNode{
/// "https://example.com/org", {}, false }}});
/// annotations.push_back(
/// {sourcemeta::core::Pointer{"name"},
/// sourcemeta::core::JSONLDDescriptor{
/// { { "https://schema.org/name", false } },
/// sourcemeta::core::JSONLDLiteral{}}});
///
/// const auto expanded{sourcemeta::core::jsonld_materialize(instance, map)};
/// const auto expanded{
/// sourcemeta::core::jsonld_materialize(instance, annotations)};
/// sourcemeta::core::prettify(expanded, std::cout);
/// std::cout << std::endl;
/// ```
SOURCEMETA_CORE_JSONLD_EXPORT
auto jsonld_materialize(const JSON &instance, const JSONLDAnnotationMap &map)
-> JSON;
auto jsonld_materialize(const JSON &instance,

@augmentcode augmentcode Bot Jul 8, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

src/core/jsonld/include/sourcemeta/core/jsonld_materialize.h:166: This changes the public API from map-based annotations to JSONLD*AnnotationList and updates the jsonld_materialize overloads accordingly; please make sure this intended breaking change is reflected in versioning/release notes for downstream users.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

const JSONLDAnnotationList &annotations) -> JSON;

/// @ingroup jsonld
///
/// Materialize an instance into expanded JSON-LD using a weak annotation map
/// whose keys are non-owning views into strings owned elsewhere. The backing
/// strings must outlive the call. The result is always a JSON array. For
/// example:
/// Materialize an instance into expanded JSON-LD using a weak annotation list
/// whose positions are non-owning views into strings owned elsewhere. The
/// backing strings must outlive the call. An undescribed member of a
/// collection defaults to a plain literal, or to an unordered collection for a
/// nested array. The result is always a JSON array. For example:
///
/// ```cpp
/// #include <sourcemeta/core/json.h>
Expand All @@ -174,23 +186,26 @@ auto jsonld_materialize(const JSON &instance, const JSONLDAnnotationMap &map)
///
/// const sourcemeta::core::JSON::String name_key{"name"};
///
/// sourcemeta::core::JSONLDWeakAnnotationMap map;
/// map.emplace(sourcemeta::core::WeakPointer{},
/// sourcemeta::core::JSONLDDescriptor{
/// {}, sourcemeta::core::JSONLDNode{
/// "https://example.com/org", {}, false }});
/// map.emplace(sourcemeta::core::WeakPointer{std::cref(name_key)},
/// sourcemeta::core::JSONLDDescriptor{
/// { { "https://schema.org/name", false } },
/// sourcemeta::core::JSONLDLiteral{}});
/// sourcemeta::core::JSONLDWeakAnnotationList annotations;
/// annotations.push_back(
/// {sourcemeta::core::WeakPointer{},
/// sourcemeta::core::JSONLDDescriptor{
/// {}, sourcemeta::core::JSONLDNode{
/// "https://example.com/org", {}, false }}});
/// annotations.push_back(
/// {sourcemeta::core::WeakPointer{std::cref(name_key)},
/// sourcemeta::core::JSONLDDescriptor{
/// { { "https://schema.org/name", false } },
/// sourcemeta::core::JSONLDLiteral{}}});
///
/// const auto expanded{sourcemeta::core::jsonld_materialize(instance, map)};
/// const auto expanded{
/// sourcemeta::core::jsonld_materialize(instance, annotations)};
/// sourcemeta::core::prettify(expanded, std::cout);
/// std::cout << std::endl;
/// ```
SOURCEMETA_CORE_JSONLD_EXPORT
auto jsonld_materialize(const JSON &instance,
const JSONLDWeakAnnotationMap &map) -> JSON;
const JSONLDWeakAnnotationList &annotations) -> JSON;

} // namespace sourcemeta::core

Expand Down
Loading
Loading