Blaze 0.0.1
The ultra high-performance JSON Schema evaluator
 
Loading...
Searching...
No Matches
frame.h
1#ifndef SOURCEMETA_BLAZE_FRAME_H_
2#define SOURCEMETA_BLAZE_FRAME_H_
3
12
13#ifndef SOURCEMETA_BLAZE_FRAME_EXPORT
14#include <sourcemeta/blaze/frame_export.h>
15#endif
16
17// NOLINTBEGIN(misc-include-cleaner)
18#include <sourcemeta/blaze/frame_error.h>
19// NOLINTEND(misc-include-cleaner)
20
21#include <sourcemeta/blaze/foundation.h>
22
23#include <sourcemeta/core/json.h>
24#include <sourcemeta/core/jsonpointer.h>
25
26#include <concepts> // std::invocable
27#include <cstdint> // std::uint8_t
28#include <functional> // std::reference_wrapper
29#include <map> // std::map
30#include <optional> // std::optional
31#include <set> // std::set
32#include <tuple> // std::tuple
33#include <unordered_map> // std::unordered_map
34#include <unordered_set> // std::unordered_set
35#include <utility> // std::pair
36#include <vector> // std::vector
37
38namespace sourcemeta::blaze {
39
70class SOURCEMETA_BLAZE_FRAME_EXPORT SchemaFrame {
71public:
74 enum class Mode : std::uint8_t { Locations, References };
75
76 SchemaFrame(const Mode mode) : mode_{mode} {}
77
78 // We rely on internal caches that would be dangling otherwise
79 SchemaFrame(const SchemaFrame &) = delete;
80 auto operator=(const SchemaFrame &) -> SchemaFrame & = delete;
81 SchemaFrame(SchemaFrame &&) = delete;
82 auto operator=(SchemaFrame &&) -> SchemaFrame & = delete;
83
84 // Query the current mode that the schema frame was configured with
85 [[nodiscard]] auto mode() const noexcept -> Mode { return this->mode_; }
86
89 std::string_view original;
90 // TODO: This one is tricky to turn into a view, as there is no
91 // location entry to point to if it is an external unresolved reference
92 sourcemeta::core::JSON::String destination;
93 // Empty means no base
94 std::string_view base;
95 std::optional<std::string_view> fragment;
96 };
97
106 std::map<std::pair<SchemaReferenceType, sourcemeta::core::WeakPointer>,
108
109#if defined(__GNUC__)
110#pragma GCC diagnostic push
111// GCC believes that a member of an enum class (which is namespaced by
112// definition), can shadow an alias defined even on a different namespace.
113#pragma GCC diagnostic ignored "-Wshadow"
114#endif
117 enum class LocationType : std::uint8_t {
118 Resource,
119 Anchor,
120 // TODO: Distinguish between a Pointer and a Keyword
121 Pointer,
122 Subschema
123 };
124#if defined(__GNUC__)
125#pragma GCC diagnostic pop
126#endif
127
129 struct Location {
130 std::optional<sourcemeta::core::WeakPointer> parent;
131 LocationType type;
132 std::string_view base;
133 sourcemeta::core::WeakPointer pointer;
134 std::size_t relative_pointer;
135 std::string_view dialect;
137 bool property_name;
138 bool orphan;
139 };
140
144 // TODO: Consider replacing std::map with std::flat_map once libc++
145 // supports it (__cpp_lib_flat_map) for better cache locality
146 using Locations =
147 // While it might seem weird that we namespace the location URIs with a
148 // reference type, it is essential for distinguishing schema resource URIs
149 // from `$recursiveRef: true` on another place of the schema schema
150 // resource, as otherwise they would both have the exact same URI, but
151 // point to different places.
152 std::map<std::pair<SchemaReferenceType, sourcemeta::core::JSON::String>,
153 Location>;
154
156 using Paths = std::vector<sourcemeta::core::WeakPointer>;
157
159 [[nodiscard]] auto to_json(
160 const std::optional<sourcemeta::core::PointerPositionTracker> &tracker =
161 std::nullopt) const -> sourcemeta::core::JSON;
162
165 auto analyse(const sourcemeta::core::JSON &root, const SchemaWalker &walker,
166 const SchemaResolver &resolver,
167 std::string_view default_dialect = "",
168 std::string_view default_id = "",
169 const Paths &paths = {sourcemeta::core::empty_weak_pointer})
170 -> void;
171
173 [[nodiscard]] auto locations() const noexcept -> const Locations &;
174
176 [[nodiscard]] auto references() const noexcept -> const References &;
177
179 [[nodiscard]] auto
180 reference(const SchemaReferenceType type,
181 const sourcemeta::core::WeakPointer &pointer) const
182 -> std::optional<std::reference_wrapper<const ReferencesEntry>>;
183
185 [[nodiscard]] auto standalone() const noexcept -> bool;
186
188 [[nodiscard]] auto root() const noexcept
189 -> const sourcemeta::core::JSON::String &;
190
192 [[nodiscard]] auto vocabularies(const Location &location,
193 const SchemaResolver &resolver) const
194 -> Vocabularies;
195
197 [[nodiscard]] auto
198 uri(const Location &location,
199 const sourcemeta::core::WeakPointer &relative_schema_location =
200 sourcemeta::core::empty_weak_pointer) const
201 -> sourcemeta::core::JSON::String;
202
204 [[nodiscard]] auto
205 traverse(const Location &location,
206 const sourcemeta::core::WeakPointer &relative_schema_location) const
207 -> const Location &;
208
210 [[nodiscard]] auto traverse(const std::string_view uri) const
211 -> std::optional<std::reference_wrapper<const Location>>;
212
214 [[nodiscard]] auto
215 traverse(const sourcemeta::core::WeakPointer &pointer) const
216 -> std::optional<std::reference_wrapper<const Location>>;
217
219 [[nodiscard]] auto traverse(const sourcemeta::core::WeakPointer &pointer,
220 const LocationType type) const
221 -> std::optional<std::reference_wrapper<const Location>>;
222
224 [[nodiscard]] auto uri(const sourcemeta::core::WeakPointer &pointer) const
225 -> std::optional<
226 std::reference_wrapper<const sourcemeta::core::JSON::String>>;
227
229 [[nodiscard]] auto
230 dereference(const Location &location,
231 const sourcemeta::core::WeakPointer &relative_schema_location =
232 sourcemeta::core::empty_weak_pointer) const
233 -> std::pair<SchemaReferenceType,
234 std::optional<std::reference_wrapper<const Location>>>;
235
237 template <std::invocable<std::string_view> F>
238 auto for_each_resource_uri(const F &callback) const -> void {
239 for (const auto &[key, location] : this->locations_) {
240 if (location.type == LocationType::Resource) {
241 callback(key.second);
242 }
243 }
244 }
245
248 template <std::invocable<const sourcemeta::core::WeakPointer &,
249 const ReferencesEntry &>
250 F>
251 auto for_each_unresolved_reference(const F &callback) const -> void {
252 for (const auto &[key, reference] : this->references_) {
253 if (!this->traverse(reference.destination).has_value()) {
254 callback(key.second, reference);
255 }
256 }
257 }
258
260 [[nodiscard]] auto
261 has_references_to(const sourcemeta::core::WeakPointer &pointer) const -> bool;
262
264 [[nodiscard]] auto
265 has_references_through(const sourcemeta::core::WeakPointer &pointer) const
266 -> bool;
269 [[nodiscard]] auto
270 has_references_through(const sourcemeta::core::WeakPointer &pointer,
271 const sourcemeta::core::WeakPointer::Token &tail) const
272 -> bool;
273
275 [[nodiscard]] auto relative_instance_location(const Location &location) const
276 -> sourcemeta::core::WeakPointer;
277
279 [[nodiscard]] auto empty() const noexcept -> bool;
280
282 auto reset() -> void;
283
285 [[nodiscard]] auto is_reachable(const Location &base,
286 const Location &location,
287 const SchemaWalker &walker,
288 const SchemaResolver &resolver) const -> bool;
289
290private:
291 Mode mode_;
292// Exporting symbols that depends on the standard C++ library is considered
293// safe.
294// https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-2-c4275?view=msvc-170&redirectedfrom=MSDN
295#if defined(_MSC_VER)
296#pragma warning(disable : 4251 4275)
297#endif
298 sourcemeta::core::JSON::String root_;
299 Locations locations_;
300 References references_;
301 // Custom meta-schemas that the resolver could not resolve but that were
302 // found embedded in the analysed document itself. The values point into
303 // the analysed document, which the frame must not outlive anyway
304 std::unordered_map<sourcemeta::core::JSON::String,
305 const sourcemeta::core::JSON *>
306 probed_metaschemas_;
307 mutable std::unordered_map<
308 std::reference_wrapper<const sourcemeta::core::WeakPointer>,
309 std::vector<const Location *>, sourcemeta::core::WeakPointer::Hasher,
310 sourcemeta::core::WeakPointer::Comparator>
311 pointer_to_location_;
312 mutable std::unordered_set<
313 std::reference_wrapper<const sourcemeta::core::WeakPointer>,
314 sourcemeta::core::WeakPointer::Hasher,
315 sourcemeta::core::WeakPointer::Comparator>
316 pointers_with_non_orphan_;
317 using ReachabilityCache =
318 std::unordered_map<const sourcemeta::core::WeakPointer *, bool>;
319 struct ReachabilityKey {
320 const sourcemeta::core::WeakPointer *pointer;
321 bool orphan;
322 auto operator==(const ReachabilityKey &other) const noexcept -> bool {
323 return this->pointer == other.pointer && this->orphan == other.orphan;
324 }
325 };
326 struct ReachabilityKeyHasher {
327 auto operator()(const ReachabilityKey &key) const noexcept -> std::size_t {
328 return std::hash<const void *>{}(key.pointer) ^
329 (std::hash<bool>{}(key.orphan) << 1);
330 }
331 };
332 mutable std::unordered_map<ReachabilityKey, ReachabilityCache,
333 ReachabilityKeyHasher>
334 reachability_;
335 mutable std::unordered_map<
336 std::reference_wrapper<const sourcemeta::core::WeakPointer>,
337 std::vector<const sourcemeta::core::WeakPointer *>,
338 sourcemeta::core::WeakPointer::Hasher,
339 sourcemeta::core::WeakPointer::Comparator>
340 references_by_destination_;
341 mutable std::unordered_set<
342 std::reference_wrapper<const sourcemeta::core::WeakPointer>,
343 sourcemeta::core::WeakPointer::Hasher,
344 sourcemeta::core::WeakPointer::Comparator>
345 location_members_children_;
346 mutable std::unordered_map<
347 std::reference_wrapper<const sourcemeta::core::WeakPointer>,
348 std::vector<const Location *>, sourcemeta::core::WeakPointer::Hasher,
349 sourcemeta::core::WeakPointer::Comparator>
350 descendants_by_pointer_;
351 struct PotentialSource {
352 const sourcemeta::core::WeakPointer *source_pointer;
353 sourcemeta::core::WeakPointer source_parent;
354 bool crosses;
355 };
356 mutable std::unordered_map<const Location *, std::vector<PotentialSource>>
357 potential_sources_by_location_;
358 struct ReachabilityEdge {
359 const Location *target;
360 bool orphan_context_only;
361 bool is_reference;
362 };
363 mutable std::unordered_map<const Location *, std::vector<ReachabilityEdge>>
364 reachability_graph_;
365 mutable std::unordered_map<
366 std::reference_wrapper<const sourcemeta::core::WeakPointer>,
367 const sourcemeta::core::WeakPointer *,
368 sourcemeta::core::WeakPointer::Hasher,
369 sourcemeta::core::WeakPointer::Comparator>
370 canonical_pointer_;
371 mutable std::unordered_map<const Location *,
372 const sourcemeta::core::WeakPointer *>
373 location_to_canonical_;
374 bool standalone_{false};
375
376 auto populate_pointer_to_location() const -> void;
377 auto populate_reference_graph() const -> void;
378 auto populate_location_members(const SchemaWalker &walker,
379 const SchemaResolver &resolver) const -> void;
380 auto populate_descendants() const -> void;
381 auto populate_potential_sources(const SchemaWalker &walker,
382 const SchemaResolver &resolver) const -> void;
383 auto populate_reachability_graph(const SchemaWalker &walker,
384 const SchemaResolver &resolver) const
385 -> void;
386 auto populate_reachability(const Location &base, const SchemaWalker &walker,
387 const SchemaResolver &resolver) const
388 -> const ReachabilityCache &;
389#if defined(_MSC_VER)
390#pragma warning(default : 4251 4275)
391#endif
392};
393
394} // namespace sourcemeta::blaze
395
396#endif
Mode
Definition compiler.h:83
std::function< const SchemaWalkerResult &(std::string_view, const Vocabularies &)> SchemaWalker
Definition foundation_types.h:217
SchemaBaseDialect
Definition foundation_types.h:41
SOURCEMETA_BLAZE_FOUNDATION_EXPORT auto dialect(const sourcemeta::core::JSON &schema, std::string_view default_dialect="", bool allow_dialect_override=true) -> std::string_view
SchemaReferenceType
Definition foundation_types.h:37
SOURCEMETA_BLAZE_FOUNDATION_EXPORT auto base_dialect(const sourcemeta::core::JSON &schema, const SchemaResolver &resolver, std::string_view default_dialect="", bool allow_dialect_override=true) -> std::optional< SchemaBaseDialect >
std::function< std::optional< sourcemeta::core::JSON >(std::string_view)> SchemaResolver
Definition foundation_types.h:33
Definition foundation_vocabularies.h:30
auto analyse(const sourcemeta::core::JSON &root, const SchemaWalker &walker, const SchemaResolver &resolver, std::string_view default_dialect="", std::string_view default_id="", const Paths &paths={sourcemeta::core::empty_weak_pointer}) -> void
std::map< std::pair< SchemaReferenceType, sourcemeta::core::JSON::String >, Location > Locations
Definition frame.h:153
auto empty() const noexcept -> bool
Check if the frame has no analysed data.
Mode
Definition frame.h:74
auto for_each_unresolved_reference(const F &callback) const -> void
Definition frame.h:251
auto to_json(const std::optional< sourcemeta::core::PointerPositionTracker > &tracker=std::nullopt) const -> sourcemeta::core::JSON
Export the frame entries as JSON.
std::vector< sourcemeta::core::WeakPointer > Paths
A list of paths to frame within a schema wrapper.
Definition frame.h:156
auto locations() const noexcept -> const Locations &
Access the analysed schema locations.
auto has_references_through(const sourcemeta::core::WeakPointer &pointer) const -> bool
Check if there are any references that go through a given location pointer.
std::map< std::pair< SchemaReferenceType, sourcemeta::core::WeakPointer >, ReferencesEntry > References
Definition frame.h:107
auto has_references_to(const sourcemeta::core::WeakPointer &pointer) const -> bool
Check if there are any references to a given location pointer.
auto relative_instance_location(const Location &location) const -> sourcemeta::core::WeakPointer
Get the relative instance location pointer for a given location entry.
auto has_references_through(const sourcemeta::core::WeakPointer &pointer, const sourcemeta::core::WeakPointer::Token &tail) const -> bool
Definition frame.h:70
LocationType
Definition frame.h:117
A location entry.
Definition frame.h:129
A single entry in a JSON Schema reference map.
Definition frame.h:88