1 // Tencent is pleased to support the open source community by making RapidJSON available->
2 //
3 // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved->
4 //
5 // Licensed under the MIT License (the "License"); you may not use this file except
6 // in compliance with the License-> You may obtain a copy of the License at
7 //
8 // http://opensource->org/licenses/MIT
9 //
10 // Unless required by applicable law or agreed to in writing, software distributed
11 // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12 // CONDITIONS OF ANY KIND, either express or implied-> See the License for the
13 // specific language governing permissions and limitations under the License->
14 
15 #ifndef CEREAL_RAPIDJSON_SCHEMA_H_
16 #define CEREAL_RAPIDJSON_SCHEMA_H_
17 
18 #include "document.h"
19 #include "pointer.h"
20 #include <cmath> // abs, floor
21 
22 #if !defined(CEREAL_RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
23 #define CEREAL_RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1
24 #else
25 #define CEREAL_RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0
26 #endif
27 
28 #if !CEREAL_RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(CEREAL_RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800))
29 #define CEREAL_RAPIDJSON_SCHEMA_USE_STDREGEX 1
30 #else
31 #define CEREAL_RAPIDJSON_SCHEMA_USE_STDREGEX 0
32 #endif
33 
34 #if CEREAL_RAPIDJSON_SCHEMA_USE_INTERNALREGEX
35 #include "internal/regex.h"
36 #elif CEREAL_RAPIDJSON_SCHEMA_USE_STDREGEX
37 #include <regex>
38 #endif
39 
40 #if CEREAL_RAPIDJSON_SCHEMA_USE_INTERNALREGEX || CEREAL_RAPIDJSON_SCHEMA_USE_STDREGEX
41 #define CEREAL_RAPIDJSON_SCHEMA_HAS_REGEX 1
42 #else
43 #define CEREAL_RAPIDJSON_SCHEMA_HAS_REGEX 0
44 #endif
45 
46 #ifndef CEREAL_RAPIDJSON_SCHEMA_VERBOSE
47 #define CEREAL_RAPIDJSON_SCHEMA_VERBOSE 0
48 #endif
49 
50 #if CEREAL_RAPIDJSON_SCHEMA_VERBOSE
51 #include "stringbuffer.h"
52 #endif
53 
54 CEREAL_RAPIDJSON_DIAG_PUSH
55 
56 #if defined(__GNUC__)
57 CEREAL_RAPIDJSON_DIAG_OFF(effc++)
58 #endif
59 
60 #ifdef __clang__
61 CEREAL_RAPIDJSON_DIAG_OFF(weak-vtables)
62 CEREAL_RAPIDJSON_DIAG_OFF(exit-time-destructors)
63 CEREAL_RAPIDJSON_DIAG_OFF(c++98-compat-pedantic)
64 CEREAL_RAPIDJSON_DIAG_OFF(variadic-macros)
65 #endif
66 
67 #ifdef _MSC_VER
68 CEREAL_RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
69 #endif
70 
71 CEREAL_RAPIDJSON_NAMESPACE_BEGIN
72 
73 ///////////////////////////////////////////////////////////////////////////////
74 // Verbose Utilities
75 
76 #if CEREAL_RAPIDJSON_SCHEMA_VERBOSE
77 
78 namespace internal {
79 
PrintInvalidKeyword(const char * keyword)80 inline void PrintInvalidKeyword(const char* keyword) {
81     printf("Fail keyword: %s\n", keyword);
82 }
83 
PrintInvalidKeyword(const wchar_t * keyword)84 inline void PrintInvalidKeyword(const wchar_t* keyword) {
85     wprintf(L"Fail keyword: %ls\n", keyword);
86 }
87 
PrintInvalidDocument(const char * document)88 inline void PrintInvalidDocument(const char* document) {
89     printf("Fail document: %s\n\n", document);
90 }
91 
PrintInvalidDocument(const wchar_t * document)92 inline void PrintInvalidDocument(const wchar_t* document) {
93     wprintf(L"Fail document: %ls\n\n", document);
94 }
95 
PrintValidatorPointers(unsigned depth,const char * s,const char * d)96 inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) {
97     printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d);
98 }
99 
PrintValidatorPointers(unsigned depth,const wchar_t * s,const wchar_t * d)100 inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) {
101     wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d);
102 }
103 
104 } // namespace internal
105 
106 #endif // CEREAL_RAPIDJSON_SCHEMA_VERBOSE
107 
108 ///////////////////////////////////////////////////////////////////////////////
109 // CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN
110 
111 #if CEREAL_RAPIDJSON_SCHEMA_VERBOSE
112 #define CEREAL_RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword)
113 #else
114 #define CEREAL_RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword)
115 #endif
116 
117 #define CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\
118 CEREAL_RAPIDJSON_MULTILINEMACRO_BEGIN\
119     context.invalidKeyword = keyword.GetString();\
120     CEREAL_RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\
121     return false;\
122 CEREAL_RAPIDJSON_MULTILINEMACRO_END
123 
124 ///////////////////////////////////////////////////////////////////////////////
125 // Forward declarations
126 
127 template <typename ValueType, typename Allocator>
128 class GenericSchemaDocument;
129 
130 namespace internal {
131 
132 template <typename SchemaDocumentType>
133 class Schema;
134 
135 ///////////////////////////////////////////////////////////////////////////////
136 // ISchemaValidator
137 
138 class ISchemaValidator {
139 public:
~ISchemaValidator()140     virtual ~ISchemaValidator() {}
141     virtual bool IsValid() const = 0;
142 };
143 
144 ///////////////////////////////////////////////////////////////////////////////
145 // ISchemaStateFactory
146 
147 template <typename SchemaType>
148 class ISchemaStateFactory {
149 public:
~ISchemaStateFactory()150     virtual ~ISchemaStateFactory() {}
151     virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0;
152     virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0;
153     virtual void* CreateHasher() = 0;
154     virtual uint64_t GetHashCode(void* hasher) = 0;
155     virtual void DestroryHasher(void* hasher) = 0;
156     virtual void* MallocState(size_t size) = 0;
157     virtual void FreeState(void* p) = 0;
158 };
159 
160 ///////////////////////////////////////////////////////////////////////////////
161 // Hasher
162 
163 // For comparison of compound value
164 template<typename Encoding, typename Allocator>
165 class Hasher {
166 public:
167     typedef typename Encoding::Ch Ch;
168 
stack_(allocator,stackCapacity)169     Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {}
170 
Null()171     bool Null() { return WriteType(kNullType); }
Bool(bool b)172     bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); }
Int(int i)173     bool Int(int i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
Uint(unsigned u)174     bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
Int64(int64_t i)175     bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
Uint64(uint64_t u)176     bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
Double(double d)177     bool Double(double d) {
178         Number n;
179         if (d < 0) n.u.i = static_cast<int64_t>(d);
180         else       n.u.u = static_cast<uint64_t>(d);
181         n.d = d;
182         return WriteNumber(n);
183     }
184 
RawNumber(const Ch * str,SizeType len,bool)185     bool RawNumber(const Ch* str, SizeType len, bool) {
186         WriteBuffer(kNumberType, str, len * sizeof(Ch));
187         return true;
188     }
189 
String(const Ch * str,SizeType len,bool)190     bool String(const Ch* str, SizeType len, bool) {
191         WriteBuffer(kStringType, str, len * sizeof(Ch));
192         return true;
193     }
194 
StartObject()195     bool StartObject() { return true; }
Key(const Ch * str,SizeType len,bool copy)196     bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); }
EndObject(SizeType memberCount)197     bool EndObject(SizeType memberCount) {
198         uint64_t h = Hash(0, kObjectType);
199         uint64_t* kv = stack_.template Pop<uint64_t>(memberCount * 2);
200         for (SizeType i = 0; i < memberCount; i++)
201             h ^= Hash(kv[i * 2], kv[i * 2 + 1]);  // Use xor to achieve member order insensitive
202         *stack_.template Push<uint64_t>() = h;
203         return true;
204     }
205 
StartArray()206     bool StartArray() { return true; }
EndArray(SizeType elementCount)207     bool EndArray(SizeType elementCount) {
208         uint64_t h = Hash(0, kArrayType);
209         uint64_t* e = stack_.template Pop<uint64_t>(elementCount);
210         for (SizeType i = 0; i < elementCount; i++)
211             h = Hash(h, e[i]); // Use hash to achieve element order sensitive
212         *stack_.template Push<uint64_t>() = h;
213         return true;
214     }
215 
IsValid()216     bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); }
217 
GetHashCode()218     uint64_t GetHashCode() const {
219         CEREAL_RAPIDJSON_ASSERT(IsValid());
220         return *stack_.template Top<uint64_t>();
221     }
222 
223 private:
224     static const size_t kDefaultSize = 256;
225     struct Number {
226         union U {
227             uint64_t u;
228             int64_t i;
229         }u;
230         double d;
231     };
232 
WriteType(Type type)233     bool WriteType(Type type) { return WriteBuffer(type, 0, 0); }
234 
WriteNumber(const Number & n)235     bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); }
236 
WriteBuffer(Type type,const void * data,size_t len)237     bool WriteBuffer(Type type, const void* data, size_t len) {
238         // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/
239         uint64_t h = Hash(CEREAL_RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type);
240         const unsigned char* d = static_cast<const unsigned char*>(data);
241         for (size_t i = 0; i < len; i++)
242             h = Hash(h, d[i]);
243         *stack_.template Push<uint64_t>() = h;
244         return true;
245     }
246 
Hash(uint64_t h,uint64_t d)247     static uint64_t Hash(uint64_t h, uint64_t d) {
248         static const uint64_t kPrime = CEREAL_RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3);
249         h ^= d;
250         h *= kPrime;
251         return h;
252     }
253 
254     Stack<Allocator> stack_;
255 };
256 
257 ///////////////////////////////////////////////////////////////////////////////
258 // SchemaValidationContext
259 
260 template <typename SchemaDocumentType>
261 struct SchemaValidationContext {
262     typedef Schema<SchemaDocumentType> SchemaType;
263     typedef ISchemaStateFactory<SchemaType> SchemaValidatorFactoryType;
264     typedef typename SchemaType::ValueType ValueType;
265     typedef typename ValueType::Ch Ch;
266 
267     enum PatternValidatorType {
268         kPatternValidatorOnly,
269         kPatternValidatorWithProperty,
270         kPatternValidatorWithAdditionalProperty
271     };
272 
SchemaValidationContextSchemaValidationContext273     SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) :
274         factory(f),
275         schema(s),
276         valueSchema(),
277         invalidKeyword(),
278         hasher(),
279         arrayElementHashCodes(),
280         validators(),
281         validatorCount(),
282         patternPropertiesValidators(),
283         patternPropertiesValidatorCount(),
284         patternPropertiesSchemas(),
285         patternPropertiesSchemaCount(),
286         valuePatternValidatorType(kPatternValidatorOnly),
287         propertyExist(),
288         inArray(false),
289         valueUniqueness(false),
290         arrayUniqueness(false)
291     {
292     }
293 
~SchemaValidationContextSchemaValidationContext294     ~SchemaValidationContext() {
295         if (hasher)
296             factory.DestroryHasher(hasher);
297         if (validators) {
298             for (SizeType i = 0; i < validatorCount; i++)
299                 factory.DestroySchemaValidator(validators[i]);
300             factory.FreeState(validators);
301         }
302         if (patternPropertiesValidators) {
303             for (SizeType i = 0; i < patternPropertiesValidatorCount; i++)
304                 factory.DestroySchemaValidator(patternPropertiesValidators[i]);
305             factory.FreeState(patternPropertiesValidators);
306         }
307         if (patternPropertiesSchemas)
308             factory.FreeState(patternPropertiesSchemas);
309         if (propertyExist)
310             factory.FreeState(propertyExist);
311     }
312 
313     SchemaValidatorFactoryType& factory;
314     const SchemaType* schema;
315     const SchemaType* valueSchema;
316     const Ch* invalidKeyword;
317     void* hasher; // Only validator access
318     void* arrayElementHashCodes; // Only validator access this
319     ISchemaValidator** validators;
320     SizeType validatorCount;
321     ISchemaValidator** patternPropertiesValidators;
322     SizeType patternPropertiesValidatorCount;
323     const SchemaType** patternPropertiesSchemas;
324     SizeType patternPropertiesSchemaCount;
325     PatternValidatorType valuePatternValidatorType;
326     PatternValidatorType objectPatternValidatorType;
327     SizeType arrayElementIndex;
328     bool* propertyExist;
329     bool inArray;
330     bool valueUniqueness;
331     bool arrayUniqueness;
332 };
333 
334 ///////////////////////////////////////////////////////////////////////////////
335 // Schema
336 
337 template <typename SchemaDocumentType>
338 class Schema {
339 public:
340     typedef typename SchemaDocumentType::ValueType ValueType;
341     typedef typename SchemaDocumentType::AllocatorType AllocatorType;
342     typedef typename SchemaDocumentType::PointerType PointerType;
343     typedef typename ValueType::EncodingType EncodingType;
344     typedef typename EncodingType::Ch Ch;
345     typedef SchemaValidationContext<SchemaDocumentType> Context;
346     typedef Schema<SchemaDocumentType> SchemaType;
347     typedef GenericValue<EncodingType, AllocatorType> SValue;
348     friend class GenericSchemaDocument<ValueType, AllocatorType>;
349 
Schema(SchemaDocumentType * schemaDocument,const PointerType & p,const ValueType & value,const ValueType & document,AllocatorType * allocator)350     Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) :
351         allocator_(allocator),
352         enum_(),
353         enumCount_(),
354         not_(),
355         type_((1 << kTotalSchemaType) - 1), // typeless
356         validatorCount_(),
357         properties_(),
358         additionalPropertiesSchema_(),
359         patternProperties_(),
360         patternPropertyCount_(),
361         propertyCount_(),
362         minProperties_(),
363         maxProperties_(SizeType(~0)),
364         additionalProperties_(true),
365         hasDependencies_(),
366         hasRequired_(),
367         hasSchemaDependencies_(),
368         additionalItemsSchema_(),
369         itemsList_(),
370         itemsTuple_(),
371         itemsTupleCount_(),
372         minItems_(),
373         maxItems_(SizeType(~0)),
374         additionalItems_(true),
375         uniqueItems_(false),
376         pattern_(),
377         minLength_(0),
378         maxLength_(~SizeType(0)),
379         exclusiveMinimum_(false),
380         exclusiveMaximum_(false)
381     {
382         typedef typename SchemaDocumentType::ValueType ValueType;
383         typedef typename ValueType::ConstValueIterator ConstValueIterator;
384         typedef typename ValueType::ConstMemberIterator ConstMemberIterator;
385 
386         if (!value.IsObject())
387             return;
388 
389         if (const ValueType* v = GetMember(value, GetTypeString())) {
390             type_ = 0;
391             if (v->IsString())
392                 AddType(*v);
393             else if (v->IsArray())
394                 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr)
395                     AddType(*itr);
396         }
397 
398         if (const ValueType* v = GetMember(value, GetEnumString()))
399             if (v->IsArray() && v->Size() > 0) {
400                 enum_ = static_cast<uint64_t*>(allocator_->Malloc(sizeof(uint64_t) * v->Size()));
401                 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) {
402                     typedef Hasher<EncodingType, MemoryPoolAllocator<> > EnumHasherType;
403                     char buffer[256 + 24];
404                     MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer));
405                     EnumHasherType h(&hasherAllocator, 256);
406                     itr->Accept(h);
407                     enum_[enumCount_++] = h.GetHashCode();
408                 }
409             }
410 
411         if (schemaDocument) {
412             AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
413             AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
414             AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
415         }
416 
417         if (const ValueType* v = GetMember(value, GetNotString())) {
418             schemaDocument->CreateSchema(&not_, p.Append(GetNotString(), allocator_), *v, document);
419             notValidatorIndex_ = validatorCount_;
420             validatorCount_++;
421         }
422 
423         // Object
424 
425         const ValueType* properties = GetMember(value, GetPropertiesString());
426         const ValueType* required = GetMember(value, GetRequiredString());
427         const ValueType* dependencies = GetMember(value, GetDependenciesString());
428         {
429             // Gather properties from properties/required/dependencies
430             SValue allProperties(kArrayType);
431 
432             if (properties && properties->IsObject())
433                 for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr)
434                     AddUniqueElement(allProperties, itr->name);
435 
436             if (required && required->IsArray())
437                 for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
438                     if (itr->IsString())
439                         AddUniqueElement(allProperties, *itr);
440 
441             if (dependencies && dependencies->IsObject())
442                 for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
443                     AddUniqueElement(allProperties, itr->name);
444                     if (itr->value.IsArray())
445                         for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i)
446                             if (i->IsString())
447                                 AddUniqueElement(allProperties, *i);
448                 }
449 
450             if (allProperties.Size() > 0) {
451                 propertyCount_ = allProperties.Size();
452                 properties_ = static_cast<Property*>(allocator_->Malloc(sizeof(Property) * propertyCount_));
453                 for (SizeType i = 0; i < propertyCount_; i++) {
454                     new (&properties_[i]) Property();
455                     properties_[i].name = allProperties[i];
456                     properties_[i].schema = GetTypeless();
457                 }
458             }
459         }
460 
461         if (properties && properties->IsObject()) {
462             PointerType q = p.Append(GetPropertiesString(), allocator_);
463             for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) {
464                 SizeType index;
465                 if (FindPropertyIndex(itr->name, &index))
466                     schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document);
467             }
468         }
469 
470         if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) {
471             PointerType q = p.Append(GetPatternPropertiesString(), allocator_);
472             patternProperties_ = static_cast<PatternProperty*>(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount()));
473             patternPropertyCount_ = 0;
474 
475             for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) {
476                 new (&patternProperties_[patternPropertyCount_]) PatternProperty();
477                 patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name);
478                 schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document);
479                 patternPropertyCount_++;
480             }
481         }
482 
483         if (required && required->IsArray())
484             for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
485                 if (itr->IsString()) {
486                     SizeType index;
487                     if (FindPropertyIndex(*itr, &index)) {
488                         properties_[index].required = true;
489                         hasRequired_ = true;
490                     }
491                 }
492 
493         if (dependencies && dependencies->IsObject()) {
494             PointerType q = p.Append(GetDependenciesString(), allocator_);
495             hasDependencies_ = true;
496             for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
497                 SizeType sourceIndex;
498                 if (FindPropertyIndex(itr->name, &sourceIndex)) {
499                     if (itr->value.IsArray()) {
500                         properties_[sourceIndex].dependencies = static_cast<bool*>(allocator_->Malloc(sizeof(bool) * propertyCount_));
501                         std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_);
502                         for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) {
503                             SizeType targetIndex;
504                             if (FindPropertyIndex(*targetItr, &targetIndex))
505                                 properties_[sourceIndex].dependencies[targetIndex] = true;
506                         }
507                     }
508                     else if (itr->value.IsObject()) {
509                         hasSchemaDependencies_ = true;
510                         schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document);
511                         properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_;
512                         validatorCount_++;
513                     }
514                 }
515             }
516         }
517 
518         if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) {
519             if (v->IsBool())
520                 additionalProperties_ = v->GetBool();
521             else if (v->IsObject())
522                 schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document);
523         }
524 
525         AssignIfExist(minProperties_, value, GetMinPropertiesString());
526         AssignIfExist(maxProperties_, value, GetMaxPropertiesString());
527 
528         // Array
529         if (const ValueType* v = GetMember(value, GetItemsString())) {
530             PointerType q = p.Append(GetItemsString(), allocator_);
531             if (v->IsObject()) // List validation
532                 schemaDocument->CreateSchema(&itemsList_, q, *v, document);
533             else if (v->IsArray()) { // Tuple validation
534                 itemsTuple_ = static_cast<const Schema**>(allocator_->Malloc(sizeof(const Schema*) * v->Size()));
535                 SizeType index = 0;
536                 for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++)
537                     schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document);
538             }
539         }
540 
541         AssignIfExist(minItems_, value, GetMinItemsString());
542         AssignIfExist(maxItems_, value, GetMaxItemsString());
543 
544         if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) {
545             if (v->IsBool())
546                 additionalItems_ = v->GetBool();
547             else if (v->IsObject())
548                 schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document);
549         }
550 
551         AssignIfExist(uniqueItems_, value, GetUniqueItemsString());
552 
553         // String
554         AssignIfExist(minLength_, value, GetMinLengthString());
555         AssignIfExist(maxLength_, value, GetMaxLengthString());
556 
557         if (const ValueType* v = GetMember(value, GetPatternString()))
558             pattern_ = CreatePattern(*v);
559 
560         // Number
561         if (const ValueType* v = GetMember(value, GetMinimumString()))
562             if (v->IsNumber())
563                 minimum_.CopyFrom(*v, *allocator_);
564 
565         if (const ValueType* v = GetMember(value, GetMaximumString()))
566             if (v->IsNumber())
567                 maximum_.CopyFrom(*v, *allocator_);
568 
569         AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString());
570         AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString());
571 
572         if (const ValueType* v = GetMember(value, GetMultipleOfString()))
573             if (v->IsNumber() && v->GetDouble() > 0.0)
574                 multipleOf_.CopyFrom(*v, *allocator_);
575     }
576 
~Schema()577     ~Schema() {
578         if (allocator_) {
579             allocator_->Free(enum_);
580         }
581         if (properties_) {
582             for (SizeType i = 0; i < propertyCount_; i++)
583                 properties_[i].~Property();
584             AllocatorType::Free(properties_);
585         }
586         if (patternProperties_) {
587             for (SizeType i = 0; i < patternPropertyCount_; i++)
588                 patternProperties_[i].~PatternProperty();
589             AllocatorType::Free(patternProperties_);
590         }
591         AllocatorType::Free(itemsTuple_);
592 #if CEREAL_RAPIDJSON_SCHEMA_HAS_REGEX
593         if (pattern_) {
594             pattern_->~RegexType();
595             allocator_->Free(pattern_);
596         }
597 #endif
598     }
599 
BeginValue(Context & context)600     bool BeginValue(Context& context) const {
601         if (context.inArray) {
602             if (uniqueItems_)
603                 context.valueUniqueness = true;
604 
605             if (itemsList_)
606                 context.valueSchema = itemsList_;
607             else if (itemsTuple_) {
608                 if (context.arrayElementIndex < itemsTupleCount_)
609                     context.valueSchema = itemsTuple_[context.arrayElementIndex];
610                 else if (additionalItemsSchema_)
611                     context.valueSchema = additionalItemsSchema_;
612                 else if (additionalItems_)
613                     context.valueSchema = GetTypeless();
614                 else
615                     CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString());
616             }
617             else
618                 context.valueSchema = GetTypeless();
619 
620             context.arrayElementIndex++;
621         }
622         return true;
623     }
624 
EndValue(Context & context)625     CEREAL_RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const {
626         if (context.patternPropertiesValidatorCount > 0) {
627             bool otherValid = false;
628             SizeType count = context.patternPropertiesValidatorCount;
629             if (context.objectPatternValidatorType != Context::kPatternValidatorOnly)
630                 otherValid = context.patternPropertiesValidators[--count]->IsValid();
631 
632             bool patternValid = true;
633             for (SizeType i = 0; i < count; i++)
634                 if (!context.patternPropertiesValidators[i]->IsValid()) {
635                     patternValid = false;
636                     break;
637                 }
638 
639             if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
640                 if (!patternValid)
641                     CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
642             }
643             else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
644                 if (!patternValid || !otherValid)
645                     CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
646             }
647             else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty)
648                 CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString());
649         }
650 
651         if (enum_) {
652             const uint64_t h = context.factory.GetHashCode(context.hasher);
653             for (SizeType i = 0; i < enumCount_; i++)
654                 if (enum_[i] == h)
655                     goto foundEnum;
656             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString());
657             foundEnum:;
658         }
659 
660         if (allOf_.schemas)
661             for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
662                 if (!context.validators[i]->IsValid())
663                     CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString());
664 
665         if (anyOf_.schemas) {
666             for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
667                 if (context.validators[i]->IsValid())
668                     goto foundAny;
669             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString());
670             foundAny:;
671         }
672 
673         if (oneOf_.schemas) {
674             bool oneValid = false;
675             for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
676                 if (context.validators[i]->IsValid()) {
677                     if (oneValid)
678                         CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
679                     else
680                         oneValid = true;
681                 }
682             if (!oneValid)
683                 CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString());
684         }
685 
686         if (not_ && context.validators[notValidatorIndex_]->IsValid())
687             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString());
688 
689         return true;
690     }
691 
Null(Context & context)692     bool Null(Context& context) const {
693         if (!(type_ & (1 << kNullSchemaType)))
694             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
695         return CreateParallelValidator(context);
696     }
697 
Bool(Context & context,bool)698     bool Bool(Context& context, bool) const {
699         if (!(type_ & (1 << kBooleanSchemaType)))
700             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
701         return CreateParallelValidator(context);
702     }
703 
Int(Context & context,int i)704     bool Int(Context& context, int i) const {
705         if (!CheckInt(context, i))
706             return false;
707         return CreateParallelValidator(context);
708     }
709 
Uint(Context & context,unsigned u)710     bool Uint(Context& context, unsigned u) const {
711         if (!CheckUint(context, u))
712             return false;
713         return CreateParallelValidator(context);
714     }
715 
Int64(Context & context,int64_t i)716     bool Int64(Context& context, int64_t i) const {
717         if (!CheckInt(context, i))
718             return false;
719         return CreateParallelValidator(context);
720     }
721 
Uint64(Context & context,uint64_t u)722     bool Uint64(Context& context, uint64_t u) const {
723         if (!CheckUint(context, u))
724             return false;
725         return CreateParallelValidator(context);
726     }
727 
Double(Context & context,double d)728     bool Double(Context& context, double d) const {
729         if (!(type_ & (1 << kNumberSchemaType)))
730             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
731 
732         if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
733             return false;
734 
735         if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d))
736             return false;
737 
738         if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d))
739             return false;
740 
741         return CreateParallelValidator(context);
742     }
743 
String(Context & context,const Ch * str,SizeType length,bool)744     bool String(Context& context, const Ch* str, SizeType length, bool) const {
745         if (!(type_ & (1 << kStringSchemaType)))
746             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
747 
748         if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
749             SizeType count;
750             if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
751                 if (count < minLength_)
752                     CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString());
753                 if (count > maxLength_)
754                     CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString());
755             }
756         }
757 
758         if (pattern_ && !IsPatternMatch(pattern_, str, length))
759             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString());
760 
761         return CreateParallelValidator(context);
762     }
763 
StartObject(Context & context)764     bool StartObject(Context& context) const {
765         if (!(type_ & (1 << kObjectSchemaType)))
766             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
767 
768         if (hasDependencies_ || hasRequired_) {
769             context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_));
770             std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_);
771         }
772 
773         if (patternProperties_) { // pre-allocate schema array
774             SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType
775             context.patternPropertiesSchemas = static_cast<const SchemaType**>(context.factory.MallocState(sizeof(const SchemaType*) * count));
776             context.patternPropertiesSchemaCount = 0;
777             std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count);
778         }
779 
780         return CreateParallelValidator(context);
781     }
782 
Key(Context & context,const Ch * str,SizeType len,bool)783     bool Key(Context& context, const Ch* str, SizeType len, bool) const {
784         if (patternProperties_) {
785             context.patternPropertiesSchemaCount = 0;
786             for (SizeType i = 0; i < patternPropertyCount_; i++)
787                 if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len))
788                     context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema;
789         }
790 
791         SizeType index;
792         if (FindPropertyIndex(ValueType(str, len).Move(), &index)) {
793             if (context.patternPropertiesSchemaCount > 0) {
794                 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema;
795                 context.valueSchema = GetTypeless();
796                 context.valuePatternValidatorType = Context::kPatternValidatorWithProperty;
797             }
798             else
799                 context.valueSchema = properties_[index].schema;
800 
801             if (context.propertyExist)
802                 context.propertyExist[index] = true;
803 
804             return true;
805         }
806 
807         if (additionalPropertiesSchema_) {
808             if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) {
809                 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
810                 context.valueSchema = GetTypeless();
811                 context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty;
812             }
813             else
814                 context.valueSchema = additionalPropertiesSchema_;
815             return true;
816         }
817         else if (additionalProperties_) {
818             context.valueSchema = GetTypeless();
819             return true;
820         }
821 
822         if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties
823             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString());
824 
825         return true;
826     }
827 
EndObject(Context & context,SizeType memberCount)828     bool EndObject(Context& context, SizeType memberCount) const {
829         if (hasRequired_)
830             for (SizeType index = 0; index < propertyCount_; index++)
831                 if (properties_[index].required)
832                     if (!context.propertyExist[index])
833                         CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString());
834 
835         if (memberCount < minProperties_)
836             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString());
837 
838         if (memberCount > maxProperties_)
839             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString());
840 
841         if (hasDependencies_) {
842             for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++)
843                 if (context.propertyExist[sourceIndex]) {
844                     if (properties_[sourceIndex].dependencies) {
845                         for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
846                             if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex])
847                                 CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
848                     }
849                     else if (properties_[sourceIndex].dependenciesSchema)
850                         if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid())
851                             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
852                 }
853         }
854 
855         return true;
856     }
857 
StartArray(Context & context)858     bool StartArray(Context& context) const {
859         if (!(type_ & (1 << kArraySchemaType)))
860             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
861 
862         context.arrayElementIndex = 0;
863         context.inArray = true;
864 
865         return CreateParallelValidator(context);
866     }
867 
EndArray(Context & context,SizeType elementCount)868     bool EndArray(Context& context, SizeType elementCount) const {
869         context.inArray = false;
870 
871         if (elementCount < minItems_)
872             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString());
873 
874         if (elementCount > maxItems_)
875             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString());
876 
877         return true;
878     }
879 
880     // Generate functions for string literal according to Ch
881 #define CEREAL_RAPIDJSON_STRING_(name, ...) \
882     static const ValueType& Get##name##String() {\
883         static const Ch s[] = { __VA_ARGS__, '\0' };\
884         static const ValueType v(s, sizeof(s) / sizeof(Ch) - 1);\
885         return v;\
886     }
887 
888     CEREAL_RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
889     CEREAL_RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n')
890     CEREAL_RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't')
891     CEREAL_RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y')
892     CEREAL_RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g')
893     CEREAL_RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r')
894     CEREAL_RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r')
895     CEREAL_RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e')
896     CEREAL_RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm')
897     CEREAL_RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f')
898     CEREAL_RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f')
899     CEREAL_RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f')
900     CEREAL_RAPIDJSON_STRING_(Not, 'n', 'o', 't')
901     CEREAL_RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
902     CEREAL_RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd')
903     CEREAL_RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's')
904     CEREAL_RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
905     CEREAL_RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
906     CEREAL_RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
907     CEREAL_RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
908     CEREAL_RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's')
909     CEREAL_RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's')
910     CEREAL_RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's')
911     CEREAL_RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's')
912     CEREAL_RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's')
913     CEREAL_RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h')
914     CEREAL_RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h')
915     CEREAL_RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n')
916     CEREAL_RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm')
917     CEREAL_RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm')
918     CEREAL_RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm')
919     CEREAL_RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
920     CEREAL_RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
921 
922 #undef CEREAL_RAPIDJSON_STRING_
923 
924 private:
925     enum SchemaValueType {
926         kNullSchemaType,
927         kBooleanSchemaType,
928         kObjectSchemaType,
929         kArraySchemaType,
930         kStringSchemaType,
931         kNumberSchemaType,
932         kIntegerSchemaType,
933         kTotalSchemaType
934     };
935 
936 #if CEREAL_RAPIDJSON_SCHEMA_USE_INTERNALREGEX
937         typedef internal::GenericRegex<EncodingType> RegexType;
938 #elif CEREAL_RAPIDJSON_SCHEMA_USE_STDREGEX
939         typedef std::basic_regex<Ch> RegexType;
940 #else
941         typedef char RegexType;
942 #endif
943 
944     struct SchemaArray {
SchemaArraySchemaArray945         SchemaArray() : schemas(), count() {}
~SchemaArraySchemaArray946         ~SchemaArray() { AllocatorType::Free(schemas); }
947         const SchemaType** schemas;
948         SizeType begin; // begin index of context.validators
949         SizeType count;
950     };
951 
GetTypeless()952     static const SchemaType* GetTypeless() {
953         static SchemaType typeless(0, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0);
954         return &typeless;
955     }
956 
957     template <typename V1, typename V2>
AddUniqueElement(V1 & a,const V2 & v)958     void AddUniqueElement(V1& a, const V2& v) {
959         for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
960             if (*itr == v)
961                 return;
962         V1 c(v, *allocator_);
963         a.PushBack(c, *allocator_);
964     }
965 
GetMember(const ValueType & value,const ValueType & name)966     static const ValueType* GetMember(const ValueType& value, const ValueType& name) {
967         typename ValueType::ConstMemberIterator itr = value.FindMember(name);
968         return itr != value.MemberEnd() ? &(itr->value) : 0;
969     }
970 
AssignIfExist(bool & out,const ValueType & value,const ValueType & name)971     static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) {
972         if (const ValueType* v = GetMember(value, name))
973             if (v->IsBool())
974                 out = v->GetBool();
975     }
976 
AssignIfExist(SizeType & out,const ValueType & value,const ValueType & name)977     static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) {
978         if (const ValueType* v = GetMember(value, name))
979             if (v->IsUint64() && v->GetUint64() <= SizeType(~0))
980                 out = static_cast<SizeType>(v->GetUint64());
981     }
982 
AssignIfExist(SchemaArray & out,SchemaDocumentType & schemaDocument,const PointerType & p,const ValueType & value,const ValueType & name,const ValueType & document)983     void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) {
984         if (const ValueType* v = GetMember(value, name)) {
985             if (v->IsArray() && v->Size() > 0) {
986                 PointerType q = p.Append(name, allocator_);
987                 out.count = v->Size();
988                 out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*)));
989                 memset(out.schemas, 0, sizeof(Schema*)* out.count);
990                 for (SizeType i = 0; i < out.count; i++)
991                     schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document);
992                 out.begin = validatorCount_;
993                 validatorCount_ += out.count;
994             }
995         }
996     }
997 
998 #if CEREAL_RAPIDJSON_SCHEMA_USE_INTERNALREGEX
999     template <typename ValueType>
CreatePattern(const ValueType & value)1000     RegexType* CreatePattern(const ValueType& value) {
1001         if (value.IsString()) {
1002             RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString());
1003             if (!r->IsValid()) {
1004                 r->~RegexType();
1005                 AllocatorType::Free(r);
1006                 r = 0;
1007             }
1008             return r;
1009         }
1010         return 0;
1011     }
1012 
IsPatternMatch(const RegexType * pattern,const Ch * str,SizeType)1013     static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) {
1014         return pattern->Search(str);
1015     }
1016 #elif CEREAL_RAPIDJSON_SCHEMA_USE_STDREGEX
1017     template <typename ValueType>
CreatePattern(const ValueType & value)1018     RegexType* CreatePattern(const ValueType& value) {
1019         if (value.IsString())
1020             try {
1021                 return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript);
1022             }
1023             catch (const std::regex_error&) {
1024             }
1025         return 0;
1026     }
1027 
IsPatternMatch(const RegexType * pattern,const Ch * str,SizeType length)1028     static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) {
1029         std::match_results<const Ch*> r;
1030         return std::regex_search(str, str + length, r, *pattern);
1031     }
1032 #else
1033     template <typename ValueType>
CreatePattern(const ValueType &)1034     RegexType* CreatePattern(const ValueType&) { return 0; }
1035 
IsPatternMatch(const RegexType *,const Ch *,SizeType)1036     static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; }
1037 #endif // CEREAL_RAPIDJSON_SCHEMA_USE_STDREGEX
1038 
AddType(const ValueType & type)1039     void AddType(const ValueType& type) {
1040         if      (type == GetNullString()   ) type_ |= 1 << kNullSchemaType;
1041         else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType;
1042         else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType;
1043         else if (type == GetArrayString()  ) type_ |= 1 << kArraySchemaType;
1044         else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType;
1045         else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType;
1046         else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType);
1047     }
1048 
CreateParallelValidator(Context & context)1049     bool CreateParallelValidator(Context& context) const {
1050         if (enum_ || context.arrayUniqueness)
1051             context.hasher = context.factory.CreateHasher();
1052 
1053         if (validatorCount_) {
1054             CEREAL_RAPIDJSON_ASSERT(context.validators == 0);
1055             context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
1056             context.validatorCount = validatorCount_;
1057 
1058             if (allOf_.schemas)
1059                 CreateSchemaValidators(context, allOf_);
1060 
1061             if (anyOf_.schemas)
1062                 CreateSchemaValidators(context, anyOf_);
1063 
1064             if (oneOf_.schemas)
1065                 CreateSchemaValidators(context, oneOf_);
1066 
1067             if (not_)
1068                 context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_);
1069 
1070             if (hasSchemaDependencies_) {
1071                 for (SizeType i = 0; i < propertyCount_; i++)
1072                     if (properties_[i].dependenciesSchema)
1073                         context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema);
1074             }
1075         }
1076 
1077         return true;
1078     }
1079 
CreateSchemaValidators(Context & context,const SchemaArray & schemas)1080     void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const {
1081         for (SizeType i = 0; i < schemas.count; i++)
1082             context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]);
1083     }
1084 
1085     // O(n)
FindPropertyIndex(const ValueType & name,SizeType * outIndex)1086     bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const {
1087         SizeType len = name.GetStringLength();
1088         const Ch* str = name.GetString();
1089         for (SizeType index = 0; index < propertyCount_; index++)
1090             if (properties_[index].name.GetStringLength() == len &&
1091                 (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0))
1092             {
1093                 *outIndex = index;
1094                 return true;
1095             }
1096         return false;
1097     }
1098 
CheckInt(Context & context,int64_t i)1099     bool CheckInt(Context& context, int64_t i) const {
1100         if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
1101             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
1102 
1103         if (!minimum_.IsNull()) {
1104             if (minimum_.IsInt64()) {
1105                 if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64())
1106                     CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1107             }
1108             else if (minimum_.IsUint64()) {
1109                 CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64()
1110             }
1111             else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1112                 return false;
1113         }
1114 
1115         if (!maximum_.IsNull()) {
1116             if (maximum_.IsInt64()) {
1117                 if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64())
1118                     CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1119             }
1120             else if (maximum_.IsUint64())
1121                 /* do nothing */; // i <= max(int64_t) < maximum_.GetUint64()
1122             else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1123                 return false;
1124         }
1125 
1126         if (!multipleOf_.IsNull()) {
1127             if (multipleOf_.IsUint64()) {
1128                 if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0)
1129                     CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1130             }
1131             else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1132                 return false;
1133         }
1134 
1135         return true;
1136     }
1137 
CheckUint(Context & context,uint64_t i)1138     bool CheckUint(Context& context, uint64_t i) const {
1139         if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
1140             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
1141 
1142         if (!minimum_.IsNull()) {
1143             if (minimum_.IsUint64()) {
1144                 if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64())
1145                     CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1146             }
1147             else if (minimum_.IsInt64())
1148                 /* do nothing */; // i >= 0 > minimum.Getint64()
1149             else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1150                 return false;
1151         }
1152 
1153         if (!maximum_.IsNull()) {
1154             if (maximum_.IsUint64()) {
1155                 if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64())
1156                     CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1157             }
1158             else if (maximum_.IsInt64())
1159                 CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_
1160             else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1161                 return false;
1162         }
1163 
1164         if (!multipleOf_.IsNull()) {
1165             if (multipleOf_.IsUint64()) {
1166                 if (i % multipleOf_.GetUint64() != 0)
1167                     CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1168             }
1169             else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1170                 return false;
1171         }
1172 
1173         return true;
1174     }
1175 
CheckDoubleMinimum(Context & context,double d)1176     bool CheckDoubleMinimum(Context& context, double d) const {
1177         if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble())
1178             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1179         return true;
1180     }
1181 
CheckDoubleMaximum(Context & context,double d)1182     bool CheckDoubleMaximum(Context& context, double d) const {
1183         if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble())
1184             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1185         return true;
1186     }
1187 
CheckDoubleMultipleOf(Context & context,double d)1188     bool CheckDoubleMultipleOf(Context& context, double d) const {
1189         double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
1190         double q = std::floor(a / b);
1191         double r = a - q * b;
1192         if (r > 0.0)
1193             CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1194         return true;
1195     }
1196 
1197     struct Property {
PropertyProperty1198         Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
~PropertyProperty1199         ~Property() { AllocatorType::Free(dependencies); }
1200         SValue name;
1201         const SchemaType* schema;
1202         const SchemaType* dependenciesSchema;
1203         SizeType dependenciesValidatorIndex;
1204         bool* dependencies;
1205         bool required;
1206     };
1207 
1208     struct PatternProperty {
PatternPropertyPatternProperty1209         PatternProperty() : schema(), pattern() {}
~PatternPropertyPatternProperty1210         ~PatternProperty() {
1211             if (pattern) {
1212                 pattern->~RegexType();
1213                 AllocatorType::Free(pattern);
1214             }
1215         }
1216         const SchemaType* schema;
1217         RegexType* pattern;
1218     };
1219 
1220     AllocatorType* allocator_;
1221     uint64_t* enum_;
1222     SizeType enumCount_;
1223     SchemaArray allOf_;
1224     SchemaArray anyOf_;
1225     SchemaArray oneOf_;
1226     const SchemaType* not_;
1227     unsigned type_; // bitmask of kSchemaType
1228     SizeType validatorCount_;
1229     SizeType notValidatorIndex_;
1230 
1231     Property* properties_;
1232     const SchemaType* additionalPropertiesSchema_;
1233     PatternProperty* patternProperties_;
1234     SizeType patternPropertyCount_;
1235     SizeType propertyCount_;
1236     SizeType minProperties_;
1237     SizeType maxProperties_;
1238     bool additionalProperties_;
1239     bool hasDependencies_;
1240     bool hasRequired_;
1241     bool hasSchemaDependencies_;
1242 
1243     const SchemaType* additionalItemsSchema_;
1244     const SchemaType* itemsList_;
1245     const SchemaType** itemsTuple_;
1246     SizeType itemsTupleCount_;
1247     SizeType minItems_;
1248     SizeType maxItems_;
1249     bool additionalItems_;
1250     bool uniqueItems_;
1251 
1252     RegexType* pattern_;
1253     SizeType minLength_;
1254     SizeType maxLength_;
1255 
1256     SValue minimum_;
1257     SValue maximum_;
1258     SValue multipleOf_;
1259     bool exclusiveMinimum_;
1260     bool exclusiveMaximum_;
1261 };
1262 
1263 template<typename Stack, typename Ch>
1264 struct TokenHelper {
AppendIndexTokenTokenHelper1265     CEREAL_RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1266         *documentStack.template Push<Ch>() = '/';
1267         char buffer[21];
1268         size_t length = static_cast<size_t>((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer);
1269         for (size_t i = 0; i < length; i++)
1270             *documentStack.template Push<Ch>() = buffer[i];
1271     }
1272 };
1273 
1274 // Partial specialized version for char to prevent buffer copying.
1275 template <typename Stack>
1276 struct TokenHelper<Stack, char> {
1277     CEREAL_RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1278         if (sizeof(SizeType) == 4) {
1279             char *buffer = documentStack.template Push<char>(1 + 10); // '/' + uint
1280             *buffer++ = '/';
1281             const char* end = internal::u32toa(index, buffer);
1282              documentStack.template Pop<char>(static_cast<size_t>(10 - (end - buffer)));
1283         }
1284         else {
1285             char *buffer = documentStack.template Push<char>(1 + 20); // '/' + uint64
1286             *buffer++ = '/';
1287             const char* end = internal::u64toa(index, buffer);
1288             documentStack.template Pop<char>(static_cast<size_t>(20 - (end - buffer)));
1289         }
1290     }
1291 };
1292 
1293 } // namespace internal
1294 
1295 ///////////////////////////////////////////////////////////////////////////////
1296 // IGenericRemoteSchemaDocumentProvider
1297 
1298 template <typename SchemaDocumentType>
1299 class IGenericRemoteSchemaDocumentProvider {
1300 public:
1301     typedef typename SchemaDocumentType::Ch Ch;
1302 
1303     virtual ~IGenericRemoteSchemaDocumentProvider() {}
1304     virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
1305 };
1306 
1307 ///////////////////////////////////////////////////////////////////////////////
1308 // GenericSchemaDocument
1309 
1310 //! JSON schema document.
1311 /*!
1312     A JSON schema document is a compiled version of a JSON schema.
1313     It is basically a tree of internal::Schema.
1314 
1315     \note This is an immutable class (i.e. its instance cannot be modified after construction).
1316     \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding.
1317     \tparam Allocator Allocator type for allocating memory of this document.
1318 */
1319 template <typename ValueT, typename Allocator = CrtAllocator>
1320 class GenericSchemaDocument {
1321 public:
1322     typedef ValueT ValueType;
1323     typedef IGenericRemoteSchemaDocumentProvider<GenericSchemaDocument> IRemoteSchemaDocumentProviderType;
1324     typedef Allocator AllocatorType;
1325     typedef typename ValueType::EncodingType EncodingType;
1326     typedef typename EncodingType::Ch Ch;
1327     typedef internal::Schema<GenericSchemaDocument> SchemaType;
1328     typedef GenericPointer<ValueType, Allocator> PointerType;
1329     friend class internal::Schema<GenericSchemaDocument>;
1330     template <typename, typename, typename>
1331     friend class GenericSchemaValidator;
1332 
1333     //! Constructor.
1334     /*!
1335         Compile a JSON document into schema document.
1336 
1337         \param document A JSON document as source.
1338         \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
1339         \param allocator An optional allocator instance for allocating memory. Can be null.
1340     */
1341     explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
1342         remoteProvider_(remoteProvider),
1343         allocator_(allocator),
1344         ownAllocator_(),
1345         root_(),
1346         schemaMap_(allocator, kInitialSchemaMapSize),
1347         schemaRef_(allocator, kInitialSchemaRefSize)
1348     {
1349         if (!allocator_)
1350             ownAllocator_ = allocator_ = CEREAL_RAPIDJSON_NEW(Allocator());
1351 
1352         // Generate root schema, it will call CreateSchema() to create sub-schemas,
1353         // And call AddRefSchema() if there are $ref.
1354         CreateSchemaRecursive(&root_, PointerType(), document, document);
1355 
1356         // Resolve $ref
1357         while (!schemaRef_.Empty()) {
1358             SchemaRefEntry* refEntry = schemaRef_.template Pop<SchemaRefEntry>(1);
1359             if (const SchemaType* s = GetSchema(refEntry->target)) {
1360                 if (refEntry->schema)
1361                     *refEntry->schema = s;
1362 
1363                 // Create entry in map if not exist
1364                 if (!GetSchema(refEntry->source)) {
1365                     new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(refEntry->source, const_cast<SchemaType*>(s), false, allocator_);
1366                 }
1367             }
1368             refEntry->~SchemaRefEntry();
1369         }
1370 
1371         CEREAL_RAPIDJSON_ASSERT(root_ != 0);
1372 
1373         schemaRef_.ShrinkToFit(); // Deallocate all memory for ref
1374     }
1375 
1376 #if CEREAL_RAPIDJSON_HAS_CXX11_RVALUE_REFS
1377     //! Move constructor in C++11
1378     GenericSchemaDocument(GenericSchemaDocument&& rhs) CEREAL_RAPIDJSON_NOEXCEPT :
1379         remoteProvider_(rhs.remoteProvider_),
1380         allocator_(rhs.allocator_),
1381         ownAllocator_(rhs.ownAllocator_),
1382         root_(rhs.root_),
1383         schemaMap_(std::move(rhs.schemaMap_)),
1384         schemaRef_(std::move(rhs.schemaRef_))
1385     {
1386         rhs.remoteProvider_ = 0;
1387         rhs.allocator_ = 0;
1388         rhs.ownAllocator_ = 0;
1389     }
1390 #endif
1391 
1392     //! Destructor
1393     ~GenericSchemaDocument() {
1394         while (!schemaMap_.Empty())
1395             schemaMap_.template Pop<SchemaEntry>(1)->~SchemaEntry();
1396 
1397         CEREAL_RAPIDJSON_DELETE(ownAllocator_);
1398     }
1399 
1400     //! Get the root schema.
1401     const SchemaType& GetRoot() const { return *root_; }
1402 
1403 private:
1404     //! Prohibit copying
1405     GenericSchemaDocument(const GenericSchemaDocument&);
1406     //! Prohibit assignment
1407     GenericSchemaDocument& operator=(const GenericSchemaDocument&);
1408 
1409     struct SchemaRefEntry {
1410         SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {}
1411         PointerType source;
1412         PointerType target;
1413         const SchemaType** schema;
1414     };
1415 
1416     struct SchemaEntry {
1417         SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {}
1418         ~SchemaEntry() {
1419             if (owned) {
1420                 schema->~SchemaType();
1421                 Allocator::Free(schema);
1422             }
1423         }
1424         PointerType pointer;
1425         SchemaType* schema;
1426         bool owned;
1427     };
1428 
1429     void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
1430         if (schema)
1431             *schema = SchemaType::GetTypeless();
1432 
1433         if (v.GetType() == kObjectType) {
1434             const SchemaType* s = GetSchema(pointer);
1435             if (!s)
1436                 CreateSchema(schema, pointer, v, document);
1437 
1438             for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
1439                 CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document);
1440         }
1441         else if (v.GetType() == kArrayType)
1442             for (SizeType i = 0; i < v.Size(); i++)
1443                 CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document);
1444     }
1445 
1446     void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
1447         CEREAL_RAPIDJSON_ASSERT(pointer.IsValid());
1448         if (v.IsObject()) {
1449             if (!HandleRefSchema(pointer, schema, v, document)) {
1450                 SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_);
1451                 new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(pointer, s, true, allocator_);
1452                 if (schema)
1453                     *schema = s;
1454             }
1455         }
1456     }
1457 
1458     bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) {
1459         static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' };
1460         static const ValueType kRefValue(kRefString, 4);
1461 
1462         typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue);
1463         if (itr == v.MemberEnd())
1464             return false;
1465 
1466         if (itr->value.IsString()) {
1467             SizeType len = itr->value.GetStringLength();
1468             if (len > 0) {
1469                 const Ch* s = itr->value.GetString();
1470                 SizeType i = 0;
1471                 while (i < len && s[i] != '#') // Find the first #
1472                     i++;
1473 
1474                 if (i > 0) { // Remote reference, resolve immediately
1475                     if (remoteProvider_) {
1476                         if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i - 1)) {
1477                             PointerType pointer(&s[i], len - i, allocator_);
1478                             if (pointer.IsValid()) {
1479                                 if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) {
1480                                     if (schema)
1481                                         *schema = sc;
1482                                     return true;
1483                                 }
1484                             }
1485                         }
1486                     }
1487                 }
1488                 else if (s[i] == '#') { // Local reference, defer resolution
1489                     PointerType pointer(&s[i], len - i, allocator_);
1490                     if (pointer.IsValid()) {
1491                         if (const ValueType* nv = pointer.Get(document))
1492                             if (HandleRefSchema(source, schema, *nv, document))
1493                                 return true;
1494 
1495                         new (schemaRef_.template Push<SchemaRefEntry>()) SchemaRefEntry(source, pointer, schema, allocator_);
1496                         return true;
1497                     }
1498                 }
1499             }
1500         }
1501         return false;
1502     }
1503 
1504     const SchemaType* GetSchema(const PointerType& pointer) const {
1505         for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
1506             if (pointer == target->pointer)
1507                 return target->schema;
1508         return 0;
1509     }
1510 
1511     PointerType GetPointer(const SchemaType* schema) const {
1512         for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
1513             if (schema == target->schema)
1514                 return target->pointer;
1515         return PointerType();
1516     }
1517 
1518     static const size_t kInitialSchemaMapSize = 64;
1519     static const size_t kInitialSchemaRefSize = 64;
1520 
1521     IRemoteSchemaDocumentProviderType* remoteProvider_;
1522     Allocator *allocator_;
1523     Allocator *ownAllocator_;
1524     const SchemaType* root_;                //!< Root schema.
1525     internal::Stack<Allocator> schemaMap_;  // Stores created Pointer -> Schemas
1526     internal::Stack<Allocator> schemaRef_;  // Stores Pointer from $ref and schema which holds the $ref
1527 };
1528 
1529 //! GenericSchemaDocument using Value type.
1530 typedef GenericSchemaDocument<Value> SchemaDocument;
1531 //! IGenericRemoteSchemaDocumentProvider using SchemaDocument.
1532 typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
1533 
1534 ///////////////////////////////////////////////////////////////////////////////
1535 // GenericSchemaValidator
1536 
1537 //! JSON Schema Validator.
1538 /*!
1539     A SAX style JSON schema validator.
1540     It uses a \c GenericSchemaDocument to validate SAX events.
1541     It delegates the incoming SAX events to an output handler.
1542     The default output handler does nothing.
1543     It can be reused multiple times by calling \c Reset().
1544 
1545     \tparam SchemaDocumentType Type of schema document.
1546     \tparam OutputHandler Type of output handler. Default handler does nothing.
1547     \tparam StateAllocator Allocator for storing the internal validation states.
1548 */
1549 template <
1550     typename SchemaDocumentType,
1551     typename OutputHandler = BaseReaderHandler<typename SchemaDocumentType::SchemaType::EncodingType>,
1552     typename StateAllocator = CrtAllocator>
1553 class GenericSchemaValidator :
1554     public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
1555     public internal::ISchemaValidator
1556 {
1557 public:
1558     typedef typename SchemaDocumentType::SchemaType SchemaType;
1559     typedef typename SchemaDocumentType::PointerType PointerType;
1560     typedef typename SchemaType::EncodingType EncodingType;
1561     typedef typename EncodingType::Ch Ch;
1562 
1563     //! Constructor without output handler.
1564     /*!
1565         \param schemaDocument The schema document to conform to.
1566         \param allocator Optional allocator for storing internal validation states.
1567         \param schemaStackCapacity Optional initial capacity of schema path stack.
1568         \param documentStackCapacity Optional initial capacity of document path stack.
1569     */
1570     GenericSchemaValidator(
1571         const SchemaDocumentType& schemaDocument,
1572         StateAllocator* allocator = 0,
1573         size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1574         size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1575         :
1576         schemaDocument_(&schemaDocument),
1577         root_(schemaDocument.GetRoot()),
1578         outputHandler_(GetNullHandler()),
1579         stateAllocator_(allocator),
1580         ownStateAllocator_(0),
1581         schemaStack_(allocator, schemaStackCapacity),
1582         documentStack_(allocator, documentStackCapacity),
1583         valid_(true)
1584 #if CEREAL_RAPIDJSON_SCHEMA_VERBOSE
1585         , depth_(0)
1586 #endif
1587     {
1588     }
1589 
1590     //! Constructor with output handler.
1591     /*!
1592         \param schemaDocument The schema document to conform to.
1593         \param allocator Optional allocator for storing internal validation states.
1594         \param schemaStackCapacity Optional initial capacity of schema path stack.
1595         \param documentStackCapacity Optional initial capacity of document path stack.
1596     */
1597     GenericSchemaValidator(
1598         const SchemaDocumentType& schemaDocument,
1599         OutputHandler& outputHandler,
1600         StateAllocator* allocator = 0,
1601         size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1602         size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1603         :
1604         schemaDocument_(&schemaDocument),
1605         root_(schemaDocument.GetRoot()),
1606         outputHandler_(outputHandler),
1607         stateAllocator_(allocator),
1608         ownStateAllocator_(0),
1609         schemaStack_(allocator, schemaStackCapacity),
1610         documentStack_(allocator, documentStackCapacity),
1611         valid_(true)
1612 #if CEREAL_RAPIDJSON_SCHEMA_VERBOSE
1613         , depth_(0)
1614 #endif
1615     {
1616     }
1617 
1618     //! Destructor.
1619     ~GenericSchemaValidator() {
1620         Reset();
1621         CEREAL_RAPIDJSON_DELETE(ownStateAllocator_);
1622     }
1623 
1624     //! Reset the internal states.
1625     void Reset() {
1626         while (!schemaStack_.Empty())
1627             PopSchema();
1628         documentStack_.Clear();
1629         valid_ = true;
1630     }
1631 
1632     //! Checks whether the current state is valid.
1633     // Implementation of ISchemaValidator
1634     virtual bool IsValid() const { return valid_; }
1635 
1636     //! Gets the JSON pointer pointed to the invalid schema.
1637     PointerType GetInvalidSchemaPointer() const {
1638         return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema());
1639     }
1640 
1641     //! Gets the keyword of invalid schema.
1642     const Ch* GetInvalidSchemaKeyword() const {
1643         return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword;
1644     }
1645 
1646     //! Gets the JSON pointer pointed to the invalid value.
1647     PointerType GetInvalidDocumentPointer() const {
1648         return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
1649     }
1650 
1651 #if CEREAL_RAPIDJSON_SCHEMA_VERBOSE
1652 #define CEREAL_RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \
1653 CEREAL_RAPIDJSON_MULTILINEMACRO_BEGIN\
1654     *documentStack_.template Push<Ch>() = '\0';\
1655     documentStack_.template Pop<Ch>(1);\
1656     internal::PrintInvalidDocument(documentStack_.template Bottom<Ch>());\
1657 CEREAL_RAPIDJSON_MULTILINEMACRO_END
1658 #else
1659 #define CEREAL_RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_()
1660 #endif
1661 
1662 #define CEREAL_RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
1663     if (!valid_) return false; \
1664     if (!BeginValue() || !CurrentSchema().method arg1) {\
1665         CEREAL_RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\
1666         return valid_ = false;\
1667     }
1668 
1669 #define CEREAL_RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\
1670     for (Context* context = schemaStack_.template Bottom<Context>(); context != schemaStack_.template End<Context>(); context++) {\
1671         if (context->hasher)\
1672             static_cast<HasherType*>(context->hasher)->method arg2;\
1673         if (context->validators)\
1674             for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\
1675                 static_cast<GenericSchemaValidator*>(context->validators[i_])->method arg2;\
1676         if (context->patternPropertiesValidators)\
1677             for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\
1678                 static_cast<GenericSchemaValidator*>(context->patternPropertiesValidators[i_])->method arg2;\
1679     }
1680 
1681 #define CEREAL_RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
1682     return valid_ = EndValue() && outputHandler_.method arg2
1683 
1684 #define CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
1685     CEREAL_RAPIDJSON_SCHEMA_HANDLE_BEGIN_   (method, arg1);\
1686     CEREAL_RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\
1687     CEREAL_RAPIDJSON_SCHEMA_HANDLE_END_     (method, arg2)
1688 
1689     bool Null()             { CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null,   (CurrentContext()   ), ( )); }
1690     bool Bool(bool b)       { CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool,   (CurrentContext(), b), (b)); }
1691     bool Int(int i)         { CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int,    (CurrentContext(), i), (i)); }
1692     bool Uint(unsigned u)   { CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint,   (CurrentContext(), u), (u)); }
1693     bool Int64(int64_t i)   { CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64,  (CurrentContext(), i), (i)); }
1694     bool Uint64(uint64_t u) { CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); }
1695     bool Double(double d)   { CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); }
1696     bool RawNumber(const Ch* str, SizeType length, bool copy)
1697                                     { CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
1698     bool String(const Ch* str, SizeType length, bool copy)
1699                                     { CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
1700 
1701     bool StartObject() {
1702         CEREAL_RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext()));
1703         CEREAL_RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
1704         return valid_ = outputHandler_.StartObject();
1705     }
1706 
1707     bool Key(const Ch* str, SizeType len, bool copy) {
1708         if (!valid_) return false;
1709         AppendToken(str, len);
1710         if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
1711         CEREAL_RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
1712         return valid_ = outputHandler_.Key(str, len, copy);
1713     }
1714 
1715     bool EndObject(SizeType memberCount) {
1716         if (!valid_) return false;
1717         CEREAL_RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
1718         if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false;
1719         CEREAL_RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
1720     }
1721 
1722     bool StartArray() {
1723         CEREAL_RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext()));
1724         CEREAL_RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
1725         return valid_ = outputHandler_.StartArray();
1726     }
1727 
1728     bool EndArray(SizeType elementCount) {
1729         if (!valid_) return false;
1730         CEREAL_RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
1731         if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false;
1732         CEREAL_RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
1733     }
1734 
1735 #undef CEREAL_RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_
1736 #undef CEREAL_RAPIDJSON_SCHEMA_HANDLE_BEGIN_
1737 #undef CEREAL_RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
1738 #undef CEREAL_RAPIDJSON_SCHEMA_HANDLE_VALUE_
1739 
1740     // Implementation of ISchemaStateFactory<SchemaType>
1741     virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
1742         return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root,
1743 #if CEREAL_RAPIDJSON_SCHEMA_VERBOSE
1744         depth_ + 1,
1745 #endif
1746         &GetStateAllocator());
1747     }
1748 
1749     virtual void DestroySchemaValidator(ISchemaValidator* validator) {
1750         GenericSchemaValidator* v = static_cast<GenericSchemaValidator*>(validator);
1751         v->~GenericSchemaValidator();
1752         StateAllocator::Free(v);
1753     }
1754 
1755     virtual void* CreateHasher() {
1756         return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator());
1757     }
1758 
1759     virtual uint64_t GetHashCode(void* hasher) {
1760         return static_cast<HasherType*>(hasher)->GetHashCode();
1761     }
1762 
1763     virtual void DestroryHasher(void* hasher) {
1764         HasherType* h = static_cast<HasherType*>(hasher);
1765         h->~HasherType();
1766         StateAllocator::Free(h);
1767     }
1768 
1769     virtual void* MallocState(size_t size) {
1770         return GetStateAllocator().Malloc(size);
1771     }
1772 
1773     virtual void FreeState(void* p) {
1774         return StateAllocator::Free(p);
1775     }
1776 
1777 private:
1778     typedef typename SchemaType::Context Context;
1779     typedef GenericValue<UTF8<>, StateAllocator> HashCodeArray;
1780     typedef internal::Hasher<EncodingType, StateAllocator> HasherType;
1781 
1782     GenericSchemaValidator(
1783         const SchemaDocumentType& schemaDocument,
1784         const SchemaType& root,
1785 #if CEREAL_RAPIDJSON_SCHEMA_VERBOSE
1786         unsigned depth,
1787 #endif
1788         StateAllocator* allocator = 0,
1789         size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1790         size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1791         :
1792         schemaDocument_(&schemaDocument),
1793         root_(root),
1794         outputHandler_(GetNullHandler()),
1795         stateAllocator_(allocator),
1796         ownStateAllocator_(0),
1797         schemaStack_(allocator, schemaStackCapacity),
1798         documentStack_(allocator, documentStackCapacity),
1799         valid_(true)
1800 #if CEREAL_RAPIDJSON_SCHEMA_VERBOSE
1801         , depth_(depth)
1802 #endif
1803     {
1804     }
1805 
1806     StateAllocator& GetStateAllocator() {
1807         if (!stateAllocator_)
1808             stateAllocator_ = ownStateAllocator_ = CEREAL_RAPIDJSON_NEW(StateAllocator());
1809         return *stateAllocator_;
1810     }
1811 
1812     bool BeginValue() {
1813         if (schemaStack_.Empty())
1814             PushSchema(root_);
1815         else {
1816             if (CurrentContext().inArray)
1817                 internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
1818 
1819             if (!CurrentSchema().BeginValue(CurrentContext()))
1820                 return false;
1821 
1822             SizeType count = CurrentContext().patternPropertiesSchemaCount;
1823             const SchemaType** sa = CurrentContext().patternPropertiesSchemas;
1824             typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType;
1825             bool valueUniqueness = CurrentContext().valueUniqueness;
1826             if (CurrentContext().valueSchema)
1827                 PushSchema(*CurrentContext().valueSchema);
1828 
1829             if (count > 0) {
1830                 CurrentContext().objectPatternValidatorType = patternValidatorType;
1831                 ISchemaValidator**& va = CurrentContext().patternPropertiesValidators;
1832                 SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
1833                 va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
1834                 for (SizeType i = 0; i < count; i++)
1835                     va[validatorCount++] = CreateSchemaValidator(*sa[i]);
1836             }
1837 
1838             CurrentContext().arrayUniqueness = valueUniqueness;
1839         }
1840         return true;
1841     }
1842 
1843     bool EndValue() {
1844         if (!CurrentSchema().EndValue(CurrentContext()))
1845             return false;
1846 
1847 #if CEREAL_RAPIDJSON_SCHEMA_VERBOSE
1848         GenericStringBuffer<EncodingType> sb;
1849         schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb);
1850 
1851         *documentStack_.template Push<Ch>() = '\0';
1852         documentStack_.template Pop<Ch>(1);
1853         internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom<Ch>());
1854 #endif
1855 
1856         uint64_t h = CurrentContext().arrayUniqueness ? static_cast<HasherType*>(CurrentContext().hasher)->GetHashCode() : 0;
1857 
1858         PopSchema();
1859 
1860         if (!schemaStack_.Empty()) {
1861             Context& context = CurrentContext();
1862             if (context.valueUniqueness) {
1863                 HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
1864                 if (!a)
1865                     CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
1866                 for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
1867                     if (itr->GetUint64() == h)
1868                         CEREAL_RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString());
1869                 a->PushBack(h, GetStateAllocator());
1870             }
1871         }
1872 
1873         // Remove the last token of document pointer
1874         while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/')
1875             ;
1876 
1877         return true;
1878     }
1879 
1880     void AppendToken(const Ch* str, SizeType len) {
1881         documentStack_.template Reserve<Ch>(1 + len * 2); // worst case all characters are escaped as two characters
1882         *documentStack_.template PushUnsafe<Ch>() = '/';
1883         for (SizeType i = 0; i < len; i++) {
1884             if (str[i] == '~') {
1885                 *documentStack_.template PushUnsafe<Ch>() = '~';
1886                 *documentStack_.template PushUnsafe<Ch>() = '0';
1887             }
1888             else if (str[i] == '/') {
1889                 *documentStack_.template PushUnsafe<Ch>() = '~';
1890                 *documentStack_.template PushUnsafe<Ch>() = '1';
1891             }
1892             else
1893                 *documentStack_.template PushUnsafe<Ch>() = str[i];
1894         }
1895     }
1896 
1897     CEREAL_RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, &schema); }
1898 
1899     CEREAL_RAPIDJSON_FORCEINLINE void PopSchema() {
1900         Context* c = schemaStack_.template Pop<Context>(1);
1901         if (HashCodeArray* a = static_cast<HashCodeArray*>(c->arrayElementHashCodes)) {
1902             a->~HashCodeArray();
1903             StateAllocator::Free(a);
1904         }
1905         c->~Context();
1906     }
1907 
1908     const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
1909     Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
1910     const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
1911 
1912     static OutputHandler& GetNullHandler() {
1913         static OutputHandler nullHandler;
1914         return nullHandler;
1915     }
1916 
1917     static const size_t kDefaultSchemaStackCapacity = 1024;
1918     static const size_t kDefaultDocumentStackCapacity = 256;
1919     const SchemaDocumentType* schemaDocument_;
1920     const SchemaType& root_;
1921     OutputHandler& outputHandler_;
1922     StateAllocator* stateAllocator_;
1923     StateAllocator* ownStateAllocator_;
1924     internal::Stack<StateAllocator> schemaStack_;    //!< stack to store the current path of schema (BaseSchemaType *)
1925     internal::Stack<StateAllocator> documentStack_;  //!< stack to store the current path of validating document (Ch)
1926     bool valid_;
1927 #if CEREAL_RAPIDJSON_SCHEMA_VERBOSE
1928     unsigned depth_;
1929 #endif
1930 };
1931 
1932 typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
1933 
1934 ///////////////////////////////////////////////////////////////////////////////
1935 // SchemaValidatingReader
1936 
1937 //! A helper class for parsing with validation.
1938 /*!
1939     This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate().
1940 
1941     \tparam parseFlags Combination of \ref ParseFlag.
1942     \tparam InputStream Type of input stream, implementing Stream concept.
1943     \tparam SourceEncoding Encoding of the input stream.
1944     \tparam SchemaDocumentType Type of schema document.
1945     \tparam StackAllocator Allocator type for stack.
1946 */
1947 template <
1948     unsigned parseFlags,
1949     typename InputStream,
1950     typename SourceEncoding,
1951     typename SchemaDocumentType = SchemaDocument,
1952     typename StackAllocator = CrtAllocator>
1953 class SchemaValidatingReader {
1954 public:
1955     typedef typename SchemaDocumentType::PointerType PointerType;
1956     typedef typename InputStream::Ch Ch;
1957 
1958     //! Constructor
1959     /*!
1960         \param is Input stream.
1961         \param sd Schema document.
1962     */
1963     SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {}
1964 
1965     template <typename Handler>
1966     bool operator()(Handler& handler) {
1967         GenericReader<SourceEncoding, typename SchemaDocumentType::EncodingType, StackAllocator> reader;
1968         GenericSchemaValidator<SchemaDocumentType, Handler> validator(sd_, handler);
1969         parseResult_ = reader.template Parse<parseFlags>(is_, validator);
1970 
1971         isValid_ = validator.IsValid();
1972         if (isValid_) {
1973             invalidSchemaPointer_ = PointerType();
1974             invalidSchemaKeyword_ = 0;
1975             invalidDocumentPointer_ = PointerType();
1976         }
1977         else {
1978             invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
1979             invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
1980             invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
1981         }
1982 
1983         return parseResult_;
1984     }
1985 
1986     const ParseResult& GetParseResult() const { return parseResult_; }
1987     bool IsValid() const { return isValid_; }
1988     const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
1989     const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
1990     const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
1991 
1992 private:
1993     InputStream& is_;
1994     const SchemaDocumentType& sd_;
1995 
1996     ParseResult parseResult_;
1997     PointerType invalidSchemaPointer_;
1998     const Ch* invalidSchemaKeyword_;
1999     PointerType invalidDocumentPointer_;
2000     bool isValid_;
2001 };
2002 
2003 CEREAL_RAPIDJSON_NAMESPACE_END
2004 CEREAL_RAPIDJSON_DIAG_POP
2005 
2006 #endif // CEREAL_RAPIDJSON_SCHEMA_H_
2007