1
1
#include < sourcemeta/core/jsonschema.h>
2
2
3
- #include < cassert> // assert
4
- #include < cstdint> // std::uint64_t
5
- #include < functional > // std::less
6
- #include < limits > // std::numeric_limits
7
- #include < numeric > // std::accumulate
8
- #include < sstream > // std::ostringstream
9
- #include < type_traits > // std::remove_reference_t
10
- #include < utility> // std::move
3
+ #include < cassert> // assert
4
+ #include < cstdint> // std::uint64_t
5
+ #include < limits > // std::numeric_limits
6
+ #include < numeric > // std::accumulate
7
+ #include < sstream > // std::ostringstream
8
+ #include < type_traits > // std::remove_reference_t
9
+ #include < unordered_map > // std::unordered_map
10
+ #include < utility> // std::move
11
11
12
12
auto sourcemeta::core::is_schema (const sourcemeta::core::JSON &schema) -> bool {
13
13
return schema.is_object () || schema.is_boolean ();
@@ -476,12 +476,10 @@ auto sourcemeta::core::vocabularies(const SchemaResolver &resolver,
476
476
return result;
477
477
}
478
478
479
- auto sourcemeta::core::schema_format_compare (
480
- const sourcemeta::core::JSON::String &left,
481
- const sourcemeta::core::JSON::String &right) -> bool {
479
+ static auto keyword_rank (const sourcemeta::core::JSON::String &keyword,
480
+ const std::uint64_t otherwise) -> std::uint64_t {
482
481
using Rank =
483
- std::map<JSON::String, std::uint64_t , std::less<>,
484
- JSON::Allocator<std::pair<const JSON::String, std::uint64_t >>>;
482
+ std::unordered_map<sourcemeta::core::JSON::String, std::uint64_t >;
485
483
static Rank rank{// Most core keywords tend to come first
486
484
{" $schema" , 0 },
487
485
{" $id" , 1 },
@@ -501,88 +499,102 @@ auto sourcemeta::core::schema_format_compare(
501
499
{" writeOnly" , 14 },
502
500
{" default" , 15 },
503
501
502
+ // This is a placeholder for "x-"-prefixed unknown keywords,
503
+ // as they are almost always metadata
504
+ {" x-" , 16 },
505
+
504
506
// Then references
505
- {" $ref" , 16 },
506
- {" $dynamicRef" , 17 },
507
- {" $recursiveRef" , 18 },
507
+ {" $ref" , 17 },
508
+ {" $dynamicRef" , 18 },
509
+ {" $recursiveRef" , 19 },
508
510
509
511
// Then keywords that apply to any type
510
- {" type" , 19 },
511
- {" disallow" , 20 },
512
- {" extends" , 21 },
513
- {" const" , 22 },
514
- {" enum" , 23 },
515
- {" optional" , 0 },
516
- {" requires" , 0 },
517
- {" allOf" , 24 },
518
- {" anyOf" , 25 },
519
- {" oneOf" , 26 },
520
- {" not" , 27 },
521
- {" if" , 28 },
522
- {" then" , 29 },
523
- {" else" , 30 },
512
+ {" type" , 20 },
513
+ {" disallow" , 21 },
514
+ {" extends" , 22 },
515
+ {" const" , 23 },
516
+ {" enum" , 24 },
517
+ {" optional" , 25 },
518
+ {" requires" , 26 },
519
+ {" allOf" , 27 },
520
+ {" anyOf" , 28 },
521
+ {" oneOf" , 29 },
522
+ {" not" , 30 },
523
+ {" if" , 31 },
524
+ {" then" , 32 },
525
+ {" else" , 33 },
524
526
525
527
// Then keywords about numbers
526
- {" exclusiveMaximum" , 31 },
527
- {" maximum" , 32 },
528
- {" maximumCanEqual" , 33 },
529
- {" exclusiveMinimum" , 34 },
530
- {" minimum" , 35 },
531
- {" minimumCanEqual" , 36 },
532
- {" multipleOf" , 37 },
533
- {" divisibleBy" , 38 },
534
- {" maxDecimal" , 39 },
528
+ {" exclusiveMaximum" , 34 },
529
+ {" maximum" , 35 },
530
+ {" maximumCanEqual" , 36 },
531
+ {" exclusiveMinimum" , 37 },
532
+ {" minimum" , 38 },
533
+ {" minimumCanEqual" , 39 },
534
+ {" multipleOf" , 40 },
535
+ {" divisibleBy" , 41 },
536
+ {" maxDecimal" , 42 },
535
537
536
538
// Then keywords about strings
537
- {" pattern" , 40 },
538
- {" format" , 41 },
539
- {" maxLength" , 42 },
540
- {" minLength" , 43 },
541
- {" contentEncoding" , 44 },
542
- {" contentMediaType" , 45 },
543
- {" contentSchema" , 46 },
539
+ {" pattern" , 43 },
540
+ {" format" , 44 },
541
+ {" maxLength" , 45 },
542
+ {" minLength" , 46 },
543
+ {" contentEncoding" , 47 },
544
+ {" contentMediaType" , 48 },
545
+ {" contentSchema" , 49 },
544
546
545
547
// Then keywords about arrays
546
- {" maxItems" , 47 },
547
- {" minItems" , 48 },
548
- {" uniqueItems" , 49 },
549
- {" maxContains" , 50 },
550
- {" minContains" , 51 },
551
- {" contains" , 52 },
552
- {" prefixItems" , 53 },
553
- {" items" , 54 },
554
- {" additionalItems" , 55 },
555
- {" unevaluatedItems" , 56 },
548
+ {" maxItems" , 50 },
549
+ {" minItems" , 51 },
550
+ {" uniqueItems" , 52 },
551
+ {" maxContains" , 53 },
552
+ {" minContains" , 54 },
553
+ {" contains" , 55 },
554
+ {" prefixItems" , 56 },
555
+ {" items" , 57 },
556
+ {" additionalItems" , 58 },
557
+ {" unevaluatedItems" , 59 },
556
558
557
559
// Object
558
- {" required" , 57 },
559
- {" maxProperties" , 58 },
560
- {" minProperties" , 59 },
561
- {" propertyNames" , 60 },
562
- {" properties" , 61 },
563
- {" patternProperties" , 62 },
564
- {" additionalProperties" , 63 },
565
- {" unevaluatedProperties" , 64 },
566
- {" dependentRequired" , 65 },
567
- {" dependencies" , 66 },
568
- {" dependentSchemas" , 67 },
560
+ {" required" , 60 },
561
+ {" maxProperties" , 61 },
562
+ {" minProperties" , 62 },
563
+ {" propertyNames" , 63 },
564
+ {" properties" , 64 },
565
+ {" patternProperties" , 65 },
566
+ {" additionalProperties" , 66 },
567
+ {" unevaluatedProperties" , 67 },
568
+ {" dependentRequired" , 68 },
569
+ {" dependencies" , 69 },
570
+ {" dependentSchemas" , 70 },
569
571
570
572
// Reusable utilities go last
571
- {" $defs" , 68 },
572
- {" definitions" , 69 }};
573
-
574
- if (rank.contains (left) || rank.contains (right)) {
575
- constexpr auto DEFAULT{std::numeric_limits<Rank::mapped_type>::max ()};
576
- const auto left_rank{rank.contains (left) ? rank.at (left) : DEFAULT};
577
- const auto right_rank{rank.contains (right) ? rank.at (right) : DEFAULT};
578
- // If the ranks are equal, then either the keywords are the same or
579
- // none of them are recognized keywords.
580
- assert ((left_rank != right_rank) ||
581
- (left == right || left_rank == DEFAULT));
582
- return left_rank < right_rank;
573
+ {" $defs" , 71 },
574
+ {" definitions" , 72 }};
575
+
576
+ const auto match{rank.find (keyword)};
577
+ if (match != rank.cend ()) {
578
+ return match->second ;
579
+ } else if (keyword.starts_with (" x-" )) {
580
+ assert (rank.contains (" x-" ));
581
+ return rank[" x-" ];
583
582
} else {
583
+ return otherwise;
584
+ }
585
+ }
586
+
587
+ auto sourcemeta::core::schema_format_compare (
588
+ const sourcemeta::core::JSON::String &left,
589
+ const sourcemeta::core::JSON::String &right) -> bool {
590
+ constexpr auto DEFAULT{std::numeric_limits<std::uint64_t >::max ()};
591
+ const auto left_rank{keyword_rank (left, DEFAULT)};
592
+ const auto right_rank{keyword_rank (right, DEFAULT)};
593
+ if (left_rank == DEFAULT && right_rank == DEFAULT) {
584
594
// For unknown keywords, go alphabetically
585
595
return left < right;
596
+ } else {
597
+ return left_rank < right_rank;
586
598
}
587
599
}
588
600
0 commit comments