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