From 7698b3cd4868d4e649d2050a31b1c29e0bd1a562 Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Thu, 25 Feb 2021 21:45:29 +0000 Subject: [PATCH 01/45] code and tests --- include/rapidjson/document.h | 2 +- include/rapidjson/schema.h | 486 +++++++++++++++++++++++------ test/unittest/schematest.cpp | 590 ++++++++++++++++++++++++++++++++++- 3 files changed, 981 insertions(+), 97 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 028235e..0b123ae 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1967,7 +1967,7 @@ public: case kArrayType: if (RAPIDJSON_UNLIKELY(!handler.StartArray())) return false; - for (const GenericValue* v = Begin(); v != End(); ++v) + for (ConstValueIterator v = Begin(); v != End(); ++v) if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) return false; return handler.EndArray(data_.a.size); diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 6576c25..9e99bf5 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -150,6 +150,9 @@ enum ValidateFlag { template class GenericSchemaDocument; +template +class Uri; + namespace internal { template @@ -432,11 +435,13 @@ public: typedef Schema SchemaType; typedef GenericValue SValue; typedef IValidationErrorHandler ErrorHandler; + typedef Uri UriType; friend class GenericSchemaDocument; - Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : + Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator, const UriType& id = UriType()) : allocator_(allocator), uri_(schemaDocument->GetURI(), *allocator), + id_(id), pointer_(p, allocator), typeless_(schemaDocument->GetTypeless()), enum_(), @@ -474,9 +479,30 @@ public: typedef typename ValueType::ConstValueIterator ConstValueIterator; typedef typename ValueType::ConstMemberIterator ConstMemberIterator; + // PR #1393 + // Early add this Schema and its $ref(s) in schemaDocument's map to avoid infinite + // recursion (with recursive schemas), since schemaDocument->getSchema() is always + // checked before creating a new one. Don't cache typeless_, though. + if (this != typeless_) { + typedef typename SchemaDocumentType::SchemaEntry SchemaEntry; + SchemaEntry *entry = schemaDocument->schemaMap_.template Push(); + new (entry) SchemaEntry(pointer_, this, true, allocator_); + schemaDocument->AddSchemaRefs(this); + } + if (!value.IsObject()) return; + // If we have an id property, resolve it with the in-scope id + if (const ValueType* v = GetMember(value, GetIdString())) { + if (v->IsString()) { + //std::cout << "Resolving local id '" << v->.GetString() << "' with in-scope id '" << id.GetString() << "'" << std::endl; + UriType local = UriType(*v); + local.Resolve(id_); + id_ = local; + } + } + if (const ValueType* v = GetMember(value, GetTypeString())) { type_ = 0; if (v->IsString()) @@ -507,7 +533,7 @@ public: } if (const ValueType* v = GetMember(value, GetNotString())) { - schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document); + schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document, id_); notValidatorIndex_ = validatorCount_; validatorCount_++; } @@ -524,7 +550,7 @@ public: if (properties && properties->IsObject()) for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) AddUniqueElement(allProperties, itr->name); - + if (required && required->IsArray()) for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) if (itr->IsString()) @@ -555,7 +581,7 @@ public: for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { SizeType index; if (FindPropertyIndex(itr->name, &index)) - schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document); + schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document, id_); } } @@ -567,7 +593,7 @@ public: for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { new (&patternProperties_[patternPropertyCount_]) PatternProperty(); patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name); - schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document); + schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document, id_); patternPropertyCount_++; } } @@ -599,7 +625,7 @@ public: } else if (itr->value.IsObject()) { hasSchemaDependencies_ = true; - schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document); + schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document, id_); properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; validatorCount_++; } @@ -611,7 +637,7 @@ public: if (v->IsBool()) additionalProperties_ = v->GetBool(); else if (v->IsObject()) - schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document); + schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document, id_); } AssignIfExist(minProperties_, value, GetMinPropertiesString()); @@ -621,12 +647,12 @@ public: if (const ValueType* v = GetMember(value, GetItemsString())) { PointerType q = p.Append(GetItemsString(), allocator_); if (v->IsObject()) // List validation - schemaDocument->CreateSchema(&itemsList_, q, *v, document); + schemaDocument->CreateSchema(&itemsList_, q, *v, document, id_); else if (v->IsArray()) { // Tuple validation itemsTuple_ = static_cast(allocator_->Malloc(sizeof(const Schema*) * v->Size())); SizeType index = 0; for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++) - schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document); + schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document, id_); } } @@ -637,7 +663,7 @@ public: if (v->IsBool()) additionalItems_ = v->GetBool(); else if (v->IsObject()) - schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document); + schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document, id_); } AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); @@ -697,7 +723,11 @@ public: return uri_; } - const PointerType& GetPointer() const { + const UriType& GetId() const { + return id_; + } + + const PointerType& GetPointer() const { return pointer_; } @@ -826,7 +856,7 @@ public: } return CreateParallelValidator(context); } - + bool Bool(Context& context, bool) const { if (!(type_ & (1 << kBooleanSchemaType))) { DisallowedType(context, GetBooleanString()); @@ -870,13 +900,13 @@ public: if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) return false; - + if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) return false; - + return CreateParallelValidator(context); } - + bool String(Context& context, const Ch* str, SizeType length, bool) const { if (!(type_ & (1 << kStringSchemaType))) { DisallowedType(context, GetStringString()); @@ -925,7 +955,7 @@ public: return CreateParallelValidator(context); } - + bool Key(Context& context, const Ch* str, SizeType len, bool) const { if (patternProperties_) { context.patternPropertiesSchemaCount = 0; @@ -1018,7 +1048,7 @@ public: } } if (context.error_handler.EndDependencyErrors()) - RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorDependencies); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorDependencies); } return true; @@ -1038,12 +1068,12 @@ public: bool EndArray(Context& context, SizeType elementCount) const { context.inArray = false; - + if (elementCount < minItems_) { context.error_handler.TooFewItems(elementCount, minItems_); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinItems); } - + if (elementCount > maxItems_) { context.error_handler.TooManyItems(elementCount, maxItems_); RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxItems); @@ -1132,6 +1162,15 @@ public: RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't') + RAPIDJSON_STRING_(Ref, '$', 'r', 'e', 'f') + RAPIDJSON_STRING_(Id, 'i', 'd') + + RAPIDJSON_STRING_(SchemeEnd, ':') + RAPIDJSON_STRING_(AuthStart, '/', '/') + RAPIDJSON_STRING_(QueryStart, '?') + RAPIDJSON_STRING_(FragStart, '#') + RAPIDJSON_STRING_(Slash, '/') + RAPIDJSON_STRING_(Dot, '.') #undef RAPIDJSON_STRING_ @@ -1197,7 +1236,7 @@ private: out.schemas = static_cast(allocator_->Malloc(out.count * sizeof(const Schema*))); memset(out.schemas, 0, sizeof(Schema*)* out.count); for (SizeType i = 0; i < out.count; i++) - schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document); + schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document, id_); out.begin = validatorCount_; validatorCount_ += out.count; } @@ -1274,10 +1313,10 @@ private: if (anyOf_.schemas) CreateSchemaValidators(context, anyOf_, false); - + if (oneOf_.schemas) CreateSchemaValidators(context, oneOf_, false); - + if (not_) context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_, false); @@ -1301,7 +1340,7 @@ private: SizeType len = name.GetStringLength(); const Ch* str = name.GetString(); for (SizeType index = 0; index < propertyCount_; index++) - if (properties_[index].name.GetStringLength() == len && + if (properties_[index].name.GetStringLength() == len && (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) { *outIndex = index; @@ -1462,7 +1501,7 @@ private: struct PatternProperty { PatternProperty() : schema(), pattern() {} - ~PatternProperty() { + ~PatternProperty() { if (pattern) { pattern->~RegexType(); AllocatorType::Free(pattern); @@ -1474,6 +1513,7 @@ private: AllocatorType* allocator_; SValue uri_; + UriType id_; PointerType pointer_; const SchemaType* typeless_; uint64_t* enum_; @@ -1516,7 +1556,7 @@ private: SValue multipleOf_; bool exclusiveMinimum_; bool exclusiveMaximum_; - + SizeType defaultValueLength_; }; @@ -1552,6 +1592,209 @@ struct TokenHelper { } // namespace internal +/////////////////////////////////////////////////////////////////////////////// +// Uri + +template +class Uri { +public: + typedef typename SchemaDocumentType::Ch Ch; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; + typedef internal::Schema SchemaType; + typedef std::basic_string String; + + // Constructors + Uri() {} + + Uri(const String& uri) { + Parse(uri); + } + + Uri(const Ch* uri, SizeType len) { + Parse(String(uri, len)); + } + + // Use with specializations of GenericValue + template Uri(const T& uri) { + Parse(uri.template Get()); + } + + // Getters + const String& Get() { + // Create uri_ on-demand + if (uri_.empty()) uri_ = this->GetDoc() + frag_; + return uri_; } + + // Use with specializations of GenericValue + template void Get(T& uri, AllocatorType& allocator) { + uri.template Set(this->Get(), allocator); + } + + const String& GetDoc() { + // Create doc_ on-demand + if (doc_.empty()) doc_ = scheme_ + auth_ + path_ + query_; + return doc_; + } + const String& GetScheme() const { return scheme_; } + const String& GetAuth() const { return auth_; } + const String& GetPath() const { return path_; } + const String& GetQuery() const { return query_; } + const String& GetFrag() const { return frag_; } + + const Ch* GetString() { return this->Get().c_str(); } + SizeType GetStringLength() { return static_cast(this->Get().length()); } + + const Ch* GetDocString() { return this->GetDoc().c_str(); } + SizeType GetDocStringLength() { return static_cast(this->GetDoc().length()); } + + const Ch* GetFragString() const { return frag_.c_str(); } + SizeType GetFragStringLength() const { return static_cast(frag_.length()); } + + // Resolve this URI against a base URI in accordance with URI resolution rules at + // https://tools.ietf.org/html/rfc3986 + // Use for resolving an id or $ref with an in-scope id. + // This URI is updated in place where needed from the base URI. + Uri& Resolve(const Uri& base) + { + if (!scheme_.empty()) { + // Use all of this URI + RemoveDotSegments(path_); + } else { + if (!auth_.empty()) { + RemoveDotSegments(path_); + } else { + if (path_.empty()) { + path_ = base.GetPath(); + if (query_.empty()) { + query_ = base.GetQuery(); + } + } else { + static const String slash = SchemaType::GetSlashString().GetString(); + if (path_.find(slash) == 0) { + // Absolute path - replace all the path + RemoveDotSegments(path_); + } else { + // Relative path - append to path after last slash + String p; + if (!base.GetAuth().empty() && base.GetPath().empty()) p = slash; + std::size_t lastslashpos = base.GetPath().find_last_of(slash); + path_ = p + base.GetPath().substr(0, lastslashpos + 1) + path_; + RemoveDotSegments(path_); + } + } + auth_ = base.GetAuth(); + } + scheme_ = base.GetScheme(); + } + //std::cout << " Resolved uri: " << this->GetString() << std::endl; + return *this; + } + +private: + // Parse a URI into constituent scheme, authority, path, query, fragment + // Supports URIs that match regex ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? as per + // https://tools.ietf.org/html/rfc3986 + void Parse(const String& uri) { + std::size_t start = 0, pos1 = 0, pos2 = 0; + const std::size_t len = uri.length(); + static const String schemeEnd = SchemaType::GetSchemeEndString().GetString(); + static const String authStart = SchemaType::GetAuthStartString().GetString(); + static const String pathStart = SchemaType::GetSlashString().GetString(); + static const String queryStart = SchemaType::GetQueryStartString().GetString(); + static const String fragStart = SchemaType::GetFragStartString().GetString(); + // Look for scheme ([^:/?#]+):)? + if (start < len) { + pos1 = uri.find(schemeEnd); + if (pos1 != std::string::npos) { + pos2 = uri.find_first_of(pathStart + queryStart + fragStart); + if (pos1 < pos2) { + pos1 += schemeEnd.length(); + scheme_ = uri.substr(start, pos1); + start = pos1; + } + } + } + // Look for auth (//([^/?#]*))? + if (start < len) { + pos1 = uri.find(authStart, start); + if (pos1 == start) { + pos2 = uri.find_first_of(pathStart + queryStart + fragStart, start + authStart.length()); + auth_ = uri.substr(start, pos2 - start); + start = pos2; + } + } + // Look for path ([^?#]*) + if (start < len) { + pos2 = uri.find_first_of(queryStart + fragStart, start); + if (start != pos2) { + path_ = uri.substr(start, pos2 - start); + if (path_.find(pathStart) == 0) { // absolute path - normalize + RemoveDotSegments(path_); + } + start = pos2; + } + } + // Look for query (\?([^#]*))? + if (start < len) { + pos2 = uri.find(fragStart, start); + if (start != pos2) { + query_ = uri.substr(start, pos2 - start); + start = pos2; + } + } + // Look for fragment (#(.*))? + if (start < len) { + frag_ = uri.substr(start); + } + //std::cout << " Parsed uri: " << "s: " << scheme_.c_str() << " a: " << auth_.c_str() << " p: " << path_.c_str() << " q: " << query_.c_str() << " f: " << frag_.c_str() << std::endl; + } + + // Remove . and .. segments from a path + // https://tools.ietf.org/html/rfc3986 + void RemoveDotSegments(String& path) { + String temp = path; + path.clear(); + static const String slash = SchemaType::GetSlashString().GetString(); + static const String dot = SchemaType::GetDotString().GetString(); + std::size_t pos = 0; + // Loop through each path segment + while (pos != std::string::npos) { + //std::cout << "Temp: '" << temp.c_str() << "' Path: '" << path.c_str() << "'" << std::endl; + pos = temp.find_first_of(slash); + // Get next segment + String seg = temp.substr(0, pos); + if (seg == dot) { + // Discard . segment + } else if (seg == dot + dot) { + // Backup a .. segment + // We expect to find a previously added slash at the end or nothing + std::size_t pos1 = path.find_last_of(slash); + // Make sure we don't go beyond the start + if (pos1 != std::string::npos && pos1 != 0) { + // Find the next to last slash and back up to it + pos1 = path.find_last_of(slash, pos1 - 1); + path = path.substr(0, pos1 + 1); + } + } else { + // Copy segment and add slash if not at end + path += seg; + if (pos != std::string::npos) path += slash; + } + // Move to next segment if not at end + if (pos != std::string::npos) temp = temp.substr(pos + 1); + } + //std::cout << "Final Temp: '" << temp.c_str() << "' Final Path: '" << path.c_str() << "'" << std::endl; + } + + String uri_; // Created on-demand + String doc_; // Created on-demand + String scheme_; // Includes the : + String auth_; // Includes the // + String path_; // Absolute if starts with / + String query_; // Includes the ? + String frag_; // Includes the # +}; + /////////////////////////////////////////////////////////////////////////////// // IGenericRemoteSchemaDocumentProvider @@ -1562,6 +1805,7 @@ public: virtual ~IGenericRemoteSchemaDocumentProvider() {} virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; + virtual const SchemaDocumentType* GetRemoteDocument(Uri uri) { return GetRemoteDocument(uri.GetDocString(), uri.GetDocStringLength()); } }; /////////////////////////////////////////////////////////////////////////////// @@ -1586,7 +1830,8 @@ public: typedef typename EncodingType::Ch Ch; typedef internal::Schema SchemaType; typedef GenericPointer PointerType; - typedef GenericValue URIType; + typedef GenericValue SValue; + typedef Uri UriType; friend class internal::Schema; template friend class GenericSchemaValidator; @@ -1600,9 +1845,11 @@ public: \param uriLength Length of \c name, in code points. \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. \param allocator An optional allocator instance for allocating memory. Can be null. + \param pointer An optional JSON pointer to the start of the schema document */ explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0, - IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : + IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0, + const PointerType& pointer = PointerType() : // PR #1393 remoteProvider_(remoteProvider), allocator_(allocator), ownAllocator_(), @@ -1616,30 +1863,20 @@ public: Ch noUri[1] = {0}; uri_.SetString(uri ? uri : noUri, uriLength, *allocator_); + UriType baseId(uri_); typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); - new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_); + new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, baseId); // Generate root schema, it will call CreateSchema() to create sub-schemas, - // And call AddRefSchema() if there are $ref. - CreateSchemaRecursive(&root_, PointerType(), document, document); - - // Resolve $ref - while (!schemaRef_.Empty()) { - SchemaRefEntry* refEntry = schemaRef_.template Pop(1); - if (const SchemaType* s = GetSchema(refEntry->target)) { - if (refEntry->schema) - *refEntry->schema = s; - - // Create entry in map if not exist - if (!GetSchema(refEntry->source)) { - new (schemaMap_.template Push()) SchemaEntry(refEntry->source, const_cast(s), false, allocator_); - } - } - else if (refEntry->schema) - *refEntry->schema = typeless_; - - refEntry->~SchemaRefEntry(); + // And call HandleRefSchema() if there are $ref. + // PR #1393 use input pointer if supplied + root_ = typeless_; + if (pointer.GetTokenCount() == 0) { + CreateSchemaRecursive(&root_, pointer, document, document, baseId); + } + else if (const ValueType* v = pointer.Get(document)) { + CreateSchema(&root_, pointer, *v, document, baseId); } RAPIDJSON_ASSERT(root_ != 0); @@ -1679,7 +1916,7 @@ public: RAPIDJSON_DELETE(ownAllocator_); } - const URIType& GetURI() const { return uri_; } + const SValue& GetURI() const { return uri_; } //! Get the root schema. const SchemaType& GetRoot() const { return *root_; } @@ -1690,12 +1927,7 @@ private: //! Prohibit assignment GenericSchemaDocument& operator=(const GenericSchemaDocument&); - struct SchemaRefEntry { - SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {} - PointerType source; - PointerType target; - const SchemaType** schema; - }; + typedef const PointerType* SchemaRefPtr; // PR #1393 struct SchemaEntry { SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} @@ -1710,79 +1942,153 @@ private: bool owned; }; - void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { - if (schema) - *schema = typeless_; - + // Changed by PR #1393 + void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { if (v.GetType() == kObjectType) { - const SchemaType* s = GetSchema(pointer); - if (!s) - CreateSchema(schema, pointer, v, document); + CreateSchema(schema, pointer, v, document, id); for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) - CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document); + CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document, id); } else if (v.GetType() == kArrayType) for (SizeType i = 0; i < v.Size(); i++) - CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document); + CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document, id); } - void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + // Changed by PR #1393 + void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { RAPIDJSON_ASSERT(pointer.IsValid()); if (v.IsObject()) { - if (!HandleRefSchema(pointer, schema, v, document)) { - SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_); - new (schemaMap_.template Push()) SchemaEntry(pointer, s, true, allocator_); + if (const SchemaType* sc = GetSchema(pointer)) { + if (schema) + *schema = sc; + //std::cout << "Using Schema with id " << sc->GetId().GetString() << std::endl; + AddSchemaRefs(const_cast(sc)); + } + else if (!HandleRefSchema(pointer, schema, v, document, id)) { + // The new schema adds itself and its $ref(s) to schemaMap_ + SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_, id); if (schema) *schema = s; } } + else { + if (schema) + *schema = typeless_; + AddSchemaRefs(typeless_); + } } - bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) { - static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' }; - static const ValueType kRefValue(kRefString, 4); - - typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue); + // Changed by PR #1393 + bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document, const UriType& id) { + //std::cout << "HandleRefSchema called with id " << id.GetString() << std::endl; + typename ValueType::ConstMemberIterator itr = v.FindMember(SchemaType::GetRefString()); if (itr == v.MemberEnd()) return false; + // Resolve the source pointer to the $ref'ed schema (finally) + new (schemaRef_.template Push()) SchemaRefPtr(&source); + if (itr->value.IsString()) { SizeType len = itr->value.GetStringLength(); if (len > 0) { const Ch* s = itr->value.GetString(); - SizeType i = 0; - while (i < len && s[i] != '#') // Find the first # - i++; - - if (i > 0) { // Remote reference, resolve immediately + if (s[0] != '#') { // Remote reference - resolve $ref against the in-scope id if (remoteProvider_) { - if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) { - PointerType pointer(&s[i], len - i, allocator_); + UriType ref = UriType(itr->value); + ref.Resolve(id); + //std::cout << "Resolved $ref '" << s << "' against in-scope id '" << id.GetString() << "' giving '" << ref.GetDocString() << "'" << std::endl; + if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref)) { + // Create a pointer from the # onwards + const PointerType pointer(ref.GetFragString(), ref.GetFragStringLength(), allocator_); if (pointer.IsValid()) { if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) { if (schema) *schema = sc; - new (schemaMap_.template Push()) SchemaEntry(source, const_cast(sc), false, allocator_); + AddSchemaRefs(const_cast(sc)); return true; } } } } } - else if (s[i] == '#') { // Local reference, defer resolution - PointerType pointer(&s[i], len - i, allocator_); - if (pointer.IsValid()) { - if (const ValueType* nv = pointer.Get(document)) - if (HandleRefSchema(source, schema, *nv, document)) - return true; - - new (schemaRef_.template Push()) SchemaRefEntry(source, pointer, schema, allocator_); + else { // Local reference + if (len == 1 || s[1] == '/') { + // JSON pointer + const PointerType pointer(s, len, allocator_); + if (pointer.IsValid() && !IsCyclicRef(pointer)) { + if (const ValueType *nv = pointer.Get(document)) { + CreateSchema(schema, pointer, *nv, document, id); return true; + } } + } else { + // Internal reference to an id + const ValueType val(s, len); + PointerType pointer = PointerType(); + ValueType *nv = FindId(document, val, pointer); + if (nv && !IsCyclicRef(pointer)) { + CreateSchema(schema, pointer, *nv, document, id); + return true; + } + } } } } + + // Invalid/Unknown $ref + if (schema) + *schema = typeless_; + AddSchemaRefs(typeless_); + return true; + } + + //! Find the first subschema with 'id' string property matching the specified value. + // Return a pointer to the subschema and its JSON pointer. + ValueType* FindId(const ValueType& doc, const ValueType& findval, PointerType& resptr, const PointerType& here = PointerType()) const { + SizeType i = 0; + ValueType* resval = 0; + switch(doc.GetType()) { + case kObjectType: + for (typename ValueType::ConstMemberIterator m = doc.MemberBegin(); m != doc.MemberEnd(); ++m) { + if (m->name == SchemaType::GetIdString() && m->value.GetType() == kStringType && m->value == findval) { + // Found the 'id' with the value + resval = const_cast(&doc); + resptr = here; + } else if (m->value.GetType() == kObjectType || m->value.GetType() == kArrayType) { + resval = FindId(m->value, findval, resptr, here.Append(m->name.GetString(), m->name.GetStringLength(), allocator_)); + } + if (resval) break; + } + return resval; + case kArrayType: + for (typename ValueType::ConstValueIterator v = doc.Begin(); v != doc.End(); ++v) { + if (v->GetType() == kObjectType || v->GetType() == kArrayType) { + resval = FindId(*v, findval, resptr, here.Append(i, allocator_)); + } + if (resval) break; + i++; + } + return resval; + default: + return resval; + } + } + + // Added by PR #1393 + void AddSchemaRefs(SchemaType* schema) { + while (!schemaRef_.Empty()) { + SchemaRefPtr *ref = schemaRef_.template Pop(1); + SchemaEntry *entry = schemaMap_.template Push(); + new (entry) SchemaEntry(**ref, schema, false, allocator_); + } + } + + // Added by PR #1393 + bool IsCyclicRef(const PointerType& pointer) const { + for (const SchemaRefPtr* ref = schemaRef_.template Bottom(); ref != schemaRef_.template End(); ++ref) + if (pointer == **ref) + return true; return false; } @@ -1811,8 +2117,8 @@ private: const SchemaType* root_; //!< Root schema. SchemaType* typeless_; internal::Stack schemaMap_; // Stores created Pointer -> Schemas - internal::Stack schemaRef_; // Stores Pointer from $ref and schema which holds the $ref - URIType uri_; + internal::Stack schemaRef_; // Stores Pointer(s) from $ref(s) until resolved + SValue uri_; }; //! GenericSchemaDocument using Value type. diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp index f381b4e..b3b9291 100644 --- a/test/unittest/schematest.cpp +++ b/test/unittest/schematest.cpp @@ -13,6 +13,7 @@ // specific language governing permissions and limitations under the License. #define RAPIDJSON_SCHEMA_VERBOSE 0 +#define RAPIDJSON_HAS_STDSTRING 1 #include "unittest.h" #include "rapidjson/schema.h" @@ -1811,6 +1812,189 @@ TEST(SchemaValidator, EscapedPointer) { "}}"); } +TEST(SchemaValidator, SchemaPointer) { + Document sd; + sd.Parse( + "{" + " \"swagger\": \"2.0\"," + " \"paths\": {" + " \"/some/path\": {" + " \"post\": {" + " \"parameters\": [" + " {" + " \"in\": \"body\"," + " \"name\": \"body\"," + " \"schema\": {" + " \"properties\": {" + " \"a\": {" + " \"$ref\": \"#/definitions/Prop_a\"" + " }," + " \"b\": {" + " \"type\": \"integer\"" + " }" + " }," + " \"type\": \"object\"" + " }" + " }" + " ]," + " \"responses\": {" + " \"200\": {" + " \"schema\": {" + " \"$ref\": \"#/definitions/Resp_200\"" + " }" + " }" + " }" + " }" + " }" + " }," + " \"definitions\": {" + " \"Prop_a\": {" + " \"properties\": {" + " \"c\": {" + " \"enum\": [" + " \"C1\"," + " \"C2\"," + " \"C3\"" + " ]," + " \"type\": \"string\"" + " }," + " \"d\": {" + " \"$ref\": \"#/definitions/Prop_d\"" + " }," + " \"s\": {" + " \"type\": \"string\"" + " }" + " }," + " \"required\": [\"c\"]," + " \"type\": \"object\"" + " }," + " \"Prop_d\": {" + " \"properties\": {" + " \"a\": {" + " \"$ref\": \"#/definitions/Prop_a\"" + " }," + " \"c\": {" + " \"$ref\": \"#/definitions/Prop_a/properties/c\"" + " }" + " }," + " \"type\": \"object\"" + " }," + " \"Resp_200\": {" + " \"properties\": {" + " \"e\": {" + " \"type\": \"string\"" + " }," + " \"f\": {" + " \"type\": \"boolean\"" + " }," + " \"cyclic_source\": {" + " \"$ref\": \"#/definitions/Resp_200/properties/cyclic_target\"" + " }," + " \"cyclic_target\": {" + " \"$ref\": \"#/definitions/Resp_200/properties/cyclic_source\"" + " }" + " }," + " \"type\": \"object\"" + " }" + " }" + "}"); + SchemaDocument s1(sd, NULL, 0, NULL, NULL, Pointer("#/paths/~1some~1path/post/parameters/0/schema")); + VALIDATE(s1, + "{" + " \"a\": {" + " \"c\": \"C1\"," + " \"d\": {" + " \"a\": {" + " \"c\": \"C2\"" + " }," + " \"c\": \"C3\"" + " }" + " }," + " \"b\": 123" + "}", + true); + INVALIDATE(s1, + "{" + " \"a\": {" + " \"c\": \"C1\"," + " \"d\": {" + " \"a\": {" + " \"c\": \"C2\"" + " }," + " \"c\": \"C3\"" + " }" + " }," + " \"b\": \"should be an int\"" + "}", + "#/paths/~1some~1path/post/parameters/0/schema/properties/b", "type", "#/b", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\":\"#/b\"," + " \"schemaRef\":\"#/paths/~1some~1path/post/parameters/0/schema/properties/b\"," + " \"expected\": [\"integer\"], \"actual\":\"string\"" + "}}"); + INVALIDATE(s1, + "{" + " \"a\": {" + " \"c\": \"C1\"," + " \"d\": {" + " \"a\": {" + " \"c\": \"should be within enum\"" + " }," + " \"c\": \"C3\"" + " }" + " }," + " \"b\": 123" + "}", + "#/definitions/Prop_a/properties/c", "enum", "#/a/d/a/c", + "{ \"enum\": {" + " \"errorCode\": 19," + " \"instanceRef\":\"#/a/d/a/c\"," + " \"schemaRef\":\"#/definitions/Prop_a/properties/c\"" + "}}"); + INVALIDATE(s1, + "{" + " \"a\": {" + " \"c\": \"C1\"," + " \"d\": {" + " \"a\": {" + " \"s\": \"required 'c' is missing\"" + " }" + " }" + " }," + " \"b\": 123" + "}", + "#/definitions/Prop_a", "required", "#/a/d/a", + "{ \"required\": {" + " \"errorCode\": 15," + " \"missing\":[\"c\"]," + " \"instanceRef\":\"#/a/d/a\"," + " \"schemaRef\":\"#/definitions/Prop_a\"" + "}}"); + SchemaDocument s2(sd, NULL, 0, NULL, NULL, Pointer("#/paths/~1some~1path/post/responses/200/schema")); + VALIDATE(s2, + "{ \"e\": \"some string\", \"f\": false }", + true); + INVALIDATE(s2, + "{ \"e\": true, \"f\": false }", + "#/definitions/Resp_200/properties/e", "type", "#/e", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\":\"#/e\"," + " \"schemaRef\":\"#/definitions/Resp_200/properties/e\"," + " \"expected\": [\"string\"], \"actual\":\"boolean\"" + "}}"); + INVALIDATE(s2, + "{ \"e\": \"some string\", \"f\": 123 }", + "#/definitions/Resp_200/properties/f", "type", "#/f", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\":\"#/f\"," + " \"schemaRef\":\"#/definitions/Resp_200/properties/f\"," + " \"expected\": [\"boolean\"], \"actual\":\"integer\"" + "}}"); +} + template static char* ReadFile(const char* filename, Allocator& allocator) { const char *paths[] = { @@ -1952,7 +2136,7 @@ public: virtual const SchemaDocumentType* GetRemoteDocument(const char* uri, SizeType length) { for (size_t i = 0; i < kCount; i++) - if (typename SchemaDocumentType::URIType(uri, length) == sd_[i]->GetURI()) + if (typename SchemaDocumentType::SType(uri, length) == sd_[i]->GetURI()) return sd_[i]; return 0; } @@ -2032,7 +2216,7 @@ TEST(SchemaValidator, TestSuite) { ADD_FAILURE(); } else { - //printf("json test suite file %s parsed ok\n", filename); + //printf("\njson test suite file %s parsed ok\n", filename); GenericDocument, MemoryPoolAllocator<>, MemoryPoolAllocator<> > d(&documentAllocator, 1024, &documentStackAllocator); d.Parse(json); if (d.HasParseError()) { @@ -2042,12 +2226,14 @@ TEST(SchemaValidator, TestSuite) { else { for (Value::ConstValueIterator schemaItr = d.Begin(); schemaItr != d.End(); ++schemaItr) { { + const char* description1 = (*schemaItr)["description"].GetString(); + //printf("\ncompiling schema for json test %s \n", description1); SchemaDocumentType schema((*schemaItr)["schema"], filenames[i], static_cast(strlen(filenames[i])), &provider, &schemaAllocator); GenericSchemaValidator >, MemoryPoolAllocator<> > validator(schema, &validatorAllocator); - const char* description1 = (*schemaItr)["description"].GetString(); const Value& tests = (*schemaItr)["tests"]; for (Value::ConstValueIterator testItr = tests.Begin(); testItr != tests.End(); ++testItr) { const char* description2 = (*testItr)["description"].GetString(); + //printf("running json test %s \n", description2); if (!onlyRunDescription || strcmp(description2, onlyRunDescription) == 0) { const Value& data = (*testItr)["data"]; bool expected = (*testItr)["valid"].GetBool(); @@ -2075,8 +2261,8 @@ TEST(SchemaValidator, TestSuite) { jsonAllocator.Clear(); } printf("%d / %d passed (%2d%%)\n", passCount, testCount, passCount * 100 / testCount); -// if (passCount != testCount) -// ADD_FAILURE(); + if (passCount != testCount) + ADD_FAILURE(); } TEST(SchemaValidatingReader, Simple) { @@ -2244,6 +2430,154 @@ TEST(SchemaValidator, Ref_remote) { kValidateDefaultFlags, SchemaValidatorType, PointerType); } +// Merge with id where $ref is full URI +TEST(SchemaValidator, Ref_remote_change_resolution_scope_uri) { + typedef GenericSchemaDocument > SchemaDocumentType; + RemoteSchemaDocumentProvider provider; + Document sd; + sd.Parse("{\"id\": \"http://ignore/blah#/ref\", \"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"http://localhost:1234/subSchemas.json#/integer\"}}}"); + SchemaDocumentType s(sd, 0, 0, &provider); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "{\"myInt\": null}", "/integer", "type", "/myInt", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/myInt\"," + " \"schemaRef\": \"http://localhost:1234/subSchemas.json#/integer\"," + " \"expected\": [\"integer\"], \"actual\": \"null\"" + "}}", + kValidateDefaultFlags, SchemaValidatorType, PointerType); +} + +// Merge with id where $ref is a relative path +TEST(SchemaValidator, Ref_remote_change_resolution_scope_relative_path) { + typedef GenericSchemaDocument > SchemaDocumentType; + RemoteSchemaDocumentProvider provider; + Document sd; + sd.Parse("{\"id\": \"http://localhost:1234/\", \"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"subSchemas.json#/integer\"}}}"); + SchemaDocumentType s(sd, 0, 0, &provider); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "{\"myInt\": null}", "/integer", "type", "/myInt", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/myInt\"," + " \"schemaRef\": \"http://localhost:1234/subSchemas.json#/integer\"," + " \"expected\": [\"integer\"], \"actual\": \"null\"" + "}}", + kValidateDefaultFlags, SchemaValidatorType, PointerType); +} + +// Merge with id where $ref is an absolute path +TEST(SchemaValidator, Ref_remote_change_resolution_scope_absolute_path) { + typedef GenericSchemaDocument > SchemaDocumentType; + RemoteSchemaDocumentProvider provider; + Document sd; + sd.Parse("{\"id\": \"http://localhost:1234/xxxx\", \"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"/subSchemas.json#/integer\"}}}"); + SchemaDocumentType s(sd, 0, 0, &provider); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "{\"myInt\": null}", "/integer", "type", "/myInt", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/myInt\"," + " \"schemaRef\": \"http://localhost:1234/subSchemas.json#/integer\"," + " \"expected\": [\"integer\"], \"actual\": \"null\"" + "}}", + kValidateDefaultFlags, SchemaValidatorType, PointerType); +} + +// Merge with id where $ref is an absolute path, and the document has a base URI +TEST(SchemaValidator, Ref_remote_change_resolution_scope_absolute_path_document) { + typedef GenericSchemaDocument > SchemaDocumentType; + RemoteSchemaDocumentProvider provider; + Document sd; + sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"/subSchemas.json#/integer\"}}}"); + SchemaDocumentType s(sd, "http://localhost:1234/xxxx", 26, &provider); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "{\"myInt\": null}", "/integer", "type", "/myInt", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/myInt\"," + " \"schemaRef\": \"http://localhost:1234/subSchemas.json#/integer\"," + " \"expected\": [\"integer\"], \"actual\": \"null\"" + "}}", + kValidateDefaultFlags, SchemaValidatorType, PointerType); +} + +// $ref is a non-JSON pointer fragment and there a matching id +TEST(SchemaValidator, Ref_internal_id_1) { + typedef GenericSchemaDocument > SchemaDocumentType; + Document sd; + sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"#myId\"}, \"myStr\": {\"type\": \"string\", \"id\": \"#myStrId\"}, \"myInt2\": {\"type\": \"integer\", \"id\": \"#myId\"}}}"); + SchemaDocumentType s(sd); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "{\"myInt1\": null}", "/properties/myInt2", "type", "/myInt1", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/myInt1\"," + " \"schemaRef\": \"#/properties/myInt2\"," + " \"expected\": [\"integer\"], \"actual\": \"null\"" + "}}", + kValidateDefaultFlags, SchemaValidatorType, PointerType); +} + +// $ref is a non-JSON pointer fragment and there are two matching ids so we take the first +TEST(SchemaValidator, Ref_internal_id_2) { + typedef GenericSchemaDocument > SchemaDocumentType; + Document sd; + sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"#myId\"}, \"myInt2\": {\"type\": \"integer\", \"id\": \"#myId\"}, \"myStr\": {\"type\": \"string\", \"id\": \"#myId\"}}}"); + SchemaDocumentType s(sd); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "{\"myInt1\": null}", "/properties/myInt2", "type", "/myInt1", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/myInt1\"," + " \"schemaRef\": \"#/properties/myInt2\"," + " \"expected\": [\"integer\"], \"actual\": \"null\"" + "}}", + kValidateDefaultFlags, SchemaValidatorType, PointerType); +} + +// $ref is a non-JSON pointer fragment and there is a matching id within array +TEST(SchemaValidator, Ref_internal_id_in_array) { + typedef GenericSchemaDocument > SchemaDocumentType; + Document sd; + sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"#myId\"}, \"myInt2\": {\"anyOf\": [{\"type\": \"string\", \"id\": \"#myStrId\"}, {\"type\": \"integer\", \"id\": \"#myId\"}]}}}"); + SchemaDocumentType s(sd); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "{\"myInt1\": null}", "/properties/myInt2/anyOf/1", "type", "/myInt1", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/myInt1\"," + " \"schemaRef\": \"#/properties/myInt2/anyOf/1\"," + " \"expected\": [\"integer\"], \"actual\": \"null\"" + "}}", + kValidateDefaultFlags, SchemaValidatorType, PointerType); +} + +// $ref is a non-JSON pointer fragment and there is a matching id, and the schema is embedded in the document +TEST(SchemaValidator, Ref_internal_id_and_schema_pointer) { + typedef GenericSchemaDocument > SchemaDocumentType; + Document sd; + sd.Parse("{ \"schema\": {\"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"#myId\"}, \"myInt2\": {\"anyOf\": [{\"type\": \"integer\", \"id\": \"#myId\"}]}}}}"); + typedef GenericPointer > PointerType; + SchemaDocumentType s(sd, 0, 0, 0, 0, PointerType("/schema")); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + INVALIDATE_(s, "{\"myInt1\": null}", "/schema/properties/myInt2/anyOf/0", "type", "/myInt1", + "{ \"type\": {" + " \"errorCode\": 20," + " \"instanceRef\": \"#/myInt1\"," + " \"schemaRef\": \"#/schema/properties/myInt2/anyOf/0\"," + " \"expected\": [\"integer\"], \"actual\": \"null\"" + "}}", + kValidateDefaultFlags, SchemaValidatorType, PointerType); +} + TEST(SchemaValidator, Ref_remote_issue1210) { class SchemaDocumentProvider : public IRemoteSchemaDocumentProvider { SchemaDocument** collection; @@ -2260,7 +2594,7 @@ TEST(SchemaValidator, Ref_remote_issue1210) { SchemaDocumentProvider(SchemaDocument** collection) : collection(collection) { } virtual const SchemaDocument* GetRemoteDocument(const char* uri, SizeType length) { int i = 0; - while (collection[i] && SchemaDocument::URIType(uri, length) != collection[i]->GetURI()) ++i; + while (collection[i] && SchemaDocument::SValue(uri, length) != collection[i]->GetURI()) ++i; return collection[i]; } }; @@ -2582,6 +2916,250 @@ TEST(SchemaValidator, Schema_UnknownError) { ASSERT_TRUE(SchemaValidator::SchemaType::GetValidateErrorKeyword(kValidateErrors).GetString() == std::string("null")); } +TEST(SchemaValidator, Uri_Parse) { + typedef std::basic_string String; + typedef Uri >> Uri; + MemoryPoolAllocator allocator; + + String s = "http://auth/path?query#frag"; + Value v; + v.SetString(s, allocator); + Uri u = Uri(v); + EXPECT_TRUE(u.GetScheme() == "http:"); + EXPECT_TRUE(u.GetAuth() == "//auth"); + EXPECT_TRUE(u.GetPath() == "/path"); + EXPECT_TRUE(u.GetDoc() == "http://auth/path?query"); + EXPECT_TRUE(u.GetQuery() == "?query"); + EXPECT_TRUE(u.GetFrag() == "#frag"); + Value w; + u.Get(w, allocator); + EXPECT_TRUE(*w.GetString() == *v.GetString()); + + s = "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"; + v.SetString(s, allocator); + u = Uri(v); + EXPECT_TRUE(u.GetScheme() == "urn:"); + EXPECT_TRUE(u.GetAuth() == ""); + EXPECT_TRUE(u.GetPath() == "uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); + EXPECT_TRUE(u.GetDoc() == "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); + EXPECT_TRUE(u.GetQuery() == ""); + EXPECT_TRUE(u.GetFrag() == ""); + u.Get(w, allocator); + EXPECT_TRUE(*w.GetString() == *v.GetString()); + + s = ""; + v.SetString(s, allocator); + u = Uri(v); + EXPECT_TRUE(u.GetScheme() == ""); + EXPECT_TRUE(u.GetAuth() == ""); + EXPECT_TRUE(u.GetPath() == ""); + EXPECT_TRUE(u.GetDoc() == ""); + EXPECT_TRUE(u.GetQuery() == ""); + EXPECT_TRUE(u.GetFrag() == ""); + + s = "http://auth/"; + v.SetString(s, allocator); + u = Uri(v); + EXPECT_TRUE(u.GetScheme() == "http:"); + EXPECT_TRUE(u.GetAuth() == "//auth"); + EXPECT_TRUE(u.GetPath() == "/"); + EXPECT_TRUE(u.GetDoc() == "http://auth/"); + EXPECT_TRUE(u.GetQuery() == ""); + EXPECT_TRUE(u.GetFrag() == ""); + + s = "/path/sub"; + u = Uri(s); + EXPECT_TRUE(u.GetScheme() == ""); + EXPECT_TRUE(u.GetAuth() == ""); + EXPECT_TRUE(u.GetPath() == "/path/sub"); + EXPECT_TRUE(u.GetDoc() == "/path/sub"); + EXPECT_TRUE(u.GetQuery() == ""); + EXPECT_TRUE(u.GetFrag() == ""); + + // absolute path gets normalized + s = "/path/../sub/"; + u = Uri(s); + EXPECT_TRUE(u.GetScheme() == ""); + EXPECT_TRUE(u.GetAuth() == ""); + EXPECT_TRUE(u.GetPath() == "/sub/"); + EXPECT_TRUE(u.GetDoc() == "/sub/"); + EXPECT_TRUE(u.GetQuery() == ""); + EXPECT_TRUE(u.GetFrag() == ""); + + // relative path does not + s = "path/../sub"; + u = Uri(s); + EXPECT_TRUE(u.GetScheme() == ""); + EXPECT_TRUE(u.GetAuth() == ""); + EXPECT_TRUE(u.GetPath() == "path/../sub"); + EXPECT_TRUE(u.GetDoc() == "path/../sub"); + EXPECT_TRUE(u.GetQuery() == ""); + EXPECT_TRUE(u.GetFrag() == ""); + + s = "http://auth#frag/stuff"; + u = Uri(s); + EXPECT_TRUE(u.GetScheme() == "http:"); + EXPECT_TRUE(u.GetAuth() == "//auth"); + EXPECT_TRUE(u.GetPath() == ""); + EXPECT_TRUE(u.GetDoc() == "http://auth"); + EXPECT_TRUE(u.GetQuery() == ""); + EXPECT_TRUE(u.GetFrag() == "#frag/stuff"); + EXPECT_TRUE(u.Get() == s); + + s = "#frag/stuff"; + u = Uri(s); + EXPECT_TRUE(u.GetScheme() == ""); + EXPECT_TRUE(u.GetAuth() == ""); + EXPECT_TRUE(u.GetPath() == ""); + EXPECT_TRUE(u.GetDoc() == ""); + EXPECT_TRUE(u.GetQuery() == ""); + EXPECT_TRUE(u.GetFrag() == "#frag/stuff"); + EXPECT_TRUE(u.Get() == s); + + Value::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'}; + u = Uri(c, 11); + EXPECT_TRUE(String(u.GetString()) == "#frag/stuff"); + EXPECT_TRUE(u.GetStringLength() == 11); + EXPECT_TRUE(String(u.GetDocString()) == ""); + EXPECT_TRUE(u.GetDocStringLength() == 0); + EXPECT_TRUE(String(u.GetFragString()) == "#frag/stuff"); + EXPECT_TRUE(u.GetFragStringLength() == 11); +} + +TEST(SchemaValidator, Uri_Resolve) { + typedef std::basic_string String; + typedef Uri >> Uri; + + // ref is full uri + Uri base = Uri(String("http://auth/path/#frag")); + Uri ref = Uri(String("http://newauth/newpath#newfrag")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://newauth/newpath#newfrag"); + + base = Uri(String("/path/#frag")); + ref = Uri(String("http://newauth/newpath#newfrag")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://newauth/newpath#newfrag"); + + // ref is alternate uri + base = Uri(String("http://auth/path/#frag")); + ref = Uri(String("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f")); + EXPECT_TRUE(ref.Resolve(base).Get() == "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); + + // ref is absolute path + base = Uri(String("http://auth/path/#")); + ref = Uri(String("/newpath#newfrag")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/newpath#newfrag"); + + // ref is relative path + base = Uri(String("http://auth/path/file.json#frag")); + ref = Uri(String("newfile.json#")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/path/newfile.json#"); + + base = Uri(String("http://auth/path/file.json#frag/stuff")); + ref = Uri(String("newfile.json#newfrag/newstuff")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/path/newfile.json#newfrag/newstuff"); + + base = Uri(String("file.json")); + ref = Uri(String("newfile.json")); + EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); + + base = Uri(String("file.json")); + ref = Uri(String("./newfile.json")); + EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); + + base = Uri(String("file.json")); + ref = Uri(String("parent/../newfile.json")); + EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); + + base = Uri(String("file.json")); + ref = Uri(String("parent/./newfile.json")); + EXPECT_TRUE(ref.Resolve(base).Get() == "parent/newfile.json"); + + base = Uri(String("file.json")); + ref = Uri(String("../../parent/.././newfile.json")); + EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); + + base = Uri(String("http://auth")); + ref = Uri(String("newfile.json")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/newfile.json"); + + // ref is fragment + base = Uri(String("#frag/stuff")); + ref = Uri(String("#newfrag/newstuff")); + EXPECT_TRUE(ref.Resolve(base).Get() == "#newfrag/newstuff"); + + // test ref fragment always wins + base = Uri(String("/path#frag")); + ref = Uri(String("")); + EXPECT_TRUE(ref.Resolve(base).Get() == "/path"); + + // Examples from RFC3896 + base = Uri(String("http://a/b/c/d;p?q")); + ref = Uri(String("g:h")); + EXPECT_TRUE(ref.Resolve(base).Get() == "g:h"); + ref = Uri(String("g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g"); + ref = Uri(String("./g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g"); + ref = Uri(String("g/")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g/"); + ref = Uri(String("/g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); + ref = Uri(String("//g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://g"); + ref = Uri(String("?y")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?y"); + ref = Uri(String("g?y")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g?y"); + ref = Uri(String("#s")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?q#s"); + ref = Uri(String("g#s")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g#s"); + ref = Uri(String("g?y#s")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g?y#s"); + ref = Uri(String(";x")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/;x"); + ref = Uri(String("g;x")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g;x"); + ref = Uri(String("g;x?y#s")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g;x?y#s"); + ref = Uri(String("")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?q"); + ref = Uri(String(".")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/"); + ref = Uri(String("./")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/"); + ref = Uri(String("..")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/"); + ref = Uri(String("../")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/"); + ref = Uri(String("../g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/g"); + ref = Uri(String("../..")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/"); + ref = Uri(String("../../")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/"); + ref = Uri(String("../../g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); + ref = Uri(String("../../../g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); + ref = Uri(String("../../../../g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); + ref = Uri(String("/./g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); + ref = Uri(String("/../g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); + ref = Uri(String("g.")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g."); + ref = Uri(String(".g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/.g"); + ref = Uri(String("g..")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g.."); + ref = Uri(String("..g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/..g"); + ref = Uri(String("g#s/../x")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g#s/../x"); +} + #if defined(_MSC_VER) || defined(__clang__) RAPIDJSON_DIAG_POP #endif From 892f6e3fd361d8f9da8753b5e1038e7f4cb02f73 Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Thu, 25 Feb 2021 22:21:20 +0000 Subject: [PATCH 02/45] fix bracket --- include/rapidjson/schema.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index a6cbfed..082fbbd 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1849,7 +1849,7 @@ public: */ explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0, - const PointerType& pointer = PointerType() : // PR #1393 + const PointerType& pointer = PointerType()) : // PR #1393 remoteProvider_(remoteProvider), allocator_(allocator), ownAllocator_(), From ad73c032e714af439d0aadb7946bf63706824736 Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Thu, 25 Feb 2021 22:51:35 +0000 Subject: [PATCH 03/45] fix compile errors --- example/schemavalidator/schemavalidator.cpp | 2 ++ include/rapidjson/schema.h | 2 +- test/unittest/schematest.cpp | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/example/schemavalidator/schemavalidator.cpp b/example/schemavalidator/schemavalidator.cpp index bffd64a..8c7e26c 100644 --- a/example/schemavalidator/schemavalidator.cpp +++ b/example/schemavalidator/schemavalidator.cpp @@ -2,6 +2,8 @@ // The example validates JSON text from stdin with a JSON schema specified in the argument. +#define RAPIDJSON_HAS_STDSTRING 1 + #include "rapidjson/error/en.h" #include "rapidjson/filereadstream.h" #include "rapidjson/schema.h" diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 082fbbd..e9ed19d 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -2118,7 +2118,7 @@ private: SchemaType* typeless_; internal::Stack schemaMap_; // Stores created Pointer -> Schemas internal::Stack schemaRef_; // Stores Pointer(s) from $ref(s) until resolved - SValue uri_; + SValue uri_; // Schema document URI }; //! GenericSchemaDocument using Value type. diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp index b3b9291..d84fcd4 100644 --- a/test/unittest/schematest.cpp +++ b/test/unittest/schematest.cpp @@ -2136,7 +2136,7 @@ public: virtual const SchemaDocumentType* GetRemoteDocument(const char* uri, SizeType length) { for (size_t i = 0; i < kCount; i++) - if (typename SchemaDocumentType::SType(uri, length) == sd_[i]->GetURI()) + if (typename SchemaDocumentType::SValue(uri, length) == sd_[i]->GetURI()) return sd_[i]; return 0; } From fe1a29ca6910f2b5ac246c42e09b4db23796f861 Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Thu, 25 Feb 2021 23:54:44 +0000 Subject: [PATCH 04/45] fix platform-dependent compiler error with >> --- test/unittest/schematest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp index d84fcd4..a076389 100644 --- a/test/unittest/schematest.cpp +++ b/test/unittest/schematest.cpp @@ -2918,7 +2918,7 @@ TEST(SchemaValidator, Schema_UnknownError) { TEST(SchemaValidator, Uri_Parse) { typedef std::basic_string String; - typedef Uri >> Uri; + typedef Uri > > Uri; MemoryPoolAllocator allocator; String s = "http://auth/path?query#frag"; @@ -3028,7 +3028,7 @@ TEST(SchemaValidator, Uri_Parse) { TEST(SchemaValidator, Uri_Resolve) { typedef std::basic_string String; - typedef Uri >> Uri; + typedef Uri > > Uri; // ref is full uri Uri base = Uri(String("http://auth/path/#frag")); From 6b57738e4a8abb62b17d5ab0ab619ef4a652d026 Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Thu, 11 Mar 2021 14:49:28 +0000 Subject: [PATCH 05/45] handle internal refs properly --- bin/unittestschema/idandref.json | Bin 0 -> 1315 bytes include/rapidjson/pointer.h | 66 +++++ include/rapidjson/schema.h | 409 ++++++++++--------------------- include/rapidjson/uri.h | 259 +++++++++++++++++++ test/unittest/CMakeLists.txt | 1 + test/unittest/pointertest.cpp | 42 ++++ test/unittest/schematest.cpp | 277 +++------------------ test/unittest/uritest.cpp | 278 +++++++++++++++++++++ 8 files changed, 804 insertions(+), 528 deletions(-) create mode 100644 bin/unittestschema/idandref.json create mode 100644 include/rapidjson/uri.h create mode 100644 test/unittest/uritest.cpp diff --git a/bin/unittestschema/idandref.json b/bin/unittestschema/idandref.json new file mode 100644 index 0000000000000000000000000000000000000000..ad485d29fbfc551bad246b229d00e2a22c17bd18 GIT binary patch literal 1315 zcma)5%Wi`(5WMFrkm<3C)ub=wMm_-L7LprjOyeqHk>f+Fh<`7;fNfkizyZ**Gdnws zZ?h<3^2k;ZJ8505I2Ygfr8qTQy)?(sD#o|*#CnVyjbd6Mb*&- zv8y)I-S@uQKJiXgvVaJ-NuC_7te<)>azo3L)`31Yn`9B<8^J7t7z`^dXvnjm*dh5k zlnBePaHC}Qy+_f-!cr{VL` UriType; + //! A token is the basic units of internal representation. /*! @@ -520,6 +523,69 @@ public: //@} + //!@name Compute URI + //@{ + + //! Compute the in-scope URI for a subtree. + // For use with JSON pointers into JSON schema documents. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. + \return Uri if it can be resolved. Otherwise null. + + \note + There are only 3 situations when a URI cannot be resolved: + 1. A value in the path is not an array nor object. + 2. An object value does not contain the token. + 3. A token is out of range of an array value. + + Use unresolvedTokenIndex to retrieve the token index. + */ + UriType GetUri(ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0) const { + static const Ch kIdString[] = { 'i', 'd', '\0' }; + static const ValueType kIdValue(kIdString, 2); + UriType base = rootUri; + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + switch (v->GetType()) { + case kObjectType: + { + // See if we have an id, and if so resolve with the current base + typename ValueType::MemberIterator m = v->FindMember(kIdValue); + if (m != v->MemberEnd() && (m->value).IsString()) { + UriType here = UriType(m->value); + here.Resolve(base); + base = here; + } + m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) + break; + v = &m->value; + } + continue; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + break; + v = &((*v)[t->index]); + continue; + default: + break; + } + + // Error: unresolved token + if (unresolvedTokenIndex) + *unresolvedTokenIndex = static_cast(t - tokens_); + return UriType(); + } + return base; + } + + UriType GetUri(const ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0) const { + return GetUri(const_cast(root), rootUri, unresolvedTokenIndex); + } + + //!@name Query value //@{ diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index e9ed19d..e290fc1 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1,6 +1,7 @@ // Tencent is pleased to support the open source community by making RapidJSON available-> // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> +// Portions (C) Copyright IBM Corporation 2021 // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License-> You may obtain a copy of the License at @@ -19,6 +20,7 @@ #include "pointer.h" #include "stringbuffer.h" #include "error/en.h" +#include "uri.h" #include // abs, floor #if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) @@ -150,9 +152,6 @@ enum ValidateFlag { template class GenericSchemaDocument; -template -class Uri; - namespace internal { template @@ -435,7 +434,7 @@ public: typedef Schema SchemaType; typedef GenericValue SValue; typedef IValidationErrorHandler ErrorHandler; - typedef Uri UriType; + typedef GenericUri UriType; friend class GenericSchemaDocument; Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator, const UriType& id = UriType()) : @@ -496,7 +495,6 @@ public: // If we have an id property, resolve it with the in-scope id if (const ValueType* v = GetMember(value, GetIdString())) { if (v->IsString()) { - //std::cout << "Resolving local id '" << v->.GetString() << "' with in-scope id '" << id.GetString() << "'" << std::endl; UriType local = UriType(*v); local.Resolve(id_); id_ = local; @@ -727,7 +725,7 @@ public: return id_; } - const PointerType& GetPointer() const { + const PointerType& GetPointer() const { return pointer_; } @@ -806,7 +804,7 @@ public: foundEnum:; } - // Only check allOf etc if we have validators + // Only check allOf etc if we have validators if (context.validatorCount > 0) { if (allOf_.schemas) for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) @@ -1592,209 +1590,6 @@ struct TokenHelper { } // namespace internal -/////////////////////////////////////////////////////////////////////////////// -// Uri - -template -class Uri { -public: - typedef typename SchemaDocumentType::Ch Ch; - typedef typename SchemaDocumentType::AllocatorType AllocatorType; - typedef internal::Schema SchemaType; - typedef std::basic_string String; - - // Constructors - Uri() {} - - Uri(const String& uri) { - Parse(uri); - } - - Uri(const Ch* uri, SizeType len) { - Parse(String(uri, len)); - } - - // Use with specializations of GenericValue - template Uri(const T& uri) { - Parse(uri.template Get()); - } - - // Getters - const String& Get() { - // Create uri_ on-demand - if (uri_.empty()) uri_ = this->GetDoc() + frag_; - return uri_; } - - // Use with specializations of GenericValue - template void Get(T& uri, AllocatorType& allocator) { - uri.template Set(this->Get(), allocator); - } - - const String& GetDoc() { - // Create doc_ on-demand - if (doc_.empty()) doc_ = scheme_ + auth_ + path_ + query_; - return doc_; - } - const String& GetScheme() const { return scheme_; } - const String& GetAuth() const { return auth_; } - const String& GetPath() const { return path_; } - const String& GetQuery() const { return query_; } - const String& GetFrag() const { return frag_; } - - const Ch* GetString() { return this->Get().c_str(); } - SizeType GetStringLength() { return static_cast(this->Get().length()); } - - const Ch* GetDocString() { return this->GetDoc().c_str(); } - SizeType GetDocStringLength() { return static_cast(this->GetDoc().length()); } - - const Ch* GetFragString() const { return frag_.c_str(); } - SizeType GetFragStringLength() const { return static_cast(frag_.length()); } - - // Resolve this URI against a base URI in accordance with URI resolution rules at - // https://tools.ietf.org/html/rfc3986 - // Use for resolving an id or $ref with an in-scope id. - // This URI is updated in place where needed from the base URI. - Uri& Resolve(const Uri& base) - { - if (!scheme_.empty()) { - // Use all of this URI - RemoveDotSegments(path_); - } else { - if (!auth_.empty()) { - RemoveDotSegments(path_); - } else { - if (path_.empty()) { - path_ = base.GetPath(); - if (query_.empty()) { - query_ = base.GetQuery(); - } - } else { - static const String slash = SchemaType::GetSlashString().GetString(); - if (path_.find(slash) == 0) { - // Absolute path - replace all the path - RemoveDotSegments(path_); - } else { - // Relative path - append to path after last slash - String p; - if (!base.GetAuth().empty() && base.GetPath().empty()) p = slash; - std::size_t lastslashpos = base.GetPath().find_last_of(slash); - path_ = p + base.GetPath().substr(0, lastslashpos + 1) + path_; - RemoveDotSegments(path_); - } - } - auth_ = base.GetAuth(); - } - scheme_ = base.GetScheme(); - } - //std::cout << " Resolved uri: " << this->GetString() << std::endl; - return *this; - } - -private: - // Parse a URI into constituent scheme, authority, path, query, fragment - // Supports URIs that match regex ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? as per - // https://tools.ietf.org/html/rfc3986 - void Parse(const String& uri) { - std::size_t start = 0, pos1 = 0, pos2 = 0; - const std::size_t len = uri.length(); - static const String schemeEnd = SchemaType::GetSchemeEndString().GetString(); - static const String authStart = SchemaType::GetAuthStartString().GetString(); - static const String pathStart = SchemaType::GetSlashString().GetString(); - static const String queryStart = SchemaType::GetQueryStartString().GetString(); - static const String fragStart = SchemaType::GetFragStartString().GetString(); - // Look for scheme ([^:/?#]+):)? - if (start < len) { - pos1 = uri.find(schemeEnd); - if (pos1 != std::string::npos) { - pos2 = uri.find_first_of(pathStart + queryStart + fragStart); - if (pos1 < pos2) { - pos1 += schemeEnd.length(); - scheme_ = uri.substr(start, pos1); - start = pos1; - } - } - } - // Look for auth (//([^/?#]*))? - if (start < len) { - pos1 = uri.find(authStart, start); - if (pos1 == start) { - pos2 = uri.find_first_of(pathStart + queryStart + fragStart, start + authStart.length()); - auth_ = uri.substr(start, pos2 - start); - start = pos2; - } - } - // Look for path ([^?#]*) - if (start < len) { - pos2 = uri.find_first_of(queryStart + fragStart, start); - if (start != pos2) { - path_ = uri.substr(start, pos2 - start); - if (path_.find(pathStart) == 0) { // absolute path - normalize - RemoveDotSegments(path_); - } - start = pos2; - } - } - // Look for query (\?([^#]*))? - if (start < len) { - pos2 = uri.find(fragStart, start); - if (start != pos2) { - query_ = uri.substr(start, pos2 - start); - start = pos2; - } - } - // Look for fragment (#(.*))? - if (start < len) { - frag_ = uri.substr(start); - } - //std::cout << " Parsed uri: " << "s: " << scheme_.c_str() << " a: " << auth_.c_str() << " p: " << path_.c_str() << " q: " << query_.c_str() << " f: " << frag_.c_str() << std::endl; - } - - // Remove . and .. segments from a path - // https://tools.ietf.org/html/rfc3986 - void RemoveDotSegments(String& path) { - String temp = path; - path.clear(); - static const String slash = SchemaType::GetSlashString().GetString(); - static const String dot = SchemaType::GetDotString().GetString(); - std::size_t pos = 0; - // Loop through each path segment - while (pos != std::string::npos) { - //std::cout << "Temp: '" << temp.c_str() << "' Path: '" << path.c_str() << "'" << std::endl; - pos = temp.find_first_of(slash); - // Get next segment - String seg = temp.substr(0, pos); - if (seg == dot) { - // Discard . segment - } else if (seg == dot + dot) { - // Backup a .. segment - // We expect to find a previously added slash at the end or nothing - std::size_t pos1 = path.find_last_of(slash); - // Make sure we don't go beyond the start - if (pos1 != std::string::npos && pos1 != 0) { - // Find the next to last slash and back up to it - pos1 = path.find_last_of(slash, pos1 - 1); - path = path.substr(0, pos1 + 1); - } - } else { - // Copy segment and add slash if not at end - path += seg; - if (pos != std::string::npos) path += slash; - } - // Move to next segment if not at end - if (pos != std::string::npos) temp = temp.substr(pos + 1); - } - //std::cout << "Final Temp: '" << temp.c_str() << "' Final Path: '" << path.c_str() << "'" << std::endl; - } - - String uri_; // Created on-demand - String doc_; // Created on-demand - String scheme_; // Includes the : - String auth_; // Includes the // - String path_; // Absolute if starts with / - String query_; // Includes the ? - String frag_; // Includes the # -}; - /////////////////////////////////////////////////////////////////////////////// // IGenericRemoteSchemaDocumentProvider @@ -1802,10 +1597,12 @@ template class IGenericRemoteSchemaDocumentProvider { public: typedef typename SchemaDocumentType::Ch Ch; + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; virtual ~IGenericRemoteSchemaDocumentProvider() {} virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; - virtual const SchemaDocumentType* GetRemoteDocument(Uri uri) { return GetRemoteDocument(uri.GetDocString(), uri.GetDocStringLength()); } + virtual const SchemaDocumentType* GetRemoteDocument(GenericUri uri) { return GetRemoteDocument(uri.GetBaseString(), uri.GetBaseStringLength()); } }; /////////////////////////////////////////////////////////////////////////////// @@ -1831,7 +1628,7 @@ public: typedef internal::Schema SchemaType; typedef GenericPointer PointerType; typedef GenericValue SValue; - typedef Uri UriType; + typedef GenericUri UriType; friend class internal::Schema; template friend class GenericSchemaValidator; @@ -1863,20 +1660,20 @@ public: Ch noUri[1] = {0}; uri_.SetString(uri ? uri : noUri, uriLength, *allocator_); - UriType baseId(uri_); + docId_ = UriType(uri_); typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); - new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, baseId); + new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, docId_); // Generate root schema, it will call CreateSchema() to create sub-schemas, // And call HandleRefSchema() if there are $ref. // PR #1393 use input pointer if supplied root_ = typeless_; if (pointer.GetTokenCount() == 0) { - CreateSchemaRecursive(&root_, pointer, document, document, baseId); + CreateSchemaRecursive(&root_, pointer, document, document, docId_); } else if (const ValueType* v = pointer.Get(document)) { - CreateSchema(&root_, pointer, *v, document, baseId); + CreateSchema(&root_, pointer, *v, document, docId_); } RAPIDJSON_ASSERT(root_ != 0); @@ -1894,7 +1691,8 @@ public: typeless_(rhs.typeless_), schemaMap_(std::move(rhs.schemaMap_)), schemaRef_(std::move(rhs.schemaRef_)), - uri_(std::move(rhs.uri_)) + uri_(std::move(rhs.uri_)), + docId_(rhs.docId_), { rhs.remoteProvider_ = 0; rhs.allocator_ = 0; @@ -1945,10 +1743,10 @@ private: // Changed by PR #1393 void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { if (v.GetType() == kObjectType) { - CreateSchema(schema, pointer, v, document, id); + UriType newid = CreateSchema(schema, pointer, v, document, id); for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) - CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document, id); + CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document, newid); } else if (v.GetType() == kArrayType) for (SizeType i = 0; i < v.Size(); i++) @@ -1956,20 +1754,20 @@ private: } // Changed by PR #1393 - void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { + const UriType& CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { RAPIDJSON_ASSERT(pointer.IsValid()); if (v.IsObject()) { - if (const SchemaType* sc = GetSchema(pointer)) { + if (const SchemaType* sc = GetSchema(pointer)) { if (schema) *schema = sc; - //std::cout << "Using Schema with id " << sc->GetId().GetString() << std::endl; - AddSchemaRefs(const_cast(sc)); + AddSchemaRefs(const_cast(sc)); } else if (!HandleRefSchema(pointer, schema, v, document, id)) { - // The new schema adds itself and its $ref(s) to schemaMap_ + // The new schema constructor adds itself and its $ref(s) to schemaMap_ SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_, id); if (schema) *schema = s; + return s->GetId(); } } else { @@ -1977,61 +1775,95 @@ private: *schema = typeless_; AddSchemaRefs(typeless_); } + return id; } // Changed by PR #1393 + // TODO should this return a UriType& ? bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document, const UriType& id) { - //std::cout << "HandleRefSchema called with id " << id.GetString() << std::endl; typename ValueType::ConstMemberIterator itr = v.FindMember(SchemaType::GetRefString()); if (itr == v.MemberEnd()) return false; - // Resolve the source pointer to the $ref'ed schema (finally) + // Resolve the source pointer to the $ref'ed schema (finally) new (schemaRef_.template Push()) SchemaRefPtr(&source); if (itr->value.IsString()) { SizeType len = itr->value.GetStringLength(); if (len > 0) { - const Ch* s = itr->value.GetString(); - if (s[0] != '#') { // Remote reference - resolve $ref against the in-scope id + // First resolve $ref against the in-scope id + UriType scopeId = id; + UriType ref = UriType(itr->value); + ref.Resolve(scopeId); + // See if the resolved $ref minus the fragment matches a resolved id in this document + // Search from the root. Returns the subschema in the document and its absolute JSON pointer. + PointerType basePointer = PointerType(); + const ValueType *base = FindId(document, ref, basePointer, docId_, false); + if (!base) { + // Remote reference - call the remote document provider if (remoteProvider_) { - UriType ref = UriType(itr->value); - ref.Resolve(id); - //std::cout << "Resolved $ref '" << s << "' against in-scope id '" << id.GetString() << "' giving '" << ref.GetDocString() << "'" << std::endl; if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref)) { - // Create a pointer from the # onwards - const PointerType pointer(ref.GetFragString(), ref.GetFragStringLength(), allocator_); - if (pointer.IsValid()) { - if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) { - if (schema) - *schema = sc; - AddSchemaRefs(const_cast(sc)); - return true; + const Ch* s = ref.GetFragString(); + len = ref.GetFragStringLength(); + if (len <= 1 || s[1] == '/') { + // JSON pointer fragment, absolute in the remote schema + const PointerType pointer(s, len, allocator_); + if (pointer.IsValid()) { + // Get the subschema + if (const SchemaType *sc = remoteDocument->GetSchema(pointer)) { + if (schema) + *schema = sc; + AddSchemaRefs(const_cast(sc)); + return true; + } } - } + } else { + // Plain name fragment, not allowed + } } } } else { // Local reference - if (len == 1 || s[1] == '/') { - // JSON pointer - const PointerType pointer(s, len, allocator_); - if (pointer.IsValid() && !IsCyclicRef(pointer)) { - if (const ValueType *nv = pointer.Get(document)) { - CreateSchema(schema, pointer, *nv, document, id); - return true; - } + const Ch* s = ref.GetFragString(); + len = ref.GetFragStringLength(); + if (len <= 1 || s[1] == '/') { + // JSON pointer fragment, relative to the resolved URI + const PointerType relPointer(s, len, allocator_); + if (relPointer.IsValid()) { + // Get the subschema + if (const ValueType *v = relPointer.Get(*base)) { + // Now get the absolute JSON pointer by adding relative to base + PointerType pointer(basePointer); + for (SizeType i = 0; i < relPointer.GetTokenCount(); i++) + pointer = pointer.Append(relPointer.GetTokens()[i], allocator_); + //GenericStringBuffer sb; + //pointer.StringifyUriFragment(sb); + if (pointer.IsValid() && !IsCyclicRef(pointer)) { + // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there + // TODO: cache pointer <-> id mapping + scopeId = pointer.GetUri(document, docId_); + CreateSchema(schema, pointer, *v, document, scopeId); + return true; + } + } + } + } else { + // Plain name fragment, relative to the resolved URI + // See if the fragment matches an id in this document. + // Search from the base we just established. Returns the subschema in the document and its absolute JSON pointer. + PointerType pointer = PointerType(); + if (const ValueType *v = FindId(*base, ref, pointer, UriType(ref.GetBase()), true, basePointer)) { + if (v && !IsCyclicRef(pointer)) { + //GenericStringBuffer sb; + //pointer.StringifyUriFragment(sb); + // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there + // TODO: cache pointer <-> id mapping + scopeId = pointer.GetUri(document, docId_); + CreateSchema(schema, pointer, *v, document, scopeId); + return true; + } + } } - } else { - // Internal reference to an id - const ValueType val(s, len); - PointerType pointer = PointerType(); - ValueType *nv = FindId(document, val, pointer); - if (nv && !IsCyclicRef(pointer)) { - CreateSchema(schema, pointer, *nv, document, id); - return true; - } - } } } } @@ -2043,36 +1875,46 @@ private: return true; } - //! Find the first subschema with 'id' string property matching the specified value. - // Return a pointer to the subschema and its JSON pointer. - ValueType* FindId(const ValueType& doc, const ValueType& findval, PointerType& resptr, const PointerType& here = PointerType()) const { + //! Find the first subschema with a resolved 'id' that matches the specified URI. + // If full specified use all URI else ignore fragment. + // If found, return a pointer to the subschema and its JSON pointer. + // TODO cache pointer <-> id mapping + ValueType* FindId(const ValueType& doc, const UriType& finduri, PointerType& resptr, const UriType& baseuri, bool full, const PointerType& here = PointerType()) const { SizeType i = 0; ValueType* resval = 0; - switch(doc.GetType()) { - case kObjectType: - for (typename ValueType::ConstMemberIterator m = doc.MemberBegin(); m != doc.MemberEnd(); ++m) { - if (m->name == SchemaType::GetIdString() && m->value.GetType() == kStringType && m->value == findval) { - // Found the 'id' with the value - resval = const_cast(&doc); - resptr = here; - } else if (m->value.GetType() == kObjectType || m->value.GetType() == kArrayType) { - resval = FindId(m->value, findval, resptr, here.Append(m->name.GetString(), m->name.GetStringLength(), allocator_)); - } - if (resval) break; + UriType tempuri = finduri; + UriType localuri = baseuri; + if (doc.GetType() == kObjectType) { + // Establish the base URI of this object + typename ValueType::ConstMemberIterator m = doc.FindMember(SchemaType::GetIdString()); + if (m != doc.MemberEnd() && m->value.GetType() == kStringType) { + localuri = UriType(m->value); + localuri.Resolve(baseuri); + } + // See if it matches + if (localuri.Match(finduri, full)) { + resval = const_cast(&doc); + resptr = here; + return resval; + } + // No match, continue looking + for (typename ValueType::ConstMemberIterator m = doc.MemberBegin(); m != doc.MemberEnd(); ++m) { + if (m->value.GetType() == kObjectType || m->value.GetType() == kArrayType) { + resval = FindId(m->value, finduri, resptr, localuri, full, here.Append(m->name.GetString(), m->name.GetStringLength(), allocator_)); } - return resval; - case kArrayType: - for (typename ValueType::ConstValueIterator v = doc.Begin(); v != doc.End(); ++v) { - if (v->GetType() == kObjectType || v->GetType() == kArrayType) { - resval = FindId(*v, findval, resptr, here.Append(i, allocator_)); - } - if (resval) break; - i++; + if (resval) break; + } + } else if (doc.GetType() == kArrayType) { + // Continue looking + for (typename ValueType::ConstValueIterator v = doc.Begin(); v != doc.End(); ++v) { + if (v->GetType() == kObjectType || v->GetType() == kArrayType) { + resval = FindId(*v, finduri, resptr, localuri, full, here.Append(i, allocator_)); } - return resval; - default: - return resval; + if (resval) break; + i++; + } } + return resval; } // Added by PR #1393 @@ -2119,6 +1961,7 @@ private: internal::Stack schemaMap_; // Stores created Pointer -> Schemas internal::Stack schemaRef_; // Stores Pointer(s) from $ref(s) until resolved SValue uri_; // Schema document URI + UriType docId_; }; //! GenericSchemaDocument using Value type. diff --git a/include/rapidjson/uri.h b/include/rapidjson/uri.h new file mode 100644 index 0000000..04bd92e --- /dev/null +++ b/include/rapidjson/uri.h @@ -0,0 +1,259 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_URI_H_ +#define RAPIDJSON_URI_H_ + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// GenericUri + +template +class GenericUri { +public: + typedef typename ValueType::Ch Ch; + typedef std::basic_string String; + + // Constructors + GenericUri() {} + + GenericUri(const String& uri) { + Parse(uri); + } + + GenericUri(const Ch* uri, SizeType len) { + Parse(String(uri, len)); + } + + // Use with specializations of GenericValue + template GenericUri(const T& uri) { + Parse(uri.template Get()); + } + + // Getters + const String& Get() const { return uri_; } + + // Use with specializations of GenericValue + template void Get(T& uri, Allocator& allocator) { + uri.template Set(this->Get(), allocator); + } + + const String& GetBase() const { return base_; } + const String& GetScheme() const { return scheme_; } + const String& GetAuth() const { return auth_; } + const String& GetPath() const { return path_; } + const String& GetQuery() const { return query_; } + const String& GetFrag() const { return frag_; } + + const Ch* GetString() const { return uri_.c_str(); } + SizeType GetStringLength() const { return static_cast(uri_.length()); } + + const Ch* GetBaseString() const { return base_.c_str(); } + SizeType GetBaseStringLength() const { return static_cast(base_.length()); } + + const Ch* GetFragString() const { return frag_.c_str(); } + SizeType GetFragStringLength() const { return static_cast(frag_.length()); } + + // Resolve this URI against another URI in accordance with URI resolution rules at + // https://tools.ietf.org/html/rfc3986 + // Use for resolving an id or $ref with an in-scope id. + // This URI is updated in place where needed from the base URI. + GenericUri& Resolve(const GenericUri& uri) { + if (!scheme_.empty()) { + // Use all of this URI + RemoveDotSegments(path_); + } else { + if (!auth_.empty()) { + RemoveDotSegments(path_); + } else { + if (path_.empty()) { + path_ = uri.GetPath(); + if (query_.empty()) { + query_ = uri.GetQuery(); + } + } else { + static const String slash = GetSlashString().GetString(); + if (path_.find(slash) == 0) { + // Absolute path - replace all the path + RemoveDotSegments(path_); + } else { + // Relative path - append to path after last slash + String p; + if (!uri.GetAuth().empty() && uri.GetPath().empty()) p = slash; + std::size_t lastslashpos = uri.GetPath().find_last_of(slash); + path_ = p + uri.GetPath().substr(0, lastslashpos + 1) + path_; + RemoveDotSegments(path_); + } + } + auth_ = uri.GetAuth(); + } + scheme_ = uri.GetScheme(); + } + base_ = scheme_ + auth_ + path_ + query_; + uri_ = base_ + frag_; + //std::cout << " Resolved uri: " << uri_ << std::endl; + return *this; + } + + bool Match(const GenericUri& uri, bool full) const { + if (full) + return uri_ == uri.Get(); + else + return base_ == uri.GetBase(); + } + + // Generate functions for string literal according to Ch +#define RAPIDJSON_STRING_(name, ...) \ + static const ValueType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const ValueType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1));\ + return v;\ + } + + RAPIDJSON_STRING_(SchemeEnd, ':') + RAPIDJSON_STRING_(AuthStart, '/', '/') + RAPIDJSON_STRING_(QueryStart, '?') + RAPIDJSON_STRING_(FragStart, '#') + RAPIDJSON_STRING_(Slash, '/') + RAPIDJSON_STRING_(Dot, '.') + +#undef RAPIDJSON_STRING_ + +private: + // Parse a URI into constituent scheme, authority, path, query, fragment + // Supports URIs that match regex ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? as per + // https://tools.ietf.org/html/rfc3986 + void Parse(const String& uri) { + std::size_t start = 0, pos1 = 0, pos2 = 0; + const std::size_t len = uri.length(); + static const String schemeEnd = GetSchemeEndString().GetString(); + static const String authStart = GetAuthStartString().GetString(); + static const String pathStart = GetSlashString().GetString(); + static const String queryStart = GetQueryStartString().GetString(); + static const String fragStart = GetFragStartString().GetString(); + // Look for scheme ([^:/?#]+):)? + if (start < len) { + pos1 = uri.find(schemeEnd); + if (pos1 != std::string::npos) { + pos2 = uri.find_first_of(pathStart + queryStart + fragStart); + if (pos1 < pos2) { + pos1 += schemeEnd.length(); + scheme_ = uri.substr(start, pos1); + start = pos1; + } + } + } + // Look for auth (//([^/?#]*))? + if (start < len) { + pos1 = uri.find(authStart, start); + if (pos1 == start) { + pos2 = uri.find_first_of(pathStart + queryStart + fragStart, start + authStart.length()); + auth_ = uri.substr(start, pos2 - start); + start = pos2; + } + } + // Look for path ([^?#]*) + if (start < len) { + pos2 = uri.find_first_of(queryStart + fragStart, start); + if (start != pos2) { + path_ = uri.substr(start, pos2 - start); + if (path_.find(pathStart) == 0) { // absolute path - normalize + RemoveDotSegments(path_); + } + start = pos2; + } + } + // Look for query (\?([^#]*))? + if (start < len) { + pos2 = uri.find(fragStart, start); + if (start != pos2) { + query_ = uri.substr(start, pos2 - start); + start = pos2; + } + } + // Look for fragment (#(.*))? + if (start < len) { + frag_ = uri.substr(start); + } + base_ = scheme_ + auth_ + path_ + query_; + uri_ = base_ + frag_; + //std::cout << " Parsed uri: " << "s: " << scheme_.c_str() << " a: " << auth_.c_str() << " p: " << path_.c_str() << " q: " << query_.c_str() << " f: " << frag_.c_str() << std::endl; + } + + // Remove . and .. segments from a path + // https://tools.ietf.org/html/rfc3986 + void RemoveDotSegments(String& path) { + String temp = path; + path.clear(); + static const String slash = GetSlashString().GetString(); + static const String dot = GetDotString().GetString(); + std::size_t pos = 0; + // Loop through each path segment + while (pos != std::string::npos) { + //std::cout << "Temp: '" << temp.c_str() << "' Path: '" << path.c_str() << "'" << std::endl; + pos = temp.find_first_of(slash); + // Get next segment + String seg = temp.substr(0, pos); + if (seg == dot) { + // Discard . segment + } else if (seg == dot + dot) { + // Backup a .. segment + // We expect to find a previously added slash at the end or nothing + std::size_t pos1 = path.find_last_of(slash); + // Make sure we don't go beyond the start + if (pos1 != std::string::npos && pos1 != 0) { + // Find the next to last slash and back up to it + pos1 = path.find_last_of(slash, pos1 - 1); + path = path.substr(0, pos1 + 1); + } + } else { + // Copy segment and add slash if not at end + path += seg; + if (pos != std::string::npos) path += slash; + } + // Move to next segment if not at end + if (pos != std::string::npos) temp = temp.substr(pos + 1); + } + //std::cout << "Final Temp: '" << temp.c_str() << "' Final Path: '" << path.c_str() << "'" << std::endl; + } + + String uri_ = String(); // Full uri + String base_ = String(); // Everything except fragment + String scheme_ = String(); // Includes the : + String auth_ = String(); // Includes the // + String path_ = String(); // Absolute if starts with / + String query_ = String(); // Includes the ? + String frag_ = String(); // Includes the # +}; + +//! GenericUri for Value (UTF-8, default allocator). +typedef GenericUri Uri; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_URI_H_ diff --git a/test/unittest/CMakeLists.txt b/test/unittest/CMakeLists.txt index fc8803e..6439c36 100644 --- a/test/unittest/CMakeLists.txt +++ b/test/unittest/CMakeLists.txt @@ -26,6 +26,7 @@ set(UNITTEST_SOURCES stringbuffertest.cpp strtodtest.cpp unittest.cpp + uritest.cpp valuetest.cpp writertest.cpp) diff --git a/test/unittest/pointertest.cpp b/test/unittest/pointertest.cpp index 4371803..c693b8f 100644 --- a/test/unittest/pointertest.cpp +++ b/test/unittest/pointertest.cpp @@ -648,6 +648,48 @@ TEST(Pointer, Create) { } } +static const char kJsonIds[] = "{\n" + " \"id\": \"/root/\"," + " \"foo\":[\"bar\", \"baz\", {\"id\": \"inarray\", \"child\": 1}],\n" + " \"int\" : 2,\n" + " \"str\" : \"val\",\n" + " \"obj\": {\"id\": \"inobj\", \"child\": 3},\n" + " \"jbo\": {\"id\": true, \"child\": 4}\n" + "}"; + + +TEST(Pointer, GetUri) { + typedef std::basic_string String; + Document d; + d.Parse(kJsonIds); + + String doc = String("http://doc"); + EXPECT_TRUE((Pointer("").GetUri(d, Pointer::UriType(doc)).Get()) == doc); + EXPECT_TRUE((Pointer("/foo").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); + EXPECT_TRUE((Pointer("/foo/0").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); + EXPECT_TRUE((Pointer("/foo/2").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); + EXPECT_TRUE((Pointer("/foo/2/child").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/inarray"); + EXPECT_TRUE((Pointer("/int").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); + EXPECT_TRUE((Pointer("/str").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); + EXPECT_TRUE((Pointer("/obj").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); + EXPECT_TRUE((Pointer("/obj/child").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/inobj"); + EXPECT_TRUE((Pointer("/jbo").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); + EXPECT_TRUE((Pointer("/jbo/child").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); // id not string + + size_t unresolvedTokenIndex; + EXPECT_TRUE((Pointer("/foo/3").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == ""); // Out of boundary + EXPECT_EQ(1u, unresolvedTokenIndex); + EXPECT_TRUE((Pointer("/foo/a").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == ""); // "/foo" is an array, cannot query by "a" + EXPECT_EQ(1u, unresolvedTokenIndex); + EXPECT_TRUE((Pointer("/foo/0/0").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == ""); // "/foo/0" is an string, cannot further query + EXPECT_EQ(2u, unresolvedTokenIndex); + EXPECT_TRUE((Pointer("/foo/0/a").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == ""); // "/foo/0" is an string, cannot further query + EXPECT_EQ(2u, unresolvedTokenIndex); + + Pointer::Token tokens[] = { { "foo ...", 3, kPointerInvalidIndex } }; + EXPECT_TRUE((Pointer(tokens, 1).GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); +} + TEST(Pointer, Get) { Document d; d.Parse(kJson); diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp index a076389..1b25e2f 100644 --- a/test/unittest/schematest.cpp +++ b/test/unittest/schematest.cpp @@ -2300,7 +2300,7 @@ TEST(SchemaValidatingReader, Invalid) { Document e; e.Parse( "{ \"maxLength\": {" -" \"errorCode\": 6," + " \"errorCode\": 6," " \"instanceRef\": \"#\", \"schemaRef\": \"#\"," " \"expected\": 3, \"actual\": \"ABCD\"" "}}"); @@ -2578,6 +2578,37 @@ TEST(SchemaValidator, Ref_internal_id_and_schema_pointer) { kValidateDefaultFlags, SchemaValidatorType, PointerType); } +// Test that $refs are correctly resolved when intermediate multiple ids are present +// Includes $ref to a part of the document with a different in-scope id, which also contains $ref.. +TEST(SchemaValidator, Ref_internal_multiple_ids) { + typedef GenericSchemaDocument > SchemaDocumentType; + //RemoteSchemaDocumentProvider provider; + CrtAllocator allocator; + char* schema = ReadFile("unittestschema/idandref.json", allocator); + Document sd; + sd.Parse(schema); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocumentType s(sd, "http://xyz", 10/*, &provider*/); + typedef GenericSchemaValidator >, MemoryPoolAllocator<> > SchemaValidatorType; + typedef GenericPointer > PointerType; + INVALIDATE_(s, "{\"PA1\": \"s\", \"PA2\": \"t\", \"PA3\": \"r\", \"PX1\": 1, \"PX2Y\": 2, \"PX3Z\": 3, \"PX4\": 4, \"PX5\": 5, \"PX6\": 6, \"PX7W\": 7, \"PX8N\": { \"NX\": 8}}", "#", "errors", "#", + "{ \"type\": [" + " {\"errorCode\": 20, \"instanceRef\": \"#/PA1\", \"schemaRef\": \"http://xyz#/definitions/A\", \"expected\": [\"integer\"], \"actual\": \"string\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PA2\", \"schemaRef\": \"http://xyz#/definitions/A\", \"expected\": [\"integer\"], \"actual\": \"string\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PA3\", \"schemaRef\": \"http://xyz#/definitions/A\", \"expected\": [\"integer\"], \"actual\": \"string\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PX1\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PX2Y\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PX3Z\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PX4\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PX5\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PX6\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PX7W\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"}," + " {\"errorCode\": 20, \"instanceRef\": \"#/PX8N/NX\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"}" + "]}", + kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidatorType, PointerType); + CrtAllocator::Free(schema); +} + TEST(SchemaValidator, Ref_remote_issue1210) { class SchemaDocumentProvider : public IRemoteSchemaDocumentProvider { SchemaDocument** collection; @@ -2916,250 +2947,6 @@ TEST(SchemaValidator, Schema_UnknownError) { ASSERT_TRUE(SchemaValidator::SchemaType::GetValidateErrorKeyword(kValidateErrors).GetString() == std::string("null")); } -TEST(SchemaValidator, Uri_Parse) { - typedef std::basic_string String; - typedef Uri > > Uri; - MemoryPoolAllocator allocator; - - String s = "http://auth/path?query#frag"; - Value v; - v.SetString(s, allocator); - Uri u = Uri(v); - EXPECT_TRUE(u.GetScheme() == "http:"); - EXPECT_TRUE(u.GetAuth() == "//auth"); - EXPECT_TRUE(u.GetPath() == "/path"); - EXPECT_TRUE(u.GetDoc() == "http://auth/path?query"); - EXPECT_TRUE(u.GetQuery() == "?query"); - EXPECT_TRUE(u.GetFrag() == "#frag"); - Value w; - u.Get(w, allocator); - EXPECT_TRUE(*w.GetString() == *v.GetString()); - - s = "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"; - v.SetString(s, allocator); - u = Uri(v); - EXPECT_TRUE(u.GetScheme() == "urn:"); - EXPECT_TRUE(u.GetAuth() == ""); - EXPECT_TRUE(u.GetPath() == "uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); - EXPECT_TRUE(u.GetDoc() == "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); - EXPECT_TRUE(u.GetQuery() == ""); - EXPECT_TRUE(u.GetFrag() == ""); - u.Get(w, allocator); - EXPECT_TRUE(*w.GetString() == *v.GetString()); - - s = ""; - v.SetString(s, allocator); - u = Uri(v); - EXPECT_TRUE(u.GetScheme() == ""); - EXPECT_TRUE(u.GetAuth() == ""); - EXPECT_TRUE(u.GetPath() == ""); - EXPECT_TRUE(u.GetDoc() == ""); - EXPECT_TRUE(u.GetQuery() == ""); - EXPECT_TRUE(u.GetFrag() == ""); - - s = "http://auth/"; - v.SetString(s, allocator); - u = Uri(v); - EXPECT_TRUE(u.GetScheme() == "http:"); - EXPECT_TRUE(u.GetAuth() == "//auth"); - EXPECT_TRUE(u.GetPath() == "/"); - EXPECT_TRUE(u.GetDoc() == "http://auth/"); - EXPECT_TRUE(u.GetQuery() == ""); - EXPECT_TRUE(u.GetFrag() == ""); - - s = "/path/sub"; - u = Uri(s); - EXPECT_TRUE(u.GetScheme() == ""); - EXPECT_TRUE(u.GetAuth() == ""); - EXPECT_TRUE(u.GetPath() == "/path/sub"); - EXPECT_TRUE(u.GetDoc() == "/path/sub"); - EXPECT_TRUE(u.GetQuery() == ""); - EXPECT_TRUE(u.GetFrag() == ""); - - // absolute path gets normalized - s = "/path/../sub/"; - u = Uri(s); - EXPECT_TRUE(u.GetScheme() == ""); - EXPECT_TRUE(u.GetAuth() == ""); - EXPECT_TRUE(u.GetPath() == "/sub/"); - EXPECT_TRUE(u.GetDoc() == "/sub/"); - EXPECT_TRUE(u.GetQuery() == ""); - EXPECT_TRUE(u.GetFrag() == ""); - - // relative path does not - s = "path/../sub"; - u = Uri(s); - EXPECT_TRUE(u.GetScheme() == ""); - EXPECT_TRUE(u.GetAuth() == ""); - EXPECT_TRUE(u.GetPath() == "path/../sub"); - EXPECT_TRUE(u.GetDoc() == "path/../sub"); - EXPECT_TRUE(u.GetQuery() == ""); - EXPECT_TRUE(u.GetFrag() == ""); - - s = "http://auth#frag/stuff"; - u = Uri(s); - EXPECT_TRUE(u.GetScheme() == "http:"); - EXPECT_TRUE(u.GetAuth() == "//auth"); - EXPECT_TRUE(u.GetPath() == ""); - EXPECT_TRUE(u.GetDoc() == "http://auth"); - EXPECT_TRUE(u.GetQuery() == ""); - EXPECT_TRUE(u.GetFrag() == "#frag/stuff"); - EXPECT_TRUE(u.Get() == s); - - s = "#frag/stuff"; - u = Uri(s); - EXPECT_TRUE(u.GetScheme() == ""); - EXPECT_TRUE(u.GetAuth() == ""); - EXPECT_TRUE(u.GetPath() == ""); - EXPECT_TRUE(u.GetDoc() == ""); - EXPECT_TRUE(u.GetQuery() == ""); - EXPECT_TRUE(u.GetFrag() == "#frag/stuff"); - EXPECT_TRUE(u.Get() == s); - - Value::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'}; - u = Uri(c, 11); - EXPECT_TRUE(String(u.GetString()) == "#frag/stuff"); - EXPECT_TRUE(u.GetStringLength() == 11); - EXPECT_TRUE(String(u.GetDocString()) == ""); - EXPECT_TRUE(u.GetDocStringLength() == 0); - EXPECT_TRUE(String(u.GetFragString()) == "#frag/stuff"); - EXPECT_TRUE(u.GetFragStringLength() == 11); -} - -TEST(SchemaValidator, Uri_Resolve) { - typedef std::basic_string String; - typedef Uri > > Uri; - - // ref is full uri - Uri base = Uri(String("http://auth/path/#frag")); - Uri ref = Uri(String("http://newauth/newpath#newfrag")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://newauth/newpath#newfrag"); - - base = Uri(String("/path/#frag")); - ref = Uri(String("http://newauth/newpath#newfrag")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://newauth/newpath#newfrag"); - - // ref is alternate uri - base = Uri(String("http://auth/path/#frag")); - ref = Uri(String("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f")); - EXPECT_TRUE(ref.Resolve(base).Get() == "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); - - // ref is absolute path - base = Uri(String("http://auth/path/#")); - ref = Uri(String("/newpath#newfrag")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/newpath#newfrag"); - - // ref is relative path - base = Uri(String("http://auth/path/file.json#frag")); - ref = Uri(String("newfile.json#")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/path/newfile.json#"); - - base = Uri(String("http://auth/path/file.json#frag/stuff")); - ref = Uri(String("newfile.json#newfrag/newstuff")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/path/newfile.json#newfrag/newstuff"); - - base = Uri(String("file.json")); - ref = Uri(String("newfile.json")); - EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); - - base = Uri(String("file.json")); - ref = Uri(String("./newfile.json")); - EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); - - base = Uri(String("file.json")); - ref = Uri(String("parent/../newfile.json")); - EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); - - base = Uri(String("file.json")); - ref = Uri(String("parent/./newfile.json")); - EXPECT_TRUE(ref.Resolve(base).Get() == "parent/newfile.json"); - - base = Uri(String("file.json")); - ref = Uri(String("../../parent/.././newfile.json")); - EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); - - base = Uri(String("http://auth")); - ref = Uri(String("newfile.json")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/newfile.json"); - - // ref is fragment - base = Uri(String("#frag/stuff")); - ref = Uri(String("#newfrag/newstuff")); - EXPECT_TRUE(ref.Resolve(base).Get() == "#newfrag/newstuff"); - - // test ref fragment always wins - base = Uri(String("/path#frag")); - ref = Uri(String("")); - EXPECT_TRUE(ref.Resolve(base).Get() == "/path"); - - // Examples from RFC3896 - base = Uri(String("http://a/b/c/d;p?q")); - ref = Uri(String("g:h")); - EXPECT_TRUE(ref.Resolve(base).Get() == "g:h"); - ref = Uri(String("g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g"); - ref = Uri(String("./g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g"); - ref = Uri(String("g/")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g/"); - ref = Uri(String("/g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); - ref = Uri(String("//g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://g"); - ref = Uri(String("?y")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?y"); - ref = Uri(String("g?y")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g?y"); - ref = Uri(String("#s")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?q#s"); - ref = Uri(String("g#s")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g#s"); - ref = Uri(String("g?y#s")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g?y#s"); - ref = Uri(String(";x")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/;x"); - ref = Uri(String("g;x")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g;x"); - ref = Uri(String("g;x?y#s")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g;x?y#s"); - ref = Uri(String("")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?q"); - ref = Uri(String(".")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/"); - ref = Uri(String("./")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/"); - ref = Uri(String("..")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/"); - ref = Uri(String("../")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/"); - ref = Uri(String("../g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/g"); - ref = Uri(String("../..")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/"); - ref = Uri(String("../../")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/"); - ref = Uri(String("../../g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); - ref = Uri(String("../../../g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); - ref = Uri(String("../../../../g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); - ref = Uri(String("/./g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); - ref = Uri(String("/../g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); - ref = Uri(String("g.")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g."); - ref = Uri(String(".g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/.g"); - ref = Uri(String("g..")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g.."); - ref = Uri(String("..g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/..g"); - ref = Uri(String("g#s/../x")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g#s/../x"); -} - #if defined(_MSC_VER) || defined(__clang__) RAPIDJSON_DIAG_POP #endif diff --git a/test/unittest/uritest.cpp b/test/unittest/uritest.cpp new file mode 100644 index 0000000..d8a78b8 --- /dev/null +++ b/test/unittest/uritest.cpp @@ -0,0 +1,278 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#define RAPIDJSON_SCHEMA_VERBOSE 0 +#define RAPIDJSON_HAS_STDSTRING 1 + +#include "unittest.h" +#include "rapidjson/document.h" +#include "rapidjson/uri.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(variadic-macros) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4822) // local class member function does not have a body +#endif + +using namespace rapidjson; + +TEST(Uri, Parse) { + typedef std::basic_string String; + typedef GenericUri > Uri; + MemoryPoolAllocator allocator; + + String s = "http://auth/path?query#frag"; + Value v; + v.SetString(s, allocator); + Uri u = Uri(v); + EXPECT_TRUE(u.GetScheme() == "http:"); + EXPECT_TRUE(u.GetAuth() == "//auth"); + EXPECT_TRUE(u.GetPath() == "/path"); + EXPECT_TRUE(u.GetBase() == "http://auth/path?query"); + EXPECT_TRUE(u.GetQuery() == "?query"); + EXPECT_TRUE(u.GetFrag() == "#frag"); + Value w; + u.Get(w, allocator); + EXPECT_TRUE(*w.GetString() == *v.GetString()); + + s = "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"; + v.SetString(s, allocator); + u = Uri(v); + EXPECT_TRUE(u.GetScheme() == "urn:"); + EXPECT_TRUE(u.GetAuth() == ""); + EXPECT_TRUE(u.GetPath() == "uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); + EXPECT_TRUE(u.GetBase() == "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); + EXPECT_TRUE(u.GetQuery() == ""); + EXPECT_TRUE(u.GetFrag() == ""); + u.Get(w, allocator); + EXPECT_TRUE(*w.GetString() == *v.GetString()); + + s = ""; + v.SetString(s, allocator); + u = Uri(v); + EXPECT_TRUE(u.GetScheme() == ""); + EXPECT_TRUE(u.GetAuth() == ""); + EXPECT_TRUE(u.GetPath() == ""); + EXPECT_TRUE(u.GetBase() == ""); + EXPECT_TRUE(u.GetQuery() == ""); + EXPECT_TRUE(u.GetFrag() == ""); + + s = "http://auth/"; + v.SetString(s, allocator); + u = Uri(v); + EXPECT_TRUE(u.GetScheme() == "http:"); + EXPECT_TRUE(u.GetAuth() == "//auth"); + EXPECT_TRUE(u.GetPath() == "/"); + EXPECT_TRUE(u.GetBase() == "http://auth/"); + EXPECT_TRUE(u.GetQuery() == ""); + EXPECT_TRUE(u.GetFrag() == ""); + + s = "/path/sub"; + u = Uri(s); + EXPECT_TRUE(u.GetScheme() == ""); + EXPECT_TRUE(u.GetAuth() == ""); + EXPECT_TRUE(u.GetPath() == "/path/sub"); + EXPECT_TRUE(u.GetBase() == "/path/sub"); + EXPECT_TRUE(u.GetQuery() == ""); + EXPECT_TRUE(u.GetFrag() == ""); + + // absolute path gets normalized + s = "/path/../sub/"; + u = Uri(s); + EXPECT_TRUE(u.GetScheme() == ""); + EXPECT_TRUE(u.GetAuth() == ""); + EXPECT_TRUE(u.GetPath() == "/sub/"); + EXPECT_TRUE(u.GetBase() == "/sub/"); + EXPECT_TRUE(u.GetQuery() == ""); + EXPECT_TRUE(u.GetFrag() == ""); + + // relative path does not + s = "path/../sub"; + u = Uri(s); + EXPECT_TRUE(u.GetScheme() == ""); + EXPECT_TRUE(u.GetAuth() == ""); + EXPECT_TRUE(u.GetPath() == "path/../sub"); + EXPECT_TRUE(u.GetBase() == "path/../sub"); + EXPECT_TRUE(u.GetQuery() == ""); + EXPECT_TRUE(u.GetFrag() == ""); + + s = "http://auth#frag/stuff"; + u = Uri(s); + EXPECT_TRUE(u.GetScheme() == "http:"); + EXPECT_TRUE(u.GetAuth() == "//auth"); + EXPECT_TRUE(u.GetPath() == ""); + EXPECT_TRUE(u.GetBase() == "http://auth"); + EXPECT_TRUE(u.GetQuery() == ""); + EXPECT_TRUE(u.GetFrag() == "#frag/stuff"); + EXPECT_TRUE(u.Get() == s); + + s = "#frag/stuff"; + u = Uri(s); + EXPECT_TRUE(u.GetScheme() == ""); + EXPECT_TRUE(u.GetAuth() == ""); + EXPECT_TRUE(u.GetPath() == ""); + EXPECT_TRUE(u.GetBase() == ""); + EXPECT_TRUE(u.GetQuery() == ""); + EXPECT_TRUE(u.GetFrag() == "#frag/stuff"); + EXPECT_TRUE(u.Get() == s); + + Value::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'}; + u = Uri(c, 11); + EXPECT_TRUE(String(u.GetString()) == "#frag/stuff"); + EXPECT_TRUE(u.GetStringLength() == 11); + EXPECT_TRUE(String(u.GetBaseString()) == ""); + EXPECT_TRUE(u.GetBaseStringLength() == 0); + EXPECT_TRUE(String(u.GetFragString()) == "#frag/stuff"); + EXPECT_TRUE(u.GetFragStringLength() == 11); +} + +TEST(Uri, Resolve) { + typedef std::basic_string String; + typedef GenericUri > Uri; + + // ref is full uri + Uri base = Uri(String("http://auth/path/#frag")); + Uri ref = Uri(String("http://newauth/newpath#newfrag")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://newauth/newpath#newfrag"); + + base = Uri(String("/path/#frag")); + ref = Uri(String("http://newauth/newpath#newfrag")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://newauth/newpath#newfrag"); + + // ref is alternate uri + base = Uri(String("http://auth/path/#frag")); + ref = Uri(String("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f")); + EXPECT_TRUE(ref.Resolve(base).Get() == "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); + + // ref is absolute path + base = Uri(String("http://auth/path/#")); + ref = Uri(String("/newpath#newfrag")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/newpath#newfrag"); + + // ref is relative path + base = Uri(String("http://auth/path/file.json#frag")); + ref = Uri(String("newfile.json#")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/path/newfile.json#"); + + base = Uri(String("http://auth/path/file.json#frag/stuff")); + ref = Uri(String("newfile.json#newfrag/newstuff")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/path/newfile.json#newfrag/newstuff"); + + base = Uri(String("file.json")); + ref = Uri(String("newfile.json")); + EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); + + base = Uri(String("file.json")); + ref = Uri(String("./newfile.json")); + EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); + + base = Uri(String("file.json")); + ref = Uri(String("parent/../newfile.json")); + EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); + + base = Uri(String("file.json")); + ref = Uri(String("parent/./newfile.json")); + EXPECT_TRUE(ref.Resolve(base).Get() == "parent/newfile.json"); + + base = Uri(String("file.json")); + ref = Uri(String("../../parent/.././newfile.json")); + EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); + + base = Uri(String("http://auth")); + ref = Uri(String("newfile.json")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/newfile.json"); + + // ref is fragment + base = Uri(String("#frag/stuff")); + ref = Uri(String("#newfrag/newstuff")); + EXPECT_TRUE(ref.Resolve(base).Get() == "#newfrag/newstuff"); + + // test ref fragment always wins + base = Uri(String("/path#frag")); + ref = Uri(String("")); + EXPECT_TRUE(ref.Resolve(base).Get() == "/path"); + + // Examples from RFC3896 + base = Uri(String("http://a/b/c/d;p?q")); + ref = Uri(String("g:h")); + EXPECT_TRUE(ref.Resolve(base).Get() == "g:h"); + ref = Uri(String("g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g"); + ref = Uri(String("./g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g"); + ref = Uri(String("g/")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g/"); + ref = Uri(String("/g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); + ref = Uri(String("//g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://g"); + ref = Uri(String("?y")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?y"); + ref = Uri(String("g?y")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g?y"); + ref = Uri(String("#s")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?q#s"); + ref = Uri(String("g#s")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g#s"); + ref = Uri(String("g?y#s")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g?y#s"); + ref = Uri(String(";x")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/;x"); + ref = Uri(String("g;x")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g;x"); + ref = Uri(String("g;x?y#s")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g;x?y#s"); + ref = Uri(String("")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?q"); + ref = Uri(String(".")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/"); + ref = Uri(String("./")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/"); + ref = Uri(String("..")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/"); + ref = Uri(String("../")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/"); + ref = Uri(String("../g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/g"); + ref = Uri(String("../..")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/"); + ref = Uri(String("../../")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/"); + ref = Uri(String("../../g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); + ref = Uri(String("../../../g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); + ref = Uri(String("../../../../g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); + ref = Uri(String("/./g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); + ref = Uri(String("/../g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); + ref = Uri(String("g.")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g."); + ref = Uri(String(".g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/.g"); + ref = Uri(String("g..")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g.."); + ref = Uri(String("..g")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/..g"); + ref = Uri(String("g#s/../x")); + EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g#s/../x"); +} + +#if defined(_MSC_VER) || defined(__clang__) +RAPIDJSON_DIAG_POP +#endif From 6c9da69abf2f121042032e8510ac843e30eff69f Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Thu, 11 Mar 2021 15:06:02 +0000 Subject: [PATCH 06/45] remove comma --- include/rapidjson/schema.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index e290fc1..226fea4 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1692,7 +1692,7 @@ public: schemaMap_(std::move(rhs.schemaMap_)), schemaRef_(std::move(rhs.schemaRef_)), uri_(std::move(rhs.uri_)), - docId_(rhs.docId_), + docId_(rhs.docId_) { rhs.remoteProvider_ = 0; rhs.allocator_ = 0; From 8768b5b1d696935bea927ee147e0bf81c933cbd0 Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Thu, 11 Mar 2021 15:13:17 +0000 Subject: [PATCH 07/45] correct #defines in uri.h --- include/rapidjson/uri.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/rapidjson/uri.h b/include/rapidjson/uri.h index 04bd92e..1e5b46f 100644 --- a/include/rapidjson/uri.h +++ b/include/rapidjson/uri.h @@ -15,13 +15,11 @@ #ifndef RAPIDJSON_URI_H_ #define RAPIDJSON_URI_H_ -#if RAPIDJSON_HAS_CXX11_RVALUE_REFS -#include // std::move -#endif - #if defined(__clang__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated #endif RAPIDJSON_NAMESPACE_BEGIN From 32722fa31ddaf5abffa1cb070530374f689c7669 Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Thu, 11 Mar 2021 16:53:05 +0000 Subject: [PATCH 08/45] satisfy all compilers --- include/rapidjson/schema.h | 10 +++++----- include/rapidjson/uri.h | 22 +++++++++++----------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 226fea4..5f25b9b 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1831,7 +1831,7 @@ private: const PointerType relPointer(s, len, allocator_); if (relPointer.IsValid()) { // Get the subschema - if (const ValueType *v = relPointer.Get(*base)) { + if (const ValueType *pv = relPointer.Get(*base)) { // Now get the absolute JSON pointer by adding relative to base PointerType pointer(basePointer); for (SizeType i = 0; i < relPointer.GetTokenCount(); i++) @@ -1842,7 +1842,7 @@ private: // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there // TODO: cache pointer <-> id mapping scopeId = pointer.GetUri(document, docId_); - CreateSchema(schema, pointer, *v, document, scopeId); + CreateSchema(schema, pointer, *pv, document, scopeId); return true; } } @@ -1852,14 +1852,14 @@ private: // See if the fragment matches an id in this document. // Search from the base we just established. Returns the subschema in the document and its absolute JSON pointer. PointerType pointer = PointerType(); - if (const ValueType *v = FindId(*base, ref, pointer, UriType(ref.GetBase()), true, basePointer)) { - if (v && !IsCyclicRef(pointer)) { + if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBase()), true, basePointer)) { + if (!IsCyclicRef(pointer)) { //GenericStringBuffer sb; //pointer.StringifyUriFragment(sb); // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there // TODO: cache pointer <-> id mapping scopeId = pointer.GetUri(document, docId_); - CreateSchema(schema, pointer, *v, document, scopeId); + CreateSchema(schema, pointer, *pv, document, scopeId); return true; } } diff --git a/include/rapidjson/uri.h b/include/rapidjson/uri.h index 1e5b46f..e72976d 100644 --- a/include/rapidjson/uri.h +++ b/include/rapidjson/uri.h @@ -34,18 +34,18 @@ public: typedef std::basic_string String; // Constructors - GenericUri() {} + GenericUri() : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_() {} - GenericUri(const String& uri) { + GenericUri(const String& uri) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_() { Parse(uri); } - GenericUri(const Ch* uri, SizeType len) { + GenericUri(const Ch* uri, SizeType len) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_() { Parse(String(uri, len)); } // Use with specializations of GenericValue - template GenericUri(const T& uri) { + template GenericUri(const T& uri) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_() { Parse(uri.template Get()); } @@ -236,13 +236,13 @@ private: //std::cout << "Final Temp: '" << temp.c_str() << "' Final Path: '" << path.c_str() << "'" << std::endl; } - String uri_ = String(); // Full uri - String base_ = String(); // Everything except fragment - String scheme_ = String(); // Includes the : - String auth_ = String(); // Includes the // - String path_ = String(); // Absolute if starts with / - String query_ = String(); // Includes the ? - String frag_ = String(); // Includes the # + String uri_; // Full uri + String base_; // Everything except fragment + String scheme_; // Includes the : + String auth_; // Includes the // + String path_; // Absolute if starts with / + String query_; // Includes the ? + String frag_; // Includes the # }; //! GenericUri for Value (UTF-8, default allocator). From 24b9b7e276995fd89bff424966bf2a9eea7624dd Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Thu, 11 Mar 2021 18:16:24 +0000 Subject: [PATCH 09/45] satisfy all compilers 2 --- include/rapidjson/schema.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 5f25b9b..933e0eb 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1898,7 +1898,7 @@ private: return resval; } // No match, continue looking - for (typename ValueType::ConstMemberIterator m = doc.MemberBegin(); m != doc.MemberEnd(); ++m) { + for (m = doc.MemberBegin(); m != doc.MemberEnd(); ++m) { if (m->value.GetType() == kObjectType || m->value.GetType() == kArrayType) { resval = FindId(m->value, finduri, resptr, localuri, full, here.Append(m->name.GetString(), m->name.GetStringLength(), allocator_)); } From bc026e3fb574411acc4f2be82177abb8b5b92202 Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Thu, 11 Mar 2021 18:25:10 +0000 Subject: [PATCH 10/45] satisfy all compilers 3 --- test/unittest/uritest.cpp | 146 +++++++++++++++++++------------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/test/unittest/uritest.cpp b/test/unittest/uritest.cpp index d8a78b8..b5eda29 100644 --- a/test/unittest/uritest.cpp +++ b/test/unittest/uritest.cpp @@ -31,13 +31,13 @@ using namespace rapidjson; TEST(Uri, Parse) { typedef std::basic_string String; - typedef GenericUri > Uri; + typedef GenericUri > UriType; MemoryPoolAllocator allocator; String s = "http://auth/path?query#frag"; Value v; v.SetString(s, allocator); - Uri u = Uri(v); + UriType u = UriType(v); EXPECT_TRUE(u.GetScheme() == "http:"); EXPECT_TRUE(u.GetAuth() == "//auth"); EXPECT_TRUE(u.GetPath() == "/path"); @@ -50,7 +50,7 @@ TEST(Uri, Parse) { s = "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"; v.SetString(s, allocator); - u = Uri(v); + u = UriType(v); EXPECT_TRUE(u.GetScheme() == "urn:"); EXPECT_TRUE(u.GetAuth() == ""); EXPECT_TRUE(u.GetPath() == "uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); @@ -62,7 +62,7 @@ TEST(Uri, Parse) { s = ""; v.SetString(s, allocator); - u = Uri(v); + u = UriType(v); EXPECT_TRUE(u.GetScheme() == ""); EXPECT_TRUE(u.GetAuth() == ""); EXPECT_TRUE(u.GetPath() == ""); @@ -72,7 +72,7 @@ TEST(Uri, Parse) { s = "http://auth/"; v.SetString(s, allocator); - u = Uri(v); + u = UriType(v); EXPECT_TRUE(u.GetScheme() == "http:"); EXPECT_TRUE(u.GetAuth() == "//auth"); EXPECT_TRUE(u.GetPath() == "/"); @@ -81,7 +81,7 @@ TEST(Uri, Parse) { EXPECT_TRUE(u.GetFrag() == ""); s = "/path/sub"; - u = Uri(s); + u = UriType(s); EXPECT_TRUE(u.GetScheme() == ""); EXPECT_TRUE(u.GetAuth() == ""); EXPECT_TRUE(u.GetPath() == "/path/sub"); @@ -91,7 +91,7 @@ TEST(Uri, Parse) { // absolute path gets normalized s = "/path/../sub/"; - u = Uri(s); + u = UriType(s); EXPECT_TRUE(u.GetScheme() == ""); EXPECT_TRUE(u.GetAuth() == ""); EXPECT_TRUE(u.GetPath() == "/sub/"); @@ -101,7 +101,7 @@ TEST(Uri, Parse) { // relative path does not s = "path/../sub"; - u = Uri(s); + u = UriType(s); EXPECT_TRUE(u.GetScheme() == ""); EXPECT_TRUE(u.GetAuth() == ""); EXPECT_TRUE(u.GetPath() == "path/../sub"); @@ -110,7 +110,7 @@ TEST(Uri, Parse) { EXPECT_TRUE(u.GetFrag() == ""); s = "http://auth#frag/stuff"; - u = Uri(s); + u = UriType(s); EXPECT_TRUE(u.GetScheme() == "http:"); EXPECT_TRUE(u.GetAuth() == "//auth"); EXPECT_TRUE(u.GetPath() == ""); @@ -120,7 +120,7 @@ TEST(Uri, Parse) { EXPECT_TRUE(u.Get() == s); s = "#frag/stuff"; - u = Uri(s); + u = UriType(s); EXPECT_TRUE(u.GetScheme() == ""); EXPECT_TRUE(u.GetAuth() == ""); EXPECT_TRUE(u.GetPath() == ""); @@ -130,7 +130,7 @@ TEST(Uri, Parse) { EXPECT_TRUE(u.Get() == s); Value::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'}; - u = Uri(c, 11); + u = UriType(c, 11); EXPECT_TRUE(String(u.GetString()) == "#frag/stuff"); EXPECT_TRUE(u.GetStringLength() == 11); EXPECT_TRUE(String(u.GetBaseString()) == ""); @@ -141,135 +141,135 @@ TEST(Uri, Parse) { TEST(Uri, Resolve) { typedef std::basic_string String; - typedef GenericUri > Uri; + typedef GenericUri > UriType; // ref is full uri - Uri base = Uri(String("http://auth/path/#frag")); - Uri ref = Uri(String("http://newauth/newpath#newfrag")); + UriType base = UriType(String("http://auth/path/#frag")); + UriType ref = UriType(String("http://newauth/newpath#newfrag")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://newauth/newpath#newfrag"); - base = Uri(String("/path/#frag")); - ref = Uri(String("http://newauth/newpath#newfrag")); + base = UriType(String("/path/#frag")); + ref = UriType(String("http://newauth/newpath#newfrag")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://newauth/newpath#newfrag"); // ref is alternate uri - base = Uri(String("http://auth/path/#frag")); - ref = Uri(String("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f")); + base = UriType(String("http://auth/path/#frag")); + ref = UriType(String("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f")); EXPECT_TRUE(ref.Resolve(base).Get() == "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); // ref is absolute path - base = Uri(String("http://auth/path/#")); - ref = Uri(String("/newpath#newfrag")); + base = UriType(String("http://auth/path/#")); + ref = UriType(String("/newpath#newfrag")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/newpath#newfrag"); // ref is relative path - base = Uri(String("http://auth/path/file.json#frag")); - ref = Uri(String("newfile.json#")); + base = UriType(String("http://auth/path/file.json#frag")); + ref = UriType(String("newfile.json#")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/path/newfile.json#"); - base = Uri(String("http://auth/path/file.json#frag/stuff")); - ref = Uri(String("newfile.json#newfrag/newstuff")); + base = UriType(String("http://auth/path/file.json#frag/stuff")); + ref = UriType(String("newfile.json#newfrag/newstuff")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/path/newfile.json#newfrag/newstuff"); - base = Uri(String("file.json")); - ref = Uri(String("newfile.json")); + base = UriType(String("file.json")); + ref = UriType(String("newfile.json")); EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); - base = Uri(String("file.json")); - ref = Uri(String("./newfile.json")); + base = UriType(String("file.json")); + ref = UriType(String("./newfile.json")); EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); - base = Uri(String("file.json")); - ref = Uri(String("parent/../newfile.json")); + base = UriType(String("file.json")); + ref = UriType(String("parent/../newfile.json")); EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); - base = Uri(String("file.json")); - ref = Uri(String("parent/./newfile.json")); + base = UriType(String("file.json")); + ref = UriType(String("parent/./newfile.json")); EXPECT_TRUE(ref.Resolve(base).Get() == "parent/newfile.json"); - base = Uri(String("file.json")); - ref = Uri(String("../../parent/.././newfile.json")); + base = UriType(String("file.json")); + ref = UriType(String("../../parent/.././newfile.json")); EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); - base = Uri(String("http://auth")); - ref = Uri(String("newfile.json")); + base = UriType(String("http://auth")); + ref = UriType(String("newfile.json")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/newfile.json"); // ref is fragment - base = Uri(String("#frag/stuff")); - ref = Uri(String("#newfrag/newstuff")); + base = UriType(String("#frag/stuff")); + ref = UriType(String("#newfrag/newstuff")); EXPECT_TRUE(ref.Resolve(base).Get() == "#newfrag/newstuff"); // test ref fragment always wins - base = Uri(String("/path#frag")); - ref = Uri(String("")); + base = UriType(String("/path#frag")); + ref = UriType(String("")); EXPECT_TRUE(ref.Resolve(base).Get() == "/path"); // Examples from RFC3896 - base = Uri(String("http://a/b/c/d;p?q")); - ref = Uri(String("g:h")); + base = UriType(String("http://a/b/c/d;p?q")); + ref = UriType(String("g:h")); EXPECT_TRUE(ref.Resolve(base).Get() == "g:h"); - ref = Uri(String("g")); + ref = UriType(String("g")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g"); - ref = Uri(String("./g")); + ref = UriType(String("./g")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g"); - ref = Uri(String("g/")); + ref = UriType(String("g/")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g/"); - ref = Uri(String("/g")); + ref = UriType(String("/g")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); - ref = Uri(String("//g")); + ref = UriType(String("//g")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://g"); - ref = Uri(String("?y")); + ref = UriType(String("?y")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?y"); - ref = Uri(String("g?y")); + ref = UriType(String("g?y")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g?y"); - ref = Uri(String("#s")); + ref = UriType(String("#s")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?q#s"); - ref = Uri(String("g#s")); + ref = UriType(String("g#s")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g#s"); - ref = Uri(String("g?y#s")); + ref = UriType(String("g?y#s")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g?y#s"); - ref = Uri(String(";x")); + ref = UriType(String(";x")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/;x"); - ref = Uri(String("g;x")); + ref = UriType(String("g;x")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g;x"); - ref = Uri(String("g;x?y#s")); + ref = UriType(String("g;x?y#s")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g;x?y#s"); - ref = Uri(String("")); + ref = UriType(String("")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?q"); - ref = Uri(String(".")); + ref = UriType(String(".")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/"); - ref = Uri(String("./")); + ref = UriType(String("./")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/"); - ref = Uri(String("..")); + ref = UriType(String("..")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/"); - ref = Uri(String("../")); + ref = UriType(String("../")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/"); - ref = Uri(String("../g")); + ref = UriType(String("../g")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/g"); - ref = Uri(String("../..")); + ref = UriType(String("../..")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/"); - ref = Uri(String("../../")); + ref = UriType(String("../../")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/"); - ref = Uri(String("../../g")); + ref = UriType(String("../../g")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); - ref = Uri(String("../../../g")); + ref = UriType(String("../../../g")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); - ref = Uri(String("../../../../g")); + ref = UriType(String("../../../../g")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); - ref = Uri(String("/./g")); + ref = UriType(String("/./g")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); - ref = Uri(String("/../g")); + ref = UriType(String("/../g")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); - ref = Uri(String("g.")); + ref = UriType(String("g.")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g."); - ref = Uri(String(".g")); + ref = UriType(String(".g")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/.g"); - ref = Uri(String("g..")); + ref = UriType(String("g..")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g.."); - ref = Uri(String("..g")); + ref = UriType(String("..g")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/..g"); - ref = Uri(String("g#s/../x")); + ref = UriType(String("g#s/../x")); EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g#s/../x"); } From 6e58a53f44164a51266772e39bfa6e6167975ac2 Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Thu, 20 May 2021 17:44:34 +0100 Subject: [PATCH 11/45] fix coverage --- test/unittest/pointertest.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/test/unittest/pointertest.cpp b/test/unittest/pointertest.cpp index c693b8f..39c7ec6 100644 --- a/test/unittest/pointertest.cpp +++ b/test/unittest/pointertest.cpp @@ -1,6 +1,7 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// Portions (C) Copyright IBM Corporation 2021 // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at @@ -676,14 +677,15 @@ TEST(Pointer, GetUri) { EXPECT_TRUE((Pointer("/jbo").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); EXPECT_TRUE((Pointer("/jbo/child").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); // id not string + EXPECT_TRUE((Pointer("/abc").GetUri(d, Pointer::UriType(doc)).Get()) == Pointer::UriType().Get()); // Out of boundary size_t unresolvedTokenIndex; - EXPECT_TRUE((Pointer("/foo/3").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == ""); // Out of boundary + EXPECT_TRUE((Pointer("/foo/3").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == Pointer::UriType().Get()); // Out of boundary EXPECT_EQ(1u, unresolvedTokenIndex); - EXPECT_TRUE((Pointer("/foo/a").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == ""); // "/foo" is an array, cannot query by "a" + EXPECT_TRUE((Pointer("/foo/a").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == Pointer::UriType().Get()); // "/foo" is an array, cannot query by "a" EXPECT_EQ(1u, unresolvedTokenIndex); - EXPECT_TRUE((Pointer("/foo/0/0").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == ""); // "/foo/0" is an string, cannot further query + EXPECT_TRUE((Pointer("/foo/0/0").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == Pointer::UriType().Get()); // "/foo/0" is an string, cannot further query EXPECT_EQ(2u, unresolvedTokenIndex); - EXPECT_TRUE((Pointer("/foo/0/a").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == ""); // "/foo/0" is an string, cannot further query + EXPECT_TRUE((Pointer("/foo/0/a").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == Pointer::UriType().Get()); // "/foo/0" is an string, cannot further query EXPECT_EQ(2u, unresolvedTokenIndex); Pointer::Token tokens[] = { { "foo ...", 3, kPointerInvalidIndex } }; @@ -706,7 +708,8 @@ TEST(Pointer, Get) { EXPECT_EQ(&d["k\"l"], Pointer("/k\"l").Get(d)); EXPECT_EQ(&d[" "], Pointer("/ ").Get(d)); EXPECT_EQ(&d["m~n"], Pointer("/m~0n").Get(d)); - EXPECT_TRUE(Pointer("/abc").Get(d) == 0); + + EXPECT_TRUE(Pointer("/abc").Get(d) == 0); // Out of boundary size_t unresolvedTokenIndex; EXPECT_TRUE(Pointer("/foo/2").Get(d, &unresolvedTokenIndex) == 0); // Out of boundary EXPECT_EQ(1u, unresolvedTokenIndex); From 494447b731b12160ac60a76a738d106863680129 Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Fri, 21 May 2021 15:55:11 +0100 Subject: [PATCH 12/45] remove copyright & debug statements --- include/rapidjson/schema.h | 1 - include/rapidjson/uri.h | 4 ---- test/unittest/pointertest.cpp | 1 - 3 files changed, 6 deletions(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 933e0eb..791aca5 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1,7 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available-> // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> -// Portions (C) Copyright IBM Corporation 2021 // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License-> You may obtain a copy of the License at diff --git a/include/rapidjson/uri.h b/include/rapidjson/uri.h index e72976d..98b0a15 100644 --- a/include/rapidjson/uri.h +++ b/include/rapidjson/uri.h @@ -110,7 +110,6 @@ public: } base_ = scheme_ + auth_ + path_ + query_; uri_ = base_ + frag_; - //std::cout << " Resolved uri: " << uri_ << std::endl; return *this; } @@ -196,7 +195,6 @@ private: } base_ = scheme_ + auth_ + path_ + query_; uri_ = base_ + frag_; - //std::cout << " Parsed uri: " << "s: " << scheme_.c_str() << " a: " << auth_.c_str() << " p: " << path_.c_str() << " q: " << query_.c_str() << " f: " << frag_.c_str() << std::endl; } // Remove . and .. segments from a path @@ -209,7 +207,6 @@ private: std::size_t pos = 0; // Loop through each path segment while (pos != std::string::npos) { - //std::cout << "Temp: '" << temp.c_str() << "' Path: '" << path.c_str() << "'" << std::endl; pos = temp.find_first_of(slash); // Get next segment String seg = temp.substr(0, pos); @@ -233,7 +230,6 @@ private: // Move to next segment if not at end if (pos != std::string::npos) temp = temp.substr(pos + 1); } - //std::cout << "Final Temp: '" << temp.c_str() << "' Final Path: '" << path.c_str() << "'" << std::endl; } String uri_; // Full uri diff --git a/test/unittest/pointertest.cpp b/test/unittest/pointertest.cpp index 39c7ec6..a835af4 100644 --- a/test/unittest/pointertest.cpp +++ b/test/unittest/pointertest.cpp @@ -1,7 +1,6 @@ // Tencent is pleased to support the open source community by making RapidJSON available. // // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. -// Portions (C) Copyright IBM Corporation 2021 // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at From 28bcbd3f3578aa3890795dec5a97570d59d9256a Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Tue, 8 Jun 2021 10:53:10 +0100 Subject: [PATCH 13/45] make std::string optional --- include/rapidjson/internal/strfunc.h | 14 + include/rapidjson/pointer.h | 5 +- include/rapidjson/schema.h | 15 +- include/rapidjson/uri.h | 552 +++++++++++++------ test/unittest/pointertest.cpp | 39 +- test/unittest/uritest.cpp | 782 +++++++++++++++++++-------- 6 files changed, 1003 insertions(+), 404 deletions(-) diff --git a/include/rapidjson/internal/strfunc.h b/include/rapidjson/internal/strfunc.h index baecb6c..b698a8f 100644 --- a/include/rapidjson/internal/strfunc.h +++ b/include/rapidjson/internal/strfunc.h @@ -45,6 +45,20 @@ inline SizeType StrLen(const wchar_t* s) { return SizeType(std::wcslen(s)); } +//! Custom strcmpn() which works on different character types. +/*! \tparam Ch Character type (e.g. char, wchar_t, short) + \param s1 Null-terminated input string. + \param s2 Null-terminated input string. + \return 0 if equal +*/ +template +inline int StrCmp(const Ch* s1, const Ch* s2) { + RAPIDJSON_ASSERT(s1 != 0); + RAPIDJSON_ASSERT(s2 != 0); + while(*s1 && (*s1 == *s2)) { s1++; s2++; } + return static_cast(*s1) < static_cast(*s2) ? -1 : static_cast(*s1) > static_cast(*s2); +} + //! Returns number of code points in a encoded string. template bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { diff --git a/include/rapidjson/pointer.h b/include/rapidjson/pointer.h index b5e952b..96f33fa 100644 --- a/include/rapidjson/pointer.h +++ b/include/rapidjson/pointer.h @@ -554,8 +554,7 @@ public: // See if we have an id, and if so resolve with the current base typename ValueType::MemberIterator m = v->FindMember(kIdValue); if (m != v->MemberEnd() && (m->value).IsString()) { - UriType here = UriType(m->value); - here.Resolve(base); + UriType here = UriType(m->value, allocator_).Resolve(base, allocator_); base = here; } m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); @@ -576,7 +575,7 @@ public: // Error: unresolved token if (unresolvedTokenIndex) *unresolvedTokenIndex = static_cast(t - tokens_); - return UriType(); + return UriType(allocator_); } return base; } diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index 791aca5..a48288b 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -494,9 +494,8 @@ public: // If we have an id property, resolve it with the in-scope id if (const ValueType* v = GetMember(value, GetIdString())) { if (v->IsString()) { - UriType local = UriType(*v); - local.Resolve(id_); - id_ = local; + UriType local(*v, allocator); + id_ = local.Resolve(id_, allocator); } } @@ -1659,7 +1658,7 @@ public: Ch noUri[1] = {0}; uri_.SetString(uri ? uri : noUri, uriLength, *allocator_); - docId_ = UriType(uri_); + docId_ = UriType(uri_, allocator_); typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, docId_); @@ -1792,8 +1791,7 @@ private: if (len > 0) { // First resolve $ref against the in-scope id UriType scopeId = id; - UriType ref = UriType(itr->value); - ref.Resolve(scopeId); + UriType ref = UriType(itr->value, allocator_).Resolve(scopeId, allocator_); // See if the resolved $ref minus the fragment matches a resolved id in this document // Search from the root. Returns the subschema in the document and its absolute JSON pointer. PointerType basePointer = PointerType(); @@ -1851,7 +1849,7 @@ private: // See if the fragment matches an id in this document. // Search from the base we just established. Returns the subschema in the document and its absolute JSON pointer. PointerType pointer = PointerType(); - if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBase()), true, basePointer)) { + if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBaseString(), ref.GetBaseStringLength(), allocator_), true, basePointer)) { if (!IsCyclicRef(pointer)) { //GenericStringBuffer sb; //pointer.StringifyUriFragment(sb); @@ -1887,8 +1885,7 @@ private: // Establish the base URI of this object typename ValueType::ConstMemberIterator m = doc.FindMember(SchemaType::GetIdString()); if (m != doc.MemberEnd() && m->value.GetType() == kStringType) { - localuri = UriType(m->value); - localuri.Resolve(baseuri); + localuri = UriType(m->value, allocator_).Resolve(baseuri, allocator_); } // See if it matches if (localuri.Match(finduri, full)) { diff --git a/include/rapidjson/uri.h b/include/rapidjson/uri.h index 98b0a15..640d793 100644 --- a/include/rapidjson/uri.h +++ b/include/rapidjson/uri.h @@ -1,20 +1,22 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// (C) Copyright IBM Corporation 2021 // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #ifndef RAPIDJSON_URI_H_ #define RAPIDJSON_URI_H_ +#include "internal/strfunc.h" + #if defined(__clang__) RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(c++98-compat) @@ -27,222 +29,462 @@ RAPIDJSON_NAMESPACE_BEGIN /////////////////////////////////////////////////////////////////////////////// // GenericUri -template -class GenericUri { -public: + template + class GenericUri { + public: typedef typename ValueType::Ch Ch; +#if RAPIDJSON_HAS_STDSTRING typedef std::basic_string String; +#endif - // Constructors - GenericUri() : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_() {} - - GenericUri(const String& uri) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_() { - Parse(uri); + //! Constructors + GenericUri(Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + //std::cout << "Default constructor" << std::endl; } - GenericUri(const Ch* uri, SizeType len) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_() { - Parse(String(uri, len)); + GenericUri(const Ch* uri, SizeType len, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + Parse(uri, len); + } + + GenericUri(const Ch* uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + Parse(uri, internal::StrLen(uri)); } // Use with specializations of GenericValue - template GenericUri(const T& uri) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_() { - Parse(uri.template Get()); + template GenericUri(const T& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + const Ch* u = uri.template Get(); // TypeHelper from document.h + Parse(u, internal::StrLen(u)); } - // Getters - const String& Get() const { return uri_; } +#if RAPIDJSON_HAS_STDSTRING + GenericUri(const String& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + Parse(uri.c_str(), internal::StrLen(uri.c_str())); + } +#endif - // Use with specializations of GenericValue - template void Get(T& uri, Allocator& allocator) { - uri.template Set(this->Get(), allocator); + //! Copy constructor + GenericUri(const GenericUri& rhs) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(rhs.allocator_), ownAllocator_() { + //std::cout << "Copy constructor" << std::endl; + *this = rhs; } - const String& GetBase() const { return base_; } - const String& GetScheme() const { return scheme_; } - const String& GetAuth() const { return auth_; } - const String& GetPath() const { return path_; } - const String& GetQuery() const { return query_; } - const String& GetFrag() const { return frag_; } + //! Copy constructor + GenericUri(const GenericUri& rhs, Allocator* allocator) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + //std::cout << "Copy constructor" << std::endl; + *this = rhs; + } - const Ch* GetString() const { return uri_.c_str(); } - SizeType GetStringLength() const { return static_cast(uri_.length()); } + //! Destructor. + ~GenericUri() { + //std::cout << "Destructor" << std::endl; + Free(); + RAPIDJSON_DELETE(ownAllocator_); + } - const Ch* GetBaseString() const { return base_.c_str(); } - SizeType GetBaseStringLength() const { return static_cast(base_.length()); } - - const Ch* GetFragString() const { return frag_.c_str(); } - SizeType GetFragStringLength() const { return static_cast(frag_.length()); } - - // Resolve this URI against another URI in accordance with URI resolution rules at - // https://tools.ietf.org/html/rfc3986 - // Use for resolving an id or $ref with an in-scope id. - // This URI is updated in place where needed from the base URI. - GenericUri& Resolve(const GenericUri& uri) { - if (!scheme_.empty()) { - // Use all of this URI - RemoveDotSegments(path_); - } else { - if (!auth_.empty()) { - RemoveDotSegments(path_); - } else { - if (path_.empty()) { - path_ = uri.GetPath(); - if (query_.empty()) { - query_ = uri.GetQuery(); - } - } else { - static const String slash = GetSlashString().GetString(); - if (path_.find(slash) == 0) { - // Absolute path - replace all the path - RemoveDotSegments(path_); - } else { - // Relative path - append to path after last slash - String p; - if (!uri.GetAuth().empty() && uri.GetPath().empty()) p = slash; - std::size_t lastslashpos = uri.GetPath().find_last_of(slash); - path_ = p + uri.GetPath().substr(0, lastslashpos + 1) + path_; - RemoveDotSegments(path_); - } - } - auth_ = uri.GetAuth(); - } - scheme_ = uri.GetScheme(); + //! Assignment operator + GenericUri& operator=(const GenericUri& rhs) { + //std::cout << "Operator=" << std::endl; + if (this != &rhs) { + // Do not delete ownAllocator + Free(); + Allocate(rhs.GetStringLength()); + auth_ = CopyPart(scheme_, rhs.scheme_, rhs.GetSchemeStringLength()); + path_ = CopyPart(auth_, rhs.auth_, rhs.GetAuthStringLength()); + query_ = CopyPart(path_, rhs.path_, rhs.GetPathStringLength()); + frag_ = CopyPart(query_, rhs.query_, rhs.GetQueryStringLength()); + base_ = CopyPart(frag_, rhs.frag_, rhs.GetFragStringLength()); + uri_ = CopyPart(base_, rhs.base_, rhs.GetBaseStringLength()); + CopyPart(uri_, rhs.uri_, rhs.GetStringLength()); + //std::wcout << L" Assignment uri: " << uri_ << ", length: " << GetStringLength() << std::endl; } - base_ = scheme_ + auth_ + path_ + query_; - uri_ = base_ + frag_; return *this; } + //! Getters + // Use with specializations of GenericValue + template void Get(T& uri, Allocator& allocator) { + uri.template Set(this->GetString(), allocator); // TypeHelper from document.h + } + + const Ch* GetString() const { return uri_; } + SizeType GetStringLength() const { return internal::StrLen(uri_); } + const Ch* GetBaseString() const { return base_; } + SizeType GetBaseStringLength() const { return internal::StrLen(base_); } + const Ch* GetSchemeString() const { return scheme_; } + SizeType GetSchemeStringLength() const { return internal::StrLen(scheme_); } + const Ch* GetAuthString() const { return auth_; } + SizeType GetAuthStringLength() const { return internal::StrLen(auth_); } + const Ch* GetPathString() const { return path_; } + SizeType GetPathStringLength() const { return internal::StrLen(path_); } + const Ch* GetQueryString() const { return query_; } + SizeType GetQueryStringLength() const { return internal::StrLen(query_); } + const Ch* GetFragString() const { return frag_; } + SizeType GetFragStringLength() const { return internal::StrLen(frag_); } + +#if RAPIDJSON_HAS_STDSTRING + static String Get(const GenericUri& uri) { return String(uri.GetString(), uri.GetStringLength()); } + static String GetBase(const GenericUri& uri) { return String(uri.GetBaseString(), uri.GetBaseStringLength()); } + static String GetScheme(const GenericUri& uri) { return String(uri.GetSchemeString(), uri.GetSchemeStringLength()); } + static String GetAuth(const GenericUri& uri) { return String(uri.GetAuthString(), uri.GetAuthStringLength()); } + static String GetPath(const GenericUri& uri) { return String(uri.GetPathString(), uri.GetPathStringLength()); } + static String GetQuery(const GenericUri& uri) { return String(uri.GetQueryString(), uri.GetQueryStringLength()); } + static String GetFrag(const GenericUri& uri) { return String(uri.GetFragString(), uri.GetFragStringLength()); } +#endif + + //! Equality operators + bool operator==(const GenericUri& rhs) const { + return Match(rhs, true); + } + + bool operator!=(const GenericUri& rhs) const { + return !Match(rhs, true); + } + bool Match(const GenericUri& uri, bool full) const { - if (full) - return uri_ == uri.Get(); - else - return base_ == uri.GetBase(); + Ch* s1; + Ch* s2; + if (full) { + s1 = uri_; + s2 = uri.uri_; + } else { + s1 = base_; + s2 = uri.base_; + } + if (s1 == s2) return true; + if (s1 == 0 || s2 == 0) return false; + return internal::StrCmp(s1, s2) == 0; } - // Generate functions for string literal according to Ch -#define RAPIDJSON_STRING_(name, ...) \ - static const ValueType& Get##name##String() {\ - static const Ch s[] = { __VA_ARGS__, '\0' };\ - static const ValueType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1));\ - return v;\ + //! Resolve this URI against another (base) URI in accordance with URI resolution rules. + // See https://tools.ietf.org/html/rfc3986 + // Use for resolving an id or $ref with an in-scope id. + // Returns a new GenericUri for the resolved URI. + GenericUri Resolve(const GenericUri& baseuri, Allocator* allocator = 0) { + //std::cout << "Resolve" << std::endl; + GenericUri resuri; + resuri.allocator_ = allocator; + // Ensure enough space for combining paths + resuri.Allocate(GetStringLength() + baseuri.GetStringLength() + 1); // + 1 for joining slash + + if (!(GetSchemeStringLength() == 0)) { + // Use all of this URI + resuri.auth_ = CopyPart(resuri.scheme_, scheme_, GetSchemeStringLength()); + resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength()); + resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + resuri.RemoveDotSegments(); + } else { + // Use the base scheme + resuri.auth_ = CopyPart(resuri.scheme_, baseuri.scheme_, baseuri.GetSchemeStringLength()); + if (!(GetAuthStringLength() == 0)) { + // Use this auth, path, query + resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength()); + resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + resuri.RemoveDotSegments(); + } else { + // Use the base auth + resuri.path_ = CopyPart(resuri.auth_, baseuri.auth_, baseuri.GetAuthStringLength()); + if (GetPathStringLength() == 0) { + // Use the base path + resuri.query_ = CopyPart(resuri.path_, baseuri.path_, baseuri.GetPathStringLength()); + if (GetQueryStringLength() == 0) { + // Use the base query + resuri.frag_ = CopyPart(resuri.query_, baseuri.query_, baseuri.GetQueryStringLength()); + } else { + // Use this query + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + } + } else { + if (path_[0] == '/') { + // Absolute path - use all of this path + resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); + resuri.RemoveDotSegments(); + } else { + // Relative path - append this path to base path after base path's last slash + size_t pos = 0; + if (!(baseuri.GetAuthStringLength() == 0) && baseuri.GetPathStringLength() == 0) { + resuri.path_[pos] = '/'; + pos++; + } + size_t lastslashpos = baseuri.GetPathStringLength(); + while (lastslashpos > 0) { + if (baseuri.path_[lastslashpos - 1] == '/') break; + lastslashpos--; + } + std::memcpy(&resuri.path_[pos], baseuri.path_, lastslashpos * sizeof(Ch)); + pos += lastslashpos; + resuri.query_ = CopyPart(&resuri.path_[pos], path_, GetPathStringLength()); + resuri.RemoveDotSegments(); + } + // Use this query + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + } + } + } + // Always use this frag + resuri.base_ = CopyPart(resuri.frag_, frag_, GetFragStringLength()); + //std::wcout << L" Resolved uri: " << L"s: " << resuri.scheme_ << L" a: " << resuri.auth_ << L" p: " << resuri.path_ << L" q: " << resuri.query_ << L" f: " << resuri.frag_ << std::endl; + + // Re-constitute base_ and uri_ + resuri.SetBase(); + resuri.uri_ = resuri.base_ + resuri.GetBaseStringLength() + 1; + resuri.SetUri(); + //std::wcout << L" Resolved rebuilt uri: " << resuri.uri_ << L", length: " << resuri.GetStringLength() << std::endl; + return resuri; } - RAPIDJSON_STRING_(SchemeEnd, ':') - RAPIDJSON_STRING_(AuthStart, '/', '/') - RAPIDJSON_STRING_(QueryStart, '?') - RAPIDJSON_STRING_(FragStart, '#') - RAPIDJSON_STRING_(Slash, '/') - RAPIDJSON_STRING_(Dot, '.') - -#undef RAPIDJSON_STRING_ + //! Get the allocator of this GenericUri. + Allocator& GetAllocator() { return *allocator_; } private: - // Parse a URI into constituent scheme, authority, path, query, fragment + // Allocate memory for a URI + // Returns total amount allocated + std::size_t Allocate(std::size_t len) { + //std::cout << "Allocate" << std::endl; + // Create own allocator if user did not supply. + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + // Allocate one block containing each part of the URI (5) plus base plus full URI, all null terminated. + // Order: scheme, auth, path, query, frag, base, uri + size_t total = (3 * len + 7) * sizeof(Ch); + scheme_ = static_cast(allocator_->Malloc(total)); + *scheme_ = '\0'; + auth_ = scheme_ + 1; + *auth_ = '\0'; + path_ = auth_ + 1; + *path_ = '\0'; + query_ = path_ + 1; + *query_ = '\0'; + frag_ = query_ + 1; + *frag_ = '\0'; + base_ = frag_ + 1; + *base_ = '\0'; + uri_ = base_ + 1; + *uri_ = '\0'; + //std::cout << " Allocating " << total << std::endl; + return total; + } + + // Free memory for a URI + void Free() { + //std::cout << "Free" << std::endl; + if (scheme_) { + //std::cout << " Freeing" << std::endl; + Allocator::Free(scheme_); + scheme_ = 0; + } + } + + // Parse a URI into constituent scheme, authority, path, query, & fragment parts // Supports URIs that match regex ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? as per // https://tools.ietf.org/html/rfc3986 - void Parse(const String& uri) { + void Parse(const Ch* uri, std::size_t len) { + //std::cout << "Parse" << std::endl; std::size_t start = 0, pos1 = 0, pos2 = 0; - const std::size_t len = uri.length(); - static const String schemeEnd = GetSchemeEndString().GetString(); - static const String authStart = GetAuthStartString().GetString(); - static const String pathStart = GetSlashString().GetString(); - static const String queryStart = GetQueryStartString().GetString(); - static const String fragStart = GetFragStartString().GetString(); + Allocate(len); + // Look for scheme ([^:/?#]+):)? if (start < len) { - pos1 = uri.find(schemeEnd); - if (pos1 != std::string::npos) { - pos2 = uri.find_first_of(pathStart + queryStart + fragStart); + while (pos1 < len) { + if (uri[pos1] == ':') break; + pos1++; + } + if (pos1 != len) { + while (pos2 < len) { + if (uri[pos2] == '/') break; + if (uri[pos2] == '?') break; + if (uri[pos2] == '#') break; + pos2++; + } if (pos1 < pos2) { - pos1 += schemeEnd.length(); - scheme_ = uri.substr(start, pos1); + pos1++; + std::memcpy(scheme_, &uri[start], pos1 * sizeof(Ch)); + scheme_[pos1] = '\0'; start = pos1; } } } // Look for auth (//([^/?#]*))? + auth_ = scheme_ + GetSchemeStringLength() + 1; + *auth_ = '\0'; if (start < len) { - pos1 = uri.find(authStart, start); + pos1 = start; + while (pos1 < len) { + if (uri[pos1] == '/' && uri[pos1 + 1] == '/') break; + pos1++; + } if (pos1 == start) { - pos2 = uri.find_first_of(pathStart + queryStart + fragStart, start + authStart.length()); - auth_ = uri.substr(start, pos2 - start); + pos2 = start + 2; + while (pos2 < len) { + if (uri[pos2] == '/') break; + if (uri[pos2] == '?') break; + if (uri[pos2] == '#') break; + pos2++; + } + std::memcpy(auth_, &uri[start], (pos2 - start) * sizeof(Ch)); + auth_[pos2 - start] = '\0'; start = pos2; } } // Look for path ([^?#]*) + path_ = auth_ + GetAuthStringLength() + 1; + *path_ = '\0'; if (start < len) { - pos2 = uri.find_first_of(queryStart + fragStart, start); + pos2 = start; + while (pos2 < len) { + if (uri[pos2] == '?') break; + if (uri[pos2] == '#') break; + pos2++; + } if (start != pos2) { - path_ = uri.substr(start, pos2 - start); - if (path_.find(pathStart) == 0) { // absolute path - normalize - RemoveDotSegments(path_); - } + std::memcpy(path_, &uri[start], (pos2 - start) * sizeof(Ch)); + path_[pos2 - start] = '\0'; + if (path_[0] == '/') + RemoveDotSegments(); // absolute path - normalize start = pos2; } } // Look for query (\?([^#]*))? + query_ = path_ + GetPathStringLength() + 1; + *query_ = '\0'; if (start < len) { - pos2 = uri.find(fragStart, start); + pos2 = start; + while (pos2 < len) { + if (uri[pos2] == '#') break; + pos2++; + } if (start != pos2) { - query_ = uri.substr(start, pos2 - start); + std::memcpy(query_, &uri[start], (pos2 - start) * sizeof(Ch)); + query_[pos2 - start] = '\0'; start = pos2; } } // Look for fragment (#(.*))? + frag_ = query_ + GetQueryStringLength() + 1; + *frag_ = '\0'; if (start < len) { - frag_ = uri.substr(start); + std::memcpy(frag_, &uri[start], (len - start) * sizeof(Ch)); + frag_[len - start] = '\0'; } - base_ = scheme_ + auth_ + path_ + query_; - uri_ = base_ + frag_; + //std::wcout << L" Parsed uri: " << L"s: " << scheme_ << L" a: " << auth_ << L" p: " << path_ << L" q: " << query_ << L" f: " << frag_ << std::endl; + + // Re-constitute base_ and uri_ + base_ = frag_ + GetFragStringLength() + 1; + SetBase(); + uri_ = base_ + GetBaseStringLength() + 1; + SetUri(); + //std::wcout << L" Rebuilt uri: " << uri_ << L", length: " << GetStringLength() << std::endl; } - // Remove . and .. segments from a path + // Reconstitute base + void SetBase() { + Ch* next = base_; + std::memcpy(next, scheme_, GetSchemeStringLength() * sizeof(Ch)); + next+= GetSchemeStringLength(); + std::memcpy(next, auth_, GetAuthStringLength() * sizeof(Ch)); + next+= GetAuthStringLength(); + std::memcpy(next, path_, GetPathStringLength() * sizeof(Ch)); + next+= GetPathStringLength(); + std::memcpy(next, query_, GetQueryStringLength() * sizeof(Ch)); + next+= GetQueryStringLength(); + *next = '\0'; + } + + // Reconstitute uri + void SetUri() { + Ch* next = uri_; + std::memcpy(next, base_, GetBaseStringLength() * sizeof(Ch)); + next+= GetBaseStringLength(); + std::memcpy(next, frag_, GetFragStringLength() * sizeof(Ch)); + next+= GetFragStringLength(); + *next = '\0'; + } + + // Copy a part from one GenericUri to another + // Return the pointer to the next part to be copied to + Ch* CopyPart(Ch* to, Ch* from, std::size_t len) { + RAPIDJSON_ASSERT(to != 0); + RAPIDJSON_ASSERT(from != 0); + std::memcpy(to, from, len * sizeof(Ch)); + to[len] = '\0'; + Ch* next = to + len + 1; + return next; + } + + // Remove . and .. segments from the path_ member. // https://tools.ietf.org/html/rfc3986 - void RemoveDotSegments(String& path) { - String temp = path; - path.clear(); - static const String slash = GetSlashString().GetString(); - static const String dot = GetDotString().GetString(); - std::size_t pos = 0; - // Loop through each path segment - while (pos != std::string::npos) { - pos = temp.find_first_of(slash); - // Get next segment - String seg = temp.substr(0, pos); - if (seg == dot) { - // Discard . segment - } else if (seg == dot + dot) { - // Backup a .. segment - // We expect to find a previously added slash at the end or nothing - std::size_t pos1 = path.find_last_of(slash); - // Make sure we don't go beyond the start - if (pos1 != std::string::npos && pos1 != 0) { - // Find the next to last slash and back up to it - pos1 = path.find_last_of(slash, pos1 - 1); - path = path.substr(0, pos1 + 1); - } - } else { - // Copy segment and add slash if not at end - path += seg; - if (pos != std::string::npos) path += slash; + // This is done in place as we are only removing segments. + void RemoveDotSegments() { + std::size_t pathlen = GetPathStringLength(); + std::size_t pathpos = 0; // Position in path_ + std::size_t newpos = 0; // Position in new path_ + + // Loop through each segment in original path_ + while (pathpos < pathlen) { + // Get next segment, bounded by '/' or end + size_t slashpos = 0; + while ((pathpos + slashpos) < pathlen) { + if (path_[pathpos + slashpos] == '/') break; + slashpos++; } - // Move to next segment if not at end - if (pos != std::string::npos) temp = temp.substr(pos + 1); + // Check for .. and . segments + if (slashpos == 2 && path_[pathpos] == '.' && path_[pathpos + 1] == '.') { + // Backup a .. segment in the new path_ + // We expect to find a previously added slash at the end or nothing + //std::wcout << L" Path - backing up .. in " << path_ << std::endl; + size_t lastslashpos = newpos; + while (lastslashpos > 0) { + if (path_[lastslashpos - 1] == '/') break; + lastslashpos--; + } + // Make sure we don't go beyond the start segment + if (lastslashpos > 1) { + // Find the next to last slash and back up to it + lastslashpos--; + while (lastslashpos > 0) { + if (path_[lastslashpos - 1] == '/') break; + lastslashpos--; + } + // Set the new path_ position + newpos = lastslashpos; + } + } else if (slashpos == 1 && path_[pathpos] == '.') { + // Discard . segment, leaves new path_ unchanged + //std::wcout << L" Path - removing . in " << path_ << std::endl; + } else { + // Move any other kind of segment to the new path_ + RAPIDJSON_ASSERT(newpos <= pathpos); + std::memmove(&path_[newpos], &path_[pathpos], slashpos * sizeof(Ch)); + newpos += slashpos; + // Add slash if not at end + if ((pathpos + slashpos) < pathlen) { + path_[newpos] = '/'; + newpos++; + } + } + // Move to next segment + pathpos += slashpos + 1; } + path_[newpos] = '\0'; + //std::wcout << L" Normalized Path: " << path_ << ", length: " << GetPathStringLength() << std::endl; } - String uri_; // Full uri - String base_; // Everything except fragment - String scheme_; // Includes the : - String auth_; // Includes the // - String path_; // Absolute if starts with / - String query_; // Includes the ? - String frag_; // Includes the # -}; + Ch* uri_; // Everything + Ch* base_; // Everything except fragment + Ch* scheme_; // Includes the : + Ch* auth_; // Includes the // + Ch* path_; // Absolute if starts with / + Ch* query_; // Includes the ? + Ch* frag_; // Includes the # + + Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. + Allocator* ownAllocator_; //!< Allocator owned by this Uri. + }; //! GenericUri for Value (UTF-8, default allocator). -typedef GenericUri Uri; + typedef GenericUri Uri; RAPIDJSON_NAMESPACE_END diff --git a/test/unittest/pointertest.cpp b/test/unittest/pointertest.cpp index a835af4..c00658c 100644 --- a/test/unittest/pointertest.cpp +++ b/test/unittest/pointertest.cpp @@ -659,36 +659,37 @@ static const char kJsonIds[] = "{\n" TEST(Pointer, GetUri) { - typedef std::basic_string String; Document d; d.Parse(kJsonIds); + Pointer::UriType doc("http://doc"); + Pointer::UriType root("http://doc/root/"); + Pointer::UriType empty = Pointer::UriType(); - String doc = String("http://doc"); - EXPECT_TRUE((Pointer("").GetUri(d, Pointer::UriType(doc)).Get()) == doc); - EXPECT_TRUE((Pointer("/foo").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); - EXPECT_TRUE((Pointer("/foo/0").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); - EXPECT_TRUE((Pointer("/foo/2").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); - EXPECT_TRUE((Pointer("/foo/2/child").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/inarray"); - EXPECT_TRUE((Pointer("/int").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); - EXPECT_TRUE((Pointer("/str").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); - EXPECT_TRUE((Pointer("/obj").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); - EXPECT_TRUE((Pointer("/obj/child").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/inobj"); - EXPECT_TRUE((Pointer("/jbo").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); - EXPECT_TRUE((Pointer("/jbo/child").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); // id not string + EXPECT_TRUE(Pointer("").GetUri(d, doc) == doc); + EXPECT_TRUE(Pointer("/foo").GetUri(d, doc) == root); + EXPECT_TRUE(Pointer("/foo/0").GetUri(d, doc) == root); + EXPECT_TRUE(Pointer("/foo/2").GetUri(d, doc) == root); + EXPECT_TRUE(Pointer("/foo/2/child").GetUri(d, doc) == Pointer::UriType("http://doc/root/inarray")); + EXPECT_TRUE(Pointer("/int").GetUri(d, doc) == root); + EXPECT_TRUE(Pointer("/str").GetUri(d, doc) == root); + EXPECT_TRUE(Pointer("/obj").GetUri(d, doc) == root); + EXPECT_TRUE(Pointer("/obj/child").GetUri(d, doc) == Pointer::UriType("http://doc/root/inobj")); + EXPECT_TRUE(Pointer("/jbo").GetUri(d, doc) == root); + EXPECT_TRUE(Pointer("/jbo/child").GetUri(d, doc) == root); // id not string - EXPECT_TRUE((Pointer("/abc").GetUri(d, Pointer::UriType(doc)).Get()) == Pointer::UriType().Get()); // Out of boundary + EXPECT_TRUE(Pointer("/abc").GetUri(d, doc) == empty); // Out of boundary size_t unresolvedTokenIndex; - EXPECT_TRUE((Pointer("/foo/3").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == Pointer::UriType().Get()); // Out of boundary + EXPECT_TRUE(Pointer("/foo/3").GetUri(d, doc, &unresolvedTokenIndex) == empty); // Out of boundary EXPECT_EQ(1u, unresolvedTokenIndex); - EXPECT_TRUE((Pointer("/foo/a").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == Pointer::UriType().Get()); // "/foo" is an array, cannot query by "a" + EXPECT_TRUE(Pointer("/foo/a").GetUri(d, doc, &unresolvedTokenIndex) == empty); // "/foo" is an array, cannot query by "a" EXPECT_EQ(1u, unresolvedTokenIndex); - EXPECT_TRUE((Pointer("/foo/0/0").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == Pointer::UriType().Get()); // "/foo/0" is an string, cannot further query + EXPECT_TRUE(Pointer("/foo/0/0").GetUri(d, doc, &unresolvedTokenIndex) == empty); // "/foo/0" is an string, cannot further query EXPECT_EQ(2u, unresolvedTokenIndex); - EXPECT_TRUE((Pointer("/foo/0/a").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == Pointer::UriType().Get()); // "/foo/0" is an string, cannot further query + EXPECT_TRUE(Pointer("/foo/0/a").GetUri(d, doc, &unresolvedTokenIndex) == empty); // "/foo/0" is an string, cannot further query EXPECT_EQ(2u, unresolvedTokenIndex); Pointer::Token tokens[] = { { "foo ...", 3, kPointerInvalidIndex } }; - EXPECT_TRUE((Pointer(tokens, 1).GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); + EXPECT_TRUE(Pointer(tokens, 1).GetUri(d, doc) == root); } TEST(Pointer, Get) { diff --git a/test/unittest/uritest.cpp b/test/unittest/uritest.cpp index b5eda29..936b831 100644 --- a/test/unittest/uritest.cpp +++ b/test/unittest/uritest.cpp @@ -1,15 +1,15 @@ // Tencent is pleased to support the open source community by making RapidJSON available. -// -// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// (C) Copyright IBM Corporation 2021 // // Licensed under the MIT License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // http://opensource.org/licenses/MIT // -// Unless required by applicable law or agreed to in writing, software distributed -// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #define RAPIDJSON_SCHEMA_VERBOSE 0 @@ -30,247 +30,593 @@ RAPIDJSON_DIAG_OFF(4822) // local class member function does not have a body using namespace rapidjson; TEST(Uri, Parse) { - typedef std::basic_string String; - typedef GenericUri > UriType; - MemoryPoolAllocator allocator; +typedef GenericUri > UriType; +MemoryPoolAllocator allocator; +Value v; +Value w; - String s = "http://auth/path?query#frag"; - Value v; - v.SetString(s, allocator); - UriType u = UriType(v); - EXPECT_TRUE(u.GetScheme() == "http:"); - EXPECT_TRUE(u.GetAuth() == "//auth"); - EXPECT_TRUE(u.GetPath() == "/path"); - EXPECT_TRUE(u.GetBase() == "http://auth/path?query"); - EXPECT_TRUE(u.GetQuery() == "?query"); - EXPECT_TRUE(u.GetFrag() == "#frag"); - Value w; - u.Get(w, allocator); - EXPECT_TRUE(*w.GetString() == *v.GetString()); +v.SetString("http://auth/path/xxx?query#frag", allocator); +UriType u = UriType(v, &allocator); +EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0); +EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0); +EXPECT_TRUE(StrCmp(u.GetPathString(), "/path/xxx") == 0); +EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth/path/xxx?query") == 0); +EXPECT_TRUE(StrCmp(u.GetQueryString(), "?query") == 0); +EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag") == 0); +u.Get(w, allocator); +EXPECT_TRUE(*w.GetString() == *v.GetString()); - s = "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"; - v.SetString(s, allocator); - u = UriType(v); - EXPECT_TRUE(u.GetScheme() == "urn:"); - EXPECT_TRUE(u.GetAuth() == ""); - EXPECT_TRUE(u.GetPath() == "uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); - EXPECT_TRUE(u.GetBase() == "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); - EXPECT_TRUE(u.GetQuery() == ""); - EXPECT_TRUE(u.GetFrag() == ""); - u.Get(w, allocator); - EXPECT_TRUE(*w.GetString() == *v.GetString()); +#if RAPIDJSON_HAS_STDSTRING +typedef std::basic_string String; +String str = "http://auth/path/xxx?query#frag"; +const UriType uri = UriType(str); +EXPECT_TRUE(UriType::GetScheme(uri) == "http:"); +EXPECT_TRUE(UriType::GetAuth(uri) == "//auth"); +EXPECT_TRUE(UriType::GetPath(uri) == "/path/xxx"); +EXPECT_TRUE(UriType::GetBase(uri) == "http://auth/path/xxx?query"); +EXPECT_TRUE(UriType::GetQuery(uri) == "?query"); +EXPECT_TRUE(UriType::GetFrag(uri) == "#frag"); +EXPECT_TRUE(UriType::Get(uri) == str); +#endif - s = ""; - v.SetString(s, allocator); - u = UriType(v); - EXPECT_TRUE(u.GetScheme() == ""); - EXPECT_TRUE(u.GetAuth() == ""); - EXPECT_TRUE(u.GetPath() == ""); - EXPECT_TRUE(u.GetBase() == ""); - EXPECT_TRUE(u.GetQuery() == ""); - EXPECT_TRUE(u.GetFrag() == ""); +v.SetString("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f", allocator); +u = UriType(v); +EXPECT_TRUE(StrCmp(u.GetSchemeString(), "urn:") == 0); +EXPECT_TRUE(u.GetAuthStringLength() == 0); +EXPECT_TRUE(StrCmp(u.GetPathString(), "uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); +EXPECT_TRUE(StrCmp(u.GetBaseString(), "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); +EXPECT_TRUE(u.GetQueryStringLength() == 0); +EXPECT_TRUE(u.GetFragStringLength() == 0); +u.Get(w, allocator); +EXPECT_TRUE(*w.GetString() == *v.GetString()); - s = "http://auth/"; - v.SetString(s, allocator); - u = UriType(v); - EXPECT_TRUE(u.GetScheme() == "http:"); - EXPECT_TRUE(u.GetAuth() == "//auth"); - EXPECT_TRUE(u.GetPath() == "/"); - EXPECT_TRUE(u.GetBase() == "http://auth/"); - EXPECT_TRUE(u.GetQuery() == ""); - EXPECT_TRUE(u.GetFrag() == ""); +v.SetString("", allocator); +u = UriType(v); +EXPECT_TRUE(u.GetSchemeStringLength() == 0); +EXPECT_TRUE(u.GetAuthStringLength() == 0); +EXPECT_TRUE(u.GetPathStringLength() == 0); +EXPECT_TRUE(u.GetBaseStringLength() == 0); +EXPECT_TRUE(u.GetQueryStringLength() == 0); +EXPECT_TRUE(u.GetFragStringLength() == 0); - s = "/path/sub"; - u = UriType(s); - EXPECT_TRUE(u.GetScheme() == ""); - EXPECT_TRUE(u.GetAuth() == ""); - EXPECT_TRUE(u.GetPath() == "/path/sub"); - EXPECT_TRUE(u.GetBase() == "/path/sub"); - EXPECT_TRUE(u.GetQuery() == ""); - EXPECT_TRUE(u.GetFrag() == ""); +v.SetString("http://auth/", allocator); +u = UriType(v); +EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0); +EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0); +EXPECT_TRUE(StrCmp(u.GetPathString(), "/") == 0); +EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth/") == 0); +EXPECT_TRUE(u.GetQueryStringLength() == 0); +EXPECT_TRUE(u.GetFragStringLength() == 0); - // absolute path gets normalized - s = "/path/../sub/"; - u = UriType(s); - EXPECT_TRUE(u.GetScheme() == ""); - EXPECT_TRUE(u.GetAuth() == ""); - EXPECT_TRUE(u.GetPath() == "/sub/"); - EXPECT_TRUE(u.GetBase() == "/sub/"); - EXPECT_TRUE(u.GetQuery() == ""); - EXPECT_TRUE(u.GetFrag() == ""); +u = UriType("/path/sub"); +EXPECT_TRUE(u.GetSchemeStringLength() == 0); +EXPECT_TRUE(u.GetAuthStringLength() == 0); +EXPECT_TRUE(StrCmp(u.GetPathString(), "/path/sub") == 0); +EXPECT_TRUE(StrCmp(u.GetBaseString(), "/path/sub") == 0); +EXPECT_TRUE(u.GetQueryStringLength() == 0); +EXPECT_TRUE(u.GetFragStringLength() == 0); - // relative path does not - s = "path/../sub"; - u = UriType(s); - EXPECT_TRUE(u.GetScheme() == ""); - EXPECT_TRUE(u.GetAuth() == ""); - EXPECT_TRUE(u.GetPath() == "path/../sub"); - EXPECT_TRUE(u.GetBase() == "path/../sub"); - EXPECT_TRUE(u.GetQuery() == ""); - EXPECT_TRUE(u.GetFrag() == ""); +// absolute path gets normalized +u = UriType("/path/../sub/"); +EXPECT_TRUE(u.GetSchemeStringLength() == 0); +EXPECT_TRUE(u.GetAuthStringLength() == 0); +EXPECT_TRUE(StrCmp(u.GetPathString(), "/sub/") == 0); +EXPECT_TRUE(StrCmp(u.GetBaseString(), "/sub/") == 0); +EXPECT_TRUE(u.GetQueryStringLength() == 0); +EXPECT_TRUE(u.GetFragStringLength() == 0); - s = "http://auth#frag/stuff"; - u = UriType(s); - EXPECT_TRUE(u.GetScheme() == "http:"); - EXPECT_TRUE(u.GetAuth() == "//auth"); - EXPECT_TRUE(u.GetPath() == ""); - EXPECT_TRUE(u.GetBase() == "http://auth"); - EXPECT_TRUE(u.GetQuery() == ""); - EXPECT_TRUE(u.GetFrag() == "#frag/stuff"); - EXPECT_TRUE(u.Get() == s); +// relative path does not +u = UriType("path/../sub"); +EXPECT_TRUE(u.GetSchemeStringLength() == 0); +EXPECT_TRUE(u.GetAuthStringLength() == 0); +EXPECT_TRUE(StrCmp(u.GetPathString(), "path/../sub") == 0); +EXPECT_TRUE(StrCmp(u.GetBaseString(), "path/../sub") == 0); +EXPECT_TRUE(u.GetQueryStringLength() == 0); +EXPECT_TRUE(u.GetFragStringLength() == 0); - s = "#frag/stuff"; - u = UriType(s); - EXPECT_TRUE(u.GetScheme() == ""); - EXPECT_TRUE(u.GetAuth() == ""); - EXPECT_TRUE(u.GetPath() == ""); - EXPECT_TRUE(u.GetBase() == ""); - EXPECT_TRUE(u.GetQuery() == ""); - EXPECT_TRUE(u.GetFrag() == "#frag/stuff"); - EXPECT_TRUE(u.Get() == s); +u = UriType("http://auth#frag/stuff"); +EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0); +EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0); +EXPECT_TRUE(u.GetPathStringLength() == 0); +EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth") == 0); +EXPECT_TRUE(u.GetQueryStringLength() == 0); +EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0); +EXPECT_TRUE(StrCmp(u.GetString(), "http://auth#frag/stuff") == 0); - Value::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'}; - u = UriType(c, 11); - EXPECT_TRUE(String(u.GetString()) == "#frag/stuff"); - EXPECT_TRUE(u.GetStringLength() == 11); - EXPECT_TRUE(String(u.GetBaseString()) == ""); - EXPECT_TRUE(u.GetBaseStringLength() == 0); - EXPECT_TRUE(String(u.GetFragString()) == "#frag/stuff"); - EXPECT_TRUE(u.GetFragStringLength() == 11); +const Value::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'}; +SizeType len = internal::StrLen(c); +u = UriType(c, len); +EXPECT_TRUE(StrCmp(u.GetString(), "#frag/stuff") == 0); +EXPECT_TRUE(u.GetStringLength() == len); +EXPECT_TRUE(StrCmp(u.GetBaseString(), "") == 0); +EXPECT_TRUE(u.GetBaseStringLength() == 0); +EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0); +EXPECT_TRUE(u.GetFragStringLength() == len); + +u = UriType(c); +EXPECT_TRUE(StrCmp(u.GetString(), "#frag/stuff") == 0); +EXPECT_TRUE(u.GetStringLength() == len); +EXPECT_TRUE(StrCmp(u.GetBaseString(), "") == 0); +EXPECT_TRUE(u.GetBaseStringLength() == 0); +EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0); +EXPECT_TRUE(u.GetFragStringLength() == len); +} + +TEST(Uri, Parse_UTF16) { +typedef GenericValue > Value; +typedef GenericUri > UriType; +MemoryPoolAllocator allocator; +Value v; +Value w; + +v.SetString(L"http://auth/path/xxx?query#frag", allocator); +UriType u = UriType(v, &allocator); +EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0); +EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0); +EXPECT_TRUE(StrCmp(u.GetPathString(), L"/path/xxx") == 0); +EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth/path/xxx?query") == 0); +EXPECT_TRUE(StrCmp(u.GetQueryString(), L"?query") == 0); +EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag") == 0); +u.Get(w, allocator); +EXPECT_TRUE(*w.GetString() == *v.GetString()); + +#if RAPIDJSON_HAS_STDSTRING +typedef std::basic_string String; +String str = L"http://auth/path/xxx?query#frag"; +const UriType uri = UriType(str); +EXPECT_TRUE(UriType::GetScheme(uri) == L"http:"); +EXPECT_TRUE(UriType::GetAuth(uri) == L"//auth"); +EXPECT_TRUE(UriType::GetPath(uri) == L"/path/xxx"); +EXPECT_TRUE(UriType::GetBase(uri) == L"http://auth/path/xxx?query"); +EXPECT_TRUE(UriType::GetQuery(uri) == L"?query"); +EXPECT_TRUE(UriType::GetFrag(uri) == L"#frag"); +EXPECT_TRUE(UriType::Get(uri) == str); +#endif + +v.SetString(L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f", allocator); +u = UriType(v); +EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"urn:") == 0); +EXPECT_TRUE(u.GetAuthStringLength() == 0); +EXPECT_TRUE(StrCmp(u.GetPathString(), L"uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); +EXPECT_TRUE(StrCmp(u.GetBaseString(), L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); +EXPECT_TRUE(u.GetQueryStringLength() == 0); +EXPECT_TRUE(u.GetFragStringLength() == 0); +u.Get(w, allocator); +EXPECT_TRUE(*w.GetString() == *v.GetString()); + +v.SetString(L"", allocator); +u = UriType(v); +EXPECT_TRUE(u.GetSchemeStringLength() == 0); +EXPECT_TRUE(u.GetAuthStringLength() == 0); +EXPECT_TRUE(u.GetPathStringLength() == 0); +EXPECT_TRUE(u.GetBaseStringLength() == 0); +EXPECT_TRUE(u.GetQueryStringLength() == 0); +EXPECT_TRUE(u.GetFragStringLength() == 0); + +v.SetString(L"http://auth/", allocator); +u = UriType(v); +EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0); +EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0); +EXPECT_TRUE(StrCmp(u.GetPathString(), L"/") == 0); +EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth/") == 0); +EXPECT_TRUE(u.GetQueryStringLength() == 0); +EXPECT_TRUE(u.GetFragStringLength() == 0); + +u = UriType(L"/path/sub"); +EXPECT_TRUE(u.GetSchemeStringLength() == 0); +EXPECT_TRUE(u.GetAuthStringLength() == 0); +EXPECT_TRUE(StrCmp(u.GetPathString(), L"/path/sub") == 0); +EXPECT_TRUE(StrCmp(u.GetBaseString(), L"/path/sub") == 0); +EXPECT_TRUE(u.GetQueryStringLength() == 0); +EXPECT_TRUE(u.GetFragStringLength() == 0); + +// absolute path gets normalized +u = UriType(L"/path/../sub/"); +EXPECT_TRUE(u.GetSchemeStringLength() == 0); +EXPECT_TRUE(u.GetAuthStringLength() == 0); +EXPECT_TRUE(StrCmp(u.GetPathString(), L"/sub/") == 0); +EXPECT_TRUE(StrCmp(u.GetBaseString(), L"/sub/") == 0); +EXPECT_TRUE(u.GetQueryStringLength() == 0); +EXPECT_TRUE(u.GetFragStringLength() == 0); + +// relative path does not +u = UriType(L"path/../sub"); +EXPECT_TRUE(u.GetSchemeStringLength() == 0); +EXPECT_TRUE(u.GetAuthStringLength() == 0); +EXPECT_TRUE(StrCmp(u.GetPathString(), L"path/../sub") == 0); +EXPECT_TRUE(StrCmp(u.GetBaseString(), L"path/../sub") == 0); +EXPECT_TRUE(u.GetQueryStringLength() == 0); +EXPECT_TRUE(u.GetFragStringLength() == 0); + +u = UriType(L"http://auth#frag/stuff"); +EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0); +EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0); +EXPECT_TRUE(u.GetPathStringLength() == 0); +EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth") == 0); +EXPECT_TRUE(u.GetQueryStringLength() == 0); +EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0); +EXPECT_TRUE(StrCmp(u.GetString(), L"http://auth#frag/stuff") == 0); + +const Value::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'}; +SizeType len = internal::StrLen(c); +u = UriType(c, len); +EXPECT_TRUE(StrCmp(u.GetString(), L"#frag/stuff") == 0); +EXPECT_TRUE(u.GetStringLength() == len); +EXPECT_TRUE(StrCmp(u.GetBaseString(), L"") == 0); +EXPECT_TRUE(u.GetBaseStringLength() == 0); +EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0); +EXPECT_TRUE(u.GetFragStringLength() == len); + +u = UriType(c); +EXPECT_TRUE(StrCmp(u.GetString(), L"#frag/stuff") == 0); +EXPECT_TRUE(u.GetStringLength() == len); +EXPECT_TRUE(StrCmp(u.GetBaseString(), L"") == 0); +EXPECT_TRUE(u.GetBaseStringLength() == 0); +EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0); +EXPECT_TRUE(u.GetFragStringLength() == len); } TEST(Uri, Resolve) { - typedef std::basic_string String; - typedef GenericUri > UriType; +typedef GenericUri UriType; +CrtAllocator allocator; - // ref is full uri - UriType base = UriType(String("http://auth/path/#frag")); - UriType ref = UriType(String("http://newauth/newpath#newfrag")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://newauth/newpath#newfrag"); +// ref is full uri +UriType base = UriType("http://auth/path/#frag"); +UriType ref = UriType("http://newauth/newpath#newfrag"); +UriType res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://newauth/newpath#newfrag") == 0); - base = UriType(String("/path/#frag")); - ref = UriType(String("http://newauth/newpath#newfrag")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://newauth/newpath#newfrag"); +base = UriType("/path/#frag", &allocator); +ref = UriType("http://newauth/newpath#newfrag", &allocator); +res = ref.Resolve(base, &allocator); +EXPECT_TRUE(StrCmp(res.GetString(), "http://newauth/newpath#newfrag") == 0); - // ref is alternate uri - base = UriType(String("http://auth/path/#frag")); - ref = UriType(String("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f")); - EXPECT_TRUE(ref.Resolve(base).Get() == "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); +// ref is alternate uri +base = UriType("http://auth/path/#frag"); +ref = UriType("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); - // ref is absolute path - base = UriType(String("http://auth/path/#")); - ref = UriType(String("/newpath#newfrag")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/newpath#newfrag"); +// ref is absolute path +base = UriType("http://auth/path/#"); +ref = UriType("/newpath#newfrag"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/newpath#newfrag") == 0); - // ref is relative path - base = UriType(String("http://auth/path/file.json#frag")); - ref = UriType(String("newfile.json#")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/path/newfile.json#"); +// ref is relative path +base = UriType("http://auth/path/file.json#frag"); +ref = UriType("newfile.json#"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/path/newfile.json#") == 0); - base = UriType(String("http://auth/path/file.json#frag/stuff")); - ref = UriType(String("newfile.json#newfrag/newstuff")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/path/newfile.json#newfrag/newstuff"); +base = UriType("http://auth/path/file.json#frag/stuff"); +ref = UriType("newfile.json#newfrag/newstuff"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/path/newfile.json#newfrag/newstuff") == 0); - base = UriType(String("file.json")); - ref = UriType(String("newfile.json")); - EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); +base = UriType("file.json", &allocator); +ref = UriType("newfile.json", &base.GetAllocator()); +res = ref.Resolve(base, &ref.GetAllocator()); +EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0); - base = UriType(String("file.json")); - ref = UriType(String("./newfile.json")); - EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); +base = UriType("file.json", &allocator); +ref = UriType("./newfile.json", &allocator); +res = ref.Resolve(base, &allocator); +EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0); - base = UriType(String("file.json")); - ref = UriType(String("parent/../newfile.json")); - EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); +base = UriType("file.json"); +ref = UriType("parent/../newfile.json"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0); - base = UriType(String("file.json")); - ref = UriType(String("parent/./newfile.json")); - EXPECT_TRUE(ref.Resolve(base).Get() == "parent/newfile.json"); +base = UriType("file.json"); +ref = UriType("parent/./newfile.json"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "parent/newfile.json") == 0); - base = UriType(String("file.json")); - ref = UriType(String("../../parent/.././newfile.json")); - EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json"); +base = UriType("file.json"); +ref = UriType("../../parent/.././newfile.json"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0); - base = UriType(String("http://auth")); - ref = UriType(String("newfile.json")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/newfile.json"); +// This adds a joining slash so resolved length is base length + ref length + 1 +base = UriType("http://auth"); +ref = UriType("newfile.json"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/newfile.json") == 0); - // ref is fragment - base = UriType(String("#frag/stuff")); - ref = UriType(String("#newfrag/newstuff")); - EXPECT_TRUE(ref.Resolve(base).Get() == "#newfrag/newstuff"); +// ref is fragment +base = UriType("#frag/stuff"); +ref = UriType("#newfrag/newstuff"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "#newfrag/newstuff") == 0); - // test ref fragment always wins - base = UriType(String("/path#frag")); - ref = UriType(String("")); - EXPECT_TRUE(ref.Resolve(base).Get() == "/path"); +// test ref fragment always wins +base = UriType("/path#frag"); +ref = UriType(""); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "/path") == 0); - // Examples from RFC3896 - base = UriType(String("http://a/b/c/d;p?q")); - ref = UriType(String("g:h")); - EXPECT_TRUE(ref.Resolve(base).Get() == "g:h"); - ref = UriType(String("g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g"); - ref = UriType(String("./g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g"); - ref = UriType(String("g/")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g/"); - ref = UriType(String("/g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); - ref = UriType(String("//g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://g"); - ref = UriType(String("?y")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?y"); - ref = UriType(String("g?y")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g?y"); - ref = UriType(String("#s")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?q#s"); - ref = UriType(String("g#s")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g#s"); - ref = UriType(String("g?y#s")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g?y#s"); - ref = UriType(String(";x")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/;x"); - ref = UriType(String("g;x")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g;x"); - ref = UriType(String("g;x?y#s")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g;x?y#s"); - ref = UriType(String("")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?q"); - ref = UriType(String(".")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/"); - ref = UriType(String("./")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/"); - ref = UriType(String("..")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/"); - ref = UriType(String("../")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/"); - ref = UriType(String("../g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/g"); - ref = UriType(String("../..")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/"); - ref = UriType(String("../../")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/"); - ref = UriType(String("../../g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); - ref = UriType(String("../../../g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); - ref = UriType(String("../../../../g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); - ref = UriType(String("/./g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); - ref = UriType(String("/../g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g"); - ref = UriType(String("g.")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g."); - ref = UriType(String(".g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/.g"); - ref = UriType(String("g..")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g.."); - ref = UriType(String("..g")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/..g"); - ref = UriType(String("g#s/../x")); - EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g#s/../x"); +// Examples from RFC3896 +base = UriType("http://a/b/c/d;p?q"); +ref = UriType("g:h"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "g:h") == 0); +ref = UriType("g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g") == 0); +ref = UriType("./g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g") == 0); +ref = UriType("g/"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g/") == 0); +ref = UriType("/g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); +ref = UriType("//g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://g") == 0); +ref = UriType("?y"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?y") == 0); +ref = UriType("g?y"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g?y") == 0); +ref = UriType("#s"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?q#s") == 0); +ref = UriType("g#s"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g#s") == 0); +ref = UriType("g?y#s"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g?y#s") == 0); +ref = UriType(";x"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/;x") == 0); +ref = UriType("g;x"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g;x") == 0); +ref = UriType("g;x?y#s"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g;x?y#s") == 0); +ref = UriType(""); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?q") == 0); +ref = UriType("."); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/") == 0); +ref = UriType("./"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/") == 0); +ref = UriType(".."); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/") == 0); +ref = UriType("../"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/") == 0); +ref = UriType("../g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/g") == 0); +ref = UriType("../.."); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/") == 0); +ref = UriType("../../"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/") == 0); +ref = UriType("../../g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); +ref = UriType("../../../g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); +ref = UriType("../../../../g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); +ref = UriType("/./g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); +ref = UriType("/../g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); +ref = UriType("g."); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g.") == 0); +ref = UriType(".g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/.g") == 0); +ref = UriType("g.."); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g..") == 0); +ref = UriType("..g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/..g") == 0); +ref = UriType("g#s/../x"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g#s/../x") == 0); +} + +TEST(Uri, Resolve_UTF16) { +typedef GenericValue > Value; +typedef GenericUri UriType; +CrtAllocator allocator; + +// ref is full uri +UriType base = UriType(L"http://auth/path/#frag"); +UriType ref = UriType(L"http://newauth/newpath#newfrag"); +UriType res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://newauth/newpath#newfrag") == 0); + +base = UriType(L"/path/#frag"); +ref = UriType(L"http://newauth/newpath#newfrag"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://newauth/newpath#newfrag") == 0); + +// ref is alternate uri +base = UriType(L"http://auth/path/#frag"); +ref = UriType(L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); + +// ref is absolute path +base = UriType(L"http://auth/path/#"); +ref = UriType(L"/newpath#newfrag"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/newpath#newfrag") == 0); + +// ref is relative path +base = UriType(L"http://auth/path/file.json#frag"); +ref = UriType(L"newfile.json#"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/path/newfile.json#") == 0); + +base = UriType(L"http://auth/path/file.json#frag/stuff"); +ref = UriType(L"newfile.json#newfrag/newstuff"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/path/newfile.json#newfrag/newstuff") == 0); + +base = UriType(L"file.json", &allocator); +ref = UriType(L"newfile.json", &base.GetAllocator()); +res = ref.Resolve(base, &ref.GetAllocator()); +EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0); + +base = UriType(L"file.json", &allocator); +ref = UriType(L"./newfile.json", &allocator); +res = ref.Resolve(base, &allocator); +EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0); + +base = UriType(L"file.json"); +ref = UriType(L"parent/../newfile.json"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0); + +base = UriType(L"file.json"); +ref = UriType(L"parent/./newfile.json"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"parent/newfile.json") == 0); + +base = UriType(L"file.json"); +ref = UriType(L"../../parent/.././newfile.json"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0); + +// This adds a joining slash so resolved length is base length + ref length + 1 +base = UriType(L"http://auth"); +ref = UriType(L"newfile.json"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/newfile.json") == 0); + +// ref is fragment +base = UriType(L"#frag/stuff"); +ref = UriType(L"#newfrag/newstuff"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"#newfrag/newstuff") == 0); + +// test ref fragment always wins +base = UriType(L"/path#frag"); +ref = UriType(L""); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"/path") == 0); + +// Examples from RFC3896 +base = UriType(L"http://a/b/c/d;p?q"); +ref = UriType(L"g:h"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"g:h") == 0); +ref = UriType(L"g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g") == 0); +ref = UriType(L"./g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g") == 0); +ref = UriType(L"g/"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g/") == 0); +ref = UriType(L"/g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); +ref = UriType(L"//g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://g") == 0); +ref = UriType(L"?y"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?y") == 0); +ref = UriType(L"g?y"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g?y") == 0); +ref = UriType(L"#s"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?q#s") == 0); +ref = UriType(L"g#s"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g#s") == 0); +ref = UriType(L"g?y#s"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g?y#s") == 0); +ref = UriType(L";x"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/;x") == 0); +ref = UriType(L"g;x"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g;x") == 0); +ref = UriType(L"g;x?y#s"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g;x?y#s") == 0); +ref = UriType(L""); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?q") == 0); +ref = UriType(L"."); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/") == 0); +ref = UriType(L"./"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/") == 0); +ref = UriType(L".."); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/") == 0); +ref = UriType(L"../"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/") == 0); +ref = UriType(L"../g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/g") == 0); +ref = UriType(L"../.."); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/") == 0); +ref = UriType(L"../../"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/") == 0); +ref = UriType(L"../../g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); +ref = UriType(L"../../../g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); +ref = UriType(L"../../../../g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); +ref = UriType(L"/./g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); +ref = UriType(L"/../g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); +ref = UriType(L"g."); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g.") == 0); +ref = UriType(L".g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/.g") == 0); +ref = UriType(L"g.."); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g..") == 0); +ref = UriType(L"..g"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/..g") == 0); +ref = UriType(L"g#s/../x"); +res = ref.Resolve(base); +EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g#s/../x") == 0); } #if defined(_MSC_VER) || defined(__clang__) From 6d253c160d002935079503f826310f00845b290b Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Tue, 8 Jun 2021 15:31:25 +0100 Subject: [PATCH 14/45] remove compiler warning --- test/unittest/uritest.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/unittest/uritest.cpp b/test/unittest/uritest.cpp index 936b831..fcedcb1 100644 --- a/test/unittest/uritest.cpp +++ b/test/unittest/uritest.cpp @@ -143,11 +143,11 @@ EXPECT_TRUE(u.GetFragStringLength() == len); } TEST(Uri, Parse_UTF16) { -typedef GenericValue > Value; -typedef GenericUri > UriType; +typedef GenericValue > Value16; +typedef GenericUri > UriType; MemoryPoolAllocator allocator; -Value v; -Value w; +Value16 v; +Value16 w; v.SetString(L"http://auth/path/xxx?query#frag", allocator); UriType u = UriType(v, &allocator); @@ -161,7 +161,7 @@ u.Get(w, allocator); EXPECT_TRUE(*w.GetString() == *v.GetString()); #if RAPIDJSON_HAS_STDSTRING -typedef std::basic_string String; +typedef std::basic_string String; String str = L"http://auth/path/xxx?query#frag"; const UriType uri = UriType(str); EXPECT_TRUE(UriType::GetScheme(uri) == L"http:"); @@ -237,8 +237,8 @@ EXPECT_TRUE(u.GetQueryStringLength() == 0); EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0); EXPECT_TRUE(StrCmp(u.GetString(), L"http://auth#frag/stuff") == 0); -const Value::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'}; -SizeType len = internal::StrLen(c); +const Value16::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'}; +SizeType len = internal::StrLen(c); u = UriType(c, len); EXPECT_TRUE(StrCmp(u.GetString(), L"#frag/stuff") == 0); EXPECT_TRUE(u.GetStringLength() == len); @@ -438,8 +438,8 @@ EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g#s/../x") == 0); } TEST(Uri, Resolve_UTF16) { -typedef GenericValue > Value; -typedef GenericUri UriType; +typedef GenericValue > Value16; +typedef GenericUri UriType; CrtAllocator allocator; // ref is full uri From 18ab3b16bc5eb31fa81035c9708bc08e10d53572 Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Tue, 8 Jun 2021 17:11:42 +0100 Subject: [PATCH 15/45] remove temp debug statements --- include/rapidjson/uri.h | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/include/rapidjson/uri.h b/include/rapidjson/uri.h index 640d793..15be29a 100644 --- a/include/rapidjson/uri.h +++ b/include/rapidjson/uri.h @@ -39,7 +39,6 @@ RAPIDJSON_NAMESPACE_BEGIN //! Constructors GenericUri(Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { - //std::cout << "Default constructor" << std::endl; } GenericUri(const Ch* uri, SizeType len, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { @@ -64,26 +63,22 @@ RAPIDJSON_NAMESPACE_BEGIN //! Copy constructor GenericUri(const GenericUri& rhs) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(rhs.allocator_), ownAllocator_() { - //std::cout << "Copy constructor" << std::endl; *this = rhs; } //! Copy constructor GenericUri(const GenericUri& rhs, Allocator* allocator) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { - //std::cout << "Copy constructor" << std::endl; *this = rhs; } //! Destructor. ~GenericUri() { - //std::cout << "Destructor" << std::endl; Free(); RAPIDJSON_DELETE(ownAllocator_); } //! Assignment operator GenericUri& operator=(const GenericUri& rhs) { - //std::cout << "Operator=" << std::endl; if (this != &rhs) { // Do not delete ownAllocator Free(); @@ -95,7 +90,6 @@ RAPIDJSON_NAMESPACE_BEGIN base_ = CopyPart(frag_, rhs.frag_, rhs.GetFragStringLength()); uri_ = CopyPart(base_, rhs.base_, rhs.GetBaseStringLength()); CopyPart(uri_, rhs.uri_, rhs.GetStringLength()); - //std::wcout << L" Assignment uri: " << uri_ << ", length: " << GetStringLength() << std::endl; } return *this; } @@ -160,7 +154,6 @@ RAPIDJSON_NAMESPACE_BEGIN // Use for resolving an id or $ref with an in-scope id. // Returns a new GenericUri for the resolved URI. GenericUri Resolve(const GenericUri& baseuri, Allocator* allocator = 0) { - //std::cout << "Resolve" << std::endl; GenericUri resuri; resuri.allocator_ = allocator; // Ensure enough space for combining paths @@ -224,13 +217,11 @@ RAPIDJSON_NAMESPACE_BEGIN } // Always use this frag resuri.base_ = CopyPart(resuri.frag_, frag_, GetFragStringLength()); - //std::wcout << L" Resolved uri: " << L"s: " << resuri.scheme_ << L" a: " << resuri.auth_ << L" p: " << resuri.path_ << L" q: " << resuri.query_ << L" f: " << resuri.frag_ << std::endl; // Re-constitute base_ and uri_ resuri.SetBase(); resuri.uri_ = resuri.base_ + resuri.GetBaseStringLength() + 1; resuri.SetUri(); - //std::wcout << L" Resolved rebuilt uri: " << resuri.uri_ << L", length: " << resuri.GetStringLength() << std::endl; return resuri; } @@ -241,7 +232,6 @@ private: // Allocate memory for a URI // Returns total amount allocated std::size_t Allocate(std::size_t len) { - //std::cout << "Allocate" << std::endl; // Create own allocator if user did not supply. if (!allocator_) ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); @@ -263,15 +253,12 @@ private: *base_ = '\0'; uri_ = base_ + 1; *uri_ = '\0'; - //std::cout << " Allocating " << total << std::endl; return total; } // Free memory for a URI void Free() { - //std::cout << "Free" << std::endl; if (scheme_) { - //std::cout << " Freeing" << std::endl; Allocator::Free(scheme_); scheme_ = 0; } @@ -281,7 +268,6 @@ private: // Supports URIs that match regex ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? as per // https://tools.ietf.org/html/rfc3986 void Parse(const Ch* uri, std::size_t len) { - //std::cout << "Parse" << std::endl; std::size_t start = 0, pos1 = 0, pos2 = 0; Allocate(len); @@ -368,14 +354,12 @@ private: std::memcpy(frag_, &uri[start], (len - start) * sizeof(Ch)); frag_[len - start] = '\0'; } - //std::wcout << L" Parsed uri: " << L"s: " << scheme_ << L" a: " << auth_ << L" p: " << path_ << L" q: " << query_ << L" f: " << frag_ << std::endl; // Re-constitute base_ and uri_ base_ = frag_ + GetFragStringLength() + 1; SetBase(); uri_ = base_ + GetBaseStringLength() + 1; SetUri(); - //std::wcout << L" Rebuilt uri: " << uri_ << L", length: " << GetStringLength() << std::endl; } // Reconstitute base @@ -433,7 +417,6 @@ private: if (slashpos == 2 && path_[pathpos] == '.' && path_[pathpos + 1] == '.') { // Backup a .. segment in the new path_ // We expect to find a previously added slash at the end or nothing - //std::wcout << L" Path - backing up .. in " << path_ << std::endl; size_t lastslashpos = newpos; while (lastslashpos > 0) { if (path_[lastslashpos - 1] == '/') break; @@ -452,7 +435,6 @@ private: } } else if (slashpos == 1 && path_[pathpos] == '.') { // Discard . segment, leaves new path_ unchanged - //std::wcout << L" Path - removing . in " << path_ << std::endl; } else { // Move any other kind of segment to the new path_ RAPIDJSON_ASSERT(newpos <= pathpos); @@ -468,7 +450,6 @@ private: pathpos += slashpos + 1; } path_[newpos] = '\0'; - //std::wcout << L" Normalized Path: " << path_ << ", length: " << GetPathStringLength() << std::endl; } Ch* uri_; // Everything From 3df804c1282c17afe899d97280be0812daed707c Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Wed, 9 Jun 2021 10:31:09 +0100 Subject: [PATCH 16/45] fix coverage, unit test allocators and equality --- include/rapidjson/pointer.h | 14 +- include/rapidjson/schema.h | 14 +- include/rapidjson/uri.h | 10 +- test/unittest/pointertest.cpp | 12 +- test/unittest/uritest.cpp | 1073 +++++++++++++++++---------------- 5 files changed, 579 insertions(+), 544 deletions(-) diff --git a/include/rapidjson/pointer.h b/include/rapidjson/pointer.h index 96f33fa..a55e7cc 100644 --- a/include/rapidjson/pointer.h +++ b/include/rapidjson/pointer.h @@ -530,7 +530,9 @@ public: // For use with JSON pointers into JSON schema documents. /*! \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param rootUri Root URI \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. + \param allocator Allocator for Uris \return Uri if it can be resolved. Otherwise null. \note @@ -541,10 +543,10 @@ public: Use unresolvedTokenIndex to retrieve the token index. */ - UriType GetUri(ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0) const { + UriType GetUri(ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0, Allocator* allocator = 0) const { static const Ch kIdString[] = { 'i', 'd', '\0' }; static const ValueType kIdValue(kIdString, 2); - UriType base = rootUri; + UriType base = UriType(rootUri, allocator); RAPIDJSON_ASSERT(IsValid()); ValueType* v = &root; for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { @@ -554,7 +556,7 @@ public: // See if we have an id, and if so resolve with the current base typename ValueType::MemberIterator m = v->FindMember(kIdValue); if (m != v->MemberEnd() && (m->value).IsString()) { - UriType here = UriType(m->value, allocator_).Resolve(base, allocator_); + UriType here = UriType(m->value, allocator).Resolve(base, allocator); base = here; } m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); @@ -575,13 +577,13 @@ public: // Error: unresolved token if (unresolvedTokenIndex) *unresolvedTokenIndex = static_cast(t - tokens_); - return UriType(allocator_); + return UriType(allocator); } return base; } - UriType GetUri(const ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0) const { - return GetUri(const_cast(root), rootUri, unresolvedTokenIndex); + UriType GetUri(const ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0, Allocator* allocator = 0) const { + return GetUri(const_cast(root), rootUri, unresolvedTokenIndex, allocator); } diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index a48288b..f0759ff 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -1741,7 +1741,7 @@ private: // Changed by PR #1393 void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { if (v.GetType() == kObjectType) { - UriType newid = CreateSchema(schema, pointer, v, document, id); + UriType newid = UriType(CreateSchema(schema, pointer, v, document, id), allocator_); for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document, newid); @@ -1790,7 +1790,7 @@ private: SizeType len = itr->value.GetStringLength(); if (len > 0) { // First resolve $ref against the in-scope id - UriType scopeId = id; + UriType scopeId = UriType(id, allocator_); UriType ref = UriType(itr->value, allocator_).Resolve(scopeId, allocator_); // See if the resolved $ref minus the fragment matches a resolved id in this document // Search from the root. Returns the subschema in the document and its absolute JSON pointer. @@ -1838,7 +1838,8 @@ private: if (pointer.IsValid() && !IsCyclicRef(pointer)) { // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there // TODO: cache pointer <-> id mapping - scopeId = pointer.GetUri(document, docId_); + size_t unresolvedTokenIndex; + scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_); CreateSchema(schema, pointer, *pv, document, scopeId); return true; } @@ -1855,7 +1856,8 @@ private: //pointer.StringifyUriFragment(sb); // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there // TODO: cache pointer <-> id mapping - scopeId = pointer.GetUri(document, docId_); + size_t unresolvedTokenIndex; + scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_); CreateSchema(schema, pointer, *pv, document, scopeId); return true; } @@ -1879,8 +1881,8 @@ private: ValueType* FindId(const ValueType& doc, const UriType& finduri, PointerType& resptr, const UriType& baseuri, bool full, const PointerType& here = PointerType()) const { SizeType i = 0; ValueType* resval = 0; - UriType tempuri = finduri; - UriType localuri = baseuri; + UriType tempuri = UriType(finduri, allocator_); + UriType localuri = UriType(baseuri, allocator_); if (doc.GetType() == kObjectType) { // Establish the base URI of this object typename ValueType::ConstMemberIterator m = doc.FindMember(SchemaType::GetIdString()); diff --git a/include/rapidjson/uri.h b/include/rapidjson/uri.h index 15be29a..f70c388 100644 --- a/include/rapidjson/uri.h +++ b/include/rapidjson/uri.h @@ -29,9 +29,9 @@ RAPIDJSON_NAMESPACE_BEGIN /////////////////////////////////////////////////////////////////////////////// // GenericUri - template - class GenericUri { - public: +template +class GenericUri { +public: typedef typename ValueType::Ch Ch; #if RAPIDJSON_HAS_STDSTRING typedef std::basic_string String; @@ -462,10 +462,10 @@ private: Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. Allocator* ownAllocator_; //!< Allocator owned by this Uri. - }; +}; //! GenericUri for Value (UTF-8, default allocator). - typedef GenericUri Uri; +typedef GenericUri Uri; RAPIDJSON_NAMESPACE_END diff --git a/test/unittest/pointertest.cpp b/test/unittest/pointertest.cpp index c00658c..31aebec 100644 --- a/test/unittest/pointertest.cpp +++ b/test/unittest/pointertest.cpp @@ -659,6 +659,7 @@ static const char kJsonIds[] = "{\n" TEST(Pointer, GetUri) { + CrtAllocator allocator; Document d; d.Parse(kJsonIds); Pointer::UriType doc("http://doc"); @@ -677,15 +678,16 @@ TEST(Pointer, GetUri) { EXPECT_TRUE(Pointer("/jbo").GetUri(d, doc) == root); EXPECT_TRUE(Pointer("/jbo/child").GetUri(d, doc) == root); // id not string - EXPECT_TRUE(Pointer("/abc").GetUri(d, doc) == empty); // Out of boundary size_t unresolvedTokenIndex; - EXPECT_TRUE(Pointer("/foo/3").GetUri(d, doc, &unresolvedTokenIndex) == empty); // Out of boundary + EXPECT_TRUE(Pointer("/abc").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // Out of boundary + EXPECT_EQ(0u, unresolvedTokenIndex); + EXPECT_TRUE(Pointer("/foo/3").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // Out of boundary EXPECT_EQ(1u, unresolvedTokenIndex); - EXPECT_TRUE(Pointer("/foo/a").GetUri(d, doc, &unresolvedTokenIndex) == empty); // "/foo" is an array, cannot query by "a" + EXPECT_TRUE(Pointer("/foo/a").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // "/foo" is an array, cannot query by "a" EXPECT_EQ(1u, unresolvedTokenIndex); - EXPECT_TRUE(Pointer("/foo/0/0").GetUri(d, doc, &unresolvedTokenIndex) == empty); // "/foo/0" is an string, cannot further query + EXPECT_TRUE(Pointer("/foo/0/0").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // "/foo/0" is an string, cannot further query EXPECT_EQ(2u, unresolvedTokenIndex); - EXPECT_TRUE(Pointer("/foo/0/a").GetUri(d, doc, &unresolvedTokenIndex) == empty); // "/foo/0" is an string, cannot further query + EXPECT_TRUE(Pointer("/foo/0/a").GetUri(d, doc, &unresolvedTokenIndex, &allocator) == empty); // "/foo/0" is an string, cannot further query EXPECT_EQ(2u, unresolvedTokenIndex); Pointer::Token tokens[] = { { "foo ...", 3, kPointerInvalidIndex } }; diff --git a/test/unittest/uritest.cpp b/test/unittest/uritest.cpp index fcedcb1..7fa7b93 100644 --- a/test/unittest/uritest.cpp +++ b/test/unittest/uritest.cpp @@ -30,593 +30,622 @@ RAPIDJSON_DIAG_OFF(4822) // local class member function does not have a body using namespace rapidjson; TEST(Uri, Parse) { -typedef GenericUri > UriType; -MemoryPoolAllocator allocator; -Value v; -Value w; + typedef GenericUri > UriType; + MemoryPoolAllocator allocator; + Value v; + Value w; -v.SetString("http://auth/path/xxx?query#frag", allocator); -UriType u = UriType(v, &allocator); -EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0); -EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0); -EXPECT_TRUE(StrCmp(u.GetPathString(), "/path/xxx") == 0); -EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth/path/xxx?query") == 0); -EXPECT_TRUE(StrCmp(u.GetQueryString(), "?query") == 0); -EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag") == 0); -u.Get(w, allocator); -EXPECT_TRUE(*w.GetString() == *v.GetString()); + v.SetString("http://auth/path/xxx?query#frag", allocator); + UriType u = UriType(v, &allocator); + EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0); + EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), "/path/xxx") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth/path/xxx?query") == 0); + EXPECT_TRUE(StrCmp(u.GetQueryString(), "?query") == 0); + EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag") == 0); + u.Get(w, allocator); + EXPECT_TRUE(*w.GetString() == *v.GetString()); #if RAPIDJSON_HAS_STDSTRING -typedef std::basic_string String; -String str = "http://auth/path/xxx?query#frag"; -const UriType uri = UriType(str); -EXPECT_TRUE(UriType::GetScheme(uri) == "http:"); -EXPECT_TRUE(UriType::GetAuth(uri) == "//auth"); -EXPECT_TRUE(UriType::GetPath(uri) == "/path/xxx"); -EXPECT_TRUE(UriType::GetBase(uri) == "http://auth/path/xxx?query"); -EXPECT_TRUE(UriType::GetQuery(uri) == "?query"); -EXPECT_TRUE(UriType::GetFrag(uri) == "#frag"); -EXPECT_TRUE(UriType::Get(uri) == str); + typedef std::basic_string String; + String str = "http://auth/path/xxx?query#frag"; + const UriType uri = UriType(str); + EXPECT_TRUE(UriType::GetScheme(uri) == "http:"); + EXPECT_TRUE(UriType::GetAuth(uri) == "//auth"); + EXPECT_TRUE(UriType::GetPath(uri) == "/path/xxx"); + EXPECT_TRUE(UriType::GetBase(uri) == "http://auth/path/xxx?query"); + EXPECT_TRUE(UriType::GetQuery(uri) == "?query"); + EXPECT_TRUE(UriType::GetFrag(uri) == "#frag"); + EXPECT_TRUE(UriType::Get(uri) == str); #endif -v.SetString("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f", allocator); -u = UriType(v); -EXPECT_TRUE(StrCmp(u.GetSchemeString(), "urn:") == 0); -EXPECT_TRUE(u.GetAuthStringLength() == 0); -EXPECT_TRUE(StrCmp(u.GetPathString(), "uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); -EXPECT_TRUE(StrCmp(u.GetBaseString(), "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); -EXPECT_TRUE(u.GetQueryStringLength() == 0); -EXPECT_TRUE(u.GetFragStringLength() == 0); -u.Get(w, allocator); -EXPECT_TRUE(*w.GetString() == *v.GetString()); + v.SetString("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f", allocator); + u = UriType(v); + EXPECT_TRUE(StrCmp(u.GetSchemeString(), "urn:") == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), "uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); + u.Get(w, allocator); + EXPECT_TRUE(*w.GetString() == *v.GetString()); -v.SetString("", allocator); -u = UriType(v); -EXPECT_TRUE(u.GetSchemeStringLength() == 0); -EXPECT_TRUE(u.GetAuthStringLength() == 0); -EXPECT_TRUE(u.GetPathStringLength() == 0); -EXPECT_TRUE(u.GetBaseStringLength() == 0); -EXPECT_TRUE(u.GetQueryStringLength() == 0); -EXPECT_TRUE(u.GetFragStringLength() == 0); + v.SetString("", allocator); + u = UriType(v); + EXPECT_TRUE(u.GetSchemeStringLength() == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(u.GetPathStringLength() == 0); + EXPECT_TRUE(u.GetBaseStringLength() == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); -v.SetString("http://auth/", allocator); -u = UriType(v); -EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0); -EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0); -EXPECT_TRUE(StrCmp(u.GetPathString(), "/") == 0); -EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth/") == 0); -EXPECT_TRUE(u.GetQueryStringLength() == 0); -EXPECT_TRUE(u.GetFragStringLength() == 0); + v.SetString("http://auth/", allocator); + u = UriType(v); + EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0); + EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), "/") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth/") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); -u = UriType("/path/sub"); -EXPECT_TRUE(u.GetSchemeStringLength() == 0); -EXPECT_TRUE(u.GetAuthStringLength() == 0); -EXPECT_TRUE(StrCmp(u.GetPathString(), "/path/sub") == 0); -EXPECT_TRUE(StrCmp(u.GetBaseString(), "/path/sub") == 0); -EXPECT_TRUE(u.GetQueryStringLength() == 0); -EXPECT_TRUE(u.GetFragStringLength() == 0); + u = UriType("/path/sub"); + EXPECT_TRUE(u.GetSchemeStringLength() == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), "/path/sub") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), "/path/sub") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); -// absolute path gets normalized -u = UriType("/path/../sub/"); -EXPECT_TRUE(u.GetSchemeStringLength() == 0); -EXPECT_TRUE(u.GetAuthStringLength() == 0); -EXPECT_TRUE(StrCmp(u.GetPathString(), "/sub/") == 0); -EXPECT_TRUE(StrCmp(u.GetBaseString(), "/sub/") == 0); -EXPECT_TRUE(u.GetQueryStringLength() == 0); -EXPECT_TRUE(u.GetFragStringLength() == 0); + // absolute path gets normalized + u = UriType("/path/../sub/"); + EXPECT_TRUE(u.GetSchemeStringLength() == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), "/sub/") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), "/sub/") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); -// relative path does not -u = UriType("path/../sub"); -EXPECT_TRUE(u.GetSchemeStringLength() == 0); -EXPECT_TRUE(u.GetAuthStringLength() == 0); -EXPECT_TRUE(StrCmp(u.GetPathString(), "path/../sub") == 0); -EXPECT_TRUE(StrCmp(u.GetBaseString(), "path/../sub") == 0); -EXPECT_TRUE(u.GetQueryStringLength() == 0); -EXPECT_TRUE(u.GetFragStringLength() == 0); + // relative path does not + u = UriType("path/../sub"); + EXPECT_TRUE(u.GetSchemeStringLength() == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), "path/../sub") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), "path/../sub") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); -u = UriType("http://auth#frag/stuff"); -EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0); -EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0); -EXPECT_TRUE(u.GetPathStringLength() == 0); -EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth") == 0); -EXPECT_TRUE(u.GetQueryStringLength() == 0); -EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0); -EXPECT_TRUE(StrCmp(u.GetString(), "http://auth#frag/stuff") == 0); + u = UriType("http://auth#frag/stuff"); + EXPECT_TRUE(StrCmp(u.GetSchemeString(), "http:") == 0); + EXPECT_TRUE(StrCmp(u.GetAuthString(), "//auth") == 0); + EXPECT_TRUE(u.GetPathStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), "http://auth") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0); + EXPECT_TRUE(StrCmp(u.GetString(), "http://auth#frag/stuff") == 0); -const Value::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'}; -SizeType len = internal::StrLen(c); -u = UriType(c, len); -EXPECT_TRUE(StrCmp(u.GetString(), "#frag/stuff") == 0); -EXPECT_TRUE(u.GetStringLength() == len); -EXPECT_TRUE(StrCmp(u.GetBaseString(), "") == 0); -EXPECT_TRUE(u.GetBaseStringLength() == 0); -EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0); -EXPECT_TRUE(u.GetFragStringLength() == len); + const Value::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'}; + SizeType len = internal::StrLen(c); + u = UriType(c, len); + EXPECT_TRUE(StrCmp(u.GetString(), "#frag/stuff") == 0); + EXPECT_TRUE(u.GetStringLength() == len); + EXPECT_TRUE(StrCmp(u.GetBaseString(), "") == 0); + EXPECT_TRUE(u.GetBaseStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0); + EXPECT_TRUE(u.GetFragStringLength() == len); -u = UriType(c); -EXPECT_TRUE(StrCmp(u.GetString(), "#frag/stuff") == 0); -EXPECT_TRUE(u.GetStringLength() == len); -EXPECT_TRUE(StrCmp(u.GetBaseString(), "") == 0); -EXPECT_TRUE(u.GetBaseStringLength() == 0); -EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0); -EXPECT_TRUE(u.GetFragStringLength() == len); + u = UriType(c); + EXPECT_TRUE(StrCmp(u.GetString(), "#frag/stuff") == 0); + EXPECT_TRUE(u.GetStringLength() == len); + EXPECT_TRUE(StrCmp(u.GetBaseString(), "") == 0); + EXPECT_TRUE(u.GetBaseStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0); + EXPECT_TRUE(u.GetFragStringLength() == len); } TEST(Uri, Parse_UTF16) { -typedef GenericValue > Value16; -typedef GenericUri > UriType; -MemoryPoolAllocator allocator; -Value16 v; -Value16 w; + typedef GenericValue > Value16; + typedef GenericUri > UriType; + MemoryPoolAllocator allocator; + Value16 v; + Value16 w; -v.SetString(L"http://auth/path/xxx?query#frag", allocator); -UriType u = UriType(v, &allocator); -EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0); -EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0); -EXPECT_TRUE(StrCmp(u.GetPathString(), L"/path/xxx") == 0); -EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth/path/xxx?query") == 0); -EXPECT_TRUE(StrCmp(u.GetQueryString(), L"?query") == 0); -EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag") == 0); -u.Get(w, allocator); -EXPECT_TRUE(*w.GetString() == *v.GetString()); + v.SetString(L"http://auth/path/xxx?query#frag", allocator); + UriType u = UriType(v, &allocator); + EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0); + EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), L"/path/xxx") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth/path/xxx?query") == 0); + EXPECT_TRUE(StrCmp(u.GetQueryString(), L"?query") == 0); + EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag") == 0); + u.Get(w, allocator); + EXPECT_TRUE(*w.GetString() == *v.GetString()); #if RAPIDJSON_HAS_STDSTRING -typedef std::basic_string String; -String str = L"http://auth/path/xxx?query#frag"; -const UriType uri = UriType(str); -EXPECT_TRUE(UriType::GetScheme(uri) == L"http:"); -EXPECT_TRUE(UriType::GetAuth(uri) == L"//auth"); -EXPECT_TRUE(UriType::GetPath(uri) == L"/path/xxx"); -EXPECT_TRUE(UriType::GetBase(uri) == L"http://auth/path/xxx?query"); -EXPECT_TRUE(UriType::GetQuery(uri) == L"?query"); -EXPECT_TRUE(UriType::GetFrag(uri) == L"#frag"); -EXPECT_TRUE(UriType::Get(uri) == str); + typedef std::basic_string String; + String str = L"http://auth/path/xxx?query#frag"; + const UriType uri = UriType(str); + EXPECT_TRUE(UriType::GetScheme(uri) == L"http:"); + EXPECT_TRUE(UriType::GetAuth(uri) == L"//auth"); + EXPECT_TRUE(UriType::GetPath(uri) == L"/path/xxx"); + EXPECT_TRUE(UriType::GetBase(uri) == L"http://auth/path/xxx?query"); + EXPECT_TRUE(UriType::GetQuery(uri) == L"?query"); + EXPECT_TRUE(UriType::GetFrag(uri) == L"#frag"); + EXPECT_TRUE(UriType::Get(uri) == str); #endif -v.SetString(L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f", allocator); -u = UriType(v); -EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"urn:") == 0); -EXPECT_TRUE(u.GetAuthStringLength() == 0); -EXPECT_TRUE(StrCmp(u.GetPathString(), L"uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); -EXPECT_TRUE(StrCmp(u.GetBaseString(), L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); -EXPECT_TRUE(u.GetQueryStringLength() == 0); -EXPECT_TRUE(u.GetFragStringLength() == 0); -u.Get(w, allocator); -EXPECT_TRUE(*w.GetString() == *v.GetString()); + v.SetString(L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f", allocator); + u = UriType(v); + EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"urn:") == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), L"uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); + u.Get(w, allocator); + EXPECT_TRUE(*w.GetString() == *v.GetString()); -v.SetString(L"", allocator); -u = UriType(v); -EXPECT_TRUE(u.GetSchemeStringLength() == 0); -EXPECT_TRUE(u.GetAuthStringLength() == 0); -EXPECT_TRUE(u.GetPathStringLength() == 0); -EXPECT_TRUE(u.GetBaseStringLength() == 0); -EXPECT_TRUE(u.GetQueryStringLength() == 0); -EXPECT_TRUE(u.GetFragStringLength() == 0); + v.SetString(L"", allocator); + u = UriType(v); + EXPECT_TRUE(u.GetSchemeStringLength() == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(u.GetPathStringLength() == 0); + EXPECT_TRUE(u.GetBaseStringLength() == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); -v.SetString(L"http://auth/", allocator); -u = UriType(v); -EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0); -EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0); -EXPECT_TRUE(StrCmp(u.GetPathString(), L"/") == 0); -EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth/") == 0); -EXPECT_TRUE(u.GetQueryStringLength() == 0); -EXPECT_TRUE(u.GetFragStringLength() == 0); + v.SetString(L"http://auth/", allocator); + u = UriType(v); + EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0); + EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), L"/") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth/") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); -u = UriType(L"/path/sub"); -EXPECT_TRUE(u.GetSchemeStringLength() == 0); -EXPECT_TRUE(u.GetAuthStringLength() == 0); -EXPECT_TRUE(StrCmp(u.GetPathString(), L"/path/sub") == 0); -EXPECT_TRUE(StrCmp(u.GetBaseString(), L"/path/sub") == 0); -EXPECT_TRUE(u.GetQueryStringLength() == 0); -EXPECT_TRUE(u.GetFragStringLength() == 0); + u = UriType(L"/path/sub"); + EXPECT_TRUE(u.GetSchemeStringLength() == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), L"/path/sub") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"/path/sub") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); -// absolute path gets normalized -u = UriType(L"/path/../sub/"); -EXPECT_TRUE(u.GetSchemeStringLength() == 0); -EXPECT_TRUE(u.GetAuthStringLength() == 0); -EXPECT_TRUE(StrCmp(u.GetPathString(), L"/sub/") == 0); -EXPECT_TRUE(StrCmp(u.GetBaseString(), L"/sub/") == 0); -EXPECT_TRUE(u.GetQueryStringLength() == 0); -EXPECT_TRUE(u.GetFragStringLength() == 0); + // absolute path gets normalized + u = UriType(L"/path/../sub/"); + EXPECT_TRUE(u.GetSchemeStringLength() == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), L"/sub/") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"/sub/") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); -// relative path does not -u = UriType(L"path/../sub"); -EXPECT_TRUE(u.GetSchemeStringLength() == 0); -EXPECT_TRUE(u.GetAuthStringLength() == 0); -EXPECT_TRUE(StrCmp(u.GetPathString(), L"path/../sub") == 0); -EXPECT_TRUE(StrCmp(u.GetBaseString(), L"path/../sub") == 0); -EXPECT_TRUE(u.GetQueryStringLength() == 0); -EXPECT_TRUE(u.GetFragStringLength() == 0); + // relative path does not + u = UriType(L"path/../sub"); + EXPECT_TRUE(u.GetSchemeStringLength() == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), L"path/../sub") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"path/../sub") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); -u = UriType(L"http://auth#frag/stuff"); -EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0); -EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0); -EXPECT_TRUE(u.GetPathStringLength() == 0); -EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth") == 0); -EXPECT_TRUE(u.GetQueryStringLength() == 0); -EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0); -EXPECT_TRUE(StrCmp(u.GetString(), L"http://auth#frag/stuff") == 0); + u = UriType(L"http://auth#frag/stuff"); + EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0); + EXPECT_TRUE(StrCmp(u.GetAuthString(), L"//auth") == 0); + EXPECT_TRUE(u.GetPathStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http://auth") == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0); + EXPECT_TRUE(StrCmp(u.GetString(), L"http://auth#frag/stuff") == 0); -const Value16::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'}; -SizeType len = internal::StrLen(c); -u = UriType(c, len); -EXPECT_TRUE(StrCmp(u.GetString(), L"#frag/stuff") == 0); -EXPECT_TRUE(u.GetStringLength() == len); -EXPECT_TRUE(StrCmp(u.GetBaseString(), L"") == 0); -EXPECT_TRUE(u.GetBaseStringLength() == 0); -EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0); -EXPECT_TRUE(u.GetFragStringLength() == len); + const Value16::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'}; + SizeType len = internal::StrLen(c); + u = UriType(c, len); + EXPECT_TRUE(StrCmp(u.GetString(), L"#frag/stuff") == 0); + EXPECT_TRUE(u.GetStringLength() == len); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"") == 0); + EXPECT_TRUE(u.GetBaseStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0); + EXPECT_TRUE(u.GetFragStringLength() == len); -u = UriType(c); -EXPECT_TRUE(StrCmp(u.GetString(), L"#frag/stuff") == 0); -EXPECT_TRUE(u.GetStringLength() == len); -EXPECT_TRUE(StrCmp(u.GetBaseString(), L"") == 0); -EXPECT_TRUE(u.GetBaseStringLength() == 0); -EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0); -EXPECT_TRUE(u.GetFragStringLength() == len); + u = UriType(c); + EXPECT_TRUE(StrCmp(u.GetString(), L"#frag/stuff") == 0); + EXPECT_TRUE(u.GetStringLength() == len); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"") == 0); + EXPECT_TRUE(u.GetBaseStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0); + EXPECT_TRUE(u.GetFragStringLength() == len); } TEST(Uri, Resolve) { -typedef GenericUri UriType; -CrtAllocator allocator; + typedef GenericUri UriType; + CrtAllocator allocator; -// ref is full uri -UriType base = UriType("http://auth/path/#frag"); -UriType ref = UriType("http://newauth/newpath#newfrag"); -UriType res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://newauth/newpath#newfrag") == 0); + // ref is full uri + UriType base = UriType("http://auth/path/#frag"); + UriType ref = UriType("http://newauth/newpath#newfrag"); + UriType res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://newauth/newpath#newfrag") == 0); -base = UriType("/path/#frag", &allocator); -ref = UriType("http://newauth/newpath#newfrag", &allocator); -res = ref.Resolve(base, &allocator); -EXPECT_TRUE(StrCmp(res.GetString(), "http://newauth/newpath#newfrag") == 0); + base = UriType("/path/#frag", &allocator); + ref = UriType("http://newauth/newpath#newfrag", &allocator); + res = ref.Resolve(base, &allocator); + EXPECT_TRUE(StrCmp(res.GetString(), "http://newauth/newpath#newfrag") == 0); -// ref is alternate uri -base = UriType("http://auth/path/#frag"); -ref = UriType("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); + // ref is alternate uri + base = UriType("http://auth/path/#frag"); + ref = UriType("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); -// ref is absolute path -base = UriType("http://auth/path/#"); -ref = UriType("/newpath#newfrag"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/newpath#newfrag") == 0); + // ref is absolute path + base = UriType("http://auth/path/#"); + ref = UriType("/newpath#newfrag"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/newpath#newfrag") == 0); -// ref is relative path -base = UriType("http://auth/path/file.json#frag"); -ref = UriType("newfile.json#"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/path/newfile.json#") == 0); + // ref is relative path + base = UriType("http://auth/path/file.json#frag"); + ref = UriType("newfile.json#"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/path/newfile.json#") == 0); -base = UriType("http://auth/path/file.json#frag/stuff"); -ref = UriType("newfile.json#newfrag/newstuff"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/path/newfile.json#newfrag/newstuff") == 0); + base = UriType("http://auth/path/file.json#frag/stuff"); + ref = UriType("newfile.json#newfrag/newstuff"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/path/newfile.json#newfrag/newstuff") == 0); -base = UriType("file.json", &allocator); -ref = UriType("newfile.json", &base.GetAllocator()); -res = ref.Resolve(base, &ref.GetAllocator()); -EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0); + base = UriType("file.json", &allocator); + ref = UriType("newfile.json", &base.GetAllocator()); + res = ref.Resolve(base, &ref.GetAllocator()); + EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0); -base = UriType("file.json", &allocator); -ref = UriType("./newfile.json", &allocator); -res = ref.Resolve(base, &allocator); -EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0); + base = UriType("file.json", &allocator); + ref = UriType("./newfile.json", &allocator); + res = ref.Resolve(base, &allocator); + EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0); -base = UriType("file.json"); -ref = UriType("parent/../newfile.json"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0); + base = UriType("file.json"); + ref = UriType("parent/../newfile.json"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0); -base = UriType("file.json"); -ref = UriType("parent/./newfile.json"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "parent/newfile.json") == 0); + base = UriType("file.json"); + ref = UriType("parent/./newfile.json"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "parent/newfile.json") == 0); -base = UriType("file.json"); -ref = UriType("../../parent/.././newfile.json"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0); + base = UriType("file.json"); + ref = UriType("../../parent/.././newfile.json"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "newfile.json") == 0); -// This adds a joining slash so resolved length is base length + ref length + 1 -base = UriType("http://auth"); -ref = UriType("newfile.json"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/newfile.json") == 0); + // This adds a joining slash so resolved length is base length + ref length + 1 + base = UriType("http://auth"); + ref = UriType("newfile.json"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://auth/newfile.json") == 0); -// ref is fragment -base = UriType("#frag/stuff"); -ref = UriType("#newfrag/newstuff"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "#newfrag/newstuff") == 0); + // ref is fragment + base = UriType("#frag/stuff"); + ref = UriType("#newfrag/newstuff"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "#newfrag/newstuff") == 0); -// test ref fragment always wins -base = UriType("/path#frag"); -ref = UriType(""); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "/path") == 0); + // test ref fragment always wins + base = UriType("/path#frag"); + ref = UriType(""); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "/path") == 0); -// Examples from RFC3896 -base = UriType("http://a/b/c/d;p?q"); -ref = UriType("g:h"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "g:h") == 0); -ref = UriType("g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g") == 0); -ref = UriType("./g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g") == 0); -ref = UriType("g/"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g/") == 0); -ref = UriType("/g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); -ref = UriType("//g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://g") == 0); -ref = UriType("?y"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?y") == 0); -ref = UriType("g?y"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g?y") == 0); -ref = UriType("#s"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?q#s") == 0); -ref = UriType("g#s"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g#s") == 0); -ref = UriType("g?y#s"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g?y#s") == 0); -ref = UriType(";x"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/;x") == 0); -ref = UriType("g;x"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g;x") == 0); -ref = UriType("g;x?y#s"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g;x?y#s") == 0); -ref = UriType(""); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?q") == 0); -ref = UriType("."); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/") == 0); -ref = UriType("./"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/") == 0); -ref = UriType(".."); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/") == 0); -ref = UriType("../"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/") == 0); -ref = UriType("../g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/g") == 0); -ref = UriType("../.."); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/") == 0); -ref = UriType("../../"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/") == 0); -ref = UriType("../../g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); -ref = UriType("../../../g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); -ref = UriType("../../../../g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); -ref = UriType("/./g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); -ref = UriType("/../g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); -ref = UriType("g."); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g.") == 0); -ref = UriType(".g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/.g") == 0); -ref = UriType("g.."); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g..") == 0); -ref = UriType("..g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/..g") == 0); -ref = UriType("g#s/../x"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g#s/../x") == 0); + // Examples from RFC3896 + base = UriType("http://a/b/c/d;p?q"); + ref = UriType("g:h"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "g:h") == 0); + ref = UriType("g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g") == 0); + ref = UriType("./g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g") == 0); + ref = UriType("g/"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g/") == 0); + ref = UriType("/g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); + ref = UriType("//g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://g") == 0); + ref = UriType("?y"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?y") == 0); + ref = UriType("g?y"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g?y") == 0); + ref = UriType("#s"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?q#s") == 0); + ref = UriType("g#s"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g#s") == 0); + ref = UriType("g?y#s"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g?y#s") == 0); + ref = UriType(";x"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/;x") == 0); + ref = UriType("g;x"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g;x") == 0); + ref = UriType("g;x?y#s"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g;x?y#s") == 0); + ref = UriType(""); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/d;p?q") == 0); + ref = UriType("."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/") == 0); + ref = UriType("./"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/") == 0); + ref = UriType(".."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/") == 0); + ref = UriType("../"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/") == 0); + ref = UriType("../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/g") == 0); + ref = UriType("../.."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/") == 0); + ref = UriType("../../"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/") == 0); + ref = UriType("../../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); + ref = UriType("../../../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); + ref = UriType("../../../../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); + ref = UriType("/./g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); + ref = UriType("/../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/g") == 0); + ref = UriType("g."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g.") == 0); + ref = UriType(".g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/.g") == 0); + ref = UriType("g.."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g..") == 0); + ref = UriType("..g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/..g") == 0); + ref = UriType("g#s/../x"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://a/b/c/g#s/../x") == 0); } TEST(Uri, Resolve_UTF16) { -typedef GenericValue > Value16; -typedef GenericUri UriType; -CrtAllocator allocator; + typedef GenericValue > Value16; + typedef GenericUri UriType; + CrtAllocator allocator; -// ref is full uri -UriType base = UriType(L"http://auth/path/#frag"); -UriType ref = UriType(L"http://newauth/newpath#newfrag"); -UriType res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://newauth/newpath#newfrag") == 0); + // ref is full uri + UriType base = UriType(L"http://auth/path/#frag"); + UriType ref = UriType(L"http://newauth/newpath#newfrag"); + UriType res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://newauth/newpath#newfrag") == 0); -base = UriType(L"/path/#frag"); -ref = UriType(L"http://newauth/newpath#newfrag"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://newauth/newpath#newfrag") == 0); + base = UriType(L"/path/#frag"); + ref = UriType(L"http://newauth/newpath#newfrag"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://newauth/newpath#newfrag") == 0); -// ref is alternate uri -base = UriType(L"http://auth/path/#frag"); -ref = UriType(L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); + // ref is alternate uri + base = UriType(L"http://auth/path/#frag"); + ref = UriType(L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f") == 0); -// ref is absolute path -base = UriType(L"http://auth/path/#"); -ref = UriType(L"/newpath#newfrag"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/newpath#newfrag") == 0); + // ref is absolute path + base = UriType(L"http://auth/path/#"); + ref = UriType(L"/newpath#newfrag"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/newpath#newfrag") == 0); -// ref is relative path -base = UriType(L"http://auth/path/file.json#frag"); -ref = UriType(L"newfile.json#"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/path/newfile.json#") == 0); + // ref is relative path + base = UriType(L"http://auth/path/file.json#frag"); + ref = UriType(L"newfile.json#"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/path/newfile.json#") == 0); -base = UriType(L"http://auth/path/file.json#frag/stuff"); -ref = UriType(L"newfile.json#newfrag/newstuff"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/path/newfile.json#newfrag/newstuff") == 0); + base = UriType(L"http://auth/path/file.json#frag/stuff"); + ref = UriType(L"newfile.json#newfrag/newstuff"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/path/newfile.json#newfrag/newstuff") == 0); -base = UriType(L"file.json", &allocator); -ref = UriType(L"newfile.json", &base.GetAllocator()); -res = ref.Resolve(base, &ref.GetAllocator()); -EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0); + base = UriType(L"file.json", &allocator); + ref = UriType(L"newfile.json", &base.GetAllocator()); + res = ref.Resolve(base, &ref.GetAllocator()); + EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0); -base = UriType(L"file.json", &allocator); -ref = UriType(L"./newfile.json", &allocator); -res = ref.Resolve(base, &allocator); -EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0); + base = UriType(L"file.json", &allocator); + ref = UriType(L"./newfile.json", &allocator); + res = ref.Resolve(base, &allocator); + EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0); -base = UriType(L"file.json"); -ref = UriType(L"parent/../newfile.json"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0); + base = UriType(L"file.json"); + ref = UriType(L"parent/../newfile.json"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0); -base = UriType(L"file.json"); -ref = UriType(L"parent/./newfile.json"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"parent/newfile.json") == 0); + base = UriType(L"file.json"); + ref = UriType(L"parent/./newfile.json"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"parent/newfile.json") == 0); -base = UriType(L"file.json"); -ref = UriType(L"../../parent/.././newfile.json"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0); + base = UriType(L"file.json"); + ref = UriType(L"../../parent/.././newfile.json"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"newfile.json") == 0); -// This adds a joining slash so resolved length is base length + ref length + 1 -base = UriType(L"http://auth"); -ref = UriType(L"newfile.json"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/newfile.json") == 0); + // This adds a joining slash so resolved length is base length + ref length + 1 + base = UriType(L"http://auth"); + ref = UriType(L"newfile.json"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://auth/newfile.json") == 0); -// ref is fragment -base = UriType(L"#frag/stuff"); -ref = UriType(L"#newfrag/newstuff"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"#newfrag/newstuff") == 0); + // ref is fragment + base = UriType(L"#frag/stuff"); + ref = UriType(L"#newfrag/newstuff"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"#newfrag/newstuff") == 0); -// test ref fragment always wins -base = UriType(L"/path#frag"); -ref = UriType(L""); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"/path") == 0); + // test ref fragment always wins + base = UriType(L"/path#frag"); + ref = UriType(L""); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"/path") == 0); -// Examples from RFC3896 -base = UriType(L"http://a/b/c/d;p?q"); -ref = UriType(L"g:h"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"g:h") == 0); -ref = UriType(L"g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g") == 0); -ref = UriType(L"./g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g") == 0); -ref = UriType(L"g/"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g/") == 0); -ref = UriType(L"/g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); -ref = UriType(L"//g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://g") == 0); -ref = UriType(L"?y"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?y") == 0); -ref = UriType(L"g?y"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g?y") == 0); -ref = UriType(L"#s"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?q#s") == 0); -ref = UriType(L"g#s"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g#s") == 0); -ref = UriType(L"g?y#s"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g?y#s") == 0); -ref = UriType(L";x"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/;x") == 0); -ref = UriType(L"g;x"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g;x") == 0); -ref = UriType(L"g;x?y#s"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g;x?y#s") == 0); -ref = UriType(L""); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?q") == 0); -ref = UriType(L"."); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/") == 0); -ref = UriType(L"./"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/") == 0); -ref = UriType(L".."); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/") == 0); -ref = UriType(L"../"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/") == 0); -ref = UriType(L"../g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/g") == 0); -ref = UriType(L"../.."); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/") == 0); -ref = UriType(L"../../"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/") == 0); -ref = UriType(L"../../g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); -ref = UriType(L"../../../g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); -ref = UriType(L"../../../../g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); -ref = UriType(L"/./g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); -ref = UriType(L"/../g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); -ref = UriType(L"g."); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g.") == 0); -ref = UriType(L".g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/.g") == 0); -ref = UriType(L"g.."); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g..") == 0); -ref = UriType(L"..g"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/..g") == 0); -ref = UriType(L"g#s/../x"); -res = ref.Resolve(base); -EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g#s/../x") == 0); + // Examples from RFC3896 + base = UriType(L"http://a/b/c/d;p?q"); + ref = UriType(L"g:h"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"g:h") == 0); + ref = UriType(L"g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g") == 0); + ref = UriType(L"./g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g") == 0); + ref = UriType(L"g/"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g/") == 0); + ref = UriType(L"/g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); + ref = UriType(L"//g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://g") == 0); + ref = UriType(L"?y"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?y") == 0); + ref = UriType(L"g?y"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g?y") == 0); + ref = UriType(L"#s"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?q#s") == 0); + ref = UriType(L"g#s"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g#s") == 0); + ref = UriType(L"g?y#s"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g?y#s") == 0); + ref = UriType(L";x"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/;x") == 0); + ref = UriType(L"g;x"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g;x") == 0); + ref = UriType(L"g;x?y#s"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g;x?y#s") == 0); + ref = UriType(L""); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/d;p?q") == 0); + ref = UriType(L"."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/") == 0); + ref = UriType(L"./"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/") == 0); + ref = UriType(L".."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/") == 0); + ref = UriType(L"../"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/") == 0); + ref = UriType(L"../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/g") == 0); + ref = UriType(L"../.."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/") == 0); + ref = UriType(L"../../"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/") == 0); + ref = UriType(L"../../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); + ref = UriType(L"../../../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); + ref = UriType(L"../../../../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); + ref = UriType(L"/./g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); + ref = UriType(L"/../g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/g") == 0); + ref = UriType(L"g."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g.") == 0); + ref = UriType(L".g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/.g") == 0); + ref = UriType(L"g.."); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g..") == 0); + ref = UriType(L"..g"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/..g") == 0); + ref = UriType(L"g#s/../x"); + res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), L"http://a/b/c/g#s/../x") == 0); +} + +TEST(Uri, Equals) { + typedef GenericUri UriType; + + UriType a = UriType("http://a/a#a"); + UriType b = UriType("http://a/a#b"); + UriType c = a; + + EXPECT_TRUE(a == a); + EXPECT_TRUE(a == c); + EXPECT_TRUE(a != b); +} + +TEST(Uri, Match) { + typedef GenericUri UriType; + + UriType a = UriType("http://a/a#a"); + UriType b = UriType("http://a/a#b"); + UriType c = a; + UriType d; + + EXPECT_TRUE(a.Match(a)); + EXPECT_TRUE(a.Match(c)); + EXPECT_FALSE(a.Match(b)); + EXPECT_FALSE(a.Match(b, true)); + EXPECT_TRUE(a.Match(b, false)); // Base Uri same + EXPECT_FALSE(a.Match(d)); + EXPECT_FALSE(d.Match(a)); } #if defined(_MSC_VER) || defined(__clang__) From f6ebcb2008f0e3f06c8d4b2a81d6295989ba4dd5 Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Wed, 9 Jun 2021 16:38:40 +0100 Subject: [PATCH 17/45] fix Uri.Match optional arg --- include/rapidjson/uri.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/uri.h b/include/rapidjson/uri.h index f70c388..97c84f1 100644 --- a/include/rapidjson/uri.h +++ b/include/rapidjson/uri.h @@ -134,7 +134,7 @@ public: return !Match(rhs, true); } - bool Match(const GenericUri& uri, bool full) const { + bool Match(const GenericUri& uri, bool full = true) const { Ch* s1; Ch* s2; if (full) { From 12b88efa6f35ae820bab05a60b7ca5d73113e41c Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Wed, 9 Jun 2021 18:11:04 +0100 Subject: [PATCH 18/45] fix coverage again --- include/rapidjson/uri.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/include/rapidjson/uri.h b/include/rapidjson/uri.h index 97c84f1..6b80147 100644 --- a/include/rapidjson/uri.h +++ b/include/rapidjson/uri.h @@ -417,11 +417,8 @@ private: if (slashpos == 2 && path_[pathpos] == '.' && path_[pathpos + 1] == '.') { // Backup a .. segment in the new path_ // We expect to find a previously added slash at the end or nothing + RAPIDJSON_ASSERT(newpos == 0 || path_[newpos - 1] == '/'); size_t lastslashpos = newpos; - while (lastslashpos > 0) { - if (path_[lastslashpos - 1] == '/') break; - lastslashpos--; - } // Make sure we don't go beyond the start segment if (lastslashpos > 1) { // Find the next to last slash and back up to it From 8c29a7b4935e5d873a5f8e0a95388db8b35a3777 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 11 Jun 2021 11:49:14 +0800 Subject: [PATCH 19/45] Fix Pointer::Append() crash for custom allocator on Windows Fix #1899 --- include/rapidjson/pointer.h | 2 +- test/unittest/pointertest.cpp | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/pointer.h b/include/rapidjson/pointer.h index 90e5903..9621442 100644 --- a/include/rapidjson/pointer.h +++ b/include/rapidjson/pointer.h @@ -163,7 +163,7 @@ public: GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} //! Copy constructor. - GenericPointer(const GenericPointer& rhs) : allocator_(rhs.allocator_), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + GenericPointer(const GenericPointer& rhs) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { *this = rhs; } diff --git a/test/unittest/pointertest.cpp b/test/unittest/pointertest.cpp index 4371803..50c678e 100644 --- a/test/unittest/pointertest.cpp +++ b/test/unittest/pointertest.cpp @@ -475,7 +475,9 @@ TEST(Pointer, CopyConstructor) { EXPECT_EQ(1u, q.GetTokens()[1].length); EXPECT_STREQ("0", q.GetTokens()[1].name); EXPECT_EQ(0u, q.GetTokens()[1].index); - EXPECT_EQ(&p.GetAllocator(), &q.GetAllocator()); + + // Copied pointer needs to have its own allocator + EXPECT_NE(&p.GetAllocator(), &q.GetAllocator()); } // Static tokens @@ -1668,3 +1670,14 @@ TEST(Pointer, Issue483) { value.SetString(mystr.c_str(), static_cast(mystr.length()), document.GetAllocator()); myjson::Pointer(path.c_str()).Set(document, value, document.GetAllocator()); } + +TEST(Pointer, Issue1899) { + typedef GenericPointer > PointerType; + PointerType p; + PointerType q = p.Append("foo"); + EXPECT_TRUE(PointerType("/foo") == q); + q = q.Append(1234); + EXPECT_TRUE(PointerType("/foo/1234") == q); + q = q.Append(""); + EXPECT_TRUE(PointerType("/foo/1234/") == q); +} \ No newline at end of file From a21cf9f7b800c86f75d4f0ff9f9de0a69ebb14af Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Mon, 14 Jun 2021 11:35:00 +0100 Subject: [PATCH 20/45] equiv fix for issue 1899 --- include/rapidjson/uri.h | 16 ++++++------- test/unittest/uritest.cpp | 50 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/include/rapidjson/uri.h b/include/rapidjson/uri.h index 6b80147..6353c32 100644 --- a/include/rapidjson/uri.h +++ b/include/rapidjson/uri.h @@ -62,7 +62,7 @@ public: #endif //! Copy constructor - GenericUri(const GenericUri& rhs) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(rhs.allocator_), ownAllocator_() { + GenericUri(const GenericUri& rhs) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(), ownAllocator_() { *this = rhs; } @@ -101,19 +101,19 @@ public: } const Ch* GetString() const { return uri_; } - SizeType GetStringLength() const { return internal::StrLen(uri_); } + SizeType GetStringLength() const { return uri_ == 0 ? 0 : internal::StrLen(uri_); } const Ch* GetBaseString() const { return base_; } - SizeType GetBaseStringLength() const { return internal::StrLen(base_); } + SizeType GetBaseStringLength() const { return base_ == 0 ? 0 : internal::StrLen(base_); } const Ch* GetSchemeString() const { return scheme_; } - SizeType GetSchemeStringLength() const { return internal::StrLen(scheme_); } + SizeType GetSchemeStringLength() const { return scheme_ == 0 ? 0 : internal::StrLen(scheme_); } const Ch* GetAuthString() const { return auth_; } - SizeType GetAuthStringLength() const { return internal::StrLen(auth_); } + SizeType GetAuthStringLength() const { return auth_ == 0 ? 0 : internal::StrLen(auth_); } const Ch* GetPathString() const { return path_; } - SizeType GetPathStringLength() const { return internal::StrLen(path_); } + SizeType GetPathStringLength() const { return path_ == 0 ? 0 : internal::StrLen(path_); } const Ch* GetQueryString() const { return query_; } - SizeType GetQueryStringLength() const { return internal::StrLen(query_); } + SizeType GetQueryStringLength() const { return query_ == 0 ? 0 : internal::StrLen(query_); } const Ch* GetFragString() const { return frag_; } - SizeType GetFragStringLength() const { return internal::StrLen(frag_); } + SizeType GetFragStringLength() const { return frag_ == 0 ? 0 : internal::StrLen(frag_); } #if RAPIDJSON_HAS_STDSTRING static String Get(const GenericUri& uri) { return String(uri.GetString(), uri.GetStringLength()); } diff --git a/test/unittest/uritest.cpp b/test/unittest/uritest.cpp index 7fa7b93..6cfa27d 100644 --- a/test/unittest/uritest.cpp +++ b/test/unittest/uritest.cpp @@ -29,6 +29,26 @@ RAPIDJSON_DIAG_OFF(4822) // local class member function does not have a body using namespace rapidjson; +TEST(Uri, DefaultConstructor) { + typedef GenericUri UriType; + UriType u; + EXPECT_TRUE(u.GetSchemeString() == 0); + EXPECT_TRUE(u.GetAuthString() == 0); + EXPECT_TRUE(u.GetPathString() == 0); + EXPECT_TRUE(u.GetBaseString() == 0); + EXPECT_TRUE(u.GetQueryString() == 0); + EXPECT_TRUE(u.GetFragString() == 0); + EXPECT_TRUE(u.GetString() == 0); + EXPECT_TRUE(u.GetSchemeStringLength() == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(u.GetPathStringLength() == 0); + EXPECT_TRUE(u.GetBaseStringLength() == 0); + EXPECT_TRUE(u.GetQueryStringLength() == 0); + EXPECT_TRUE(u.GetFragStringLength() == 0); + EXPECT_TRUE(u.GetStringLength() == 0); +} + + TEST(Uri, Parse) { typedef GenericUri > UriType; MemoryPoolAllocator allocator; @@ -256,6 +276,27 @@ TEST(Uri, Parse_UTF16) { EXPECT_TRUE(u.GetFragStringLength() == len); } +TEST(Uri, CopyConstructor) { + typedef GenericUri UriType; + CrtAllocator allocator; + + UriType u("http://auth/path/xxx?query#frag", &allocator); + UriType u2(u); + EXPECT_TRUE(u == u2); + EXPECT_NE(&u.GetAllocator(), &u2.GetAllocator()); +} + +TEST(Uri, Assignment) { + typedef GenericUri UriType; + CrtAllocator allocator; + + UriType u("http://auth/path/xxx?query#frag", &allocator); + UriType u2; + u2 = u; + EXPECT_TRUE(u == u2); + EXPECT_NE(&u.GetAllocator(), &u2.GetAllocator()); +} + TEST(Uri, Resolve) { typedef GenericUri UriType; CrtAllocator allocator; @@ -648,6 +689,15 @@ TEST(Uri, Match) { EXPECT_FALSE(d.Match(a)); } +TEST(Uri, Issue1899) { + typedef GenericUri > UriType; + + UriType base = UriType("http://auth/path/#frag"); + UriType ref = UriType("http://newauth/newpath#newfrag"); + UriType res = ref.Resolve(base); + EXPECT_TRUE(StrCmp(res.GetString(), "http://newauth/newpath#newfrag") == 0); +} + #if defined(_MSC_VER) || defined(__clang__) RAPIDJSON_DIAG_POP #endif From 8d16abd980122b4d4b24aad345efb95373c11c18 Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Wed, 30 Jun 2021 17:09:52 +0100 Subject: [PATCH 21/45] Uri Parse improvements --- include/rapidjson/uri.h | 33 +++++++++++++-------------------- test/unittest/uritest.cpp | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/include/rapidjson/uri.h b/include/rapidjson/uri.h index 6353c32..7de7b80 100644 --- a/include/rapidjson/uri.h +++ b/include/rapidjson/uri.h @@ -295,24 +295,17 @@ private: // Look for auth (//([^/?#]*))? auth_ = scheme_ + GetSchemeStringLength() + 1; *auth_ = '\0'; - if (start < len) { - pos1 = start; - while (pos1 < len) { - if (uri[pos1] == '/' && uri[pos1 + 1] == '/') break; - pos1++; - } - if (pos1 == start) { - pos2 = start + 2; - while (pos2 < len) { - if (uri[pos2] == '/') break; - if (uri[pos2] == '?') break; - if (uri[pos2] == '#') break; - pos2++; - } - std::memcpy(auth_, &uri[start], (pos2 - start) * sizeof(Ch)); - auth_[pos2 - start] = '\0'; - start = pos2; + if (start < len - 1 && uri[start] == '/' && uri[start + 1] == '/') { + pos2 = start + 2; + while (pos2 < len) { + if (uri[pos2] == '/') break; + if (uri[pos2] == '?') break; + if (uri[pos2] == '#') break; + pos2++; } + std::memcpy(auth_, &uri[start], (pos2 - start) * sizeof(Ch)); + auth_[pos2 - start] = '\0'; + start = pos2; } // Look for path ([^?#]*) path_ = auth_ + GetAuthStringLength() + 1; @@ -335,8 +328,8 @@ private: // Look for query (\?([^#]*))? query_ = path_ + GetPathStringLength() + 1; *query_ = '\0'; - if (start < len) { - pos2 = start; + if (start < len && uri[start] == '?') { + pos2 = start + 1; while (pos2 < len) { if (uri[pos2] == '#') break; pos2++; @@ -350,7 +343,7 @@ private: // Look for fragment (#(.*))? frag_ = query_ + GetQueryStringLength() + 1; *frag_ = '\0'; - if (start < len) { + if (start < len && uri[start] == '#') { std::memcpy(frag_, &uri[start], (len - start) * sizeof(Ch)); frag_[len - start] = '\0'; } diff --git a/test/unittest/uritest.cpp b/test/unittest/uritest.cpp index 6cfa27d..5506aa1 100644 --- a/test/unittest/uritest.cpp +++ b/test/unittest/uritest.cpp @@ -160,6 +160,14 @@ TEST(Uri, Parse) { EXPECT_TRUE(u.GetBaseStringLength() == 0); EXPECT_TRUE(StrCmp(u.GetFragString(), "#frag/stuff") == 0); EXPECT_TRUE(u.GetFragStringLength() == len); + + // Incomplete auth treated as path + str = "http:/"; + const UriType u2 = UriType(str); + EXPECT_TRUE(StrCmp(u2.GetSchemeString(), "http:") == 0); + EXPECT_TRUE(u2.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u2.GetPathString(), "/") == 0); + EXPECT_TRUE(StrCmp(u2.GetBaseString(), "http:/") == 0); } TEST(Uri, Parse_UTF16) { @@ -274,6 +282,13 @@ TEST(Uri, Parse_UTF16) { EXPECT_TRUE(u.GetBaseStringLength() == 0); EXPECT_TRUE(StrCmp(u.GetFragString(), L"#frag/stuff") == 0); EXPECT_TRUE(u.GetFragStringLength() == len); + + // Incomplete auth treated as path + u = UriType(L"http:/"); + EXPECT_TRUE(StrCmp(u.GetSchemeString(), L"http:") == 0); + EXPECT_TRUE(u.GetAuthStringLength() == 0); + EXPECT_TRUE(StrCmp(u.GetPathString(), L"/") == 0); + EXPECT_TRUE(StrCmp(u.GetBaseString(), L"http:/") == 0); } TEST(Uri, CopyConstructor) { From bb0621108802d7839059cede4dee5daca7205e28 Mon Sep 17 00:00:00 2001 From: jack_perisich Date: Tue, 27 Jul 2021 19:50:51 -0400 Subject: [PATCH 22/45] Fix small errors in dtoa output for certain doubles --- include/rapidjson/internal/dtoa.h | 10 +++++++--- test/unittest/dtoatest.cpp | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/rapidjson/internal/dtoa.h b/include/rapidjson/internal/dtoa.h index 621402f..9f6ae3b 100644 --- a/include/rapidjson/internal/dtoa.h +++ b/include/rapidjson/internal/dtoa.h @@ -58,7 +58,11 @@ inline int CountDecimalDigit32(uint32_t n) { } inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { - static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + static const uint64_t kPow10[] = { 1U, 10U, 100U, 1000U, 10000U, 100000U, 1000000U, 10000000U, 100000000U, + 1000000000U, 10000000000U, 100000000000U, 1000000000000U, + 10000000000000U, 100000000000000U, 1000000000000000U, + 10000000000000000U, 100000000000000000U, 1000000000000000000U, + 10000000000000000000U }; const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); const DiyFp wp_w = Mp - W; uint32_t p1 = static_cast(Mp.f >> -one.e); @@ -86,7 +90,7 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff uint64_t tmp = (static_cast(p1) << -one.e) + p2; if (tmp <= delta) { *K += kappa; - GrisuRound(buffer, *len, delta, tmp, static_cast(kPow10[kappa]) << -one.e, wp_w.f); + GrisuRound(buffer, *len, delta, tmp, kPow10[kappa] << -one.e, wp_w.f); return; } } @@ -103,7 +107,7 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff if (p2 < delta) { *K += kappa; int index = -kappa; - GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[index] : 0)); + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 20 ? kPow10[index] : 0)); return; } } diff --git a/test/unittest/dtoatest.cpp b/test/unittest/dtoatest.cpp index 66576bd..3ec8982 100644 --- a/test/unittest/dtoatest.cpp +++ b/test/unittest/dtoatest.cpp @@ -38,6 +38,7 @@ TEST(dtoa, normal) { TEST_DTOA(0.123456789012, "0.123456789012"); TEST_DTOA(1234567.8, "1234567.8"); TEST_DTOA(-79.39773355813419, "-79.39773355813419"); + TEST_DTOA(-36.973846435546875, "-36.973846435546875"); TEST_DTOA(0.000001, "0.000001"); TEST_DTOA(0.0000001, "1e-7"); TEST_DTOA(1e30, "1e30"); From b952a592a480e551517468bbf4794061653b840c Mon Sep 17 00:00:00 2001 From: Kyrega Date: Tue, 10 Aug 2021 09:04:30 +0200 Subject: [PATCH 23/45] Fix RawNumber for longer char types --- include/rapidjson/internal/biginteger.h | 15 ++- include/rapidjson/internal/strtod.h | 15 ++- include/rapidjson/reader.h | 38 ++++--- test/unittest/readertest.cpp | 142 ++++++++++++++++++++++++ 4 files changed, 180 insertions(+), 30 deletions(-) diff --git a/include/rapidjson/internal/biginteger.h b/include/rapidjson/internal/biginteger.h index 1245578..7b14563 100644 --- a/include/rapidjson/internal/biginteger.h +++ b/include/rapidjson/internal/biginteger.h @@ -37,7 +37,8 @@ public: digits_[0] = u; } - BigInteger(const char* decimals, size_t length) : count_(1) { + template + BigInteger(const Ch* decimals, size_t length) : count_(1) { RAPIDJSON_ASSERT(length > 0); digits_[0] = 0; size_t i = 0; @@ -221,7 +222,8 @@ public: bool IsZero() const { return count_ == 1 && digits_[0] == 0; } private: - void AppendDecimal64(const char* begin, const char* end) { + template + void AppendDecimal64(const Ch* begin, const Ch* end) { uint64_t u = ParseUint64(begin, end); if (IsZero()) *this = u; @@ -236,11 +238,12 @@ private: digits_[count_++] = digit; } - static uint64_t ParseUint64(const char* begin, const char* end) { + template + static uint64_t ParseUint64(const Ch* begin, const Ch* end) { uint64_t r = 0; - for (const char* p = begin; p != end; ++p) { - RAPIDJSON_ASSERT(*p >= '0' && *p <= '9'); - r = r * 10u + static_cast(*p - '0'); + for (const Ch* p = begin; p != end; ++p) { + RAPIDJSON_ASSERT(*p >= Ch('0') && *p <= Ch('9')); + r = r * 10u + static_cast(*p - Ch('0')); } return r; } diff --git a/include/rapidjson/internal/strtod.h b/include/rapidjson/internal/strtod.h index d61a67a..55f0e38 100644 --- a/include/rapidjson/internal/strtod.h +++ b/include/rapidjson/internal/strtod.h @@ -128,17 +128,18 @@ inline bool StrtodFast(double d, int p, double* result) { } // Compute an approximation and see if it is within 1/2 ULP -inline bool StrtodDiyFp(const char* decimals, int dLen, int dExp, double* result) { +template +inline bool StrtodDiyFp(const Ch* decimals, int dLen, int dExp, double* result) { uint64_t significand = 0; int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 for (; i < dLen; i++) { if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || - (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) + (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > Ch('5'))) break; - significand = significand * 10u + static_cast(decimals[i] - '0'); + significand = significand * 10u + static_cast(decimals[i] - Ch('0')); } - if (i < dLen && decimals[i] >= '5') // Rounding + if (i < dLen && decimals[i] >= Ch('5')) // Rounding significand++; int remaining = dLen - i; @@ -205,7 +206,8 @@ inline bool StrtodDiyFp(const char* decimals, int dLen, int dExp, double* result return halfWay - static_cast(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); } -inline double StrtodBigInteger(double approx, const char* decimals, int dLen, int dExp) { +template +inline double StrtodBigInteger(double approx, const Ch* decimals, int dLen, int dExp) { RAPIDJSON_ASSERT(dLen >= 0); const BigInteger dInt(decimals, static_cast(dLen)); Double a(approx); @@ -223,7 +225,8 @@ inline double StrtodBigInteger(double approx, const char* decimals, int dLen, in return a.NextPositiveDouble(); } -inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) { +template +inline double StrtodFullPrecision(double d, int p, const Ch* decimals, size_t length, size_t decimalPosition, int exp) { RAPIDJSON_ASSERT(d >= 0.0); RAPIDJSON_ASSERT(length >= 1); diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 09ace4e..8f7e533 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -1404,11 +1404,11 @@ private: } #endif // RAPIDJSON_NEON - template + template class NumberStream; - template - class NumberStream { + template + class NumberStream { public: typedef typename InputStream::Ch Ch; @@ -1421,7 +1421,7 @@ private: size_t Tell() { return is.Tell(); } size_t Length() { return 0; } - const char* Pop() { return 0; } + const StackCharacter* Pop() { return 0; } protected: NumberStream& operator=(const NumberStream&); @@ -1429,35 +1429,35 @@ private: InputStream& is; }; - template - class NumberStream : public NumberStream { - typedef NumberStream Base; + template + class NumberStream : public NumberStream { + typedef NumberStream Base; public: NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {} RAPIDJSON_FORCEINLINE Ch TakePush() { - stackStream.Put(static_cast(Base::is.Peek())); + stackStream.Put(static_cast(Base::is.Peek())); return Base::is.Take(); } - RAPIDJSON_FORCEINLINE void Push(char c) { + RAPIDJSON_FORCEINLINE void Push(StackCharacter c) { stackStream.Put(c); } size_t Length() { return stackStream.Length(); } - const char* Pop() { + const StackCharacter* Pop() { stackStream.Put('\0'); return stackStream.Pop(); } private: - StackStream stackStream; + StackStream stackStream; }; - template - class NumberStream : public NumberStream { - typedef NumberStream Base; + template + class NumberStream : public NumberStream { + typedef NumberStream Base; public: NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {} @@ -1466,8 +1466,10 @@ private: template void ParseNumber(InputStream& is, Handler& handler) { + typedef std::conditional<(parseFlags & kParseNumbersAsStringsFlag) != 0, typename TargetEncoding::Ch, char>::type NumberCharacter; + internal::StreamLocalCopy copy(is); - NumberStream(s.Length()); - StringStream srcStream(s.Pop()); + GenericStringStream> srcStream(s.Pop()); StackStream dstStream(stack_); while (numCharsToCopy--) { - Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); + Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); } dstStream.Put('\0'); const typename TargetEncoding::Ch* str = dstStream.Pop(); @@ -1705,7 +1707,7 @@ private: } else { size_t length = s.Length(); - const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. + const NumberCharacter* decimal = s.Pop(); // Pop stack no matter if it will be used or not. if (useDouble) { int p = exp + expFrac; diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index d3fcdef..995d6db 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -1392,6 +1392,36 @@ private: std::istream& is_; }; +class WIStreamWrapper { +public: + typedef wchar_t Ch; + + WIStreamWrapper(std::wistream& is) : is_(is) {} + + Ch Peek() const { + int c = is_.peek(); + return c == std::char_traits::eof() ? Ch('\0') : static_cast(c); + } + + Ch Take() { + int c = is_.get(); + return c == std::char_traits::eof() ? Ch('\0') : static_cast(c); + } + + size_t Tell() const { return static_cast(is_.tellg()); } + + Ch* PutBegin() { assert(false); return 0; } + void Put(Ch) { assert(false); } + void Flush() { assert(false); } + size_t PutEnd(Ch*) { assert(false); return 0; } + +private: + WIStreamWrapper(const IStreamWrapper&); + WIStreamWrapper& operator=(const IStreamWrapper&); + + std::wistream& is_; +}; + TEST(Reader, Parse_IStreamWrapper_StringStream) { const char* json = "[1,2,3,4]"; @@ -1991,6 +2021,118 @@ TEST(Reader, NumbersAsStrings) { } } +struct NumbersAsStringsHandlerWChar_t { + bool Null() { return true; } + bool Bool(bool) { return true; } + bool Int(int) { return true; } + bool Uint(unsigned) { return true; } + bool Int64(int64_t) { return true; } + bool Uint64(uint64_t) { return true; } + bool Double(double) { return true; } + // 'str' is not null-terminated + bool RawNumber(const wchar_t* str, SizeType length, bool) { + EXPECT_TRUE(str != 0); + EXPECT_TRUE(expected_len_ == length); + EXPECT_TRUE(wcsncmp(str, expected_, length) == 0); + return true; + } + bool String(const wchar_t*, SizeType, bool) { return true; } + bool StartObject() { return true; } + bool Key(const wchar_t*, SizeType, bool) { return true; } + bool EndObject(SizeType) { return true; } + bool StartArray() { return true; } + bool EndArray(SizeType) { return true; } + + NumbersAsStringsHandlerWChar_t(const wchar_t* expected) + : expected_(expected) + , expected_len_(wcslen(expected)) {} + + const wchar_t* expected_; + size_t expected_len_; +}; + +TEST(Reader, NumbersAsStringsWChar_t) { + { + const wchar_t* json = L"{ \"pi\": 3.1416 } "; + GenericStringStream> s(json); + NumbersAsStringsHandlerWChar_t h(L"3.1416"); + GenericReader, UTF16<>> reader; + EXPECT_TRUE(reader.Parse(s, h)); + } + { + wchar_t* json = StrDup(L"{ \"pi\": 3.1416 } "); + GenericInsituStringStream> s(json); + NumbersAsStringsHandlerWChar_t h(L"3.1416"); + GenericReader, UTF16<>> reader; + EXPECT_TRUE(reader.Parse(s, h)); + free(json); + } + { + const wchar_t* json = L"{ \"gigabyte\": 1.0e9 } "; + GenericStringStream> s(json); + NumbersAsStringsHandlerWChar_t h(L"1.0e9"); + GenericReader, UTF16<>> reader; + EXPECT_TRUE(reader.Parse(s, h)); + } + { + wchar_t* json = StrDup(L"{ \"gigabyte\": 1.0e9 } "); + GenericInsituStringStream> s(json); + NumbersAsStringsHandlerWChar_t h(L"1.0e9"); + GenericReader, UTF16<>> reader; + EXPECT_TRUE(reader.Parse(s, h)); + free(json); + } + { + const wchar_t* json = L"{ \"pi\": 314.159e-2 } "; + GenericStringStream> s(json); + NumbersAsStringsHandlerWChar_t h(L"314.159e-2"); + GenericReader, UTF16<>> reader; + EXPECT_TRUE(reader.Parse(s, h)); + } + { + wchar_t* json = StrDup(L"{ \"gigabyte\": 314.159e-2 } "); + GenericInsituStringStream> s(json); + NumbersAsStringsHandlerWChar_t h(L"314.159e-2"); + GenericReader, UTF16<>> reader; + EXPECT_TRUE(reader.Parse(s, h)); + free(json); + } + { + const wchar_t* json = L"{ \"negative\": -1.54321 } "; + GenericStringStream> s(json); + NumbersAsStringsHandlerWChar_t h(L"-1.54321"); + GenericReader, UTF16<>> reader; + EXPECT_TRUE(reader.Parse(s, h)); + } + { + wchar_t* json = StrDup(L"{ \"negative\": -1.54321 } "); + GenericInsituStringStream> s(json); + NumbersAsStringsHandlerWChar_t h(L"-1.54321"); + GenericReader, UTF16<>> reader; + EXPECT_TRUE(reader.Parse(s, h)); + free(json); + } + { + const wchar_t* json = L"{ \"pi\": 314.159e-2 } "; + std::wstringstream ss(json); + WIStreamWrapper s(ss); + NumbersAsStringsHandlerWChar_t h(L"314.159e-2"); + GenericReader, UTF16<>> reader; + EXPECT_TRUE(reader.Parse(s, h)); + } + { + wchar_t n1e319[321]; // '1' followed by 319 '0' + n1e319[0] = L'1'; + for(int i = 1; i < 320; i++) + n1e319[i] = L'0'; + n1e319[320] = L'\0'; + GenericStringStream> s(n1e319); + NumbersAsStringsHandlerWChar_t h(n1e319); + GenericReader, UTF16<>> reader; + EXPECT_TRUE(reader.Parse(s, h)); + } +} + template void TestTrailingCommas() { { From 7fac34f7bbe451b10ab70c08cf6820387f7a1d92 Mon Sep 17 00:00:00 2001 From: Kyrega Date: Tue, 10 Aug 2021 09:33:17 +0200 Subject: [PATCH 24/45] Added typename --- include/rapidjson/reader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 8f7e533..a4e5b3b 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -1466,7 +1466,7 @@ private: template void ParseNumber(InputStream& is, Handler& handler) { - typedef std::conditional<(parseFlags & kParseNumbersAsStringsFlag) != 0, typename TargetEncoding::Ch, char>::type NumberCharacter; + typedef typename std::conditional<(parseFlags & kParseNumbersAsStringsFlag) != 0, typename TargetEncoding::Ch, char>::type NumberCharacter; internal::StreamLocalCopy copy(is); NumberStream Date: Tue, 10 Aug 2021 09:40:03 +0200 Subject: [PATCH 25/45] Using unsigned for WIStreamWrapper --- test/unittest/readertest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 995d6db..c805eef 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -1399,12 +1399,12 @@ public: WIStreamWrapper(std::wistream& is) : is_(is) {} Ch Peek() const { - int c = is_.peek(); + unsigned c = is_.peek(); return c == std::char_traits::eof() ? Ch('\0') : static_cast(c); } Ch Take() { - int c = is_.get(); + unsigned c = is_.get(); return c == std::char_traits::eof() ? Ch('\0') : static_cast(c); } From 8710d7e9893e826001c4de7c45e247d9a0084e78 Mon Sep 17 00:00:00 2001 From: Kyrega Date: Tue, 10 Aug 2021 10:12:29 +0200 Subject: [PATCH 26/45] Do not depend on c++11 conditional --- include/rapidjson/reader.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index a4e5b3b..906f657 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -1464,9 +1464,24 @@ private: RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } }; + template + struct NumberCharacterConditional { + typedef char type; + }; + + template<> + struct NumberCharacterConditional { + typedef typename TargetEncoding::Ch type; + }; + + template<> + struct NumberCharacterConditional { + typedef char type; + }; + template void ParseNumber(InputStream& is, Handler& handler) { - typedef typename std::conditional<(parseFlags & kParseNumbersAsStringsFlag) != 0, typename TargetEncoding::Ch, char>::type NumberCharacter; + typedef NumberCharacterConditional<(parseFlags& kParseNumbersAsStringsFlag) != 0 >::type NumberCharacter; internal::StreamLocalCopy copy(is); NumberStream Date: Tue, 10 Aug 2021 10:20:18 +0200 Subject: [PATCH 27/45] Use rapidjson internal::SelectIf --- include/rapidjson/reader.h | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 906f657..542b7c0 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -1464,24 +1464,9 @@ private: RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } }; - template - struct NumberCharacterConditional { - typedef char type; - }; - - template<> - struct NumberCharacterConditional { - typedef typename TargetEncoding::Ch type; - }; - - template<> - struct NumberCharacterConditional { - typedef char type; - }; - template void ParseNumber(InputStream& is, Handler& handler) { - typedef NumberCharacterConditional<(parseFlags& kParseNumbersAsStringsFlag) != 0 >::type NumberCharacter; + typedef typename internal::SelectIf, typename TargetEncoding::Ch, char>::Type NumberCharacter; internal::StreamLocalCopy copy(is); NumberStream Date: Tue, 10 Aug 2021 10:46:21 +0200 Subject: [PATCH 28/45] No default template parameter for older compilers --- include/rapidjson/internal/biginteger.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/internal/biginteger.h b/include/rapidjson/internal/biginteger.h index 7b14563..009372b 100644 --- a/include/rapidjson/internal/biginteger.h +++ b/include/rapidjson/internal/biginteger.h @@ -37,7 +37,7 @@ public: digits_[0] = u; } - template + template BigInteger(const Ch* decimals, size_t length) : count_(1) { RAPIDJSON_ASSERT(length > 0); digits_[0] = 0; From a3d52c75b7d4099e86199659be7ccd2869781f70 Mon Sep 17 00:00:00 2001 From: Kyrega Date: Tue, 10 Aug 2021 10:51:45 +0200 Subject: [PATCH 29/45] No default template parameter for older compilers --- include/rapidjson/internal/biginteger.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/internal/biginteger.h b/include/rapidjson/internal/biginteger.h index 009372b..514a176 100644 --- a/include/rapidjson/internal/biginteger.h +++ b/include/rapidjson/internal/biginteger.h @@ -222,7 +222,7 @@ public: bool IsZero() const { return count_ == 1 && digits_[0] == 0; } private: - template + template void AppendDecimal64(const Ch* begin, const Ch* end) { uint64_t u = ParseUint64(begin, end); if (IsZero()) @@ -238,7 +238,7 @@ private: digits_[count_++] = digit; } - template + template static uint64_t ParseUint64(const Ch* begin, const Ch* end) { uint64_t r = 0; for (const Ch* p = begin; p != end; ++p) { From 22ee8b07c89fc791dd2a1e290539888bc1235fad Mon Sep 17 00:00:00 2001 From: Kyrega Date: Tue, 10 Aug 2021 11:00:40 +0200 Subject: [PATCH 30/45] Correct WIStreamWrapper --- test/unittest/readertest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index c805eef..057940f 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -1416,8 +1416,8 @@ public: size_t PutEnd(Ch*) { assert(false); return 0; } private: - WIStreamWrapper(const IStreamWrapper&); - WIStreamWrapper& operator=(const IStreamWrapper&); + WIStreamWrapper(const WIStreamWrapper&); + WIStreamWrapper& operator=(const WIStreamWrapper&); std::wistream& is_; }; From 553a3ea31fc28a7d16193c3bb08ee2a6da4e1637 Mon Sep 17 00:00:00 2001 From: Andrew <920076768@qq.com> Date: Fri, 24 Sep 2021 10:57:46 +0800 Subject: [PATCH 31/45] =?UTF-8?q?typo=20on=20documentation=20dom.zh-cn.md:?= =?UTF-8?q?=20"=E5=B7=B1=E4=BA=8E"=20->=20"=E5=B7=B2=E4=BA=8E"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/dom.zh-cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/dom.zh-cn.md b/doc/dom.zh-cn.md index 8fcd538..7a555dc 100644 --- a/doc/dom.zh-cn.md +++ b/doc/dom.zh-cn.md @@ -1,6 +1,6 @@ # DOM -文档对象模型(Document Object Model, DOM)是一种罝于内存中的 JSON 表示方式,以供查询及操作。我们己于 [教程](doc/tutorial.zh-cn.md) 中介绍了 DOM 的基本用法,本节将讲述一些细节及高级用法。 +文档对象模型(Document Object Model, DOM)是一种罝于内存中的 JSON 表示方式,以供查询及操作。我们已于 [教程](doc/tutorial.zh-cn.md) 中介绍了 DOM 的基本用法,本节将讲述一些细节及高级用法。 [TOC] From 9ea3f45dbdd12a1020c412aebfaa87717e8954f3 Mon Sep 17 00:00:00 2001 From: Steve Hanson Date: Fri, 24 Sep 2021 08:51:03 +0100 Subject: [PATCH 32/45] fix the warning --- include/rapidjson/uri.h | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/include/rapidjson/uri.h b/include/rapidjson/uri.h index 7de7b80..f93e508 100644 --- a/include/rapidjson/uri.h +++ b/include/rapidjson/uri.h @@ -238,20 +238,27 @@ private: // Allocate one block containing each part of the URI (5) plus base plus full URI, all null terminated. // Order: scheme, auth, path, query, frag, base, uri + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. size_t total = (3 * len + 7) * sizeof(Ch); scheme_ = static_cast(allocator_->Malloc(total)); *scheme_ = '\0'; - auth_ = scheme_ + 1; + auth_ = scheme_; + auth_++; *auth_ = '\0'; - path_ = auth_ + 1; + path_ = auth_; + path_++; *path_ = '\0'; - query_ = path_ + 1; + query_ = path_; + query_++; *query_ = '\0'; - frag_ = query_ + 1; + frag_ = query_; + frag_++; *frag_ = '\0'; - base_ = frag_ + 1; + base_ = frag_; + base_++; *base_ = '\0'; - uri_ = base_ + 1; + uri_ = base_; + uri_++; *uri_ = '\0'; return total; } @@ -293,7 +300,9 @@ private: } } // Look for auth (//([^/?#]*))? - auth_ = scheme_ + GetSchemeStringLength() + 1; + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + auth_ = scheme_ + GetSchemeStringLength(); + auth_++; *auth_ = '\0'; if (start < len - 1 && uri[start] == '/' && uri[start + 1] == '/') { pos2 = start + 2; @@ -308,7 +317,9 @@ private: start = pos2; } // Look for path ([^?#]*) - path_ = auth_ + GetAuthStringLength() + 1; + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + path_ = auth_ + GetAuthStringLength(); + path_++; *path_ = '\0'; if (start < len) { pos2 = start; @@ -326,7 +337,9 @@ private: } } // Look for query (\?([^#]*))? - query_ = path_ + GetPathStringLength() + 1; + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + query_ = path_ + GetPathStringLength(); + query_++; *query_ = '\0'; if (start < len && uri[start] == '?') { pos2 = start + 1; @@ -341,7 +354,9 @@ private: } } // Look for fragment (#(.*))? - frag_ = query_ + GetQueryStringLength() + 1; + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + frag_ = query_ + GetQueryStringLength(); + frag_++; *frag_ = '\0'; if (start < len && uri[start] == '#') { std::memcpy(frag_, &uri[start], (len - start) * sizeof(Ch)); From e6736d1baa5836fa4e39bc06e4569df8c1562635 Mon Sep 17 00:00:00 2001 From: Ivan Le Lann Date: Sat, 2 Oct 2021 15:26:17 +0200 Subject: [PATCH 33/45] Support CMake none targets When trying to import rapidjson with for exemple : fetchcontent_declare(rapidjson GIT_REPOSITORY https://github.com/Tencent/rapidjson.git) if your CMake/Clang is "bare metal", exemple given : set(CMAKE_SYSTEM_NAME none) set(CMAKE_SYSTEM_PROCESSOR x86_64) set(CMAKE_C_COMPILER_TARGET x86_64-elf-none) set(CMAKE_CXX_COMPILER_TARGET x86_64-elf-none) CMake fails to process CMakeLists.txt because of the switch on UNIX/CYGWIN/WIN32 for install directory. Error is: CMake Error at cmake-build-debug-clang/_deps/rapidjson-src/CMakeLists.txt:244 (INSTALL): INSTALL FILES given no DESTINATION! --- CMakeLists.txt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dc2072a..bdfdd67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -241,8 +241,10 @@ INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAM DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) # Install files -INSTALL(FILES - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake - ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake - DESTINATION "${CMAKE_INSTALL_DIR}" - COMPONENT dev) +IF(CMAKE_INSTALL_DIR) + INSTALL(FILES + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + DESTINATION "${CMAKE_INSTALL_DIR}" + COMPONENT dev) +ENDIF() From 14f1e37f850bace26aadce735bb204aaaaee9c2e Mon Sep 17 00:00:00 2001 From: ardb-uk <59880669+ardb-uk@users.noreply.github.com> Date: Tue, 12 Oct 2021 16:22:22 +0100 Subject: [PATCH 34/45] Resolve issue 1948 Correct instances of >> as they failed compilation. --- include/rapidjson/reader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 542b7c0..b37afff 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -1694,7 +1694,7 @@ private: } else { SizeType numCharsToCopy = static_cast(s.Length()); - GenericStringStream> srcStream(s.Pop()); + GenericStringStream > srcStream(s.Pop()); StackStream dstStream(stack_); while (numCharsToCopy--) { Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); From 4bbaf28ffcc0db791a8c39e9db09d380d15f9e01 Mon Sep 17 00:00:00 2001 From: ardb-uk <59880669+ardb-uk@users.noreply.github.com> Date: Tue, 12 Oct 2021 16:23:44 +0100 Subject: [PATCH 35/45] Add files via upload --- test/unittest/readertest.cpp | 38 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 057940f..f828dbb 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -2054,61 +2054,61 @@ struct NumbersAsStringsHandlerWChar_t { TEST(Reader, NumbersAsStringsWChar_t) { { const wchar_t* json = L"{ \"pi\": 3.1416 } "; - GenericStringStream> s(json); + GenericStringStream > s(json); NumbersAsStringsHandlerWChar_t h(L"3.1416"); - GenericReader, UTF16<>> reader; + GenericReader, UTF16<> > reader; EXPECT_TRUE(reader.Parse(s, h)); } { wchar_t* json = StrDup(L"{ \"pi\": 3.1416 } "); - GenericInsituStringStream> s(json); + GenericInsituStringStream > s(json); NumbersAsStringsHandlerWChar_t h(L"3.1416"); - GenericReader, UTF16<>> reader; + GenericReader, UTF16<> > reader; EXPECT_TRUE(reader.Parse(s, h)); free(json); } { const wchar_t* json = L"{ \"gigabyte\": 1.0e9 } "; - GenericStringStream> s(json); + GenericStringStream > s(json); NumbersAsStringsHandlerWChar_t h(L"1.0e9"); - GenericReader, UTF16<>> reader; + GenericReader, UTF16<> > reader; EXPECT_TRUE(reader.Parse(s, h)); } { wchar_t* json = StrDup(L"{ \"gigabyte\": 1.0e9 } "); - GenericInsituStringStream> s(json); + GenericInsituStringStream > s(json); NumbersAsStringsHandlerWChar_t h(L"1.0e9"); - GenericReader, UTF16<>> reader; + GenericReader, UTF16<> > reader; EXPECT_TRUE(reader.Parse(s, h)); free(json); } { const wchar_t* json = L"{ \"pi\": 314.159e-2 } "; - GenericStringStream> s(json); + GenericStringStream > s(json); NumbersAsStringsHandlerWChar_t h(L"314.159e-2"); - GenericReader, UTF16<>> reader; + GenericReader, UTF16<> > reader; EXPECT_TRUE(reader.Parse(s, h)); } { wchar_t* json = StrDup(L"{ \"gigabyte\": 314.159e-2 } "); - GenericInsituStringStream> s(json); + GenericInsituStringStream > s(json); NumbersAsStringsHandlerWChar_t h(L"314.159e-2"); - GenericReader, UTF16<>> reader; + GenericReader, UTF16<> > reader; EXPECT_TRUE(reader.Parse(s, h)); free(json); } { const wchar_t* json = L"{ \"negative\": -1.54321 } "; - GenericStringStream> s(json); + GenericStringStream > s(json); NumbersAsStringsHandlerWChar_t h(L"-1.54321"); - GenericReader, UTF16<>> reader; + GenericReader, UTF16<> > reader; EXPECT_TRUE(reader.Parse(s, h)); } { wchar_t* json = StrDup(L"{ \"negative\": -1.54321 } "); - GenericInsituStringStream> s(json); + GenericInsituStringStream > s(json); NumbersAsStringsHandlerWChar_t h(L"-1.54321"); - GenericReader, UTF16<>> reader; + GenericReader, UTF16<> > reader; EXPECT_TRUE(reader.Parse(s, h)); free(json); } @@ -2117,7 +2117,7 @@ TEST(Reader, NumbersAsStringsWChar_t) { std::wstringstream ss(json); WIStreamWrapper s(ss); NumbersAsStringsHandlerWChar_t h(L"314.159e-2"); - GenericReader, UTF16<>> reader; + GenericReader, UTF16<> > reader; EXPECT_TRUE(reader.Parse(s, h)); } { @@ -2126,9 +2126,9 @@ TEST(Reader, NumbersAsStringsWChar_t) { for(int i = 1; i < 320; i++) n1e319[i] = L'0'; n1e319[320] = L'\0'; - GenericStringStream> s(n1e319); + GenericStringStream > s(n1e319); NumbersAsStringsHandlerWChar_t h(n1e319); - GenericReader, UTF16<>> reader; + GenericReader, UTF16<> > reader; EXPECT_TRUE(reader.Parse(s, h)); } } From 060c348ea09a4b11df12342a7cb5995a94059c4e Mon Sep 17 00:00:00 2001 From: jedwards Date: Fri, 29 Oct 2021 15:31:10 -0700 Subject: [PATCH 36/45] use softintrin on arm64ec --- include/rapidjson/internal/biginteger.h | 4 ++++ include/rapidjson/internal/diyfp.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/include/rapidjson/internal/biginteger.h b/include/rapidjson/internal/biginteger.h index 514a176..af48738 100644 --- a/include/rapidjson/internal/biginteger.h +++ b/include/rapidjson/internal/biginteger.h @@ -19,7 +19,11 @@ #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && defined(_M_AMD64) #include // for _umul128 +#if !defined(_ARM64EC_) #pragma intrinsic(_umul128) +#else +#pragma comment(lib,"softintrin") +#endif #endif RAPIDJSON_NAMESPACE_BEGIN diff --git a/include/rapidjson/internal/diyfp.h b/include/rapidjson/internal/diyfp.h index a40797e..f7d4653 100644 --- a/include/rapidjson/internal/diyfp.h +++ b/include/rapidjson/internal/diyfp.h @@ -25,7 +25,11 @@ #if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER) #include +#if !defined(_ARM64EC_) #pragma intrinsic(_umul128) +#else +#pragma comment(lib,"softintrin") +#endif #endif RAPIDJSON_NAMESPACE_BEGIN From 53602ec6bb8e19fc46f34d3028f3f9ff2af1192e Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Wed, 17 Nov 2021 09:31:22 +0000 Subject: [PATCH 37/45] Sanitize the code in schema.h --- include/rapidjson/schema.h | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index f0759ff..248632b 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -2349,7 +2349,8 @@ RAPIDJSON_MULTILINEMACRO_END if (!valid_) return false; \ if ((!BeginValue() && !GetContinueOnErrors()) || (!CurrentSchema().method arg1 && !GetContinueOnErrors())) {\ RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\ - return valid_ = false;\ + valid_ = false;\ + return valid_;\ } #define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ @@ -2388,34 +2389,46 @@ RAPIDJSON_MULTILINEMACRO_END bool StartObject() { RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); - return valid_ = !outputHandler_ || outputHandler_->StartObject(); + valid_ = !outputHandler_ || outputHandler_->StartObject(); + return valid_; } bool Key(const Ch* str, SizeType len, bool copy) { if (!valid_) return false; AppendToken(str, len); - if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) return valid_ = false; + if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) { + valid_ = false; + return valid_; + } RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); - return valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); + valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); + return valid_; } bool EndObject(SizeType memberCount) { if (!valid_) return false; RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); - if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) return valid_ = false; + if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) { + valid_ = false; + return valid_; + } RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); } bool StartArray() { RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); - return valid_ = !outputHandler_ || outputHandler_->StartArray(); + valid_ = !outputHandler_ || outputHandler_->StartArray(); + return valid_; } bool EndArray(SizeType elementCount) { if (!valid_) return false; RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); - if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) return valid_ = false; + if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) { + valid_ = false; + return valid_; + } RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); } From 5b242b6b2d63b8d5cb422fbe740cdfce3edca3b5 Mon Sep 17 00:00:00 2001 From: Adam Calhoon Date: Tue, 8 Feb 2022 12:00:56 -0500 Subject: [PATCH 38/45] Fix the alignment of placement new buffer for GenericValue. When using operator[] on a GenericValue type clang-tidy complains, appropriately, about the alignment of the buffer used for placement-new of the "dummy" GenericValue. --- include/rapidjson/document.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index e2cc600..9279474 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1235,8 +1235,8 @@ public: // return NullValue; // Use static buffer and placement-new to prevent destruction - static char buffer[sizeof(GenericValue)]; - return *new (buffer) GenericValue(); + static GenericValid buffer; + return *new (reinterpret_cast(&buffer)) GenericValue(); } } template From 88bbd87ddd28f45a8b8a983365becfa923174783 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Wed, 9 Feb 2022 10:48:05 +0800 Subject: [PATCH 39/45] Revert "Fix the alignment of placement new buffer for GenericValue." --- include/rapidjson/document.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 9279474..e2cc600 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1235,8 +1235,8 @@ public: // return NullValue; // Use static buffer and placement-new to prevent destruction - static GenericValid buffer; - return *new (reinterpret_cast(&buffer)) GenericValue(); + static char buffer[sizeof(GenericValue)]; + return *new (buffer) GenericValue(); } } template From 1dff2abff78ddb0105c7bc0629816ad779f921e2 Mon Sep 17 00:00:00 2001 From: Adam Calhoon Date: Tue, 8 Feb 2022 22:29:15 -0500 Subject: [PATCH 40/45] Fix the alignment of placement new buffer for GenericValue. When using operator[] on a GenericValue type clang-tidy complains, appropriately, about the alignment of the buffer used for placement-new of the "dummy" GenericValue. --- include/rapidjson/document.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index e2cc600..1cdc29c 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1235,8 +1235,8 @@ public: // return NullValue; // Use static buffer and placement-new to prevent destruction - static char buffer[sizeof(GenericValue)]; - return *new (buffer) GenericValue(); + static GenericValue buffer; + return *new (reinterpret_cast(&buffer)) GenericValue(); } } template From 9965ab37f6cfae3d58a0a6e34c76112866ace0b1 Mon Sep 17 00:00:00 2001 From: agate-pris Date: Fri, 18 Mar 2022 19:22:19 +0900 Subject: [PATCH 41/45] Allow the macro RAPIDJSON_DEFAULT_ALLOCATOR to be used in any namespace `RAPIDJSON_DEFAULT_ALLOCATOR` uses names in the namespace `RAPIDJSON_NAMESPACE`. Replace this with a name starting in the global namespace. --- include/rapidjson/document.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 1cdc29c..41f92e8 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -75,7 +75,7 @@ class GenericDocument; User can define this to use CrtAllocator or MemoryPoolAllocator. */ #ifndef RAPIDJSON_DEFAULT_ALLOCATOR -#define RAPIDJSON_DEFAULT_ALLOCATOR MemoryPoolAllocator +#define RAPIDJSON_DEFAULT_ALLOCATOR ::RAPIDJSON_NAMESPACE::MemoryPoolAllocator<::RAPIDJSON_NAMESPACE::CrtAllocator> #endif /*! \def RAPIDJSON_DEFAULT_STACK_ALLOCATOR From 79d7a448e93b449815ab8db7822d7828e48e83f1 Mon Sep 17 00:00:00 2001 From: agate-pris Date: Fri, 18 Mar 2022 19:32:01 +0900 Subject: [PATCH 42/45] Allow the macro RAPIDJSON_DEFAULT_STACK_ALLOCATOR to be used in any namespace RAPIDJSON_DEFAULT_STACK_ALLOCATOR uses names in the namespace `RAPIDJSON_NAMESPACE`. Replace this with a name starting in the global namespace. --- include/rapidjson/document.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 41f92e8..dd29f69 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -85,7 +85,7 @@ class GenericDocument; User can define this to use CrtAllocator or MemoryPoolAllocator. */ #ifndef RAPIDJSON_DEFAULT_STACK_ALLOCATOR -#define RAPIDJSON_DEFAULT_STACK_ALLOCATOR CrtAllocator +#define RAPIDJSON_DEFAULT_STACK_ALLOCATOR ::RAPIDJSON_NAMESPACE::CrtAllocator #endif /*! \def RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY From 386d31ab69c38e3798200d21eb02ea62c98c4591 Mon Sep 17 00:00:00 2001 From: agate-pris Date: Fri, 18 Mar 2022 19:34:11 +0900 Subject: [PATCH 43/45] Allow access to the template parameter StackAllocator in the GenericDocument Add the typedef declaration `StackAllocatorType` to the class template `GenericDocument`. This allows the user to access the template parameter `StackAllocator`. --- include/rapidjson/document.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index dd29f69..74089cb 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -2486,6 +2486,7 @@ public: typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. typedef GenericValue ValueType; //!< Value type of the document. typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef StackAllocator StackAllocatorType; //!< StackAllocator type from template parameter. //! Constructor /*! Creates an empty document of specified type. From 3988c5e25e37e9e399e5408dbdec1595ff45b71f Mon Sep 17 00:00:00 2001 From: Leonid Terenin Date: Wed, 6 Apr 2022 09:58:56 +0900 Subject: [PATCH 44/45] fix shadowed variable --- include/rapidjson/reader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index b37afff..5e5a917 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -1433,7 +1433,7 @@ private: class NumberStream : public NumberStream { typedef NumberStream Base; public: - NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {} + NumberStream(GenericReader& reader, InputStream& s) : Base(reader, s), stackStream(reader.stack_) {} RAPIDJSON_FORCEINLINE Ch TakePush() { stackStream.Put(static_cast(Base::is.Peek())); From 6b500986c4c8d3f7fb276013be9e2d17177301fb Mon Sep 17 00:00:00 2001 From: Leonid Terenin Date: Wed, 6 Apr 2022 10:42:40 +0900 Subject: [PATCH 45/45] fix shadowed variable, take 2 --- include/rapidjson/reader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 5e5a917..5554660 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -1459,7 +1459,7 @@ private: class NumberStream : public NumberStream { typedef NumberStream Base; public: - NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {} + NumberStream(GenericReader& reader, InputStream& s) : Base(reader, s) {} RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } };