make std::string optional

This commit is contained in:
Steve Hanson 2021-06-08 10:53:10 +01:00
parent 494447b731
commit 28bcbd3f35
6 changed files with 1003 additions and 404 deletions

View File

@ -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<typename Ch>
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<unsigned>(*s1) < static_cast<unsigned>(*s2) ? -1 : static_cast<unsigned>(*s1) > static_cast<unsigned>(*s2);
}
//! Returns number of code points in a encoded string.
template<typename Encoding>
bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) {

View File

@ -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<EncodingType>(GenericStringRef<Ch>(t->name, t->length)));
@ -576,7 +575,7 @@ public:
// Error: unresolved token
if (unresolvedTokenIndex)
*unresolvedTokenIndex = static_cast<size_t>(t - tokens_);
return UriType();
return UriType(allocator_);
}
return base;
}

View File

@ -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<SchemaType*>(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<EncodingType> 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)) {

View File

@ -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 <typename ValueType, typename Allocator=CrtAllocator>
class GenericUri {
public:
template <typename ValueType, typename Allocator=CrtAllocator>
class GenericUri {
public:
typedef typename ValueType::Ch Ch;
#if RAPIDJSON_HAS_STDSTRING
typedef std::basic_string<Ch> 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<Ch>(uri));
}
// Use with specializations of GenericValue
template<typename T> GenericUri(const T& uri) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_() {
Parse(uri.template Get<String>());
template<typename T> GenericUri(const T& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
const Ch* u = uri.template Get<const Ch*>(); // TypeHelper from document.h
Parse(u, internal::StrLen<Ch>(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<Ch>(uri.c_str()));
}
#endif
// Use with specializations of GenericValue
template<typename T> void Get(T& uri, Allocator& allocator) {
uri.template Set<String>(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<SizeType>(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<SizeType>(base_.length()); }
const Ch* GetFragString() const { return frag_.c_str(); }
SizeType GetFragStringLength() const { return static_cast<SizeType>(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<typename T> void Get(T& uri, Allocator& allocator) {
uri.template Set<const Ch*>(this->GetString(), allocator); // TypeHelper from document.h
}
const Ch* GetString() const { return uri_; }
SizeType GetStringLength() const { return internal::StrLen<Ch>(uri_); }
const Ch* GetBaseString() const { return base_; }
SizeType GetBaseStringLength() const { return internal::StrLen<Ch>(base_); }
const Ch* GetSchemeString() const { return scheme_; }
SizeType GetSchemeStringLength() const { return internal::StrLen<Ch>(scheme_); }
const Ch* GetAuthString() const { return auth_; }
SizeType GetAuthStringLength() const { return internal::StrLen<Ch>(auth_); }
const Ch* GetPathString() const { return path_; }
SizeType GetPathStringLength() const { return internal::StrLen<Ch>(path_); }
const Ch* GetQueryString() const { return query_; }
SizeType GetQueryStringLength() const { return internal::StrLen<Ch>(query_); }
const Ch* GetFragString() const { return frag_; }
SizeType GetFragStringLength() const { return internal::StrLen<Ch>(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<Ch>(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<SizeType>(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<Ch*>(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<Value> Uri;
typedef GenericUri<Value> Uri;
RAPIDJSON_NAMESPACE_END

View File

@ -659,36 +659,37 @@ static const char kJsonIds[] = "{\n"
TEST(Pointer, GetUri) {
typedef std::basic_string<Value::Ch> 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) {

View File

@ -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<Value::Ch> String;
typedef GenericUri<Value, MemoryPoolAllocator<> > UriType;
MemoryPoolAllocator<CrtAllocator> allocator;
typedef GenericUri<Value, MemoryPoolAllocator<> > UriType;
MemoryPoolAllocator<CrtAllocator> 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<Value::Ch> 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<Value::Ch>(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<UTF16<> > Value;
typedef GenericUri<Value, MemoryPoolAllocator<> > UriType;
MemoryPoolAllocator<CrtAllocator> 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<Value::Ch> 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<Value::Ch>(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<Value::Ch> String;
typedef GenericUri<Value, MemoryPoolAllocator<> > UriType;
typedef GenericUri<Value> 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<UTF16<> > Value;
typedef GenericUri<Value> 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__)