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                     context.valueSchema = typeless_;
789                 }
790         }
791 
792         SizeType index;
793         if (FindPropertyIndex(ValueType(str, len).Move(), &index)) {
794             if (context.patternPropertiesSchemaCount > 0) {
795                 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema;
796                 context.valueSchema = typeless_;
797                 context.valuePatternValidatorType = Context::kPatternValidatorWithProperty;
798             }
799             else
800                 context.valueSchema = properties_[index].schema;
801 
802             if (context.propertyExist)
803                 context.propertyExist[index] = true;
804 
805             return true;
806         }
807 
808         if (additionalPropertiesSchema_) {
809             if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) {
810                 context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
811                 context.valueSchema = typeless_;
812                 context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty;
813             }
814             else
815                 context.valueSchema = additionalPropertiesSchema_;
816             return true;
817         }
818         else if (additionalProperties_) {
819             context.valueSchema = typeless_;
820             return true;
821         }
822 
823         if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties
824             RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString());
825 
826         return true;
827     }
828 
EndObject(Context & context,SizeType memberCount)829     bool EndObject(Context& context, SizeType memberCount) const {
830         if (hasRequired_)
831             for (SizeType index = 0; index < propertyCount_; index++)
832                 if (properties_[index].required)
833                     if (!context.propertyExist[index])
834                         RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString());
835 
836         if (memberCount < minProperties_)
837             RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString());
838 
839         if (memberCount > maxProperties_)
840             RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString());
841 
842         if (hasDependencies_) {
843             for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++)
844                 if (context.propertyExist[sourceIndex]) {
845                     if (properties_[sourceIndex].dependencies) {
846                         for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
847                             if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex])
848                                 RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
849                     }
850                     else if (properties_[sourceIndex].dependenciesSchema)
851                         if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid())
852                             RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString());
853                 }
854         }
855 
856         return true;
857     }
858 
StartArray(Context & context)859     bool StartArray(Context& context) const {
860         if (!(type_ & (1 << kArraySchemaType)))
861             RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
862 
863         context.arrayElementIndex = 0;
864         context.inArray = true;
865 
866         return CreateParallelValidator(context);
867     }
868 
EndArray(Context & context,SizeType elementCount)869     bool EndArray(Context& context, SizeType elementCount) const {
870         context.inArray = false;
871 
872         if (elementCount < minItems_)
873             RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString());
874 
875         if (elementCount > maxItems_)
876             RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString());
877 
878         return true;
879     }
880 
881     // Generate functions for string literal according to Ch
882 #define RAPIDJSON_STRING_(name, ...) \
883     static const ValueType& Get##name##String() {\
884         static const Ch s[] = { __VA_ARGS__, '\0' };\
885         static const ValueType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1));\
886         return v;\
887     }
888 
889     RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
890     RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n')
891     RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't')
892     RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y')
893     RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g')
894     RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r')
895     RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r')
896     RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e')
897     RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm')
898     RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f')
899     RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f')
900     RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f')
901     RAPIDJSON_STRING_(Not, 'n', 'o', 't')
902     RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
903     RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd')
904     RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's')
905     RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
906     RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
907     RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
908     RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
909     RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's')
910     RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's')
911     RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's')
912     RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's')
913     RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's')
914     RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h')
915     RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h')
916     RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n')
917     RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm')
918     RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm')
919     RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm')
920     RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
921     RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
922 
923 #undef RAPIDJSON_STRING_
924 
925 private:
926     enum SchemaValueType {
927         kNullSchemaType,
928         kBooleanSchemaType,
929         kObjectSchemaType,
930         kArraySchemaType,
931         kStringSchemaType,
932         kNumberSchemaType,
933         kIntegerSchemaType,
934         kTotalSchemaType
935     };
936 
937 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
938         typedef internal::GenericRegex<EncodingType> RegexType;
939 #elif RAPIDJSON_SCHEMA_USE_STDREGEX
940         typedef std::basic_regex<Ch> RegexType;
941 #else
942         typedef char RegexType;
943 #endif
944 
945     struct SchemaArray {
SchemaArraySchemaArray946         SchemaArray() : schemas(), count() {}
~SchemaArraySchemaArray947         ~SchemaArray() { AllocatorType::Free(schemas); }
948         const SchemaType** schemas;
949         SizeType begin; // begin index of context.validators
950         SizeType count;
951     };
952 
953     template <typename V1, typename V2>
AddUniqueElement(V1 & a,const V2 & v)954     void AddUniqueElement(V1& a, const V2& v) {
955         for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
956             if (*itr == v)
957                 return;
958         V1 c(v, *allocator_);
959         a.PushBack(c, *allocator_);
960     }
961 
GetMember(const ValueType & value,const ValueType & name)962     static const ValueType* GetMember(const ValueType& value, const ValueType& name) {
963         typename ValueType::ConstMemberIterator itr = value.FindMember(name);
964         return itr != value.MemberEnd() ? &(itr->value) : 0;
965     }
966 
AssignIfExist(bool & out,const ValueType & value,const ValueType & name)967     static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) {
968         if (const ValueType* v = GetMember(value, name))
969             if (v->IsBool())
970                 out = v->GetBool();
971     }
972 
AssignIfExist(SizeType & out,const ValueType & value,const ValueType & name)973     static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) {
974         if (const ValueType* v = GetMember(value, name))
975             if (v->IsUint64() && v->GetUint64() <= SizeType(~0))
976                 out = static_cast<SizeType>(v->GetUint64());
977     }
978 
AssignIfExist(SchemaArray & out,SchemaDocumentType & schemaDocument,const PointerType & p,const ValueType & value,const ValueType & name,const ValueType & document)979     void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) {
980         if (const ValueType* v = GetMember(value, name)) {
981             if (v->IsArray() && v->Size() > 0) {
982                 PointerType q = p.Append(name, allocator_);
983                 out.count = v->Size();
984                 out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*)));
985                 memset(out.schemas, 0, sizeof(Schema*)* out.count);
986                 for (SizeType i = 0; i < out.count; i++)
987                     schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document);
988                 out.begin = validatorCount_;
989                 validatorCount_ += out.count;
990             }
991         }
992     }
993 
994 #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
995     template <typename ValueType>
CreatePattern(const ValueType & value)996     RegexType* CreatePattern(const ValueType& value) {
997         if (value.IsString()) {
998             RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString());
999             if (!r->IsValid()) {
1000                 r->~RegexType();
1001                 AllocatorType::Free(r);
1002                 r = 0;
1003             }
1004             return r;
1005         }
1006         return 0;
1007     }
1008 
IsPatternMatch(const RegexType * pattern,const Ch * str,SizeType)1009     static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) {
1010         GenericRegexSearch<RegexType> rs(*pattern);
1011         return rs.Search(str);
1012     }
1013 #elif RAPIDJSON_SCHEMA_USE_STDREGEX
1014     template <typename ValueType>
CreatePattern(const ValueType & value)1015     RegexType* CreatePattern(const ValueType& value) {
1016         if (value.IsString())
1017             try {
1018                 return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript);
1019             }
1020             catch (const std::regex_error&) {
1021             }
1022         return 0;
1023     }
1024 
IsPatternMatch(const RegexType * pattern,const Ch * str,SizeType length)1025     static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) {
1026         std::match_results<const Ch*> r;
1027         return std::regex_search(str, str + length, r, *pattern);
1028     }
1029 #else
1030     template <typename ValueType>
CreatePattern(const ValueType &)1031     RegexType* CreatePattern(const ValueType&) { return 0; }
1032 
IsPatternMatch(const RegexType *,const Ch *,SizeType)1033     static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; }
1034 #endif // RAPIDJSON_SCHEMA_USE_STDREGEX
1035 
AddType(const ValueType & type)1036     void AddType(const ValueType& type) {
1037         if      (type == GetNullString()   ) type_ |= 1 << kNullSchemaType;
1038         else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType;
1039         else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType;
1040         else if (type == GetArrayString()  ) type_ |= 1 << kArraySchemaType;
1041         else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType;
1042         else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType;
1043         else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType);
1044     }
1045 
CreateParallelValidator(Context & context)1046     bool CreateParallelValidator(Context& context) const {
1047         if (enum_ || context.arrayUniqueness)
1048             context.hasher = context.factory.CreateHasher();
1049 
1050         if (validatorCount_) {
1051             RAPIDJSON_ASSERT(context.validators == 0);
1052             context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
1053             context.validatorCount = validatorCount_;
1054 
1055             if (allOf_.schemas)
1056                 CreateSchemaValidators(context, allOf_);
1057 
1058             if (anyOf_.schemas)
1059                 CreateSchemaValidators(context, anyOf_);
1060 
1061             if (oneOf_.schemas)
1062                 CreateSchemaValidators(context, oneOf_);
1063 
1064             if (not_)
1065                 context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_);
1066 
1067             if (hasSchemaDependencies_) {
1068                 for (SizeType i = 0; i < propertyCount_; i++)
1069                     if (properties_[i].dependenciesSchema)
1070                         context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema);
1071             }
1072         }
1073 
1074         return true;
1075     }
1076 
CreateSchemaValidators(Context & context,const SchemaArray & schemas)1077     void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const {
1078         for (SizeType i = 0; i < schemas.count; i++)
1079             context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]);
1080     }
1081 
1082     // O(n)
FindPropertyIndex(const ValueType & name,SizeType * outIndex)1083     bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const {
1084         SizeType len = name.GetStringLength();
1085         const Ch* str = name.GetString();
1086         for (SizeType index = 0; index < propertyCount_; index++)
1087             if (properties_[index].name.GetStringLength() == len &&
1088                 (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0))
1089             {
1090                 *outIndex = index;
1091                 return true;
1092             }
1093         return false;
1094     }
1095 
CheckInt(Context & context,int64_t i)1096     bool CheckInt(Context& context, int64_t i) const {
1097         if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
1098             RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
1099 
1100         if (!minimum_.IsNull()) {
1101             if (minimum_.IsInt64()) {
1102                 if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64())
1103                     RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1104             }
1105             else if (minimum_.IsUint64()) {
1106                 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64()
1107             }
1108             else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1109                 return false;
1110         }
1111 
1112         if (!maximum_.IsNull()) {
1113             if (maximum_.IsInt64()) {
1114                 if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64())
1115                     RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1116             }
1117             else if (maximum_.IsUint64()) { }
1118                 /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64()
1119             else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1120                 return false;
1121         }
1122 
1123         if (!multipleOf_.IsNull()) {
1124             if (multipleOf_.IsUint64()) {
1125                 if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0)
1126                     RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1127             }
1128             else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1129                 return false;
1130         }
1131 
1132         return true;
1133     }
1134 
CheckUint(Context & context,uint64_t i)1135     bool CheckUint(Context& context, uint64_t i) const {
1136         if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType))))
1137             RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString());
1138 
1139         if (!minimum_.IsNull()) {
1140             if (minimum_.IsUint64()) {
1141                 if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64())
1142                     RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1143             }
1144             else if (minimum_.IsInt64())
1145                 /* do nothing */; // i >= 0 > minimum.Getint64()
1146             else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
1147                 return false;
1148         }
1149 
1150         if (!maximum_.IsNull()) {
1151             if (maximum_.IsUint64()) {
1152                 if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64())
1153                     RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1154             }
1155             else if (maximum_.IsInt64())
1156                 RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_
1157             else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
1158                 return false;
1159         }
1160 
1161         if (!multipleOf_.IsNull()) {
1162             if (multipleOf_.IsUint64()) {
1163                 if (i % multipleOf_.GetUint64() != 0)
1164                     RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1165             }
1166             else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
1167                 return false;
1168         }
1169 
1170         return true;
1171     }
1172 
CheckDoubleMinimum(Context & context,double d)1173     bool CheckDoubleMinimum(Context& context, double d) const {
1174         if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble())
1175             RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString());
1176         return true;
1177     }
1178 
CheckDoubleMaximum(Context & context,double d)1179     bool CheckDoubleMaximum(Context& context, double d) const {
1180         if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble())
1181             RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString());
1182         return true;
1183     }
1184 
CheckDoubleMultipleOf(Context & context,double d)1185     bool CheckDoubleMultipleOf(Context& context, double d) const {
1186         double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
1187         double q = std::floor(a / b);
1188         double r = a - q * b;
1189         if (r > 0.0)
1190             RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString());
1191         return true;
1192     }
1193 
1194     struct Property {
PropertyProperty1195         Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
~PropertyProperty1196         ~Property() { AllocatorType::Free(dependencies); }
1197         SValue name;
1198         const SchemaType* schema;
1199         const SchemaType* dependenciesSchema;
1200         SizeType dependenciesValidatorIndex;
1201         bool* dependencies;
1202         bool required;
1203     };
1204 
1205     struct PatternProperty {
PatternPropertyPatternProperty1206         PatternProperty() : schema(), pattern() {}
~PatternPropertyPatternProperty1207         ~PatternProperty() {
1208             if (pattern) {
1209                 pattern->~RegexType();
1210                 AllocatorType::Free(pattern);
1211             }
1212         }
1213         const SchemaType* schema;
1214         RegexType* pattern;
1215     };
1216 
1217     AllocatorType* allocator_;
1218     const SchemaType* typeless_;
1219     uint64_t* enum_;
1220     SizeType enumCount_;
1221     SchemaArray allOf_;
1222     SchemaArray anyOf_;
1223     SchemaArray oneOf_;
1224     const SchemaType* not_;
1225     unsigned type_; // bitmask of kSchemaType
1226     SizeType validatorCount_;
1227     SizeType notValidatorIndex_;
1228 
1229     Property* properties_;
1230     const SchemaType* additionalPropertiesSchema_;
1231     PatternProperty* patternProperties_;
1232     SizeType patternPropertyCount_;
1233     SizeType propertyCount_;
1234     SizeType minProperties_;
1235     SizeType maxProperties_;
1236     bool additionalProperties_;
1237     bool hasDependencies_;
1238     bool hasRequired_;
1239     bool hasSchemaDependencies_;
1240 
1241     const SchemaType* additionalItemsSchema_;
1242     const SchemaType* itemsList_;
1243     const SchemaType** itemsTuple_;
1244     SizeType itemsTupleCount_;
1245     SizeType minItems_;
1246     SizeType maxItems_;
1247     bool additionalItems_;
1248     bool uniqueItems_;
1249 
1250     RegexType* pattern_;
1251     SizeType minLength_;
1252     SizeType maxLength_;
1253 
1254     SValue minimum_;
1255     SValue maximum_;
1256     SValue multipleOf_;
1257     bool exclusiveMinimum_;
1258     bool exclusiveMaximum_;
1259 };
1260 
1261 template<typename Stack, typename Ch>
1262 struct TokenHelper {
AppendIndexTokenTokenHelper1263     RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1264         *documentStack.template Push<Ch>() = '/';
1265         char buffer[21];
1266         size_t length = static_cast<size_t>((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer);
1267         for (size_t i = 0; i < length; i++)
1268             *documentStack.template Push<Ch>() = static_cast<Ch>(buffer[i]);
1269     }
1270 };
1271 
1272 // Partial specialized version for char to prevent buffer copying.
1273 template <typename Stack>
1274 struct TokenHelper<Stack, char> {
1275     RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
1276         if (sizeof(SizeType) == 4) {
1277             char *buffer = documentStack.template Push<char>(1 + 10); // '/' + uint
1278             *buffer++ = '/';
1279             const char* end = internal::u32toa(index, buffer);
1280              documentStack.template Pop<char>(static_cast<size_t>(10 - (end - buffer)));
1281         }
1282         else {
1283             char *buffer = documentStack.template Push<char>(1 + 20); // '/' + uint64
1284             *buffer++ = '/';
1285             const char* end = internal::u64toa(index, buffer);
1286             documentStack.template Pop<char>(static_cast<size_t>(20 - (end - buffer)));
1287         }
1288     }
1289 };
1290 
1291 } // namespace internal
1292 
1293 ///////////////////////////////////////////////////////////////////////////////
1294 // IGenericRemoteSchemaDocumentProvider
1295 
1296 template <typename SchemaDocumentType>
1297 class IGenericRemoteSchemaDocumentProvider {
1298 public:
1299     typedef typename SchemaDocumentType::Ch Ch;
1300 
1301     virtual ~IGenericRemoteSchemaDocumentProvider() {}
1302     virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
1303 };
1304 
1305 ///////////////////////////////////////////////////////////////////////////////
1306 // GenericSchemaDocument
1307 
1308 //! JSON schema document.
1309 /*!
1310     A JSON schema document is a compiled version of a JSON schema.
1311     It is basically a tree of internal::Schema.
1312 
1313     \note This is an immutable class (i.e. its instance cannot be modified after construction).
1314     \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding.
1315     \tparam Allocator Allocator type for allocating memory of this document.
1316 */
1317 template <typename ValueT, typename Allocator = CrtAllocator>
1318 class GenericSchemaDocument {
1319 public:
1320     typedef ValueT ValueType;
1321     typedef IGenericRemoteSchemaDocumentProvider<GenericSchemaDocument> IRemoteSchemaDocumentProviderType;
1322     typedef Allocator AllocatorType;
1323     typedef typename ValueType::EncodingType EncodingType;
1324     typedef typename EncodingType::Ch Ch;
1325     typedef internal::Schema<GenericSchemaDocument> SchemaType;
1326     typedef GenericPointer<ValueType, Allocator> PointerType;
1327     friend class internal::Schema<GenericSchemaDocument>;
1328     template <typename, typename, typename>
1329     friend class GenericSchemaValidator;
1330 
1331     //! Constructor.
1332     /*!
1333         Compile a JSON document into schema document.
1334 
1335         \param document A JSON document as source.
1336         \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
1337         \param allocator An optional allocator instance for allocating memory. Can be null.
1338     */
1339     explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) :
1340         remoteProvider_(remoteProvider),
1341         allocator_(allocator),
1342         ownAllocator_(),
1343         root_(),
1344         typeless_(),
1345         schemaMap_(allocator, kInitialSchemaMapSize),
1346         schemaRef_(allocator, kInitialSchemaRefSize)
1347     {
1348         if (!allocator_)
1349             ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
1350 
1351         typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType)));
1352         new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0);
1353 
1354         // Generate root schema, it will call CreateSchema() to create sub-schemas,
1355         // And call AddRefSchema() if there are $ref.
1356         CreateSchemaRecursive(&root_, PointerType(), document, document);
1357 
1358         // Resolve $ref
1359         while (!schemaRef_.Empty()) {
1360             SchemaRefEntry* refEntry = schemaRef_.template Pop<SchemaRefEntry>(1);
1361             if (const SchemaType* s = GetSchema(refEntry->target)) {
1362                 if (refEntry->schema)
1363                     *refEntry->schema = s;
1364 
1365                 // Create entry in map if not exist
1366                 if (!GetSchema(refEntry->source)) {
1367                     new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(refEntry->source, const_cast<SchemaType*>(s), false, allocator_);
1368                 }
1369             }
1370             else if (refEntry->schema)
1371                 *refEntry->schema = typeless_;
1372 
1373             refEntry->~SchemaRefEntry();
1374         }
1375 
1376         RAPIDJSON_ASSERT(root_ != 0);
1377 
1378         schemaRef_.ShrinkToFit(); // Deallocate all memory for ref
1379     }
1380 
1381 #if RAPIDJSON_HAS_CXX11_RVALUE_REFS
1382     //! Move constructor in C++11
1383     GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT :
1384         remoteProvider_(rhs.remoteProvider_),
1385         allocator_(rhs.allocator_),
1386         ownAllocator_(rhs.ownAllocator_),
1387         root_(rhs.root_),
1388         typeless_(rhs.typeless_),
1389         schemaMap_(std::move(rhs.schemaMap_)),
1390         schemaRef_(std::move(rhs.schemaRef_))
1391     {
1392         rhs.remoteProvider_ = 0;
1393         rhs.allocator_ = 0;
1394         rhs.ownAllocator_ = 0;
1395         rhs.typeless_ = 0;
1396     }
1397 #endif
1398 
1399     //! Destructor
1400     ~GenericSchemaDocument() {
1401         while (!schemaMap_.Empty())
1402             schemaMap_.template Pop<SchemaEntry>(1)->~SchemaEntry();
1403 
1404         if (typeless_) {
1405             typeless_->~SchemaType();
1406             Allocator::Free(typeless_);
1407         }
1408 
1409         RAPIDJSON_DELETE(ownAllocator_);
1410     }
1411 
1412     //! Get the root schema.
1413     const SchemaType& GetRoot() const { return *root_; }
1414 
1415 private:
1416     //! Prohibit copying
1417     GenericSchemaDocument(const GenericSchemaDocument&);
1418     //! Prohibit assignment
1419     GenericSchemaDocument& operator=(const GenericSchemaDocument&);
1420 
1421     struct SchemaRefEntry {
1422         SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {}
1423         PointerType source;
1424         PointerType target;
1425         const SchemaType** schema;
1426     };
1427 
1428     struct SchemaEntry {
1429         SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {}
1430         ~SchemaEntry() {
1431             if (owned) {
1432                 schema->~SchemaType();
1433                 Allocator::Free(schema);
1434             }
1435         }
1436         PointerType pointer;
1437         SchemaType* schema;
1438         bool owned;
1439     };
1440 
1441     void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
1442         if (schema)
1443             *schema = typeless_;
1444 
1445         if (v.GetType() == kObjectType) {
1446             const SchemaType* s = GetSchema(pointer);
1447             if (!s)
1448                 CreateSchema(schema, pointer, v, document);
1449 
1450             for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
1451                 CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document);
1452         }
1453         else if (v.GetType() == kArrayType)
1454             for (SizeType i = 0; i < v.Size(); i++)
1455                 CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document);
1456     }
1457 
1458     void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) {
1459         RAPIDJSON_ASSERT(pointer.IsValid());
1460         if (v.IsObject()) {
1461             if (!HandleRefSchema(pointer, schema, v, document)) {
1462                 SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_);
1463                 new (schemaMap_.template Push<SchemaEntry>()) SchemaEntry(pointer, s, true, allocator_);
1464                 if (schema)
1465                     *schema = s;
1466             }
1467         }
1468     }
1469 
1470     bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) {
1471         static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' };
1472         static const ValueType kRefValue(kRefString, 4);
1473 
1474         typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue);
1475         if (itr == v.MemberEnd())
1476             return false;
1477 
1478         if (itr->value.IsString()) {
1479             SizeType len = itr->value.GetStringLength();
1480             if (len > 0) {
1481                 const Ch* s = itr->value.GetString();
1482                 SizeType i = 0;
1483                 while (i < len && s[i] != '#') // Find the first #
1484                     i++;
1485 
1486                 if (i > 0) { // Remote reference, resolve immediately
1487                     if (remoteProvider_) {
1488                         if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) {
1489                             PointerType pointer(&s[i], len - i, allocator_);
1490                             if (pointer.IsValid()) {
1491                                 if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) {
1492                                     if (schema)
1493                                         *schema = sc;
1494                                     return true;
1495                                 }
1496                             }
1497                         }
1498                     }
1499                 }
1500                 else if (s[i] == '#') { // Local reference, defer resolution
1501                     PointerType pointer(&s[i], len - i, allocator_);
1502                     if (pointer.IsValid()) {
1503                         if (const ValueType* nv = pointer.Get(document))
1504                             if (HandleRefSchema(source, schema, *nv, document))
1505                                 return true;
1506 
1507                         new (schemaRef_.template Push<SchemaRefEntry>()) SchemaRefEntry(source, pointer, schema, allocator_);
1508                         return true;
1509                     }
1510                 }
1511             }
1512         }
1513         return false;
1514     }
1515 
1516     const SchemaType* GetSchema(const PointerType& pointer) const {
1517         for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
1518             if (pointer == target->pointer)
1519                 return target->schema;
1520         return 0;
1521     }
1522 
1523     PointerType GetPointer(const SchemaType* schema) const {
1524         for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
1525             if (schema == target->schema)
1526                 return target->pointer;
1527         return PointerType();
1528     }
1529 
1530     const SchemaType* GetTypeless() const { return typeless_; }
1531 
1532     static const size_t kInitialSchemaMapSize = 64;
1533     static const size_t kInitialSchemaRefSize = 64;
1534 
1535     IRemoteSchemaDocumentProviderType* remoteProvider_;
1536     Allocator *allocator_;
1537     Allocator *ownAllocator_;
1538     const SchemaType* root_;                //!< Root schema.
1539     SchemaType* typeless_;
1540     internal::Stack<Allocator> schemaMap_;  // Stores created Pointer -> Schemas
1541     internal::Stack<Allocator> schemaRef_;  // Stores Pointer from $ref and schema which holds the $ref
1542 };
1543 
1544 //! GenericSchemaDocument using Value type.
1545 typedef GenericSchemaDocument<Value> SchemaDocument;
1546 //! IGenericRemoteSchemaDocumentProvider using SchemaDocument.
1547 typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
1548 
1549 ///////////////////////////////////////////////////////////////////////////////
1550 // GenericSchemaValidator
1551 
1552 //! JSON Schema Validator.
1553 /*!
1554     A SAX style JSON schema validator.
1555     It uses a \c GenericSchemaDocument to validate SAX events.
1556     It delegates the incoming SAX events to an output handler.
1557     The default output handler does nothing.
1558     It can be reused multiple times by calling \c Reset().
1559 
1560     \tparam SchemaDocumentType Type of schema document.
1561     \tparam OutputHandler Type of output handler. Default handler does nothing.
1562     \tparam StateAllocator Allocator for storing the internal validation states.
1563 */
1564 template <
1565     typename SchemaDocumentType,
1566     typename OutputHandler = BaseReaderHandler<typename SchemaDocumentType::SchemaType::EncodingType>,
1567     typename StateAllocator = CrtAllocator>
1568 class GenericSchemaValidator :
1569     public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
1570     public internal::ISchemaValidator
1571 {
1572 public:
1573     typedef typename SchemaDocumentType::SchemaType SchemaType;
1574     typedef typename SchemaDocumentType::PointerType PointerType;
1575     typedef typename SchemaType::EncodingType EncodingType;
1576     typedef typename EncodingType::Ch Ch;
1577 
1578     //! Constructor without output handler.
1579     /*!
1580         \param schemaDocument The schema document to conform to.
1581         \param allocator Optional allocator for storing internal validation states.
1582         \param schemaStackCapacity Optional initial capacity of schema path stack.
1583         \param documentStackCapacity Optional initial capacity of document path stack.
1584     */
1585     GenericSchemaValidator(
1586         const SchemaDocumentType& schemaDocument,
1587         StateAllocator* allocator = 0,
1588         size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1589         size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1590         :
1591         schemaDocument_(&schemaDocument),
1592         root_(schemaDocument.GetRoot()),
1593         stateAllocator_(allocator),
1594         ownStateAllocator_(0),
1595         schemaStack_(allocator, schemaStackCapacity),
1596         documentStack_(allocator, documentStackCapacity),
1597         outputHandler_(0),
1598         valid_(true)
1599 #if RAPIDJSON_SCHEMA_VERBOSE
1600         , depth_(0)
1601 #endif
1602     {
1603     }
1604 
1605     //! Constructor with output handler.
1606     /*!
1607         \param schemaDocument The schema document to conform to.
1608         \param allocator Optional allocator for storing internal validation states.
1609         \param schemaStackCapacity Optional initial capacity of schema path stack.
1610         \param documentStackCapacity Optional initial capacity of document path stack.
1611     */
1612     GenericSchemaValidator(
1613         const SchemaDocumentType& schemaDocument,
1614         OutputHandler& outputHandler,
1615         StateAllocator* allocator = 0,
1616         size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1617         size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1618         :
1619         schemaDocument_(&schemaDocument),
1620         root_(schemaDocument.GetRoot()),
1621         stateAllocator_(allocator),
1622         ownStateAllocator_(0),
1623         schemaStack_(allocator, schemaStackCapacity),
1624         documentStack_(allocator, documentStackCapacity),
1625         outputHandler_(&outputHandler),
1626         valid_(true)
1627 #if RAPIDJSON_SCHEMA_VERBOSE
1628         , depth_(0)
1629 #endif
1630     {
1631     }
1632 
1633     //! Destructor.
1634     ~GenericSchemaValidator() {
1635         Reset();
1636         RAPIDJSON_DELETE(ownStateAllocator_);
1637     }
1638 
1639     //! Reset the internal states.
1640     void Reset() {
1641         while (!schemaStack_.Empty())
1642             PopSchema();
1643         documentStack_.Clear();
1644         valid_ = true;
1645     }
1646 
1647     //! Checks whether the current state is valid.
1648     // Implementation of ISchemaValidator
1649     virtual bool IsValid() const { return valid_; }
1650 
1651     //! Gets the JSON pointer pointed to the invalid schema.
1652     PointerType GetInvalidSchemaPointer() const {
1653         return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema());
1654     }
1655 
1656     //! Gets the keyword of invalid schema.
1657     const Ch* GetInvalidSchemaKeyword() const {
1658         return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword;
1659     }
1660 
1661     //! Gets the JSON pointer pointed to the invalid value.
1662     PointerType GetInvalidDocumentPointer() const {
1663         return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
1664     }
1665 
1666 #if RAPIDJSON_SCHEMA_VERBOSE
1667 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \
1668 RAPIDJSON_MULTILINEMACRO_BEGIN\
1669     *documentStack_.template Push<Ch>() = '\0';\
1670     documentStack_.template Pop<Ch>(1);\
1671     internal::PrintInvalidDocument(documentStack_.template Bottom<Ch>());\
1672 RAPIDJSON_MULTILINEMACRO_END
1673 #else
1674 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_()
1675 #endif
1676 
1677 #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
1678     if (!valid_) return false; \
1679     if (!BeginValue() || !CurrentSchema().method arg1) {\
1680         RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\
1681         return valid_ = false;\
1682     }
1683 
1684 #define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\
1685     for (Context* context = schemaStack_.template Bottom<Context>(); context != schemaStack_.template End<Context>(); context++) {\
1686         if (context->hasher)\
1687             static_cast<HasherType*>(context->hasher)->method arg2;\
1688         if (context->validators)\
1689             for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\
1690                 static_cast<GenericSchemaValidator*>(context->validators[i_])->method arg2;\
1691         if (context->patternPropertiesValidators)\
1692             for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\
1693                 static_cast<GenericSchemaValidator*>(context->patternPropertiesValidators[i_])->method arg2;\
1694     }
1695 
1696 #define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
1697     return valid_ = EndValue() && (!outputHandler_ || outputHandler_->method arg2)
1698 
1699 #define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
1700     RAPIDJSON_SCHEMA_HANDLE_BEGIN_   (method, arg1);\
1701     RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\
1702     RAPIDJSON_SCHEMA_HANDLE_END_     (method, arg2)
1703 
1704     bool Null()             { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null,   (CurrentContext()   ), ( )); }
1705     bool Bool(bool b)       { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool,   (CurrentContext(), b), (b)); }
1706     bool Int(int i)         { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int,    (CurrentContext(), i), (i)); }
1707     bool Uint(unsigned u)   { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint,   (CurrentContext(), u), (u)); }
1708     bool Int64(int64_t i)   { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64,  (CurrentContext(), i), (i)); }
1709     bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); }
1710     bool Double(double d)   { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); }
1711     bool RawNumber(const Ch* str, SizeType length, bool copy)
1712                                     { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
1713     bool String(const Ch* str, SizeType length, bool copy)
1714                                     { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
1715 
1716     bool StartObject() {
1717         RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext()));
1718         RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
1719         return valid_ = !outputHandler_ || outputHandler_->StartObject();
1720     }
1721 
1722     bool Key(const Ch* str, SizeType len, bool copy) {
1723         if (!valid_) return false;
1724         AppendToken(str, len);
1725         if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false;
1726         RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
1727         return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy);
1728     }
1729 
1730     bool EndObject(SizeType memberCount) {
1731         if (!valid_) return false;
1732         RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
1733         if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false;
1734         RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
1735     }
1736 
1737     bool StartArray() {
1738         RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext()));
1739         RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
1740         return valid_ = !outputHandler_ || outputHandler_->StartArray();
1741     }
1742 
1743     bool EndArray(SizeType elementCount) {
1744         if (!valid_) return false;
1745         RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
1746         if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false;
1747         RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
1748     }
1749 
1750 #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_
1751 #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_
1752 #undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
1753 #undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
1754 
1755     // Implementation of ISchemaStateFactory<SchemaType>
1756     virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) {
1757         return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root,
1758 #if RAPIDJSON_SCHEMA_VERBOSE
1759         depth_ + 1,
1760 #endif
1761         &GetStateAllocator());
1762     }
1763 
1764     virtual void DestroySchemaValidator(ISchemaValidator* validator) {
1765         GenericSchemaValidator* v = static_cast<GenericSchemaValidator*>(validator);
1766         v->~GenericSchemaValidator();
1767         StateAllocator::Free(v);
1768     }
1769 
1770     virtual void* CreateHasher() {
1771         return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator());
1772     }
1773 
1774     virtual uint64_t GetHashCode(void* hasher) {
1775         return static_cast<HasherType*>(hasher)->GetHashCode();
1776     }
1777 
1778     virtual void DestroryHasher(void* hasher) {
1779         HasherType* h = static_cast<HasherType*>(hasher);
1780         h->~HasherType();
1781         StateAllocator::Free(h);
1782     }
1783 
1784     virtual void* MallocState(size_t size) {
1785         return GetStateAllocator().Malloc(size);
1786     }
1787 
1788     virtual void FreeState(void* p) {
1789         StateAllocator::Free(p);
1790     }
1791 
1792 private:
1793     typedef typename SchemaType::Context Context;
1794     typedef GenericValue<UTF8<>, StateAllocator> HashCodeArray;
1795     typedef internal::Hasher<EncodingType, StateAllocator> HasherType;
1796 
1797     GenericSchemaValidator(
1798         const SchemaDocumentType& schemaDocument,
1799         const SchemaType& root,
1800 #if RAPIDJSON_SCHEMA_VERBOSE
1801         unsigned depth,
1802 #endif
1803         StateAllocator* allocator = 0,
1804         size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
1805         size_t documentStackCapacity = kDefaultDocumentStackCapacity)
1806         :
1807         schemaDocument_(&schemaDocument),
1808         root_(root),
1809         stateAllocator_(allocator),
1810         ownStateAllocator_(0),
1811         schemaStack_(allocator, schemaStackCapacity),
1812         documentStack_(allocator, documentStackCapacity),
1813         outputHandler_(0),
1814         valid_(true)
1815 #if RAPIDJSON_SCHEMA_VERBOSE
1816         , depth_(depth)
1817 #endif
1818     {
1819     }
1820 
1821     StateAllocator& GetStateAllocator() {
1822         if (!stateAllocator_)
1823             stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)();
1824         return *stateAllocator_;
1825     }
1826 
1827     bool BeginValue() {
1828         if (schemaStack_.Empty())
1829             PushSchema(root_);
1830         else {
1831             if (CurrentContext().inArray)
1832                 internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
1833 
1834             if (!CurrentSchema().BeginValue(CurrentContext()))
1835                 return false;
1836 
1837             SizeType count = CurrentContext().patternPropertiesSchemaCount;
1838             const SchemaType** sa = CurrentContext().patternPropertiesSchemas;
1839             typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType;
1840             bool valueUniqueness = CurrentContext().valueUniqueness;
1841             RAPIDJSON_ASSERT(CurrentContext().valueSchema);
1842             PushSchema(*CurrentContext().valueSchema);
1843 
1844             if (count > 0) {
1845                 CurrentContext().objectPatternValidatorType = patternValidatorType;
1846                 ISchemaValidator**& va = CurrentContext().patternPropertiesValidators;
1847                 SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
1848                 va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
1849                 for (SizeType i = 0; i < count; i++)
1850                     va[validatorCount++] = CreateSchemaValidator(*sa[i]);
1851             }
1852 
1853             CurrentContext().arrayUniqueness = valueUniqueness;
1854         }
1855         return true;
1856     }
1857 
1858     bool EndValue() {
1859         if (!CurrentSchema().EndValue(CurrentContext()))
1860             return false;
1861 
1862 #if RAPIDJSON_SCHEMA_VERBOSE
1863         GenericStringBuffer<EncodingType> sb;
1864         schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb);
1865 
1866         *documentStack_.template Push<Ch>() = '\0';
1867         documentStack_.template Pop<Ch>(1);
1868         internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom<Ch>());
1869 #endif
1870 
1871         uint64_t h = CurrentContext().arrayUniqueness ? static_cast<HasherType*>(CurrentContext().hasher)->GetHashCode() : 0;
1872 
1873         PopSchema();
1874 
1875         if (!schemaStack_.Empty()) {
1876             Context& context = CurrentContext();
1877             if (context.valueUniqueness) {
1878                 HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
1879                 if (!a)
1880                     CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
1881                 for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
1882                     if (itr->GetUint64() == h)
1883                         RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString());
1884                 a->PushBack(h, GetStateAllocator());
1885             }
1886         }
1887 
1888         // Remove the last token of document pointer
1889         while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/')
1890             ;
1891 
1892         return true;
1893     }
1894 
1895     void AppendToken(const Ch* str, SizeType len) {
1896         documentStack_.template Reserve<Ch>(1 + len * 2); // worst case all characters are escaped as two characters
1897         *documentStack_.template PushUnsafe<Ch>() = '/';
1898         for (SizeType i = 0; i < len; i++) {
1899             if (str[i] == '~') {
1900                 *documentStack_.template PushUnsafe<Ch>() = '~';
1901                 *documentStack_.template PushUnsafe<Ch>() = '0';
1902             }
1903             else if (str[i] == '/') {
1904                 *documentStack_.template PushUnsafe<Ch>() = '~';
1905                 *documentStack_.template PushUnsafe<Ch>() = '1';
1906             }
1907             else
1908                 *documentStack_.template PushUnsafe<Ch>() = str[i];
1909         }
1910     }
1911 
1912     RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, &schema); }
1913 
1914     RAPIDJSON_FORCEINLINE void PopSchema() {
1915         Context* c = schemaStack_.template Pop<Context>(1);
1916         if (HashCodeArray* a = static_cast<HashCodeArray*>(c->arrayElementHashCodes)) {
1917             a->~HashCodeArray();
1918             StateAllocator::Free(a);
1919         }
1920         c->~Context();
1921     }
1922 
1923     const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
1924     Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
1925     const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
1926 
1927     static const size_t kDefaultSchemaStackCapacity = 1024;
1928     static const size_t kDefaultDocumentStackCapacity = 256;
1929     const SchemaDocumentType* schemaDocument_;
1930     const SchemaType& root_;
1931     StateAllocator* stateAllocator_;
1932     StateAllocator* ownStateAllocator_;
1933     internal::Stack<StateAllocator> schemaStack_;    //!< stack to store the current path of schema (BaseSchemaType *)
1934     internal::Stack<StateAllocator> documentStack_;  //!< stack to store the current path of validating document (Ch)
1935     OutputHandler* outputHandler_;
1936     bool valid_;
1937 #if RAPIDJSON_SCHEMA_VERBOSE
1938     unsigned depth_;
1939 #endif
1940 };
1941 
1942 typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
1943 
1944 ///////////////////////////////////////////////////////////////////////////////
1945 // SchemaValidatingReader
1946 
1947 //! A helper class for parsing with validation.
1948 /*!
1949     This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate().
1950 
1951     \tparam parseFlags Combination of \ref ParseFlag.
1952     \tparam InputStream Type of input stream, implementing Stream concept.
1953     \tparam SourceEncoding Encoding of the input stream.
1954     \tparam SchemaDocumentType Type of schema document.
1955     \tparam StackAllocator Allocator type for stack.
1956 */
1957 template <
1958     unsigned parseFlags,
1959     typename InputStream,
1960     typename SourceEncoding,
1961     typename SchemaDocumentType = SchemaDocument,
1962     typename StackAllocator = CrtAllocator>
1963 class SchemaValidatingReader {
1964 public:
1965     typedef typename SchemaDocumentType::PointerType PointerType;
1966     typedef typename InputStream::Ch Ch;
1967 
1968     //! Constructor
1969     /*!
1970         \param is Input stream.
1971         \param sd Schema document.
1972     */
1973     SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {}
1974 
1975     template <typename Handler>
1976     bool operator()(Handler& handler) {
1977         GenericReader<SourceEncoding, typename SchemaDocumentType::EncodingType, StackAllocator> reader;
1978         GenericSchemaValidator<SchemaDocumentType, Handler> validator(sd_, handler);
1979         parseResult_ = reader.template Parse<parseFlags>(is_, validator);
1980 
1981         isValid_ = validator.IsValid();
1982         if (isValid_) {
1983             invalidSchemaPointer_ = PointerType();
1984             invalidSchemaKeyword_ = 0;
1985             invalidDocumentPointer_ = PointerType();
1986         }
1987         else {
1988             invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
1989             invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
1990             invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
1991         }
1992 
1993         return parseResult_;
1994     }
1995 
1996     const ParseResult& GetParseResult() const { return parseResult_; }
1997     bool IsValid() const { return isValid_; }
1998     const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
1999     const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
2000     const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
2001 
2002 private:
2003     InputStream& is_;
2004     const SchemaDocumentType& sd_;
2005 
2006     ParseResult parseResult_;
2007     PointerType invalidSchemaPointer_;
2008     const Ch* invalidSchemaKeyword_;
2009     PointerType invalidDocumentPointer_;
2010     bool isValid_;
2011 };
2012 
2013 RAPIDJSON_NAMESPACE_END
2014 RAPIDJSON_DIAG_POP
2015 
2016 #endif // RAPIDJSON_SCHEMA_H_
2017