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